Two guards for the 2026-07-09 wrong-tree/evidence-loss incident:
wckn: pawl_tip_coherence refuses (exit 3, fix-and-re-run) when the review
target commit cites bead ids sharing the reviewed bead's prefix and none is
the bead or a dotted relative - a verdict can no longer bind a tree that
belongs to a different bead. Positive-mismatch only; PAWL_ALLOW_TIP_MISMATCH=1
opts out. 7 bats.
np1e: evidence transcripts and membrane catches now root at the CANONICAL
checkout (git-common-dir parent) via pawl_evidence_dir/pawl_canonical_root,
so reviews run in disposable land worktrees no longer write artifacts that
die with 'git worktree remove', and ledger evidence_path never dangles.
5 bats. Both libs registered in validate-embedded-sync + embedded copies synced.
Claude-Session: https://claude.ai/code/session_01BkgdP88SiccCSyYdi8GHNa
Kills the operator-error class that fired ~4x on 2026-07-07, each costing a wasted
review round: after the pawl auto-binds a #trivial provenance commit on the feat, a
'git commit --amend' folds the fix INTO the bind — HEAD becomes a #trivial commit
carrying code, the reviewer's trivial-waiver walk steps PAST it to review the STALE
original feat, and the verdict binds the wrong tree (surfacing only as an opaque
'hidden non-trivial commit' gate failure at push).
scripts/lib/pawl-amend-guard.sh (new, sourceable, unit-tested): pawl_amend_guard
returns 2 when HEAD is a trailing-#trivial commit changing non-provenance files,
printing the diagnosis + the rebuild-don't-amend recovery. Wired EARLY into
pawl-review.sh (scope=head, before the review — where it saves the round) and as a
land-time backstop in pawl-land.sh; embedded pawl bundle + Makefile + embed validator
updated. Safe: provenance-only #trivial binds, plain feats, and mid-subject #trivial
mentions all pass; opt out PAWL_NO_AMEND_GUARD=1. bats 5/5; guard proven to fire on
the trap and stay silent on legitimate binds; race suite green.
THE bottleneck fix. On 2026-07-07, closing skill-doc changes took 4-7 model-review
rounds each because ~90% of the defects were DETERMINISTIC (format/link/twin/
gate-hint) — but the expensive cross-family verdict was requested BEFORE the local
deterministic checks were green, so the membrane re-discovered them serially, each
round a full re-review + re-land.
scripts/lib/pawl-preflight.sh (new, sourceable, unit-tested): pawl_preflight <scope>
<repo> runs the reviewed repo's deterministic battery before the reviewer and fails
fast (exit 3, same fix-and-re-run disposition as the --smoke/REFUTED path) on a
CONFIRMED-red battery — WITHOUT spending a reviewer round (0 tokens). Command:
PAWL_PREFLIGHT_CMD if set (operator owns exit semantics), else the default
'ao gate check --fast --scope head' (ao resolved AO_BIN -> repo cli/bin/ao -> PATH,
mirroring check-pawl-pre-push.sh). Wired into pawl-review.sh after the HEAD snapshot,
before packet assembly + the meter clock.
SAFETY — pure ACCELERATOR, never a false blocker: a nonzero that cannot be CONFIRMED
as a real gate run (RCE trust-guard, missing/stale ao, build error — distinguished by
the absence of the gate's 'checks —' summary marker) is SKIP-and-proceed, not RED.
Worst case it no-ops and behavior is byte-identical to today (the pre-push gate still
backstops). Disabled for PAWL_UNTRUSTED_REPO=1 (never run a repo's own battery over an
untrusted checkout) and scope != head (no committed HEAD). Opt out: PAWL_NO_PREFLIGHT=1.
Converts the inert prose lesson 'run the battery before the verdict' into MECHANISM
(the skills audit proved prose lessons are inert). bats 9/9; embedded pawl bundle +
Makefile sync + embed-sync validator updated; Go embed test green.
Ship gen-membrane-receipts.sh + check-membrane-receipts-freshness.sh in the
embedded pawl bundle (make sync-hooks + //go:embed all:pawl) so a stranger repo
can render its own membrane-receipts proof page with no AgentOps checkout.
ao verify receipts mirrors the pawl review trust split: in-checkout dogfood runs
the live repo generator; anywhere else it extracts the EMBEDDED generator and
runs it against the user's OWN git repo under the sanitized cold env
(pawlReviewColdEnv) with AO_BIN pinned to the running binary — no ao-on-PATH
needed, no repo-local script trusted. The generator refuses the build-from-cli/
fallback when PAWL_UNTRUSTED_REPO=1 (compiling+running an untrusted repo's
cmd/ao would be RCE); the in-repo path keeps its fallback. Fail-closed
chain-verify-before-render and the count-parity check are unchanged.
Byte-identical to the current generator on this repo's ledger (behavior lock).
Bats acceptance in a throwaway repo with a production-writer-built ledger:
render + tamper-refusal + embedded==in-repo parity.
# Conflicts:
# docs/cli-surface.json
# docs/cli-surface.md
S2 — decouple the guaranteed cross-family verb from the AgentOps source checkout
so a fresh external user can run the catch on their OWN non-AgentOps repo.
Before: ao pawl review died on a stranger's repo with 'locating AgentOps repo root'
before ever reaching codex (S0). Now: a GENUINE in-checkout run (the running ao binary
lives inside the resolved checkout — aoBinaryInside) uses the live scripts (EDGE 1);
everyone else (installed ao, or forged markers) extracts the embedded pawl bundle
(go:embed cli/embedded/pawl/, sibling scripts/+schemas/ for EDGE 2) and runs it against
the user's own git repo via AGENTOPS_REPO_ROOT + PAWL_NO_SERVICE. cwd stays the user's
repo so the read-only codex refuter can READ the changed files for large diffs.
SECURITY — the cross-family pawl caught a sequence of real defects on this commit:
- TRUSTED PATH: the stranger run's PATH drops every entry the repo could control — '',
'.', any relative entry, AND any absolute dir INSIDE the repo (e.g. $PWD/bin on PATH).
So a bare codex/git/jq/timeout can never resolve a repo-planted binary. Go-side bash
resolves on the same trusted PATH; repo-root discovery is PURE Go (.git walk, no binary)
so no planted git runs during discovery.
- resolve_ao no longer searches $REPO_ROOT/cli/bin/ao; live path gated on aoBinaryInside
(forged markers can't trigger it); pawl-verdict.sh emits + resolve_ao use ONLY the
absolute pinned AO_BIN when untrusted. In-checkout behavior unchanged (flags unset).
- INTEGRITY: the refuter runs codex with project_doc_max_bytes=0 so a repo-planted
AGENTS.md cannot hijack the reviewer into a fail-open VERDICT: CONFIRMED; the cold env
neutralizes BASH_ENV/ENV so non-interactive bash cannot source a repo file.
- ROOT PRE-RUN: ao's root PersistentPreRunE runs git in the cwd (RepairSharedCore
WorktreeConfig) before any command; that git is now resolved via worktreeconfig.
trustedGitBin — absolute, excluding '.'/relative/repo-internal PATH dirs — so a
repo-planted git can't run at startup on the stranger path (CLI-wide).
- GIT DIFF HELPERS: rendering an untrusted-repo diff (git show/diff) is hardened with
--no-ext-diff --no-textconv -c core.fsmonitor= (+ GIT_EXTERNAL_DIFF= in the cold env)
so a repo's diff.external / .gitattributes textconv driver can't run code pre-review.
Packaging/resolution + resolution-safety only — no review LOGIC/gate/daemon change.
Tests: embed-parity gate (+ validate-embedded-sync.sh extended); extract-layout (schema
sibling, EDGE 2); cold-env seam contract; trustedPATH unit test; gitToplevel-no-exec test;
an L2 e2e running the verb in a throwaway non-AgentOps repo with codex stubbed (no
resolveAgentsRepoRoot error, commit-bound verdict written into the USER's repo, schema-
validated check passes); RCE regression tests — planted cli/bin/ao + cli/ao + ./ao + ./codex
(with '.' first in PATH) + BASH_ENV that must never run, and a forged-markers repo that must
route to embedded. Verified on the real binary against a fresh stranger repo: CONFIRMED +
verdict written + merge authorized, exit 0.
## Main is red — hotfix
`cli/embedded/skills/standards/references/{go,test-pyramid}.md` were
**empty (0 bytes)** on main HEAD; #776's merge truncated them.
`contracts-sync` / `correctness` run the full embedded-sync check on
main-push (changed-files-scoped on PRs, so it slipped through during the
batch drain) → main Validate failed (run on 3df599df, def8fc9b).
## Fix
- Re-synced both embedded files from their sources via the
`cli/Makefile` recipe (`cp ../skills/standards/references/*`).
`scripts/validate-embedded-sync.sh` now reports **All embedded files are
in sync**.
- Corrected that script's stale fix hint: `make sync-embedded` (no such
target) → `make sync-hooks` (the real recipe).
## Evidence
`bash scripts/validate-embedded-sync.sh` → "All embedded files are in
sync."
Bounded-context: BC5-Runtime
Evidence: cli/embedded/skills/standards/references/go.md