Scoped adaptation of PR #2716: keep compact rendering as the default and require ECC_STATUSLINE_USAGE=1. Installer registration, managed aliases and the Codex background bar remain outside this slice.
8a7d6129 added the external-contract question for every code change that
handles data and updated the pinned tests to match, including the legacy
test "code condensed Edit hint for an unknown profile is unchanged". Its own
evaluation then found the gated arm did not improve (25 to 23 of 48), so the
question has no evidence behind it as a default.
- Restore the test expectations 8a7d6129 changed: questionIdsFor, the
byte-identical code text for src/a.py, the condensed hints, and the hook
question lists default to data-schema without external-contract.
- Add questionIdsFor/condensedHintFor cases with { externalContract: true }.
- Add hook cases: GATEGUARD_EXTERNAL_CONTRACT_QUESTION=1/true/ON asks it
beside data-schema and in the unknown-profile set, never without data;
unset, 0, false and off keep the original four.
Against main: target-class 5 failures, fact-force 10 failures.
Each of these is credited on main, reproduced against upstream 2d515e4f:
- rg -t py / -T js / --type-not js, fd -e py, and the Grep tool's
type: "py" credit an Edit of tokenizer.js. Type and extension values
are skipped as plain value flags, so they never filter.
- Glob **/tokenizer.py, rg tokenizer src/*.py, and
git grep tokenizer -- '*.py' credit tokenizer.js. The Glob pattern is
never a filter, and a glob operand scopes as its literal directory.
- Glob **/tokenizer.js, find -name tokenizer.js, rg -g tokenizer.js and
Grep glob: "tokenizer.js" credit tokenizer.js, although each search
could only find the target itself.
- An Edit of "// note<U+2028>foo(1);" to "// note<U+2028>foo(2);" passes
as comment-only, although JavaScript ends the comment at U+2028 and
foo(2) runs.
Scenario 21-bypass-filter-forms adds ten must-deny steps and four
controls. Four hook controls that asserted exact-name lookups still
credit (find -name widget.py, Grep glob widget.py, Get-ChildItem
-Filter widget.py) now assert the lookup is denied; each keeps its
original purpose, that the exclusion parser leaves a positive include
alone, through a wildcard include (widget.*).
Replace JSON-interpolated child JavaScript with fixed preload and summarizer programs. Pass paths and dates through dedicated fixture environment variables; use a fixed quoted exec wrapper so configured executable and script paths remain data. Preserve real Date.now, deadlines, calendar rollover, captured resume metadata and no-next-day duplicate assertions. Actual13/0 Node18.20.8, Node20.20.2 with Bun setting and Node24.9.0; actual13/0 with spaces/apostrophes/literal dollar in fixture paths; next-day negative control12/1; lint and diff checks pass. Addresses CodeQL393/394/395 without suppression or dismissal.
The Stop hook captures its session filename date before waiting for a slow summary, while the fixture previously recomputed it after timeout. Advance only the fixture calendar when the fake summarizer starts and assert persistence under the captured date, matching resume metadata, and no next-day duplicate. Keep Date.now, child delay and lifecycle deadlines unchanged. Actual workflow baseline and corrected fixture13/0; mismatched-day lookup negative control12/1; Node20 with Bun setting and Node18.20.8 both13/0; lint and diff checks pass.
Isolate spawned Stop hooks through TMPDIR/TMP/TEMP, open transcript fixtures exclusively with private modes, and derive intentional stale-race fixture identity from its opened descriptor.
Isolate deadline fixtures from inherited dry-run/disabled hook flags. Remove an unapproved emoji from the new routing regression to satisfy Unicode validation.
Greptile P1: sedScriptParts stopped at -- and discarded the remaining
operands, so 'sed -- <script>' skipped script collection entirely. GNU sed
4.9 (verified): with no -e/--expression anywhere, the first operand after --
is the script and an s///e in it still executes.
The operands after -- are now collected as positionals, so the existing
first-positional-becomes-script rule applies. Operands after the script stay
file names (data). 2 regression tests pin both sides. Suite: 812/812.
Greptile P1 findings (both verified by running GNU sed against the hook):
1. Escaped replacements evade the check: GNU sed decodes \xHH, \oOOO, \dNNN,
case operations and unknown escapes like \g before executing an s///e
replacement or e operand. Scanning the raw text let \x67it decode to git.
Decoding that surface faithfully is out of scope, so any backslash in an
exec-bearing fragment now throws UnsupportedSedEscape and the hook blocks
with an explicit reason — mirroring the existing env -S escape precedent
instead of pretending to interpret sed. Escaped-newline continuations of an
e operand are refused by the same check.
2. Continued commands went unchecked: the e operand reader stopped at every
newline, but GNU sed joins escaped newlines into one operand. Now covered
by the same conservative refusal.
Also decomposes the sed readers into sub-50-line helpers per the repository
function-size rule (AGENTS.md): readSedDelimited, readSedCommand,
readSedExecute, readSedLineOperand, assertSedLiteral.
5 regression tests pin the escape paths and the benign boundary (a backslash
in a non-executing script stays data). Suite: 810/810.
CodeRabbit security review (Major, CWE-693) correctly identified that GNU sed
collects -e/--expression scripts from any argv position: a script option after
positional operands is still executed, so stopping the option walk at the first
positional let an e-flagged script hide behind it.
Verified against GNU sed 4.9 before fixing:
- 'sed -e s/x/y/ - -e s/y/X/e' runs X (the later -e executes)
- with any script option present, every positional is a file operand
sedScriptParts now walks the full argv, collects scripts from all -e/
--expression positions, and only promotes the first positional to the script
when no script option was given. Unknown options after positionals still fall
back to the opaque default.
4 regression tests pin the reported bypass paths (suite: 805/805).