The fresh-context adversarial pass caught a live bug in this branch's own
performance fix: gstack-wtree exported GIT_INDEX_FILE BEFORE resolving the
real index path, so `git rev-parse --git-path index` returned the temp index
itself, the stat-cache copy self-copied and failed, and every invocation fell
back to the full re-hash — the fast path was dead code (verified with bash -x).
Resolution now happens before the export; measured 0.08s per call on this repo.
Also fixed: careful fails to an ASK (not silence) when its own helper file is
missing (same partial-install state freeze already defends against); the
--source label is sanitized inside the envelope lib (newline-stripped,
sentinel-defused, length-capped — it sits in trusted framing); the HIGH rm
tokenizer skips redirections/backgrounding/`--` (rm -rf / 2>/dev/null now
denies) and knows ${HOME}; user pattern lines starting with a dash work
(grep --); greptile bodies carry per-comment id headers inside the envelope so
multi-comment PRs stay attributable (ids verified against raw metadata, never
trusted in-body); the release-body tripwire fails CLOSED when its input files
are missing (separate-shell $$ reality); land 3.5b gets the same allow-paths
as ship; the "either side dirty" fallback leftover is gone from both grading
surfaces; the evidence pump races drain against error (EPIPE consumers can't
hang the wrapper); an unset HOME skips bookkeeping instead of creating a
literal ~ dir inside the repo; a write-failure log ends with a visible marker;
freeze expands a literal leading ~ in the boundary; review-log documents its
log-time binding window.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Specialist army findings, all quote-verified before fixing:
Security: careful force-push guard now catches git's plus-refspec force
syntax (git push origin +main carried force with no flag — silently allowed
before) and refspec-form targets (HEAD:main); default-branch matching is
tokenized FIXED-STRING comparison on the full branch path (slashed defaults
like release/2.0 work; no ERE interpolation), glob-safe via noglob. HIGH rm
tier is tokenized too: trailing long options (--no-preserve-root) and /* are
root-class. Stored evidence fingerprints are 40-hex re-validated before
reaching git argv. normalizeForDetection sweeps ALL Unicode format chars
(\p{Cf}: soft hyphens, bidi marks, tag chars) instead of five enumerated
zero-widths. The wiring scanner gains flagless gh pr/issue view patterns. The
release-body banner tripwire diffs against the fetched original so a hostile
pre-existing banner string can't permanently DoS doc updates. Ship/land
evidence checks now pass --expect-cmd (a green `echo ok` recorded under the
label can never mint FRESH); package.json stays allow-listed with the
residual documented.
Performance: gstack-wtree seeds its temp index by COPYING the real index
(stat cache preserved — measured 40x faster than read-tree seeding, identical
hash) with read-tree fallback; evidence uses findLast and one gstack-slug
spawn; the stream pump honors backpressure via drain; careful's pattern block
short-circuits before slug resolution when no pattern file exists.
Testing: the gh-failure envelope test was VACUOUS (killing PATH killed the
bun shebang before the code under test ran) — replaced with a PATH gh shim
that exercises the real branch, plus shimmed happy paths (issue/pr-body/
unparseable JSON); evidence check --all + empty ledger + non-numeric
--max-age (now a usage error, was silent fail-open) covered; HIGH-tier
variants pinned; hook analytics respect GSTACK_HOME so tests stop writing the
operator's real skill-usage.jsonl.
Maintainability: dead exit ternary removed; flagValue deduped into
bin-context; sentinel defusal derived from the banner constants (no invisible
literals — \u escapes only); scratch-repo git fixture extracted to
test/helpers/scratch-repo.ts (one hermetic incantation, three consumers);
shared gstack_hook_log_fire in hook-extract.sh; the dashboard/land diff-scoped
row lists are aligned (codex-review) and drift-pinned.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Review records now bind to the content they were made on. bin/gstack-review-log
stamps every appended record with commit_full, tree, dirty (informational) and
wtree — a working-tree fingerprint from the new bin/gstack-wtree (temp index
seeded from HEAD + git add -A + write-tree). The binding fields are computed
authoritatively; caller-supplied values for those keys are ignored, so a stale
rendered template or a forged field can't bind a record to content it wasn't
made on.
Why a working-tree fingerprint instead of HEAD^{tree}: committing identical
content doesn't change it (a record made on a dirty tree stays valid after the
same content is committed), untracked new source files DO change it (new code
can't hide from freshness), and gitignored scratch stays out. Rebase, amend
and squash with identical content grade CURRENT instead of stale.
Grading: the dashboard (scripts/resolvers/review.ts) and /land-and-deploy Step
3.5a apply a content-first rule to diff-scoped review rows — wtree match with
both sides clean is CURRENT, full stop. Plan-tier reviews grade a plan file,
not the repo tree, so they keep the 7-day logic (optional plan_sha256 caller
field noted). The rev-list fallback no longer errors when the stored commit
was rebased away: it grades UNKNOWN and treats it as stale.
bin/gstack-review-read emits ---WTREE---/---TREE---/---DIRTY--- so graders
consume one tool output. Old records without wtree fall back to the existing
heuristics; no migration.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>