Adversarial review (Claude subagent) found a CRITICAL the specialist pass missed:
- F1 (CRITICAL): 'Human:'/'Assistant:' turn-prefixes bypassed BOTH the write-time
denylist AND datamark(), landing verbatim in agent context inside the trusted
ACTIVE DECISIONS fence. Add 'human:' (+ 'disregard previous', 'from now on') to
the shared denylist, and have datamark() neutralize Human:/Assistant:/System:/User:
turn-prefixes (ZWSP) at the render boundary.
- F2: datamark() only stripped ASCII C0; extend to Unicode line terminators
(U+0085/2028/2029) and U+007F so 'strip newlines' actually holds.
- F3: validateDecide blocked only HIGH secrets; MEDIUM-tier PII (e.g. SSN) persisted
silently and synced cross-machine. The store is non-interactive (no confirm path),
so fail closed on MEDIUM too.
- F4: compact() was a lock-free read-modify-rewrite that could clobber a concurrent
append (lost decision). Add an O_EXCL compact lock + a pre-rename size recheck that
aborts untouched (skipped=true) if an append landed; caller re-runs.
- F7: filterByScope unknown/garbage scope fell through to 'return true' (leaked into
every context); fail conservative (false).
F5 (pid reuse) and F6 (pgrep over-match) are intentionally left as-is: both fail SAFE
(over-refuse sync); making them precise would introduce a fail-DANGEROUS path
(allowing sync during a real autopilot). True disambiguation needs gbrain to stamp the
lock with a start-time, which gstack doesn't own. F8 (compact moves history to archive)
is by design.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Addresses the pre-landing review findings (all INFORMATIONAL, no criticals):
- security: datamark resurfaced decision text at the render boundary
(lib/gstack-decision.ts datamark() — neutralizes code fences, --- banners,
<|role|>/</system> markers, control chars, newlines). Applied in
gstack-decision-search human output so stored text can't masquerade as
instructions in Context Recovery (codex hardening #3 / AC #7). --json stays raw.
- DRY: extract resolveSlug/gitBranch/flagValue to lib/bin-context.ts; both
decision bins use it instead of duplicating the helpers.
- compact(): batch the archive append (one write, not N) and shrink the
mid-compact crash window; simplify the opaque branch/issue ternary.
- coverage: learnings-log injection rejection (D2A wiring), search --recent/
--scope + NaN-safe --recent, datamark-applied, unparseable lock body,
compact-empty, corrupt-snapshot degrade.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Two bins mirroring gstack-learnings-* (D3A). log writes decide/--supersede/--redact/
--compact events + refreshes the bounded snapshot + enqueues for cross-machine sync;
search reads the O(active) snapshot, scope-filtered to current branch, newest-first,
--all to include superseded, --json for machines. Empty store returns silently
(no snapshot write on an empty read).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>