Repository navigation
fix(worktree): never delete or misreport what meta didn't check; remove snapshot - #33
Merged
Merged
Conversation
|
Important
This repository does not receive automatic reviews because it has fewer than 10 stars.
|
gold-silver-copper
force-pushed
the
fix/meta-git-safety
branch
12 times, most recently
from
October 25, 2026 15:50
2c8d33d to
52121ac
Compare
Contributor
|
I would honestly lobby for removing the snapshot command entirely. I don't think anyone uses it. |
gold-silver-copper
force-pushed
the
fix/meta-git-safety
branch
6 times, most recently
from
October 25, 2026 17:38
a8e162c to
eb02b9f
Compare
This was referenced Oct 25, 2026
gold-silver-copper
force-pushed
the
fix/meta-git-safety
branch
from
October 25, 2026 22:59
2f968b5 to
df4d1c0
Compare
Implements [[tasks/harmony-2754]]
Drops the snapshot command, its help text and its test. A leftover `meta git snapshot ...` call now passes through to git, which fails with "'snapshot' is not a git command" and writes nothing. Refs [[tasks/harmony-2754]]
Refs [[tasks/harmony-2754]]
gold-silver-copper
force-pushed
the
fix/meta-git-safety
branch
from
October 25, 2026 23:14
df4d1c0 to
bd49786
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
CLI half of the meta-git safety fixes. Merge together with the PRs linked below (same branch name, so CI builds them together).
What changed in meta_git_cli
worktree remove: requires a store record for the exact path, runs the safety check, refuses repositories that aren't linked worktrees even with--force, lists unsaved work (needs--force, which prints what it discards), and removes exactly the checked members.worktree prune:.and nested aliases count as existing; each candidate is checked without force and deleted withdelete_checked_set; unsafe ones are kept and listed (JSONkept); a missing directory only loses its record; removal failures are listed and exit 1.worktree create:--from-prwith no matching repo or a failed fetch is an error;--from-refthat matches no repo fails; the set directory is claimed atomically and any failure rolls back what the call created (worktrees and new branches); failing to record the worktree is an error.{e:#}), so git's own message reaches the user.snapshot: removed (src/snapshot.rs, the routes inlib.rs, the help text and the plugin's command list inmain.rs, thedialoguerdependency it alone used, and its test).meta git snapshot …now passes through to git and fails.git update/git clone: failed clones are printed to stderr with the last lines of git's output (kept in a bounded buffer while the pipe is drained) and the command exits 1.tests/safety.rs: end-to-end tests through the plugin protocol.Note for the other meta tabs: this touches
commands/worktree/mod.rs(one line),clone_worker.rs,update.rs,clone.rs, and deletessnapshot.rs; whoever merges second may need a small rebase.Linked PRs (same branch
fix/meta-git-safety)Companions that don't depend on the code above (can merge on their own):
Why
A fuzz run on 2026-10-24 (fresh install in
debian:stable) found meta-git commands that delete data they never checked, report failures as success, or panic on bad input. I re-ran every repro on currentmainbefore changing anything; all nine still reproduced. Incidents:incidents/fuzz-2026-10-24-{06,07,13,14,16,17,22,23,32}; task[[tasks/harmony-2754]].worktree removeworktrees_dir: "."orMETA_WORKTREES), and uncommitted work in nested repos (only top-level repos were dirty-checked).gitdir, bare repo,--separate-git-dir, submodule, vanished repo) a checkout git can't inspect, or another recorded set inside it, is refused even with--force; unsaved work is listed and needs--force, which prints what it discards; deletion re-checks the set first (complete git status, commit ids, staged blob ids, and size and mtime of every listed file, not just the summary) and refuses if anything changed, then removes only what was checked (git unregisters each worktree, stopping at the first failure, then the listed loose files if forced, then empty dirs); a symlinked set root is refusedworktree prune--repo .) worktree, or root plus removed children, as orphaned and force-deleted it with its work, in every project (global store); force-deleted when the source project moved.always exists (a set holding it is never orphaned), nested aliases resolve through child.metas; every candidate goes through the same check without force; unsafe ones are kept and listed (JSONkept); a record whose directory is gone only loses the record (only a path that is really gone counts as missing: one that reappears, a dangling link, or one prune can't read is kept); removal failures are listed and exit 1; a record is removed only if it's still the one prune read--ttl18446744073709551615wrapped to -1 → expired at once → prune deleted itworktree createforever--from-pr/--from-reforg/api#5matchedorg/api-legacy(url.contains); no match or fetch failure was only a warning; a ref no repo has printed "✓ Created" for an empty worktreeowner/repomatch on the remote URL; no match or fetch failure is an error with git's text; a ref no repo has is an error and nothing is left behind; only a repo that lacks the ref is skipped (any other failure, including a repo git can't read, fails the create and rolls it back); upstream tracking for--from-ref origin/xis keptmeta git snapshotsnapshot delete ../packagedeletedpackage.json;restoremoved branches back withcheckout -B, stranding later commits silently, said "stashed" for empty stashes and exited 0 on failure; a short SHA panickedmeta_git_lib::snapshot, themeta_snapshot_*MCP tools, help text, docs, skills and tests. A leftovermeta git snapshot …now passes through to git, which fails with "'snapshot' is not a git command" and writes nothinggit update/git cloneThings a reviewer should look at
git worktree remove. Counting them would make plainremoverefuse on almost every worktree (target/,node_modules/), which trains--force, and--forcealso discards real uncommitted work. Two helper reviewers raised this; I kept git's semantics on purpose./dev/null, so--jsonoutput is unaffected.worktree removeof a set whose source repo was moved or deleted now refuses even with--force(meta can't attribute the checkout to a repo); the message says so, and the user can delete the directory themselves. The same applies to a set holding a submodule checkout or any repository inside an ignored directory (e.g. a.gitunder.venv/src/): meta refuses rather than guess.worktree createis all-or-nothing now. Becauseremoveonly deletes recorded worktrees, a create that fails partway rolls back what it made: it claims the set directory with an atomicmkdirfirst (so it can never roll back another create's set), deletes the set only if the safety check is clear, and deletes only the branches its owngit worktree add -bcreated (git refuses-bfor an existing branch, so success proves ownership), and only while they still point at the commit they were created at (a post-checkout hook may have committed on one) and git would let them go (not checked out, rebased or bisected anywhere); the deletion is an atomicupdate-ref -d <ref> <expected>. Since that commit came from another ref, a deleted branch never strands commits. A branch that appeared although the add failed is kept and named. Failing to write the store record is an error instead of a warning.prune --strictexits 1 when it keeps any worktree, consistent with strict mode elsewhere.created_branch(the "(new)" label and the store field) now means "this create made the local branch", which includes a tracking branch made fromorigin/<branch>.mainof gitkb/meta is only consistent once all of them are in. The harmony, loop_cli and agent PRs don't depend on the code above and can merge on their own.meta git snapshotis removed, not fixed (owner's decision). The guard's denial messages and the destructive-commands rule now say to commit or stash first (a backup branch keeps only commits) and recover commits withgit reflog. Not changed:agent scorestill grades "snapshot safety" (25% of the score, countingmeta git snapshot createbefore destructive commands); that metric needs its own decision in gitkb/agent. Copies of the old rule and skills checked into gitkb/gitkb and ttyeye (from an earliermeta init) still mention snapshot; re-runningmeta initthere after release refreshes them.How it was verified
Rebased onto meta-shell-args (root meta 47eeca3: commands run as argv,
--include/--excludename whole repos). The child rebases were clean; the one conflict was main's edit to.context/tasks/meta-snapshot.md, which this set deletes. Main's new--includerefusal inmeta_git_cliand a README line and bats test still namedgit snapshot; they no longer do. Themeta_execexamples indocs/agent_workflows.mdnow pass argv lists (["sh", "-c", ...]where a shell is needed), matching main's MCP schema. Everything below was re-run after the rebase.Every fuzz repro re-run (adapted to an isolated
HOMEunder~/.cache/claude-tmp/, never a real meta or harmony checkout), onmainand on this branch. Onmainall nine findings reproduce; on this branch each worktree, clone and TTL finding is refused, reported or rejected as described above, and the three snapshot findings are gone with the command (meta git snapshot create xexits 1 with git's "not a git command" and writes nothing). For 17, this machine's default pipe buffer is 256 KB (the fuzz container's was 64 KB), so the repro used 2 MB.End-to-end tests in
meta_git_cli/tests/safety.rsdrive themeta-gitbinary through the plugin protocol: 15 tests, one or more per remaining finding. Withmain's sources and these tests, all of them fail (the hook test hits its 120 s timeout); on this branch all pass. Unit tests cover the rest (safety check, duration overflow,owner/repomatching, TTL saturation, branch in use, clone failure tracking).cargo testformeta_git_lib(167),meta_git_cli(76 + 15 + 4),meta-mcp(22) andmetapass, andcargo clippy --all-targets -D warningsis clean. Locally twometaregistry tests (*_fails_outside_workspace) fail only because my temp dir is under a$HOMEthat has~/.meta; this PR doesn't touch that code, and CI runs them. Root batsgit,help,worktreeandworktree_edge_cases: 143/143 with an isolatedHOME. gitkb/agent:cargo test(126) andtests/guard.batspass.Reviewed by two read-only helper reviewers over several rounds, then repeated final passes by four fresh read-only reviewers until all four said yes on green CI; the snapshot removal was reviewed the same way.
Try it in a scratch meta repo: