Skip to content

fix: review-1 findings — legacy-fleet compat + self-referential REAL_GIT foot-gun#2

Merged
suzuke merged 1 commit into
mainfrom
fix/review-1-extraction-compat
Jul 4, 2026
Merged

fix: review-1 findings — legacy-fleet compat + self-referential REAL_GIT foot-gun#2
suzuke merged 1 commit into
mainfrom
fix/review-1-extraction-compat

Conversation

@suzuke

@suzuke suzuke commented Jul 4, 2026

Copy link
Copy Markdown
Owner

Fixes the three findings from adversarial review #1 (REJECTED):

  1. Heartbeat trailer driftcommit_is_empty_heartbeat now tolerates both Agentic-* and legacy Agend-* trailer generations (main.rs heartbeat scan). Regression: empty_heartbeat_with_legacy_agend_trailers_detected_review1.
  2. Self-referential REAL_GITresolve_real_git canonicalize-compares the env value against current_exe and ignores it when they match (falls back to the self-excluding PATH search). README "Trying it" ordering fixed + annotated. Regression: self_referential_real_git_env_ignored_review1 (repro was exit 70 recursion-guard FATAL; now exit 0).
  3. PowerShell hook fallbackprepare-commit-msg.ps1 now falls back to AGEND_INSTANCE_NAME/AGEND_HOME like the bash hook.

Review board: t-20260704140553348828-14440-9 · re-review dispatched to fugu.

🤖 Generated with Claude Code

…GIT foot-gun

Adversarial review #1 (fugu, codex backend) REJECTED the extraction with
three findings; all fixed with regression coverage:

1. Heartbeat trailer drift: commit_is_empty_heartbeat only accepted the
   renamed Agentic-* trailer keys, so a legacy fleet's hooks (which write
   Agend-*) would silently stop having their heartbeats recognized by
   init-pile cleanup. Now tolerates BOTH trailer generations.
2. Self-referential REAL_GIT: README's original env ordering captured the
   shim itself into AGENTIC_GIT_REAL_GIT (command -v git after the PATH
   prepend), which resolve_real_git trusted verbatim -> self-exec loop
   killed only by the #1504 depth cap (exit 70). resolve_real_git now
   canonicalize-compares the env value against current_exe and falls back
   to the self-excluding PATH search when they match; README ordering
   fixed and annotated.
3. prepare-commit-msg.ps1 read only the AGENTIC_GIT_* env — no legacy
   fallback, breaking the zero-daemon-change adoption claim on Windows.
   Now mirrors the bash hook's AGEND_INSTANCE_NAME/AGEND_HOME fallbacks.

Considered and rejected: dual-accepting Agentic-*/Agend-* in the bash
hook's idempotence grep — hooks are installed per-home by exactly one
orchestrator generation, so mixed-generation trailers in one commit do
not occur in either deployment.

cargo test --workspace: 85 unit + 13 phase2 (both +1 review1 regression)
all green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@suzuke suzuke merged commit 19782f5 into main Jul 4, 2026
2 checks passed
@suzuke suzuke deleted the fix/review-1-extraction-compat branch July 4, 2026 14:17
suzuke added a commit that referenced this pull request Jul 4, 2026
* fix: make agentic-git publishable + close 3 run bugs + smoke tests

Adversarial code review (fugu) of main found 3 real defects; all fixed with
regression coverage, plus a CI publish gate and end-to-end smoke tests.

1. **agentic-git crate was not publishable** (the real cause of the failed
   release run). `cli.rs`/`shim_phase1.rs` did `include_str!("../../../assets/
   hooks/…")` — workspace-ROOT assets that `cargo package` does not put in the
   crate tarball, so the verify build failed. Move `assets/hooks/` INTO the
   crate (`crates/agentic-git/assets/`), fix the 6 include paths. `cargo
   package -p agentic-git` now verifies clean.
2. **`run` could launch UNGUARDED.** `spawn_agent`'s child PATH used
   `join_paths(...).unwrap_or(path_env)`; a home path containing ':' made
   join fail → silent fallback to the real PATH → the agent's git bypassed the
   shim entirely. Now a hard EX_CONFIG refusal.
3. **`.agend-managed` defeated skip-when-clean.** The provisioned marker is an
   untracked file, so every fresh session's tree looked dirty and clean-tree
   destructive ops still snapshotted. `is_clean` now treats a tree whose only
   untracked entry is the marker as clean.

Also: **smoke tests** (`tests/smoke.rs`) drive the shipped binary through the
real `agentic-git run` flow (routing→trailer→snapshot→recover→deny) + pin #2/#3;
**CI `cargo package` gate** (both crates, verify build) so #1 can't regress;
**release.yml `set +e`** so `bash -e` no longer aborts the idempotent publish
on an already-uploaded crate.

cargo test --workspace: 150 green (+3 smoke); clippy clean; both crates package.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix: allow the documented snapshot restore through the shim (fugu hunt #4)

fugu verified the 3 packaging/run fixes, then found a real usability
blocker: the recovery layer's OWN documented restore —
`git checkout <snapshot-ref> -- .` — was DENIED by the shim. The
cross-branch guard reads args[1] as a branch target, so a snapshot ref
tripped 'cross-branch — cannot switch to refs/agentic-git/snapshots/...',
leaving snapshots un-restorable unless the user knew to set
AGENTIC_GIT_BYPASS=1. The recovery feature was effectively broken for a
docs-following user.

Fix: a `checkout <tree-ish> -- <pathspec>` is a working-tree path restore,
not a branch switch (the bound branch is unchanged), so the cross-branch
guard must not deny it. Recognize the `--` pathspec form and skip the
cross-branch check for checkout.

+smoke test run_session_documented_restore_works_without_bypass_smoke: full
real-session round-trip (dirty+untracked → reset --hard snapshot → delete
untracked → `git checkout <snap> -- .` through the shim, NO bypass → both
recovered).

Also harden legacy_env_adoption's bypass assertion: check the deny SIGNATURE
('cross-branch'), not the loose word 'denied' (the #2158 bypass audit dumps
process ancestry to stderr, which a parent command's text could false-trip).

cargo test --workspace: green; clippy clean.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(snapshot): force a self-contained identity in commit-tree (Linux CI)

The new smoke tests + Linux CI caught a real cross-platform bug invisible on
macOS: on a runner/container/fresh machine with NO git user.name/user.email
(ubuntu-latest has none; macOS derives a default), the snapshot's
`git commit-tree` — which REQUIRES an author+committer — silently failed,
so a real session's destructive op created NO snapshot (SNAPS=0, fail-open),
i.e. the recovery layer didn't work exactly where it's supposed to. And the
agent's own `git commit` failed (TRAILER=no).

Fix: the snapshot forces a fixed GIT_AUTHOR/COMMITTER_NAME+EMAIL
('agentic-git <agentic-git@localhost>') alongside the dates it already
forces, making snapshots environment-independent. The smoke session also
sets an identity so the agent's own commits succeed on bare runners.

Verified by reproducing the failure on macOS with GIT_CONFIG_GLOBAL=/dev/null
+ empty HOME: smoke now passes (previously SNAP=NONE, as on ubuntu CI).

cargo test --workspace: green; smoke green under a no-git-identity env.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
suzuke added a commit that referenced this pull request Jul 8, 2026
…embedder P1a) (#24)

* feat(core): consolidate signing/verify primitives + scheme envelope (embedder P1a)

P1a moves the security contract into agentic-git-core so signer and verifier share
ONE source (no drift). Isolated to the agentic-git repo — the agend-terminal daemon
wiring + daemon signer swap is P1b.

- Move `ensure_key` (lock-free first-writer-wins `hard_link` provisioning) + its
  `open_new_0600` helper verbatim from the run CLI into core as the single key
  primitive; core gains a `getrandom` dep. The run CLI now calls
  `core::ensure_key` / `core::sign_binding` (its local copies removed).
- Add `sign_binding` = ensure_key + sign — closes the reviewer4 hole where the
  low-level `sign` panicked on a fresh home.
- `verify` becomes a typed `Result<(), VerifyError{MissingKey | MalformedTag |
  MacMismatch | UnsupportedScheme{..}}>` (was `bool`). It parses a self-describing
  tag ENVELOPE (`ag-hmac-sha256:v<algo>:<key-format>:<hex>`) BEFORE the MAC check,
  so an HMAC scheme skew is diagnosable — the shim maps `UnsupportedScheme` to a
  LOUD deny (`verify_sidecar`) instead of a silent "unbound".
- Add `BINDING_FORMAT_VERSION` / `HMAC_ALGO_VERSION` consts and `envelope_tag`
  (the "can produce" capability).

ORDERING INVARIANT (fleet-safety — the emphasized landmine): the DEFAULT emit stays
the BARE hex tag (implicit scheme v1), byte-identical to the pre-P1a signer, so an
unswapped shim and every on-disk sidecar keep verifying. The envelope is a
capability core can parse + produce, NOT the default output (that flips in P2).
RED-first proven: making `sign_binding` emit the envelope fails the byte-identical
test against an independent python-HMAC oracle.

reviewer4 acceptance conditions (RED-first): #2 a malformed `ag-hmac-sha256:`
prefixed tag NEVER falls back to bare-hex legacy (extra colon / non-`v` algo /
non-hex / unknown key-format -> MalformedTag/UnsupportedScheme); #3 a newer scheme
or any tamper NEVER authenticates; #4 bare-hex legacy accepted (the migration
window). (#1 lockfile-single-source is P1b — agend-terminal.)

Regression: the run CLI's concurrent first-writer-wins is preserved — the existing
session_mode race tests (driving the real `run` binary) stay green through the core
reroute, plus a direct core-level N-thread race test.

cargo test (workspace) 0-failed; clippy --workspace --all-targets clean; cargo
package -p agentic-git-core green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: Huang ChiaCheng (黃家政) <1557604+suzuke@users.noreply.github.com>

* fix(release): bump agentic-git-core to 0.2.0 + package interdependent crates together (reviewer4 D1)

P1a added new public API (`VerifyError`, `ensure_key`, `sign_binding`) + a breaking
`verify` bool->Result change to agentic-git-core WITHOUT bumping its version, so the
version/package contract was incoherent: `cargo package -p agentic-git` resolves
`agentic-git-core = "0.1.0"` from crates.io (the OLD published API) and fails to
compile the new call sites — the PR's ubuntu CI failure. A released binary depending
on published core 0.1.0 could not build with the new code.

- Bump the workspace (core) version 0.1.0 -> 0.2.0 — a 0.x minor bump for the new +
  breaking API, exactly what the workspace comment prescribes ("core bumps when its
  API changes"). The shim keeps its own independent 0.2.2.
- Update the binary's dep requirement to `agentic-git-core { path, version = "0.2.0" }`.
- CI: package the two interdependent crates in ONE `cargo package -p
  agentic-git-core -p agentic-git --allow-dirty` so the binary's verify build
  resolves core from the just-packaged sibling (build-from-workspace), NOT the older
  published registry version. No crates.io publish happens (the release-time publish
  stays manual / operator-gated).

dev3 VERIFIED the crypto surface and reviewer4 confirmed no crypto fail-open — this
is purely the package/version contract fix; no core logic changed.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: Huang ChiaCheng (黃家政) <1557604+suzuke@users.noreply.github.com>

---------

Signed-off-by: Huang ChiaCheng (黃家政) <1557604+suzuke@users.noreply.github.com>
Co-authored-by: Huang ChiaCheng (黃家政) <1557604+suzuke@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant