fix(agents-vault): repair slug migration, keep the network off the launch path
Six findings from the whole-branch review, all of which end in the same place: a backup tool reporting success while nothing was backed up. Slug migration nested the old entry inside the new one. The clear before the rename was gated on the destination's claude/memory subdirectory rather than on the destination itself, so an entry that exists without one survived, `git mv A B` moved A *inside* B, and the mkdir below fabricated a fresh empty memory directory for the live link to point at. The real memory ended up one level deeper than --status and --restore ever look, and the run returned 0. That shape is not exotic: git cannot track an empty directory, so an entry committed while its memory was empty comes back from a clone as projects/<slug>/origin and nothing else -- and cloning the vault is this feature's own recovery path. The destination is now moved aside the way --adopt already does it rather than deleted (widening the rm -rf would have destroyed the clone's origin log), its provenance is folded into the migrated entry, and every failure path rolls back and reports. The launch path pulled over the network. Both wrappers call agents-vault synchronously before starting an agent, and the pull in the shared sync helper was unguarded once an upstream existed: against a blackholed remote it blocked the launch indefinitely and then aborted the commit, so an offline laptop silently stopped being backed up at all. Committing never needed a remote, so the pull moved to the push path, which was already opt-in for exactly this reason. A failure there now distinguishes a real rebase conflict (rebase-merge/ or rebase-apply/ present) from an unreachable remote instead of calling both a conflict, and both network calls set GIT_TERMINAL_PROMPT=0 and GIT_ASKPASS so they fail fast rather than prompt with nobody watching. The helper still refuses to commit a rebase in progress, and leaves it standing rather than aborting one it did not start. This also restores agents-init's pre-refactor ability to commit while offline. The agy knowledge copy was unfiltered. The allowlist held at the agy root and nowhere below it, so a planted .credentials.json inside knowledge/ was committed verbatim while the documentation promised nothing new upstream added could leak in. Only *.md and *.json are copied now -- which is what the store actually holds -- so lock files, transcripts and conversation databases are excluded by having no business in a backup rather than by being known about. The scaffolded .gitignore also ignored only the SQLite sidecars and not the databases, which is worse than ignoring neither: a torn database landed in history with the write-ahead log that would have completed it deliberately excluded. Both changes are template-only, on a feature that has never shipped. agents-init reported success when nothing was committed. It ended on a branchless `if` with no arm for a failed commit, which fish resolves to 0 -- the same false zero already fixed in agents-vault, left in the function the refactor was rewriting. It now has the arm and an explicit final status. The --adopt forward-failure path with no stash left a raw coreutils `mv:` line and no statement that the adopt had been abandoned cleanly; it is branded like every other error exit in the function. Tests: the suite now clones a vault with git and runs agents-vault against the clone, instead of trusting hand-built fixtures to have shapes git can actually produce -- that blind spot shipped both of the merge blockers. The "present but empty" migration fixture is rebuilt as the origin-only directory a clone leaves behind, with the hand-built shape kept as a separate case. Reverting each fix drops the suite from 285 to 279 (migration), 261 (network), 275 (knowledge allowlist) and 283 (agents-init status).
This commit is contained in:
@@ -53,11 +53,16 @@
|
||||
#
|
||||
# With no flags, runs both --agents and --plugins setup; --agents re-runs
|
||||
# only the AGENTS.md / symlink step and --plugins only the plans/specs/
|
||||
# devlogs wiring step. Managed paths are added to .gitignore. The sub-repo
|
||||
# is pulled first when it has an upstream, and at the end of every
|
||||
# invocation any uncommitted changes inside it are auto-committed so
|
||||
# agent-made edits are captured automatically. Fully idempotent: a second
|
||||
# run produces no output and no new commits.
|
||||
# devlogs wiring step. Managed paths are added to .gitignore. At the end
|
||||
# of every invocation any uncommitted changes inside the sub-repo are
|
||||
# auto-committed so agent-made edits are captured automatically. Fully
|
||||
# idempotent: a second run produces no output and no new commits.
|
||||
#
|
||||
# The commit is local only. Nothing here fetches or pushes: the wrappers
|
||||
# call this synchronously before starting an agent, and a network round
|
||||
# trip there blocks the launch until an unreachable remote times out and
|
||||
# can prompt for credentials with nobody watching. A sub-repo that has an
|
||||
# upstream is pulled by hand, on the user's own schedule.
|
||||
#
|
||||
# Called automatically by the claude and agy wrappers on every invocation.
|
||||
#
|
||||
@@ -71,7 +76,8 @@
|
||||
#
|
||||
# EXIT STATUS
|
||||
# 0 Setup completed successfully
|
||||
# 1 Fatal error (git init failed, move failed, etc.)
|
||||
# 1 Fatal error (git init failed, move failed, the AGENTS/ commit was
|
||||
# rejected, or an unresolved rebase blocked it)
|
||||
#
|
||||
# EXAMPLE
|
||||
# agents-init
|
||||
@@ -452,14 +458,27 @@ function agents-init --description 'scaffold AGENTS/ sub-repo with agent spec fi
|
||||
end
|
||||
|
||||
# ──────────────────────── Auto-commit AGENTS/ ────────────────────────────
|
||||
# Pulls first when an upstream is configured (no-op for local-only repos)
|
||||
# and refuses to commit a failed rebase's conflict markers.
|
||||
# Purely local: no fetch, no push. This function runs synchronously on
|
||||
# every agent launch, and a network round trip there blocks the launch
|
||||
# for as long as an unreachable remote takes to time out. Committing
|
||||
# never needed one -- see _agents_repo_sync.
|
||||
#
|
||||
# Every way the commit can fail is an arm of its own. A sync that did
|
||||
# not commit means agent-made edits were not captured, so it is a
|
||||
# failure rather than a line to walk past -- and the missing `-ne 0`
|
||||
# arm was not a cosmetic gap: fish resolves a branchless `if` to 0, so
|
||||
# a hook-rejected commit fell straight through to a reported success.
|
||||
set -l msg "chore: sync AGENTS repository"
|
||||
test $did_init -eq 1; and set msg "chore: initialize AGENTS repository"
|
||||
set -l sync_out (_agents_repo_sync "$agents_dir" "$msg")
|
||||
set -l sync_rc $status
|
||||
set -l failed 0
|
||||
if test $sync_rc -eq 2
|
||||
echo "$c_warn→ AGENTS/ has an unresolved rebase conflict; nothing committed$c_reset" >&2
|
||||
echo "$c_warn→ AGENTS/ has an unresolved rebase; nothing committed$c_reset" >&2
|
||||
set failed 1
|
||||
else if test $sync_rc -ne 0
|
||||
echo "$c_err""Error: the AGENTS/ commit failed; nothing recorded$c_reset" >&2
|
||||
set failed 1
|
||||
else if test -n "$sync_out"
|
||||
set changed 1
|
||||
test $verbose -eq 1; and echo "$c_ok$sync_out$c_reset"
|
||||
@@ -473,4 +492,8 @@ function agents-init --description 'scaffold AGENTS/ sub-repo with agent spec fi
|
||||
echo "$c_ok→ Synced AGENTS scaffolding$c_reset"
|
||||
end
|
||||
end
|
||||
|
||||
# Explicit, because the branchless `if` above resolves to 0 and would
|
||||
# otherwise be this function's exit status.
|
||||
test $failed -eq 0
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user