mirror of
https://github.com/garrytan/gstack.git
synced 2026-11-01 14:34:42 +01:00
2d42e4ece834a7fa2c16edda87ec136af6db4c26
16
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
50d020ad9d |
v1.91.85.0 fix(cso,deploy): /cso on Windows Git 2.56 and macOS Docker, zsh-safe loops, honest deploy stops (Wave 9 Lane D) (#3134)
/cso sets GIT_CONFIG_GLOBAL=/dev/null on every platform (Git for Windows 2.56 rejects NUL), shared by childEnvironment and the eval and producer Git environments, with a Windows-safe test that `git config --global --list` is empty. On macOS the local Docker socket resolves without opening it when Bun's realpathSync throws EOPNOTSUPP; Linux admission and the local-Unix-only rule are unchanged. /test-audit and /ship loop over file lists zsh does not word-split, with a static test against `for x in $VAR` in generated bash. /setup-deploy stops and writes nothing when nothing is deployed yet; /land-and-deploy runs a recorded command trigger only with explicit approval before claiming revision proof. TODOS.md records the open P2/P3 bugs by area, the Wave 9 parked items and the feature themes. Closes #3082. Adopts #3094, #3095, #3096 and #3109. Co-authored-by: marcode <244097767+vilano93@users.noreply.github.com> Co-authored-by: Nathan <196341+nathanshox@users.noreply.github.com> Co-authored-by: mikehasa <91774998+mikehasa@users.noreply.github.com> Co-authored-by: Andrey <95643518+Alfabit7@users.noreply.github.com> |
||
|
|
5b9b8372c4 |
feat: honest work and gate integrity — gate-edit detector, review/ship gate pass, zero-run green, claims-need-evidence kernel (#3114)
* docs(designs): honest work and gate integrity plan (autoplan-reviewed) Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * test(e2e): review-gate-integrity rule case registered in touchfiles and the PR profile Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * feat(honesty): claims-need-evidence kernel, no_claim card field, CEO value test, implementer dispatch rules, ACCEPTANCE_EDITED, Honest Work ethos Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * feat(gate-diff): gate-edit detector lib + CLI, patch fixtures, calibration script, tests_ran in gstack-evidence Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix(gate-diff): RH-15? only on CI/config floors after calibration Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * feat(review,ship): Gate Integrity category, GATE_SCAN_BLOCK, one-way gate disposition, actor stamping, ZERO-RUN lanes, draft-while-open Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * chore: re-measure parity and context budgets on the integrated tree Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * test(touchfiles): gate-diff fixtures and helpers select review-gate-integrity Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix: archaeology lint wording, paid-file census, harness-version non-behavioral record Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix: pin review Step 3.5 rename, plan-path placeholder, mktemp template, per-stream evidence tail Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * test: ACCEPTANCE_EDITED fence substitutes the <plan-path> placeholder Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix(gate-diff): shallow boundary commits report no_base instead of a whole-tree inventory; recalibrate Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> --------- Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> |
||
|
+8 |
28f1385eac |
v1.91.32.0 fix: Codex works from zsh, free text never enters shell commands, honest failures (#3057)
* fix: sourced helpers locate themselves under zsh (#3024) Skill blocks source bin/gstack-codex-probe and bin/gstack-egress-lib.sh into the host's shell. Under zsh (macOS default) BASH_SOURCE is empty, so Codex model selection resolved /../scripts/... and every outside pass reported CODEX_MODEL: invalid; the egress lib resolved to the caller's cwd. Resolve the file once at source time (BASH_SOURCE under bash, %x under zsh), and replace the bash-only ${!name} indirection in the auth probe. A free test derives every sourced helper from the generated skills and sources each under bash and zsh. Co-authored-by: marcosdetry <marcosdetry@users.noreply.github.com> Co-authored-by: akshaykalia <akshaykalia@users.noreply.github.com> Co-authored-by: scottgigante-hubflow <scottgigante-hubflow@users.noreply.github.com> Co-authored-by: woodsonl <woodsonl@users.noreply.github.com> Co-authored-by: bezdekt <bezdekt@users.noreply.github.com> * fix: reply and issue templates never pass untrusted text in double quotes (#3046) greptile-triage's reply APIs posted -f body="<reply text>" while the templates quote SHAs and diff code in backticks; in double quotes bash ran each span (replies posted as 'Fixed in .', and diff or reviewer text ran as a command). The pre-existing-failure issue template and /spec's issue title had the same shape. All three now go through quoted heredocs. The generated-bash lint gains untrusted-in-quotes and now also covers runtime reference docs in skill directories (review/greptile-triage.md). Co-authored-by: dgrant <dgrant@users.noreply.github.com> * fix: a Codex usage limit is its own state, not 'inconclusive; proceeding' (#3051) The model probe matched only model and broken-install errors, so a usage or rate limit fell through to MODEL_PROBE_INCONCLUSIVE: Codex's own message (with the reset time) was dropped, the hint pointed at model config, and every later skill re-probed for 30 s. The probe now returns MODEL_QUOTA_EXHAUSTED (exit 4) on a failed, non-timeout call that names a usage limit, rate limit, insufficient_quota or 429, relays Codex's line verbatim and caches it for 15 minutes. Preflights map it to CODEX_MODE: quota_exhausted (missing coverage, no further Codex calls), and classifyOutsideReview reports a mid-run limit as unavailable (quota) with its own troubleshooting anchor. Co-authored-by: venkatareddyg <venkatareddyg@users.noreply.github.com> * test: record ship/review union sizes after the quota branch and issue template * fix: Codex watchdog freezes the command before reaping its children The bash-native watchdog (stock macOS, no timeout binary) ran pkill -P on the command's children and then killed the command. Between the two, the command continued past its killed child and printed output after its deadline. Stop it first. The test's minimal PATH also lacked pkill, which the watchdog uses to reap children (stock macOS ships it), so the orphaned child held stdout open and the capture waited out its sleep. * fix(autoplan): an oversize session journal is a hard too_large denial, not a retryable identity failure (#3050) The guard capped journal reads at 32 MiB and reported a longer journal as 'identity', which the hook treats as deny-and-retry. A journal only grows, so /autoplan could never enter Phase 1 in a long session, and the troubleshooting row sent people chasing symlinks. Oversize journals now report too_large as a hard cause that names the size and the supported fallback (run the reviews by hand or start a new session). Co-authored-by: venkatareddyg <venkatareddyg@users.noreply.github.com> * fix(deps): clear the weekly OSV findings (#3040) Raise overrides to fixed releases: ip-address 10.7.3 (our own pin was the vulnerable 10.3.1; reachable through socks in browse's proxy path), fast-uri 3.1.8, hono 4.13.13, proxy-addr 2.0.8, @modelcontextprotocol/sdk 1.31.0, sharp 0.35.5, and in lib/diagram-render dompurify 3.4.16 (bundled by mermaid) and source-map-js 1.2.2; the render page is rebuilt. Three advisories without a usable fix get justified, expiring ignores: braces (build-time only, excalidraw -> sass -> chokidar; a test checks the page carries no chokidar code), sprintf-js (roarr's constant-format logger) and katex (needs pre-existing prototype pollution; fix outside mermaid's range). osv-scanner with the repo config: no issues found. * fix(autoplan): name a shell-built snapshot init as the cause of the missing-evidence denial (#3045) The guard binds a phase entry only to a literal snapshot-init Bash call. When the model resolved the tool path into a variable, chained or piped the call, init ran and succeeded but the denial said 'Complete the existing snapshot init step', which reads as already done, so the run gave up. When no init binds but a Bash call looks like one, the denial now says the shape cannot be bound and prints the literal re-run (answers reused:true). The binding rule is unchanged, and the skill says to run init on its own. Co-authored-by: CharlesBerg85 <CharlesBerg85@users.noreply.github.com> * fix(browse): chain reads piped stdin synchronously, so Windows runs the flow (#3039) On Windows, an awaited Bun.stdin.text() inside the CLI's un-awaited main() let the process exit 0 before reading stdin: a replayed flow reported success with nothing sent to the daemon. Read fd 0 synchronously (skipped for a terminal); empty input now gets the server's usage error and a non-zero exit. Co-authored-by: VaibDeveloper <VaibDeveloper@users.noreply.github.com> * fix(browse): clearer config-mismatch refusal and a WebGL note for the macOS GPU flags (#3030) A plain call refused because another session started the daemon with --headed/--proxy was told to 'apply --proxy/--headed', flags it never passed. It now says who started the daemon and offers both remedies. The BROWSER.md env table notes that the default macOS GPU flags disable WebGL and that GSTACK_DISABLE_GPU=off restores it for WebGL QA. Co-authored-by: Hagetino <Hagetino@users.noreply.github.com> * fix(ship): skip Greptile triage when no PR exists yet (#3020) /ship's first run on a branch dispatched a triage subagent only to learn there was no PR. Step 10 now checks for a PR inline (gh's 'no pull requests found') and records that Greptile runs on the PR once it exists; other gh errors still dispatch so the child can report unavailable. Co-authored-by: dgrant <dgrant@users.noreply.github.com> * fix(router): route only to loaded skills, and say when not to invoke (#3018) The root router listed every skill and ended 'When in doubt, invoke the skill'. Current Claude models follow that literally: they tried to invoke skills a user had turned off in Claude Code's skillOverrides (which gstack's own disabled_skills render cannot see), and borderline requests tipped into full workflows. The router now routes only to skills in the session's available-skills list and names when to answer directly; the CLAUDE.md routing block skill-start offers says the same. Co-authored-by: Rekin226 <Rekin226@users.noreply.github.com> * fix: spawned sessions never auto-grant consent or publish off the machine (#3016) Spawned sessions auto-choose the recommended option except for destructive or irreversible choices. Main already withholds the artifacts-sync consent prompt from spawned sessions; the remaining gap was any other prompt whose recommended option grants a consent or syncs data off the machine. Every spawned directive (skill-start's block, the escape sentence, the Conductor deny reason and the AUQ-error fallback) now names that as irreversible. A consent the user already gave interactively stays in force. Co-authored-by: ekson73 <ekson73@users.noreply.github.com> * test: fix the spawned-block slice and type the #3045 re-init events * fix(redact): TypeScript types and JSX expressions are code, not env.kv secrets (#3048) A credential-named parameter or prop with an unquoted code value (`session: SessionState,`, `key={turn.requestId + turn.role}`, `apiKey={settings.apiKey}`) cleared the entropy gate and blocked pushes as a MEDIUM env.kv finding. One rule replaces the PR's two keyword heuristics: a value that reads as a code name (no digits, word-like camelCase or dotted parts) with a code terminator, or a brace expression of such names with no quoted literal, is source syntax. Paired controls keep firing on quoted, brace-wrapped, concatenated, YAML-bare and random-letter literals. Co-authored-by: tianxchen <tianxchen@users.noreply.github.com> * fix(setup): refuse to register a hook that does not parse (#3032) Claude Code runs gstack's hook shims through /bin/sh; a shim that does not parse exits 2, which for a PreToolUse hook blocks the tool call in every session until someone finds the file. Before any hook registration is healed or written, setup now runs bash -n on each registered shim and a Bun parse of its TypeScript entry, and stops with the file, line and fix. A lean rewrite of the PR's idea (one gate block plus a test that runs it). Co-authored-by: BenjaminDSmithy <BenjaminDSmithy@users.noreply.github.com> * fix(browse): Windows runtime roots start the server from the source checkout's bundle (#3026) On Windows a host runtime root (~/.codex/skills/gstack and the other env-var hosts) holds file copies of browse/dist and no node_modules, so its server-node.mjs could not import playwright and every browser skill died (the module-scope server.ts half was fixed in v1.91.19.0). setup now records the source checkout in every runtime root's .source-path, and the CLI runs that checkout's bundle when the adjacent one cannot reach playwright. Verified by unit layout tests and the Windows CI lane, not live on Windows. Co-authored-by: ubay887 <ubay887@users.noreply.github.com> * fix(copilot): on Windows, run bash blocks in Git for Windows Bash (#3047) Copilot CLI on Windows ran gstack's extensionless helpers straight from PowerShell, which opened an app picker, and bare bash can resolve to WSL. The Copilot tool glossary (rendered into every Copilot skill) now says to run each bash block in Git for Windows Bash by its full path and never to translate a block into PowerShell. The PR's PowerShell repair skill and README section are not taken. Co-authored-by: nareshh74 <nareshh74@users.noreply.github.com> * test: record ship's union size after the Greptile no-PR check * fix: keep issue numbers out of model-read Copilot text; seed the new routing sentence in the autoplan fixture * docs: Oct 6 fix-wave plan, reviewed through /autoplan * fix(autoplan): every oversize-journal path reports too_large with a recovery that keeps the work Review follow-up to the #3050 fix. The summed batch read and the narrowed parent loop now report too_large with the real size instead of a plain error naming a hard-coded 32 MiB. The denial names the size and the cap, and gives the recovery that keeps work (/context-save, new session, /context-restore, /autoplan) with a docs anchor. A test seam that can only lower the cap replaces the 33 MiB fixture. * test(autoplan): negative controls and more shapes for the shell-built init denial * fix(redact): decide TS/JSX source syntax by file context, not by value shape Review follow-up to the #3048 fix. The word-shape heuristic also exempted real secrets made of words (API_KEY=VelvetRiverOrbitSunset; in a .env). The scanner now takes the file path (ScanOptions.sourcePath): the pre-push hook scans the pushed diff per file with its +++ b/ path, and gstack-redact --from-file passes its path. Only in a TypeScript/JSX file is an unquoted value after ':' or a JSX brace of names treated as source; the same line in .env/YAML, or with no path, is scanned as a literal, and the jwt pattern still fires in .ts files. Co-authored-by: tianxchen <tianxchen@users.noreply.github.com> * docs: gstack's own CLAUDE.md routing block matches the new router wording Live check (Claude Code 2.1.284): a skill set to "off" in skillOverrides is absent from the session's skill list while its sibling is listed, and both are listed without the override, so 'route only to skills in your available-skills list' holds. * fix(sync-gbrain): don't offer a call-graph build for a source with no code pages (#3029) Step 3.5 offered --dream for any source with pages, including one that holds only markdown (synced with gbrain's default strategy), where a build cannot help, and the injected CLAUDE.md guidance told future sessions to run --dream for any count: 0. On gbrain >= 0.60, code-def for an impossible symbol answers .status (probed live against gbrain 0.60.96: ready for a code source, out_of_scope for a markdown-only one). Step 3.5 now probes the pinned source first, reports the cause instead of asking on out_of_scope, and keeps today's offer when the probe is unknown (older gbrain, an error). gstack's own sync uses --strategy code, so the PR's soft-delete claim does not apply to gstack's path. Co-authored-by: syong <syong@users.noreply.github.com> * fix(setup): register the hooks that parse, refuse the rest with file:line (#3032) The landed gate stopped setup before any registration, so one unparseable hook left every other hook unregistered. On the Claude install the canonical root is the checkout Claude Code runs hooks from, so there is no staged swap to abort: setup now registers the hooks that parse, refuses the ones that do not with file and line, finishes the install and exits non-zero, saying it is a gstack bug and that a skipped hook keeps running from the checkout. bin/gstack-hook-check derives its list from setup's _hook_command_path calls plus the autoplan phase-publication hook (a test keeps them equal), parses each shim with its shebang's interpreter, and bundles each TypeScript entry with its local imports (packages external) into a temp dir, so a broken or missing import, or a missing entry, fails. Co-authored-by: BenjaminDSmithy <BenjaminDSmithy@users.noreply.github.com> * fix(auto-update): hold an update whose hooks do not parse, before the checkout moves bin/gstack-session-update fast-forwards the live checkout before setup runs, and Claude Code runs hooks straight from it, so setup's gate cannot protect an auto-update. After the Bun floor check the updater extracts the incoming revision with git archive and runs bin/gstack-hook-check on it; a failure holds the update like bun-too-old, records hook-does-not-parse with file:line in the update status, and the next session start says the current hooks keep running. Fixture-remote tests cover a broken shim, a broken imported module and a parseable control. Co-authored-by: BenjaminDSmithy <BenjaminDSmithy@users.noreply.github.com> * fix(browse): chain with no flow is a usage error before any daemon boots (#3039) Empty piped input, a terminal with no arguments, and a readFileSync(0) error (EAGAIN, EOF) now print the chain usage with the cause and its troubleshooting link and exit 1 before ensureServer(), instead of sending an empty argument to a daemon the CLI booted for it. Tests run the real CLI with no daemon and no browser, plus the stub-daemon flow. Co-authored-by: VaibDeveloper <VaibDeveloper@users.noreply.github.com> * fix(browse): refuse a runtime-root CLI and checkout server from different builds (#3026) A Windows runtime root runs the source checkout's Node server bundle through .source-path. When the root's CLI and the checkout's bundle carry different browse/dist/.version builds (the checkout was rebuilt without refreshing the root), the CLI now refuses to start the server with both builds, the ./setup fix and its troubleshooting anchor. Unversioned dev builds are not called skew. Co-authored-by: ubay887 <ubay887@users.noreply.github.com> * ci(windows): real isolated ./setup install, then a piped chain flow through the installed browse windows-setup-e2e built the checkout but never ran ./setup. It now installs the codex host into an isolated HOME (a runtime root copy with no node_modules), then from an unrelated directory pipes a two-command chain flow at a loopback page into the installed browse and asserts the page text, and that empty piped input exits 1 with the usage error. Covers the checkout-bundle path and the piped-stdin path end to end. * test(lint): free text never enters a generated command (structural rule) The generated-bash lint's untrusted-in-quotes rule (gh/glab/git/curl text flags in double quotes only) becomes free-text-placeholder: any <placeholder> anywhere in any command, in double, single or no quotes, and in heredoc bodies (quoted or not), unless it is on the reviewed identifier allowlist (test/helpers/placeholder-allowlist.ts) with the grammar a value must match. Quoted-only identifiers (URLs, selectors, paths printed by gstack) must sit inside quotes. Backslash-newline continuations now read as one command, so a flag and its value on separate lines are still seen together. Co-authored-by: dgrant <dgrant@users.noreply.github.com> * fix: reply, issue and spec text travel as agent-written files, never through a heredoc Replaces the quoted-heredoc bodies with CEO-12's file mechanism: a bash block creates a private mktemp file in the project's .gstack/tmp/ (kept out of git through .git/info/exclude), the agent writes the text with its file-write tool, and the next block posts with -F body=@file, --body-file or "$(cat file)". A heredoc ends early at any body line equal to its delimiter; a file has no such line. When the file was never written the block refuses with the path and the command to send it by hand, and it deletes the file after a successful post. - review/greptile-triage.md: replies; the fetched JSON moves from fixed /tmp/greptile_*.json paths to a mktemp directory; REPO and PR are re-derived per block instead of read from a previous shell. - Test-failure issues: title and body files, platform case on github/gitlab, assignee only when it is a valid login. - /spec: one draft file feeds the audit hash, every redaction scan, the issue and the archive; the archive no longer expands <title>/<body> inside an unquoted heredoc; the decision-log JSON is built by jq from files. Location verified against Claude Code 2.1.284 (Dockerfile.ci pin): project-local .gstack/tmp is writable under acceptEdits with no prompt and keeps mode 0600; $TMPDIR and the gstack state root prompt even under acceptEdits. Co-authored-by: dgrant <dgrant@users.noreply.github.com> * fix(ship): Greptile triage runs only when a PR exists; every other case says why PR: unknown (gh missing, not logged in, a non-GitHub remote, or a failed lookup) used to dispatch the triage child anyway. The Step 10 check now tests gh, its login, the remote and the PR in that order, and every case but 'PR: exists' skips the dispatch and records 'Greptile: not run (<reason>); runs on the PR once it exists'. Co-authored-by: dgrant <dgrant@users.noreply.github.com> * fix: question-tuning commands carry no free text The question log took the summary in single quotes and the preference write took the user's own words; an apostrophe ended the quote and the rest ran as shell. The summary is now a kebab-case slug (an identifier with a grammar), and the optional free_text field is no longer passed from skills (the binary still accepts it). * fix(cso): Windows finds docker.exe only under known-folder roots and says transport is unsupported (#3028) On Windows /cso looked for docker only in Git's directory and System32, so Docker Desktop was never found and the gap read as 'not installed'. It now resolves Program Files, Program Files (x86) and the Windows directory through PowerShell's [Environment]::GetFolderPath (not the process environment), checks each candidate's real path against those roots and rejects reparse points. lib/cso/docker.ts admits only unix:/// endpoints, so a trusted docker.exe reports 'native Windows Docker transport is not supported yet; static assessment only'; one outside the roots is refused as outside the trusted install locations, not overridable. Both carry an anchor. Native named-pipe transport is not implemented. * fix: sourced helpers share one self-locate block and fail loudly when they cannot locate themselves (#3024 rework) The codex probe and the egress lib now carry one byte-identical block: BASH_SOURCE under bash or %x under zsh, made absolute with $PWD, else a set GSTACK_ROOT that holds the helper. Anything else (dash, sh, a shell that cannot name the file) prints "gstack: cannot locate <helper> (shell: ...)" with the docs/troubleshooting.md#sourced-helper-location URL and returns before any bash- or zsh-only syntax. The Claude-only default path and the $(pwd) fallback are gone. The generated Codex preflight dot-sources the probe and, when it cannot load, reports CODEX_MODE: helper_unavailable with the helper's own line instead of falling through to not_authed. The class guard enumerates nested sources, makes real resolver, auth, state-root and egress-home calls under bash and zsh (absolute, relative then cd, bare name via PATH), covers dash and an unknown shell, runs the full preflight, and lints BASH_SOURCE to the shared block plus other bash-only constructs. Co-authored-by: marcosdetry <marcosdetry@users.noreply.github.com> Co-authored-by: akshaykalia <akshaykalia@users.noreply.github.com> Co-authored-by: scottgigante-hubflow <scottgigante-hubflow@users.noreply.github.com> Co-authored-by: woodsonl <woodsonl@users.noreply.github.com> Co-authored-by: bezdekt <bezdekt@users.noreply.github.com> * fix(cso): a Maven or Gradle repository is Java, not Python (#3028) With no supported stack metadata, preparation defaulted to Python and asked a Java repository for requirements.txt. A root pom.xml, build.gradle or build.gradle.kts now names the stack as Java (unsupported for runtime preparation, so the prerequisite says so), and Python helper scripts or a root requirements.txt beside it no longer make it Python. * fix(browse): BROWSER.md says plainly how to enable WebGL; the mismatch refusal links Daemon discipline (#3030) The GSTACK_DISABLE_GPU row now states the effect and the switch: set GSTACK_DISABLE_GPU=off to enable WebGL (off is the only recognized value). The reworded config-mismatch refusal links BROWSER.md's Daemon discipline section. Free tests pin both. Co-authored-by: Hagetino <Hagetino@users.noreply.github.com> * test(windows): name the B2, B4, B5 and B12 tests in the curated Windows lane The runtime-root bundle tests lived in browse/test/config.test.ts and the Copilot glossary test in test/host-config.test.ts, both excluded from the Windows lane, so the two Windows-only fixes were never checked on Windows. Move each into its own Windows-safe file (unchanged assertions) and list it, the piped-stdin chain test and the /cso docker.exe discovery test as named Windows coverage in scripts/lib/windows-curation.ts. * fix: the free-text block exits loudly when mktemp fails; spec and redact tests follow the file mechanism The redact resolver test, the #2679 mktemp guard test, the /spec invariant and the spec secret-sink harness pinned the heredoc transport. They now pin the agent-written file: the harness runs the draft-file block, writes the draft the way the file-write tool would, then runs the real scan fence, with every HIGH/MEDIUM/scanner-failure assertion unchanged. Co-authored-by: dgrant <dgrant@users.noreply.github.com> * test: hostile payloads through the generated posting blocks, identifier grammars, every quoting shape - The lint reads a whole command, so an unquoted multi-word placeholder is caught; negative controls cover double, single and no quotes, quoted and unquoted heredocs, continuation lines, and quoted-only identifiers used unquoted. - Every allowlisted grammar rejects quote-breaking characters (and shell operators and whitespace when used unquoted); git accepts 'fix;id' and 'fix`id`' as branch names, the ref grammar does not. - Greptile replies, test-failure issues (GitHub and GitLab) and /spec filing and archive run from the real generated text with stub gh/glab: a body with backticks, $(...), an apostrophe and lines equal to EOF and the old heredoc delimiters arrives byte for byte, nothing in it runs, and the temp file is gone. An unwritten file is refused with its path and the manual command, and nothing is sent. Co-authored-by: dgrant <dgrant@users.noreply.github.com> * test(ship): the PR check precedes the Greptile dispatch, and each skip names its reason Runs Step 10's generated check against stub gh for gh missing, not logged in, a non-GitHub remote, no PR, a failed lookup and an existing PR. Fails on the previous text (which printed PR: unknown and dispatched). Co-authored-by: dgrant <dgrant@users.noreply.github.com> * feat(ship): ship-measure runner for the measure-then-fix loop (A6) scripts/ship-measure.ts owns /ship's diagnostic measurement of a red eval case: per-kind aggregation (rule trials; behavior panels of 3, each at its own 2 of 3 plus 90% of trials; judge outputs scored by 3-sample panels, standalone judge ids accepted), one artifact directory per baseline, repair round and trial, a diagnostic label with verdict null that refuses the eval history dir, an estimated admission budget that reserves every concurrent trial and reconciles actual cost, the ask-once rule, the repair-round limit, the unmeasured skip and the per-case PR table. Free-suite flakes (CEO-1): 'free' reruns one shard's exact file list N times, rerun 1 alone as the baseline and the rest in parallel at max(1, floor(cores / shard concurrency)), each with its own HOME, state root and flake ledger, GSTACK_FREE_RETRY_FLAKY=0, under a wall cap that reports a partial count. test:ubicloud --diagnostic runs the same on one VM with the retry off. bin/gstack-config gains the DX-7 keys with their CEO-9 defaults and rejecting validators. Free tests drive every kind, the budget stop, the unmeasured skip and the free reruns with fake runners. * fix: a Codex usage limit is quota_exhausted, a plain 429 is rate_limited, and only Codex's own error lines count (#3051 rework) The probe's quota signature used to treat any rate limit, "too many requests" or 429 as quota, and read the whole output, which echoes the prompt. Now only Codex's trailing ERROR: / stream error lines are read. A usage-limit or insufficient_quota line is MODEL_QUOTA_EXHAUSTED (exit 4, cached 15 minutes; quota wins over a 429 that names insufficient_quota); a plain 429 is MODEL_PROBE_RATE_LIMITED (exit 0, state rate_limited, never cached), reported as CODEX_MODE: unverified (rate_limited) so the review still runs and decides. The quota HINT says how many minutes gstack skips Codex and how to retry now (GSTACK_CODEX_PROBE_RETRY=1 skips the negative cache for that probe, or delete <state root>/.codex-model-probe), with the full troubleshooting URL. classifyOutsideReview reads the same trailing lines: a mid-review quota is unavailable (quota_exhausted), a mid-review 429 unavailable (rate_limited). New gate row rate_limited and docs anchor codex-rate-limited; the quota row documents the retry. Tests cover each line shape, the echoed prompt, exit 0, timeout, retry recovery, probe/classifier parity, and two fresh-shell preflight blocks where the second makes no paid Codex call. Co-authored-by: venkatareddyg <venkatareddyg@users.noreply.github.com> * ci: the eval image installs zsh, and the macOS lane sources the Codex probe from zsh Dockerfile.ci adds zsh so the sourced-helper guard runs (and fails loudly when zsh is missing) inside the eval container too. The macOS named regressions job sources bin/gstack-codex-probe from zsh in an unrelated directory and runs _gstack_codex_select_model exec (no paid call), then runs the sourced-helper guard on the macOS runner. A free test pins both lanes and runs the macOS step's script locally. * test: deterministic regression for the Codex watchdog freezing the command before reaping its children The existing stubborn-child test caught the reap-then-kill race only some of the time (2 of 3 runs on the pre-fix watchdog). A pkill that sleeps after reaping widens the window between reaping the children and killing the command, so a watchdog that does not SIGSTOP the command first always lets it print "late" after the deadline. Fails 3 of 3 on the pre-fix watchdog, passes 3 of 3 on the current one. * test: live codex smokes run codex in a private HOME and CODEX_HOME codex-resume-flag-semantics and codex-e2e-sol-scope probe the codex CLI (the latter at module load, in every free run), and every codex run writes $CODEX_HOME/tmp/arg0, so with a real codex on PATH they wrote into the developer's ~/.codex. Both now give codex a private HOME and CODEX_HOME and remove it afterwards. A free test runs each file against a stub codex that records HOME and CODEX_HOME and asserts neither is the caller's. * feat(ship): a red eval runs classify, measure, fix, re-measure, gate once (A6) Step 6 no longer stops at the first red paid eval: it reads the new lazily loaded measure section and runs the loop for each red case, and Step 5 measures a failed free-suite shard (diagnostic reruns, retry off) before the triage can call it pre-existing. The section states the diagnostic- not-verdict rule, the per-kind table, the ask-once and named-red stops, the unmeasured label and the honest-fix wording (observed k/n after fix at <cause>). A free test pins the steps and their order in the generated skill and checks every command and flag it names exists in the runner. Ship's parity cap rises to 1.455 (measured 272,944 / 187,706 = 1.4541). * docs(testing): diagnostic measurement is not a verdict; per-kind measure table (A6) * test(ship): seeded-flake paid case for the measure loop (A6, periodic, behavior) A fixture repo with no remote documents a single-case eval command and a gate command, both free local stubs. queue-priorities claims trial slots with atomic mkdir (reset per ship-measure round) and fails slots 2, 5 and 9 while src/priorities.js leaves its seed-shuffled list unsorted, printing failure_cause and failure_detail that name the line. The paid case gives /ship's generated Step 6 and measure section to a live session and asserts it measured the case alone, fixed the named line without touching the eval, re-measured at least 10 passing trials on the sorted file, and only then ran the gate once; any question other than the spend question is a contract violation. A free test measures the fixture through the real ship-measure CLI: 7/10 unsorted, 7/10 after an unrelated edit with the gate still red (ENG-13's negative control), 10/10 after the sort with the gate green. * test(cso): the missing-scanner case points Docker at a dead socket 'an optional scanner prerequisite does not turn completed manual coverage partial' assumed no usable Docker. On a machine with Docker and a pulled gitleaks image the scan completed and the test failed (also on main). The scan step now gets a DOCKER_HOST that cannot connect, which is the prerequisite-missing condition the test describes. * fix: Aside prompts travel as agent-written files, never inside the command The open-ended reading recipe (browse, devex-review), scrape's fuzzy-intent read and the shared web-research block put the model's question, query and reply format inside a double-quoted _aside_exec argument, where backticks and $(...) from page or repo text would run. The agent now writes the prompt into a private .gstack/tmp file; the send block rebuilds the path from the printed basename, refuses when the file is empty, and passes "$(cat "$PROMPT_FILE")". The fixed read-only sentence stays in the shell source. aside-driver pins move from the inline prompt to the file-based send block. Co-authored-by: dgrant <dgrant@users.noreply.github.com> * fix: design briefs and approval feedback travel as agent-written files Design briefs went into $D variants/generate/check as a double-quoted argument, and approval feedback was spliced into a single-quoted echo of JSON, so a quote, backtick or $(...) in either broke the command or ran. The agent now writes each brief into a private .gstack/tmp file and the block passes --brief "$(cat "$BRIEF_FILE")" (the CLI's --brief-file expects a JSON brief, so plain text uses --brief). variants and the later check reuse the same brief file. approved.json is built with jq -n from --arg values and --rawfile feedback, so the feedback is data. The office-hours mockup flow creates its brief and feedback files in one block. design-approval-flow now writes the feedback file (with quotes, backticks and $(...) in it) instead of substituting <FEEDBACK>, and checks the file is removed after the save. Co-authored-by: dgrant <dgrant@users.noreply.github.com> * fix: carve office-hours on every host to stay under the 160,000-byte ceiling With its brief, feedback and research prompts moved into files, the inlined office-hours render is 161,329 bytes on Codex and 160,302 on Factory, over SKILL_BYTE_CEILING, so gen-skill-docs --host all fails. Per the C4 policy (carve every skill whose external render exceeds the ceiling), office-hours joins ship and plan-ceo-review in CARVED_ON_EVERY_HOST. The Claude render is unchanged; external hosts read its sections relative to the installed skill. external-host-carve and qa-lazy-sections now include office-hours in their carved-skill lists. Co-authored-by: dgrant <dgrant@users.noreply.github.com> * chore: regenerate after the free-text mktemp guard * fix(codex): prompts and review focus travel as agent-written files Co-authored-by: dgrant <dgrant@users.noreply.github.com> Challenge and consult prompts, and the /codex review focus text, are written by the agent into private .gstack/tmp files and fed to Codex on stdin or read back with cat; no heredoc carries them. Consult's new and resumed runs share one block (and one parser) so no placeholder stands in for the parser; a resumed run reads its session id from .context/codex-session-id and checks it. Adds the A3 group 2 placeholder allowlist block. * fix(benchmark-models): an inline prompt goes through a prompt file Co-authored-by: dgrant <dgrant@users.noreply.github.com> * fix(land-and-deploy): PR head repository and branch travel as data Co-authored-by: dgrant <dgrant@users.noreply.github.com> The branch check reads headRepository/headRefName from gh's JSON into quoted shell variables; ls-remote, the confirm-first delete and the local delete use those variables instead of model-substituted names. The postfail test now pins the variable form it replaces. * fix(office-hours): the reviewer receipt must match the receipt grammar before the check runs Co-authored-by: dgrant <dgrant@users.noreply.github.com> * fix(setup-gbrain): cleanup-orphans collects the PAT with read_secret_to_env Co-authored-by: dgrant <dgrant@users.noreply.github.com> * fix(ship): PR body drafts and rejected test paths are agent-written files Co-authored-by: dgrant <dgrant@users.noreply.github.com> The PR body is written into two private drafts (through ## Documentation, and from ## Test plan on); the scan block splices Step 14.5's section between them into the scanned temp file and deletes the drafts. Rejected test paths are read from a written list, and paths outside the repository are skipped. Tests that ran the old heredoc blocks now write the drafts and list first. * fix(sync-gbrain): orchestrator flags must match an allowlisted grammar Co-authored-by: dgrant <dgrant@users.noreply.github.com> * fix: the gbrain page save and external-host placeholders pass the free-text rule The gbrain save block passed the whole page through a quoted heredoc inside --content; the agent now writes it to a mktemp file and gbrain put reads it on stdin. The Claude Code outside-voice runtime root and session mode, and the feature slug, join the allowlist with grammars; unused entries are dropped, and a test keeps every allowlisted placeholder tied to a real use. Co-authored-by: dgrant <dgrant@users.noreply.github.com> * fix: skill-specific Aside research queries travel as prompt files investigate, office-hours, plan-ceo-review, review, plan-eng-review, design-consultation Phase 2, the shared test bootstrap (B2) and QA's test bootstrap each carried their own _aside_exec call with the model-filled query inside a double-quoted argument. Skills that already carry the Web research block now give only the prompt-file text and use that block to create, write and send it; design-consultation, the shared bootstrap and QA create the file and send it with the new {{ASIDE_RESEARCH_SEND}} resolver, which is the same send block the research section uses (read-only sentence stays in the shell). aside-driver's B2 pin and qa-browser-preservation's bootstrap contract move from the inline call to the prompt file and the shared send resolver. Co-authored-by: dgrant <dgrant@users.noreply.github.com> * fix: prose design commands pass briefs and feedback as data The DESIGN_SETUP command list taught `iterate --feedback "..."`, and design-consultation's late-change and plan-design-review's slop-regenerate lines showed `--feedback "<feedback>"` / `"..."`; design-shotgun's retry showed `generate --brief "<brief>"` and `evolve --brief "<brief>"`. Feedback now reads "$(cat "$FEEDBACK_FILE")" from an agent-written mktemp file (the setup note says how), and the shotgun retry reads the variant's own brief out of briefs.json with jq instead of retyping it. Co-authored-by: dgrant <dgrant@users.noreply.github.com> * test: record parity sizes after the free-text file mechanism; refresh ship goldens Measured union and skeleton sizes for ship, plan-eng-review, plan-design-review, office-hours, design-consultation, review, qa, browse and design-shotgun after every posting, prompt and brief site moved to agent-written mktemp files; the ship goldens pick up the question-tuning slug and the dropped free_text field. * test: parity caps for the combined wave head (rounded up past the measured ratio) * fix(plan-ceo-review): the mode handoff helper reminds the model to lead its chat with the line (#3038) Periodic run 37272185151's auto-decide-preserved red replays as the Step 0E handoff-line class: the model ran gstack-ceo-mode-handoff --auto, got the exact line, then opened its chat with a heading and a paraphrase, so the user never saw the auto-decide line (the terminal collapses tool output) and the detector saw no outcome. The helper now prints a one-line reminder on stderr beside the line; the detector accepts the line followed by that reminder and nothing else. The ship Greptile check test reads its template (test-of-test ratchet). * test: every troubleshooting link in shipped code resolves, relative or full URL * docs(todos): Oct 6 fix-wave follow-ups * v1.91.32.0 release notes: Codex works from zsh, free text never enters shell commands, honest failures * fix(plan-ceo-review): show the mode handoff line through a PostToolUse hook, not model compliance (#3038) The stderr reminder beside the handoff line measured 1/10 on auto-decide-preserved: the model still opened its chat with a paraphrase ('I've set review mode to HOLD SCOPE...'), so the user never saw the line and the detector saw no outcome. /plan-ceo-review now declares a Claude-only PostToolUse hook that shows the helper's exact line as a system message. Diagnostic measurement on this head: 10/10, the line visible in every trial while no trial's chat led with it. The reminder is reverted. * fix: type the handoff hook's line filter * fix: repairs from the integrated free suite - The self-locate markers looked like heredocs to the pipe-deadlock guard; they are plain comment fences now. - The spawned block keeps the canonical 'conservative non-destructive' phrase the cross-surface drift guard pins. - The free-text block's mktemp refusal says 'mktemp failed', as every /codex temp file must. - The batch transcript read reports sizes in MiB. - docs/TESTING_INTERNALS.md gets a real 'Ship measure' heading for its anchor. - The early-question harness admits /plan-ceo-review's handoff hook by its exact command (it only adds a system message); any other PostToolUse Bash hook is still refused. - Paid file count and the unconsumed-file example follow the new seeded case; ship goldens refreshed. - The CSO scanner test points Docker at a dead socket, so a machine with the pulled image does not scan. * test(cso): a host with no Docker binary is also a no-scan host The missing-image scan test failed on a Docker-less VM (docker is not installed in a trusted directory). Every accepted message is a reason the scan cannot run here; the not_assessed, helper-owned artifact assertions are unchanged. * ci: split the USERPROFILE assignment from export (SC2155) * fix(browse): Windows runtime roots read .source-path as a native path (#3026) The new windows-setup-e2e journey showed the installed browse could not start its server. setup runs under Git Bash and wrote .source-path as an MSYS path (/d/a/gstack); Node read it as \d\a\gstack on the current drive, found nothing, and fell back to the root's own bundle, which cannot import playwright. setup now writes cygpath -m, and the CLI maps an MSYS drive path for installs written by an older setup. The journey prints the daemon and startup logs when the chain fails. * fix(ship-measure): defects from the parallel #3034 plan review - A trial passes only with a JUnit testcase that executed and passed; a --case that selected nothing also exits 0. - measure runs a no-cost --list preflight that must plan the case's own file before any paid trial. - Diagnostic trials drop EVALS_CACHE_DIR/EVALS_CACHE_RUNTIME_ID, so they never reuse a stored judge pass or publish one the gate could reuse. - Gate once: when CI runs the gate on push, the push is the gate run; the skill no longer also starts eval:bg:pr (a second paid gate). - ubi-runner: down fails unless the VM is confirmed gone, and nothing sweeps other runners' VMs by default (UBI_GC_HOURS opt-in). * test(windows): the seeded-flake fixture drives a POSIX shell stub, so it stays off the Windows lane CreateProcess cannot exec the fixture's ./evals.sh shebang script (status null on windows-free-shard 6). The measure runner is covered on Windows by ship-measure.test.ts, which uses in-process fake runners. * fix: the free-text block refuses an unset temp dir instead of writing at / The rendered block ran mktemp "$_GT/..." with _GT unset in the portability test; on Linux that fails at /, but Git Bash on Windows can write to its install root, so the block went on (windows-free-shard 3). It now uses ${_GT:?}, so an unset directory always fails closed with the mktemp message. * test: pin the guarded ${_GT:?} temp-file form * test: the Greptile check's gh stub uses a portable shebang On Windows the absolute bash path has a space, so the stub never ran and every case read 'gh not logged in' (windows-free-shard 1). * test: the Greptile check runs with the native PATH and the gh stub first The PATH-only stub bin could not run its stub on Windows (every case read 'gh not logged in'). Stub cases now keep the real PATH with the stub first; only the no-gh case drops directories holding a real gh. --------- Co-authored-by: marcosdetry <marcosdetry@users.noreply.github.com> Co-authored-by: akshaykalia <akshaykalia@users.noreply.github.com> Co-authored-by: scottgigante-hubflow <scottgigante-hubflow@users.noreply.github.com> Co-authored-by: woodsonl <woodsonl@users.noreply.github.com> Co-authored-by: bezdekt <bezdekt@users.noreply.github.com> Co-authored-by: dgrant <dgrant@users.noreply.github.com> Co-authored-by: venkatareddyg <venkatareddyg@users.noreply.github.com> Co-authored-by: CharlesBerg85 <CharlesBerg85@users.noreply.github.com> Co-authored-by: VaibDeveloper <VaibDeveloper@users.noreply.github.com> Co-authored-by: Hagetino <Hagetino@users.noreply.github.com> Co-authored-by: Rekin226 <Rekin226@users.noreply.github.com> Co-authored-by: ekson73 <ekson73@users.noreply.github.com> Co-authored-by: tianxchen <tianxchen@users.noreply.github.com> Co-authored-by: BenjaminDSmithy <BenjaminDSmithy@users.noreply.github.com> Co-authored-by: ubay887 <ubay887@users.noreply.github.com> Co-authored-by: nareshh74 <nareshh74@users.noreply.github.com> Co-authored-by: syong <syong@users.noreply.github.com> |
||
|
|
55f8151fbe |
v1.91.30.0 fix: honest eval failure reports, Bun floor with safe auto-update, make-pdf and /review on Codex (#3033)
* docs: approved reliability follow-ups plan (Oct 2026)
* D1: score overlay wrong answers as comparison results (contract v4)
A completed answer that fails the fixture's output check is now a valid
measurement with taskCorrect false and answerError set, in either arm.
Only execution, scope and metric failures invalidate a trial. ON
correctness still gates the case, so an OFF wrong answer stays in the
comparison. OVERLAY_CONTRACT moves to v4; v3 verdicts are not
reinterpreted. Replays the 37198445662 sonnet trials (OFF-0 wrong).
* C4: score auq-matrix substance with a 3-sample judge panel
Each skill keeps one native capture in the same execution; the
recommendation-substance grader now runs as a JUDGE_PANEL_SAMPLES panel on
that capture and gates on the mean against the unchanged minimum of 4. A
failed sample fails the panel without resampling. Approved at the gate
2026-10-04 for censuses after this change.
* E1: one Bun version source; setup refuses below the 1.3.3 floor, updater checks before advancing
bin/gstack-bun-version.sh holds the security floor (1.3.3) and the tested
version (1.4.0, the CI pin and engines.bun). setup refuses Bun below the
floor before writing anything, warns between floor and tested, warns and
continues on an unparseable version, and prints a 1.4.0 install hint.
Prereleases sort below their release (semver).
gstack-session-update now fetches, reads the incoming release's floor and
checks the bun setup will run before fast-forwarding to that exact
revision; below it the update is held with reason bun-too-old and a
resume command, leaving the revision and installs untouched.
Fixture copies of setup (team-mode, claude-code migration) now include the
sourced file; the slow-git lock test sleeps on fetch, the updater's
network step.
* E3: record the owned Chromium's PID over CDP so the #2709 reap runs on Playwright 1.62
Playwright 1.62's Browser has no process(), so chromiumProcInfo was always
null and browse stop never reaped a surviving headless Chromium. The owned
headless launch now reads the browser PID from CDP SystemInfo.getProcessInfo
(bounded at 2s; a hung or failed call logs once and records nothing). The
reaper's start-time and cmdline identity checks are unchanged, so a recycled
PID or an externally owned browser is never signalled.
* A1: ship docs gate records post-child hashes with gstack-docs-candidate, saves each record once
Stored transcripts (37198445662 gate-census-2) show the parent's reads of
release files take ~1 s; the parent's own model time dominates (completion
268 s of 392 s, store ~270 s of 387 s), and 45-78 s of it is retyping hash
maps, child output and records into several artifacts after the child
returns. The child contract is unchanged.
* A2: /plan-ceo-review sends the mode handoff line before any other tool call
plan-ceo-mode-routing's HOLD SCOPE session spends its 240 s post-selection
window on model work: 37176837432 skipped the handoff helper and paraphrased
the mode, 37198445662 ran it, then wrote the plan, loaded sections and probed
Codex for 114 s before the posture chat. The handoff line is the posture.
* B3: accept wrapped unchanged rows in the cropped Edit preview
PR run 37176835584 timed out with the floor's owned Edit menu on screen:
unchanged diff rows soft-wrap under their gutter with a blank marker
column, and a trimmed empty unchanged row keeps only its number. The
parser accepted wrapped +/- rows only, so it never verified the owned
edit. Both forms are now parsed and still compared with the exact source
lines; the captured viewport is replayed with negative controls.
* A3: record per-phase headroom diagnosis for docsync, plan-mode-no-op, mode-routing and gate slice 11
* E4: /review Step 1 keeps going when the base fetch fails and reports stale base coverage
Under Codex's read-only .git, 'git fetch && merge-base && diff --stat'
stopped at the fetch, so the empty output read as 'Nothing to review'.
The fetch is now separate, prints BASE_REFRESH fresh or stale with the
local base revision, and the review states stale coverage.
* D2: reword the format-approach question prompt
Both plan-ceo-review-format-approach refusals in census 37195203538
(Fable 5.1 model_refusal_no_fallback, category reasoning_extraction:
"duplicating model outputs") were anchored on the session prompt, whose
capture instruction asked for the "verbatim" question and "the exact
prose you would have shown" in a "format-capture test". The approach
case now asks for the same question in the same file as the step's
output, without that wording. The other three plan-format cases keep
their unchanged prompt (no refusals in 72 trials).
* C2: /qa defines its outcome filename fields; QA report templates name the measured timing fields
Low judge samples in failing panels named both: qa workflow (37186854666)
'an undefined {user} in the outcome filename', and qa-only workflow
(37198445662) 'template asks for Duration / durationMs totals while
reporting.md mandates Probe budget and Guarded command time'. The qa
union stays under its 1.11 size ratio (93292 of 93294 bytes).
* 0.1: session ledger written by the claude -p, Agent SDK, PTY and Codex runners
Each armed model session appends one best-effort line to
$GSTACK_EVAL_DIR/session-ledger.jsonl: a stable session key, the timeout it
armed, monotonic elapsed time, its end reason (completed, session_timeout,
observer_timeout, api_error, refusal, aborted, error), one sanitized evidence
line and, for stream-JSON runners, a compact liveness summary (longest
in-request silence with nothing outstanding, last event kind, tool open at
end). Partial messages now stream for every claude -p session and for SDK
sessions without a custom query provider (runSharedInteractive opts in); their
rows feed only the summary, so transcripts keep their size unless
publicStreamDiagnostics is set. Structured refusal and API-error signals win
over phrases, and tool results are never read for them.
* 0.2: failure_cause beside failure_class, from runner facts and the session ledger
One pure failureCauseOf() beside failureClassOf() sets failure_cause
(contract > pre_turn_infra > api_error > refusal > provider_stall >
session_timeout > observer_timeout > assertion > unknown; no machine-set
'detector') with one bounded evidence line, and failureDetailOf() keeps a
Bun matcher's Expected/Received or a judge's failing dimensions in
failure_detail. Both record builders (classifyTrialShard, junitCensus) call
them and attach the trial's ledger sessions. failure_class, error and
TRIAL_ERROR_MAX are computed exactly as before; the new fields are optional,
bounded and validated only when present, and a frozen copy of the v1 reader
accepts every new record. A provider stall needs partial messages and
STALL_WINDOW_MS (120 s, eval-budgets.ts beside HEADROOM_WARN/FAIL) of
in-request silence with nothing outstanding; it stays a failure under
EVAL_POLICY v1.
* E4: fingerprint under a read-only .git via a private object dir owned by review-log
Codex's sandbox mounts .git read-only, so gstack-wtree's git add -A could not
write blobs and /review lost its start capture. gstack-review-log now creates
a private 0700 object directory under TMPDIR, passes it to gstack-wtree (which
writes new objects there with the common-dir object store, resolved before
exporting, as an alternate, so linked worktrees work), exposes it to the
bindReview/checkSharedLibsReuse consumers that read the tree and blobs, and
removes it on EXIT/INT/TERM after they finish. gstack-wtree prints one stderr
line and exits 1 on a failed write or unwritable TMPDIR instead of printing a
partial hash; review-log no longer swallows that line. Standalone gstack-wtree
keeps writing to the repository store so git diff between saved snapshots
(/ship) still works.
* 0.3: red lines name their values, evidence and an after-a-repair command; one published-text sanitizer
Each red line in the census report keeps its stored error and adds the
machine cause with its evidence, the failure_detail values (Expected/Received,
or each failing judge dimension with its mean, sample count and the sample
sentence naming it), the slice artifact and shard directory that hold the
transcript, a fetch command (eval:pass-rates --run <id> --case <id>), and
'after a repair:' with a command that selects only that case (E2E ids via
--case, judges via their selection and an exact test-name filter). A
session that completed and failed a check says so instead of 'assertion
(success)'. Report text that reaches a PR comment, issue or committed file
goes through scripts/lib/published-text.ts: control and bidi characters
stripped, credential-scan patterns redacted, mentions neutralized, HTML and
link brackets escaped inline, blocks in a fence longer than any backtick run,
length capped. Verdicts and collector verdict objects are unchanged.
* E2: one make-pdf resolver for readiness and every $P block on env-var hosts
make-pdf exported $P from its readiness block, which Codex and the other
env-var hosts lose between blocks. binaryAssignment gains 'make-pdf': the
MAKE-PDF SETUP readiness check and the runtime prelude both use it, so every
block that runs $P derives the same make-pdf/dist/pdf binary, with
MAKE_PDF_BIN (dev builds) winning when it is executable. Claude output keeps
its repo-local-first probe; only the false 'subsequent blocks' comment
changed. Root plus P=... fits the 400-byte prelude budget; the fixed
MAKE_PDF_OVERRIDE segment is the one exemption, named in runtime-root.ts and
the conformance test.
* 0.1: eval:pass-rates --headroom, --reds and --run report views; 85% headroom alarm in --gate
A new pure module, scripts/lib/eval-history.ts, reports per case the slowest
armed session against the timeout it armed (censored when it timed out,
insufficient below 3 samples, unknown without a session ledger, with the
case wall shown separately as an upper bound), the census critical path from
the slice jobs' timings, the verdict red ledger per census by failure class
and machine cause with shrunk per-case rates, and the all-green probability
(an approximation: the product of 1 - p_i, with an interval). --run <id>
lists one census's reds with their values and history and downloads only the
slice artifacts those reds name, reporting any over 64 MB instead of skipping
them. eval-flake-rank.ts only routes flags: --help works offline, an unknown
flag or case id exits 2 before any fetch, --case narrows the JSON and the
alarms, every view prints its run/branch scope, and --gate also alarms on a
session above HEADROOM_FAIL (85%) of its budget.
* E5: GSTACK_DESIGN_IMAGE_MODEL override; the live model check always tests the defaults
The image_generation tool model gains an env override validated against a
conservative gpt-image-<version> pattern; a bad value fails before any
request and the error names the variable. Pairing and rejection hints name
the tool model actually requested. design/scripts/live-model-check.ts now
ignores GSTACK_DESIGN_MODEL and GSTACK_DESIGN_IMAGE_MODEL (and says so), so
an override cannot mask a retirement of a default, and its exit 2 (no key)
says nothing was checked.
* C3: /deslop-shared-libs names a concrete risk for each recommendation, never 'none'
Both shared-libs-opportunity-judgment reds (37193478719 t3, 37195203538 t1)
failed adoption_and_risk because the top recommendation said 'Main risk:
none found inside the repo' / 'none in behavior'. The skill asked to 'name
the main risk or uncertainty' without saying what counts.
* B4: --case selects loop-registered cases; an unknown id exits before any process
caseSelection() resolves the one paid file that registers a case and how a
trial selects it: by its literal Bun test name, by the id a registration loop
names its test at runtime (coverage audits, TPA, journeys), or by running the
whole file when it registers no other case (plan-mode-no-op, whose test names
are not the id, so the old name pattern ran nothing). A whole-file case runs
one file shard per trial and is judged by shard status, as CI judges it.
--list prints the selection. Every E2E_TIERS id now resolves except
carve-section-loading, which fifteen files register (CI runs each as its own
shard) and which still fails with the owning files named.
* 0.3: after-a-repair runs the shard file when --case cannot resolve a case
A case that several files register (carve-section-loading) cannot be selected
by --case; its red line now prints the bun test command for the shard file
that went red, so every printed command selects exactly one case.
* C1: pre-register workflow-judge control bundles for qa, qa-only, review and setup-deploy
Positive (production bundle), negative (headings only) and one targeted
execution-blocking defect per judge, one 3-sample panel each, built with the
eval's own reader, prompt builder, options and floors.
* F: cost_known: a trial whose harness captured no billing says so; totals never hide it
cost_usd stays a finite number. Trial and eval records gain an optional
cost_known boolean (absent means known, so old records still parse; the
validator accepts only a boolean). Session-ledger rows record whether the
session's terminal result carried total_cost_usd; PTY and Codex sessions
never do, and SDK captures without a terminal result do not. A trial is
cost-known only when it has eval records and every record and session
captured billing, so JUnit-only PTY cases (125 of 298 trials in run
37198445662 recorded 0) now read cost unknown. The census headline prints the
known sum plus the unknown count, never a bare total over unknown costs, and
no total when every cost is unknown; the history import keeps the flag.
PTY transcripts are not priced: test/helpers/pricing.ts estimates exclude
cache writes, so they cannot give the billed cost.
* B1: replay corpora for the five repeat-offender cases
shared-libs-plan-callers, plan-ceo-section-loading, auto-decide-preserved,
plan-design-review-plan-mode and ship-docsync-late-result each get an
inventory of every wave census in scope (all artifacts still available)
and minimal, redacted detector or actor inputs replayed through the real
detector in the free suite: census passes, every distinct census red, and
derived negative controls labelled as derived. Fixed false reds stay as
expected passes with their history; the two genuine auto-decide product
failures (37176837432, 37179171083) stay refused. A contract test bounds
each entry at 64 KB, scans it for HIGH credentials, requires a pass and a
refusal per case, and reports entries whose series identity is stale.
Building the auto-decide corpus exposed a latent native detector misread:
a bold 'Mode application and rationale.' heading was parsed as a
withdrawn Mode field (37186854666). A field status now ends at its
sentence.
* B2: read approved stale-fill decisions from more valid ledger records
The structured ledger reader is authoritative before the prose fallback,
but it missed two valid approved records in passing censuses
(37186854666: a camelCase writeProfile subject; 37195203538: a
slash-joined 'D1 / STALE-FILL' row id), which then rested on the prose
detector that caused both historical reds. Both forms are accepted; an
unresolved row, an unrelated decision, the copied fixture plan and
non-write camelCase words stay refused.
* C1: record workflow-judge control results (negatives separated; qa and review miss a removed step)
* C2: record every low workflow-judge sample in the wave censuses with its disposition
* 0.3: published lines cannot break a fixed fence
evals.yml wraps collector-outcomes failures in a fixed three-backtick fence
for the PR comment, so those lines (and eval:pass-rates output meant for the
tracking issue) go through sanitizeFixedFenceLine: cleaned like every
published line, with backticks turned into quotes. Inline published text
also neutralizes backticks.
* A1: fit the post-child record wording under ship's 1.428 parity size cap
* E4: inline the Step 1 base refresh to fit review's 1.199 parity size cap
* B2: inventory of gate and periodic phrase-only detector checks
docs/evals/detector-inventory.md lists every pass/fail decision in gate
and periodic E2E cases that rests on phrases in model free text, with
its structured source (if any), whether the contract is user-visible,
and a disposition: already structured-first, convert candidate, phrase
witness required, or no structured source. Convert candidates are
ordered by census red history.
* 0.2: failureCauseOf takes the stall window instead of importing it; the PTY ledger never throws
eval-store.ts imported STALL_WINDOW_MS from eval-budgets.ts, which free tests
replace with mock.module stubs (review-finalization-budget, office-hours,
qa-fix-loop, qa-only), so loading eval-store failed there. failureCauseOf is
now dependency-free: the record builders pass STALL_WINDOW_MS. A PTY session
whose handle cannot show its text (test doubles) records no panel instead of
throwing from runPtySession's cleanup. The 37186854666 replay also runs
through classifyTrialShard.
* A2/auto-decide: the CEO handoff chat repeats the helper's printed line verbatim
Claude Code 2.1.284 collapses the helper's Bash output ('Ran 1 shell
command'), so the 'Auto-decided review mode → …' line reaches the user only
through chat. In lane D's auto-decide-preserved corpus every passing capture's
chat starts with that line, and the reds paraphrased it ('HOLD SCOPE was
auto-selected…', 'Mode decision done…', "I'm defaulting to HOLD SCOPE…").
The template now says verbatim and why.
* C2: record the judge panels rerun on the final ship and review wording
* integrate: touchfiles and module-size ratchet entries for the wave's new modules
GLOBAL_TOUCHFILES gains session-ledger.ts, published-text.ts and
auq-substance-panel.ts; bin/gstack-bun-version.sh joins every entry that
lists setup or the updater; auq-matrix stays a rule case with its C4
evidence beside E2E_KINDS; eval-history, published-text and session-ledger
are registered new modules.
* E1: BROWSE SETUP install hint pins the tested Bun (1.4.0)
* E2: quote "$P" in make-pdf commands so install paths with spaces work
* E1: /gstack-upgrade checks the incoming Bun floor before pulling
gstack_bun_incoming_hold (bin/gstack-bun-version.sh) is now the one
pre-advance check: the auto-updater calls it before its fast-forward and
/gstack-upgrade's git path calls it after fetching. Below the incoming
release's floor the upgrade stops with BUN_TOO_OLD and the checkout,
VERSION and installs stay untouched. Vendored installs were already safe:
setup refuses before writing and the swap restores the previous install.
* 0.3: only main posts or closes tracking issues; reports carry alarms, red ledger, headroom and all-green
A branch-dispatched periodic or marathon run writes its report to the step
summary and a report artifact; the upsert and close steps run only on
refs/heads/main. The periodic body now keeps every pass-rate alarm line (the
old grep kept only the header), adds the red ledger and session headroom read
with an explicit --branch, the all-green estimate and the triage guide link,
and routes report text through scripts/lib/published-text.ts. The marathon
lane gains the matching close-on-green step.
Migrated: evals-workflow-wiring pinned report-summary.md in the upsert step;
the body is now written by 'Write the census report'.
* C3: prosons neutral-posture detector ignores a negated "taste call"
Both plan-review-prosons-neutral-neg reds (37179171083 t3, 37193478719 t3)
recommended A and closed with "a coverage call, not a taste call"; the
old /taste call/i matched the negation. The detector moves to a helper so
the two census captures replay free as negatives, with positive controls.
* B2: credit the CEO mode handoff only when the chat shows its line
Claude Code 2.1.284 collapses the gstack-ceo-mode-handoff Bash result, so
the printed AUTO_DECIDE line is user-visible only when a later chat message
of the session begins with it. The native detector now requires that
witness; the helper's tool result locates it but no longer replaces it, and
a generic mode statement cannot stand in once a handoff ran. Census
37182865432 is relabelled a genuine miss (expected: refused): the chat said
"I'm defaulting to HOLD SCOPE…" and "Mode decided: HOLD SCOPE". Corpus
replay: 5 credited, 3 refused. ceo-mode-handoff tests migrated to the
witness rule (deliberate behavior change).
* test: baseline the wave's approved detector and harness replay tests
B1 corpora, B3, C3, C4 and D2 replay captures through paid-case detectors,
actors or fixtures (test code by design); each entry carries its reason.
* E6: live Codex multi-block periodic case in the host-run Codex job
test/codex-e2e-multiblock-live.test.ts runs ./setup --host codex once from the
job's checkout into a fresh HOME and CODEX_HOME (auth.json copied from the CI
login home), asserts codex debug prompt-input lists the gstack router as a
real file, seeds one learning, and runs /learn's Show recent block through
codex exec: that later block must root itself through its prelude, run the
installed gstack-learnings-search and print the seeded learning; no block may
report a missing install or an unresolved gstack helper path.
The file joins CODEX_CI_FILES so the planner gives it to the host-run Codex
job. paid-test-durations.json carries a 180 s packing estimate (refreshed by
--write-durations after the first census) so the plan keeps every slice
within max-parallel. Oracles live in test/helpers/codex-multiblock.ts with
free replays in test/codex-multiblock.test.ts. Registration (E2E_TOUCHFILES,
E2E_TIERS periodic, E2E_KINDS rule) is the integrator's.
* E5: design live-model check as a periodic case
test/skill-e2e-design-model-smoke.test.ts runs design/scripts/live-model-check.ts
(default models only) under a temp HOME. Without OPENAI_API_KEY the case is a
Bun skip that the census lists as SKIPPED with no coverage credit; with a key,
exit 0 passes and any other exit fails with the script output (exit 2 under a
present key fails as 'nothing was checked'). paid-test-durations.json carries
a 90 s packing estimate. Registration (E2E_TOUCHFILES, E2E_TIERS periodic,
E2E_KINDS rule) is the integrator's.
* A1: docsync parent points children at a saved interface file
The /ship docsync fixture saved its observation interface only in the
parent prompt and told the parent to copy it into every child prompt,
about 30-40 s of parent generation per session (lane S, 37198445662).
docsSessionOptions now writes it to <home>/fixture-interface.md (0600) and
the parent tells each child to Read that file first. Fault-actor prompts,
which prepare already writes to a file, are unchanged. Fixture-only.
* A3: a JUnit case's recorded wall is capped by its shard's wall
junitCensus summed a case's JUnit test times as duration_ms; for
plan-mode-no-op's concurrent PTY tests that is 346 s inside a 176 s shard
(the "432-613 s" history). The case wall headroom shows is now
min(sum, shard elapsed), a true upper bound. Session headroom already
used the ledger's per-session clocks.
* A3: refresh paid-test durations from census 37198445662
Recorded 2026-09-29 estimates under-packed gate slice 11 (17 files
estimated 1078 s ran 1353 s; plan-review-report 76 s -> 198 s,
plan-ceo-plan-mode 37 s -> 112 s, safety-codex-consult 83 s -> 150 s).
Refreshed both tiers with --report --write-durations from the latest
census's slice walls. Old plan re-costed with observed walls: worst slice
~677 s (slice 11); refreshed plan: every slice estimated <= 539 s.
* C2: Browser Quick gets a feasible budget; QA loop names its directory and stop path
Judge rationales on the qa-only panels (2 of the last 3) cited the 30 s
Browser Quick budget: homepage + 5 targets, each a checkpoint write and a
guarded command, left ~5 s per probe. Quick is now 180 s (method text: 3
minutes); the qa-only-no-fix paid check expects the same native deadline.
PROBE_DIR points at its source line, a stopped loop goes to the final
report steps (annotations, materialize, Markdown), and /review and /ship
say that after smoke expiry only plan checks rerun in revalidation.
* C2: name the flows judges found implicit in review resolvers
Structured outside review: MISSING COVERAGE asks no fix question and is
never reported clean (was "preserve the existing user decision flow").
/review's HIGH-impact plan gate: spawned or non-interactive runs report
REQUIREMENTS MISSING and continue, writing no TODOs. Value cards: the byte
clamp applies to card lines; JSON records you write keep full values.
* C1: a free test catches a removed workflow step, which the judges missed
C1 controls showed the qa workflow judge passing /qa without Phase 10 and
the review judge passing /review without Step 3. The new test pins every
step/phase heading of qa, qa-only, review, ship, plan-ceo-review,
plan-eng-review and setup-deploy (derived from the templates' step
headings) and names any that goes missing or out of order; new step
headings must be pinned. The judge rubric is unchanged (EVAL_POLICY).
* size: fit the wave's wording under ship, review and qa parity caps
Measured after all lane merges: ship -79, review -210, qa -68 bytes over.
Trimmed the added wording first (post-expiry rule, MISSING COVERAGE,
plan-gate default, value-card JSON, stop line) and folded /review's
redundant INFORMATIONAL sentence and findings bullet. ship 268,036 (room
8), review 130,118 (room 1). qa needed 11 more bytes: cap 1.11 -> 1.1102
with the measured 93,305 / 84,049 = 1.11013 and its reason beside it.
* docs: troubleshooting sections for the wave's new messages
Bun floor (setup, auto-update, /gstack-upgrade), /review stale base and
fingerprint temp-dir errors, the design image model override, the
unrecorded Chromium PID, and a contributor section for census report
lines (pass-rates flags, failure causes, headroom, cost unknown, overlay
v4, stale corpora, auq substance). A free test checks every
troubleshooting.md#anchor printed by setup or bin scripts exists.
* TODOS: reliability follow-ups deferrals; close the Bun floor, make-pdf $P and #2709 reap
* 0.4: 'A census went red' guide; docs for causes, session ledger, headroom and new messages
docs/evals/census-red.md walks one red line to a diagnosis: prerequisites, the
report fields, eval:pass-rates --run/--reds/--headroom with expected output,
what unknown/insufficient/unrecorded data means, free replay, and the single
'after a repair' paid run allowed by TESTING_INTERNALS. Linked from
CONTRIBUTING, TESTING_INTERNALS and both census reports.
TESTING_INTERNALS documents the failure_cause glossary (one row per
FAILURE_CAUSES value, diagnostic only under EVAL_POLICY v1), failure_detail,
sessions[], cost_known, the session ledger, the pass-rates views and the 85%
headroom alarm, main-only tracking issues, overlay contract v4, auq-matrix's
3-sample substance panel and the E5/E6 periodic cases. troubleshooting.md
gains #bun-too-old and an anchor per new user-facing message from lanes P, D
and F. README names GSTACK_DESIGN_IMAGE_MODEL. test/census-red-docs.test.ts
pins the glossary to FAILURE_CAUSES, parses every guide command and checks the
links.
* v1.91.20.0: CHANGELOG and version for the reliability follow-ups
* regen: SKILL.md and sections for all hosts, digest, context-budget fixture
bun run gen:skill-docs --host all; goldens and llms.txt re-rendered
unchanged; bun test/helpers/capture-context-budget.ts.
* integrate: register codex-multiblock-live and design-model-smoke (periodic, rule)
* 0.4: census report and eval:pass-rates views link the census-red guide
* test: cookie approval fixture reverses the Bun install-hint pin (E1)
The historical approved cookie-workflow prompt carried BUN_VERSION=1.3.10;
the regenerated install hint now pins 1.4.0, so the fixture's reversal
list gains that edit like the earlier waves' entries.
* test: release floor counts the two new periodic cases (E5, E6)
* fix: /freeze, /guard, /unfreeze and /investigate work on Codex and other env-var hosts
Templates quoted "$HOME/.claude/skills/gstack/...", which the env-var hosts'
rewrites turned into "$HOME/.agents/skills/gstack/..." on Codex (a path a
global install never has; same class on the other hosts), and setup's
runtime roots had no freeze/bin. Quoted runtime paths now use
{{RUNTIME_ROOT}}: $GSTACK_ROOT on env-var hosts (the fence gets the
runtime prelude), the unchanged $HOME path on Claude. _link_runtime_dists
links freeze/bin and the careful/bin helper it sources into every
env-var host's root. Also fixes the same guess in /benchmark-models,
/retro and /diagram. test/env-host-runtime-assets.test.ts fails on the
previous code: no fence guesses a $HOME/<dir>/skills/gstack path, every
$GSTACK_ROOT path a fence uses is in the host's runtime root, and the
installed Codex /freeze and /unfreeze fences run from a fresh shell.
Template-reading tests now read the Claude render or expect the
placeholder (deliberate change).
* CHANGELOG/TODOS: lane W's workflow, cases and guide; the env-var host freeze fix
* E6: a block that reads a SKILL.md is not a missing install
The first live codex-multiblock-live run passed its real check (the /learn
block found the install and printed the seeded learning) but the oracle
flagged a sed of the learn SKILL.md, whose prelude source quotes the
'gstack: no install found (tried $_r)' text. Only an emitted line that
starts with the message and names a real root counts now; free replay
negative added.
* test: qa-only fixture keeps its caller-set 30 s deadline
The qa-only-no-fix fixture tells the model to start the guard with 30 s
(a caller limit), so its deadline assertion's default stays 30 000 ms; the
product's Browser Quick default (180 s) does not apply to it. Reverts the
helper and its test to their base.
* C2: keep PROBE_DIR 'owned' (qa-functional-prompt pins it); qa measured 93,311
* test: paid file count and periodic floor include the two new periodic cases
* test: ship docs freshness pin checks meaning after A1's wording trim
The snapshot still hashes selected release paths (--select narrows them),
generated outputs and docs; the pin now checks that instead of the
pre-trim sentence.
* E3: reap the recorded Chromium only after the daemon has exited
With the PID now recorded (E3), browse stop reaped Chromium while the
daemon was still shutting down; the daemon read that as a crash, exited 1
and left its state file (chromium-profile-isolation's headless stop test
failed 3/3 locally, passes on base). The CLI waits up to 5 s for the daemon
to exit before reaping a survivor.
* regen: qa and qa-only exploratory sections (PROBE_DIR wording)
* B1: batching counter reads a target declared mid-line; replay corpus
GSTA-23's census 37228573062 failed plan-eng-multi-finding-batching with
reviewCount=0 after 975 s although D1-D10 were asked one per finding,
each with its saved decision-ledger record. The report wrote 'Report
destination requested by the user. Review target (fixed): `PLAN.md` ...'
and the counter read target fields only at a line start, so no brief
inherited the plan identity. A target field may now open any sentence of
its line; a mid-sentence mention or another file still gives no identity.
Floor (3), budget and kind unchanged. Corpus: 9 earlier passing censuses,
the red (now credited at D5), and a derived batching refusal; 975 s noted
in the headroom diagnosis.
* B1: shared-libs corpus labels its git path and covers the supported shape
Every census entry was recorded in CI's eval image (git 2.43), where
gstack-safe-git falls back because --no-lazy-fetch is rejected; each entry
now says so (git_path: fallback). Two synthetic supported-path entries
(HEAD and a history citation in the brief, as git >= 2.44 produces) keep
the 37193478719 pass and the 37195203538 refusal, and a test asserts both
shapes are covered and the actor reads no git-path-specific text.
* v1.91.21.0: move off v1.91.20.0 (claimed by #3031)
* test: settle nav-guard commands before asserting; make two new tests Windows-safe
navigation-guard: Bun's expect(promise).rejects slows each Playwright round-trip
to ~1s while it waits (50 page.evaluate: 43ms plain vs 46.6s inside the matcher),
so the click's 5s timeout expired before the guard saw the link-local request
(CI: 'click: Timeout 5000ms exceeded ... waiting for scheduled navigations').
Settle the command first, then assert on the refusal; a source check keeps it so.
env-host-runtime-assets: PROBE_FILES now matches either path separator.
detector-corpus ship-docsync-late-result: JSON-escape the <ROOT> substitution so a
Windows temp path's backslashes do not break JSON.parse.
* fix: CI run/job ids are not phone numbers; context-recovery fixture gets the full runtime bin
pii.phone.e164 redacted the run id in the published eval report's fetch command
(--run 37235771700), breaking the command it prints. Digit-only spans in a run/job
flag, gh run argument, actions/runs/ or /job/ URL segment, or run_id/job_id key are
now exempt; the same digits elsewhere still report.
context-recovery-artifacts: the fixture's ./bin/ lacked gstack-skill-start and
gstack-context-recovery (the preamble's runtime scripts since v1.71), so the agent
spent its 10 turns hunting for them and hit error_max_turns with no final message
(run 37235771700, 11 turns). Link the whole runtime bin; diagnostic rerun passed
in 6 turns.
* test: replay run 37237194905's late-result capture through the docs authority detector
The only failure was call 20's write to a mistyped shard root (3GanyY for 3GanyU),
which the model disclosed; the detector is correct and the A1 post-snapshot
--out under the fixture home stays permitted. Named red recorded in TODOS.
* test: keep docsync-authority in the Windows profile
The replay test's literal helper path tripped the Windows curator's shebang-spawn
pattern, dropping the whole file from the Windows profile (test-free-shards
invariant). The snapshot --out case is already covered by docsync-command-grammar,
so it is removed here, and the Linux-path replay skips on win32.
* fix: gstack-docs-candidate --help works; docs gate red lines name the denying tool failure
PR run 37239016858 ship-docsync-completion: the parent ran
'gstack-docs-candidate --help' (the helper's usage error points at --help, which
failed). That undeclared command made docsToolFailures non-empty, which denied
the atomic-replacement proof of the child's legitimate native Edit, so the red
line blamed the Edit's temp file. A replay of the capture proves the atomic
replace once --help is declared.
- the helper prints its usage for --help/-h and exits 0
- the fixture interface and command grammar admit the helper's bare --help
- the atomic-proof denial names its check and the tool failures behind it
(deny labels renumbered to their lines)
- replay with negative controls (undeclared command, temp left behind, renamed
elsewhere); diagnostic rerun of ship-docsync-completion passed
* test: retro-base-branch fixture uses main and a shallow report path
PR run 37241493377: the agent wrote retro-output.md one directory above the
requested <tmp>/retro-base/retro-output.md (it dropped the retro-base segment),
so the report check found nothing. The fixture also ran plain git init, which
makes master on CI's git while the prompt says the repo is on main; the agent
logged that mismatch as a learning. Init on main and use the temp dir itself as
the repo. Diagnostic rerun passed (18 turns, 203s).
* test: qa-quick sets a caller probe limit and drains past its session budget
C2 raised Browser Quick's native budget to 180 s. qa-quick is the only gate case
on the native budget, and its wall went 194 s (main periodic, pre-C2) -> 254 s and
274 s (this PR's runs) -> 300 s session timeout (run 37242691913): the agent now
probes every navigation target with a checkpoint, then needs ~80 s to report.
Like qa-only-no-fix, the eval now sets a caller probe limit (30 s), scopes the
fixture, and gives bun's outer timeout the session drain margin instead of racing
the 300 s session. The product's 180 s budget and the session budget are unchanged.
Diagnostic rerun passed in 195 s.
* review/ship: use main's post-expiry smoke sentence; v1.91.24.0
GSTA-23's measurement on the review workflow judge: main's 'Post-expiry smoke
rechecks are not-run.' passed 10/10; the C2 wording passed 6/10 and the
'..., even in 4c.' variant failed in PR run 37248650879. qa-probe-gates pins
main's sentence before step 4c.
* test: record the plan-design corpus test in the test-of-test baseline
#3031's ratchet now counts tests that slice a paid test file's source; the B1
plan-design-review-plan-mode replay corpus reads test/skill-e2e-design.test.ts
like its sibling corpora already listed.
* fix: --case no longer deadlocks importing the running CLI
runCaseDiagnosis loaded runPaidShards with a dynamic import of
scripts/test-paid-shards.ts; when that file is the entry it is still in its
top-level await, so the import never resolved and `--case` hung after its
first line (on main since #3031, found by the per-case measurement runs). The
CLI now passes its runner in; library callers keep the lazy import.
* test: name the in-flight file and the native error when bun dies before its summary (#3037)
* Bun 1.4.2 everywhere CI and setup pin the tested version
Bun 1.4.0 on Windows aborts about 3% of free-suite shards with native
bad-handle errors (main too; #3037); the same head on 1.4.2 had none in 180
shard runs. Every current 1.4.0 pin moves together, as bun-version-drift
requires: setup-bun in every workflow (CSO orchestration included), the eval
image (sha256 from bun-v1.4.2's SHASUMS256.txt, re-hashed locally), GitLab,
the Ubicloud free-suite setup, gstack-bun-version.sh's tested version,
engines.bun and @types/bun, the install hint in generated skills, the Dia and
cookie native-qualification pins, and docs and tests that state the current
pin. Recorded 1.4.0 observations, fixtures, CHANGELOG history and the CSO
images' oven/bun:1.3.10 base stay.
* fix: --case runs a single-case file whole
A session option naming the case (testName: 'office-hours-section-loading')
made caseSelection pick test-name mode, whose filter matched no Bun test, so
every trial would run hollow. A file that registers only this case now runs
whole before any name match is tried.
* fix(office-hours): reviewer returns a hash-bound receipt instead of echoing its verdict
The spec reviewer now Writes its verdict once, seals it with
gstack-office-hours-review seal (validates the saved JSON and prints
OFFICE_HOURS_VERDICT round=N sha256=<hash> path=<verdict>), and returns
only that line. check --receipt recomputes the hash and fails closed on a
missing, malformed, stale-round, wrong-path or mismatched receipt. The
saved file stays the sole findings inventory; validators bind the receipt
to the saved bytes and the judge reads those bytes.
* fix(office-hours): only blocking findings send a design back for another review round
Review findings carry severity blocking|minor (verdict schema v2). PASS when
no blocking findings remain; minor findings are recorded in Reviewer
Concerns and the Spec Review table, never a reason for another round.
CONVERGENCE needs a persisting blocking prior. A persisting or unverified
blocking prior cannot be relabeled minor. The 3-round cap is unchanged.
* fix(office-hours): rounds 2 and 3 are delta re-reviews of the captured design diff
prepare saves each reviewed design as round-N.design.md and gives later
reviewers the exact line diff since the previous round. A new later-round
blocking finding must cite (changed_text) an excerpt of a changed line;
seal, check and the validators verify it against the snapshots and fail
closed when a citation or snapshot is missing. Other new observations are
minor. Severity, anti-downgrade and CONVERGENCE rules are unchanged.
* test(office-hours): preservation judge accepts COMPLETED with recorded minor findings
* fix(office-hours): parent fixes only blocking findings; callers may set --max-rounds
Between rounds the parent edits for blocking findings only; minor findings
stay recorded. gstack-office-hours-review takes --max-rounds 1|2|3 (default
3) on every command; reaching it stops at MAX_ITERATIONS. The marathon
fixture sets 2 rounds so round 1 (full) and round 2 (delta) run live.
* docs(changelog): office-hours review loop changes in v1.91.28.0
* fix(cso): helper JSON keeps run URLs only in the validated workflow slot
The shared redactor now treats GitHub Actions run ids as CI metadata
(
|
||
|
|
ac20ef1e13 |
v1.91.16.0 fix: safe installs and upgrades, skills start everywhere, truthful completion (parallel wave) (#3014)
* fix(make-pdf,design): real TOC page numbers and anchors; design models move to gpt-5.5 + gpt-image-2 (#2903, #2807) * ci: pin every action to a SHA, Scorecard, checksummed Bun, scoped Codex CLI in the CI image, macOS named-regressions job (#1948, #1706, #1997) * fix(cso,browse): Windows/macOS lease identity, retention skips bad runs, truthful 'not assessed' reports, env.kv false positive, Windows browse spawn fixes (#2894, #2912, #2048, #2637, #2805) * fix(outside-voice): one Codex model precedence for probe and dispatch, bounded timeouts with KILL escalation, probe cache identity, guarded mktemp, skills hidden in nested Codex (#2776, #2914, #2787, #2881, #2847) * feat(setup): install registry, ./setup --status, staged activation, per-scope migrations, host tiers and conformance kit, OpenCode/Hermes fixes, Copilot CLI (experimental), disabled_skills, slimmer CLAUDE.md (#1925, #2879, #2347, #2338, #2626, #2629, #2825, #2015, #393, #1206, #2096) Co-authored-by: Andrey Esipov <61357035+andrey-esipov@users.noreply.github.com> Co-authored-by: Matthew Williams <5863886+ridermw@users.noreply.github.com> Co-authored-by: Rocky Wu <92764750+lolisaigao1234@users.noreply.github.com> * fix(preamble): skills start in worktree-isolated Claude Code sessions — one literal skill-start line, context-recovery helper, no eval in skill blocks, $N token tripwire, honest plan-mode precedence (#2763, #2896, #2851) * fix(setup): per-install renders for renamed, vendored and custom-config installs, space-safe alias roots, per-host /cso launcher path (#1882, #2906) * test: free tests never write the real HOME (home-write tripwire), no orphaned browse daemons, symlinked-TMPDIR (macOS) fixes, gbrain-on-PATH isolation (#2895, #2816, #2829, #2841, #2982, #2911, #2909, #2908, #2910) * chore: integrate wave follow-ups — regenerate all hosts, README model docs, TODOS, touchfiles, duration seeds, Bun 1.3 telemetry test fix, replay fixtures pinned to their recorded bytes * v1.91.14.0: release metadata * test(browse): group-kill cleanup falls back to the leader when macOS returns EPERM for a live group * ci: Codex login steps run under bash (the CI container's default sh rejects pipefail) * fix: full-suite repairs — private state roots for 7 real-HOME writers, truthful home-guard attribution with --attribute-home, gstack-paths shim in ship-hook fixtures, Copilot in team-init probes, test-of-test detector, cookie review fixture pinned to the approved excerpt * fix(windows): LF for Dockerfiles, normalized home-guard exclusions, path.relative fixture containment, ship push test spawn timeouts * test(design-detect): widen the whole-scan budget test's timing margins so a loaded macOS runner can't time out the single-batch call * fix(evals): qa protocol names defined for the judge, async PTY judge so overlapping sessions keep their cleanup deadline, bws setup cases use the standard JUDGE_MS tier * test(pty): the non-blocking judge case is POSIX-only (its fake judge is a shebang script, like the PTY harness it covers) * test(evals): ship-coverage-audit names absolute skill paths, uses Read, and skips the Step 7 artifact nothing consumes (removes the mechanism behind 3 of 4 historical timeouts) * test: deflake terminal-agent concurrent startup (write the placeholder record before spawning, like the product) and qa-evidence resumed pipe (barrier on a full high-water mark instead of a sleep) * fix(design): daemon log stream handles open errors instead of crashing the daemon; shutdown test awaits the stubbed exit * test(evals): skillify checks the product's resolved state root (no GSTACK_HOME bypass), negative cases check every root; tpa shims live outside the agent's cwd and answer consistently * test(evals): coverage-audit detector splits parallel columns at │ rails and [+] groups; coverage prompts name absolute skill paths; TODOs from the gate eval census * fix(plan-eng-review): scope gate always sends the menu (no headless 'Scope pending' exit); PTY harness ends idle turn-ends and API-error panels promptly with a precise diagnostic * test(evals): one position-bound coverage-read rule credits read-only && chains (labels, context ranges, git displays in either order) and still rejects forged output * fix(ship): documentation section has one saved source inserted by cat (never retyped); spawned-marker evidence is the child output; refreshed codex/factory ship goldens * test(docsync): the byte-exact insert positive is POSIX-only (the bash-command observer refuses backslash paths by design) * test(evals): plan-design-with-ui focus-title rule accepts the comma-less 'dimensions or focus' wording (replayed on the captured CI run, negative controls kept) * fix(qa-callers): exploratory preflight says the scope/method Reads come after exploratory.md; design-review scan check credits an executed scan proven by one engine run over the changed files --------- Co-authored-by: Andrey Esipov <61357035+andrey-esipov@users.noreply.github.com> Co-authored-by: Matthew Williams <5863886+ridermw@users.noreply.github.com> Co-authored-by: Rocky Wu <92764750+lolisaigao1234@users.noreply.github.com> |
||
|
|
74512c20a7 |
v1.91.15.0 v1.91.15.0: Opus 5.5 prompt cleanup, behavior-level skill tests, transcript consent (#3017)
* fix(models): allowlist routing for model-pinned Claude families (W1) resolveModel lowercases input and strips one trailing [...] context marker. Opus 4.7, Opus 4.8 and Sonnet 5 keep their pinned overlay only for the bare ID, the bare ID plus an 8-digit date, and the bare ID plus -latest; any other suffix (point releases such as claude-sonnet-5-5, claude-opus-4-7-1) falls to the generic claude overlay. claude-opus-5-5 routes to claude explicitly (no new overlay, no new ALL_MODEL_NAMES entry). Fable keeps its prefix match while CLAUDE_FRONTIER_EVAL_MODEL is claude-fable-5-1, guarded by a test. Tests: one test/model-overlays.test.ts case per shape in the plan, plus the lowercase / [1m] marker / family-name cases and the Fable guard. * docs(CLAUDE.md): define when prompt bytes are a test contract (W7 prompt-byte rule) Adds the gstack-repo definition to the Testing section at normal volume: prompt bytes are a contract only when software reads them or a recorded eval shows the wording matters. Amends the test-value-bar line so 'prompt bytes' refers to that definition. scripts/resolvers/test-value.ts and docs/test-value-bar.md are unchanged. * fix(browse): sidebar tab hint names real tabs.json fields and how $B resolves (s11 H1, s11 M8, s11 M9) * fix(router): route telemetry uses the echoed SESSION_ID (s01 H1) $_SESSION_ID is never set in the router's bash block since bin/gstack-skill-start took over session bookkeeping, so every /gstack route event logged an empty session id. Use the literal SESSION_ID placeholder and tell the model to substitute the echoed value, matching the completion-status telemetry block. No W7a pins on the changed line. * docs(CLAUDE.md): correct stale test-infra facts (s01 H2, H3, H4, H5) H2: TREE_MUTATING is not empty; it still holds bootstrap-retention. H3: catalog ceiling is CATALOG_BUDGET_TOKEN_EQUIVALENTS (1,194), not 1,171. H4: detach timeouts point at package.json's --timeout values (gate is 49320s). H5: Writing Style lives in preamble/generate-writing-style.ts and the jargon list is Read at runtime, not baked at generation time. No W7a pins on the changed lines. * fix(preamble): drop the stale jargon-list count (s01 H6) The list has 77 terms, not 80+, and the model Reads the file anyway. No W7a pins on the changed text. * fix(overlay): claude.md dedicated-tools nudge stops naming tools the host may lack (s01 M1) The nudge named Glob and Grep, which some Claude hosts do not expose. It now names the host's dedicated file tools and its search tools when it has them, keeping the prefer-dedicated-tools behavior the claude-dedicated-tools-vs-bash overlay eval measures. Updates the prose comment at test/gen-skill-docs.test.ts:643 (comment only, no assertion change). The matching comment in test/fixtures/overlay-nudges.ts is left to its owner. * fix(preamble): progress notes on phase changes, not a [PROGRESS] cadence (s01 M2) Context Health told the model to periodically write a tagged [PROGRESS] summary. No code reads that tag; the line now says when to tell the user (finishing a phase or changing direction). Loop detection stays. W7a pin: test/terse-build.test.ts:103 pinned 'PROGRESS'; it now checks the default build emits a non-empty Context Health body (terse still emits ''). * fix(preamble): learnings review without incident archaeology (s01 M3) Drops the issue number, the 43-of-44 statistic and the ALWAYS caps from the Operational Self-Improvement step; keeps the rule that the review runs every time and that an empty result is stated explicitly. No W7a pins on the removed text. * docs(CLAUDE.md): fold 'Long-running tasks' into the detach section (s01 M4, CEO W4) The 'don't give up' section told agents to poll a plain background task with sleep 180 + TaskOutput, which the next section forbids for evals. Its one durable rule (keep checking until the run finishes, report progress at each check) now lives in the detach section's poll step, pointed at the gstack-detach EXIT sentinel, at normal volume. No tests pin the removed text. * fix(preamble): test-failure triage questions defer to the AskUserQuestion Format (s01 M5) The two quoted pseudo-briefs pinned a superseded question shape (RECOMMENDATION: Choose X, a completeness score on the recommendation, no D<N>/ELI10/Net). The triage step now gives the facts, the options with their completeness values, and the recommended option as data, and defers the shape to the Format section. Option labels that Step T4 and test/skill-validation.test.ts:2004-2016 depend on are verbatim. No W7a pins on the removed text. * fix(proactive): PROACTIVE=false means no suggestions, including 'want me to run /X?' (W5.1) The router forbade suggesting skills when PROACTIVE is false while the preamble told the model to ask 'want me to run it?'. Decision: false means never auto-invoke and never suggest, including by asking. The preamble line in generate-upgrade-check.ts now matches the router, and both quote the value as `false`, the way gstack-skill-start prints it. Restore with gstack-config set proactive true. W7a pin: test/gen-skill-docs.test.ts:4488 used the old quoted sentence as a preamble-order marker; it now finds the PROACTIVE-false rule with a quote-tolerant, case-insensitive pattern. * docs(ETHOS.md): correct the claim that ETHOS is injected into every preamble (W5.7) Nothing injects ETHOS.md. Preambles carry condensed principles (completeness, voice, search before building) and the Search Before Building section links to this file. Edit approved by Garry for this PR. * office-hours: fix stale refs, state-root path, profile query, SESSION_ID (s07 H1, H2, H3, M7, M10; s01 H1 office-hours note) - Phase 4.5 points at Founder Resources instead of the nonexistent Beat 3.5 (H1). - builder-journey.md is written to $GSTACK_STATE_ROOT, where it is opened (H2). - builder-profile context query reads developer-profile.json, the file the migration leaves behind; drop meaningless tail (H3). - Drop issue numbers from three headings (M7) and the MANDATORY/NOT optional markers on Phase 4 (M10). - Handoff telemetry uses the echoed SESSION_ID placeholder; $_SESSION_ID is never set in a fresh shell. Template-only commit: generated SKILL.md is expected red until the regeneration commit. * spec: plain Phase 3 gate, drop duplicates, dead telemetry and review IDs (s07 H4, M1, M2, M3, M4, M5, M6) - H4: Standard template heading no longer names nonexistent --bug/--feature/--refactor flags. - M1: Phase 3 read-code-first gate at normal volume, constraint and reason kept. - M2: drop the in-phase repeat of the read-first rule (Rules recap stays). - M3: drop the duplicate audit-sink paragraph. - M4: drop TTHW telemetry block and its never-set frontmatter keys. - M5/M6: drop plan-review finding IDs from Phase 5 headings and the /ship handoff. W7a pins: spec-template-invariants:314 (Mandatory/MUST) becomes a Phase 3 section check for the read-evidence-before-asking constraint; :203 matches 'raw spec must not be persisted' across line wraps so no duplicate paragraph is needed to satisfy it. Template-only commit: generated SKILL.md is expected red until the regeneration commit. * investigate: drop issue numbers from freeze-script comment (s07 M8) Template-only commit: generated SKILL.md is expected red until the regeneration commit. * qa: replace WTF point table with named signals and a plain stop rule (s07 M9) The self-regulation check keeps its cadence (every 5 fixes or after a revert), its signals (reverts, unrelated files, sprawl, long runs, low-severity tail), STOP-and-ask, and the 50-fix hard cap. The percentage table goes; the stop threshold (two reverts, or one unrelated-file edit) stops at least as early. W7a pins: qa-browser-preservation:220 drops 'WTF > 20%' and checks the signals case-insensitively; skill-validation:1250 checks the test-only exclusion rule instead of the heading name; carve-guards qa anchor 'WTF-LIKELIHOOD' becomes the '### 8f. Self-Regulation' heading. Template-only commit: generated SKILL.md is expected red until the regeneration commit. * design-html: match Pretext docs to the vendored bundle; fix cross-block server state (s08 H1, H2, H3, H4, H7, M6) - H1-H3: walkLineRanges, layoutNextLine and layoutWithLines signatures and return shapes now match design-html/vendor/pretext.js; patterns 2-4 rewritten. - H4: inline the ES-module bundle in <script type="module"> with page code in the same module; no window.Pretext global exists. - H7: remember the printed server URL and PID; later blocks restate them. - M6: drop the 2-3 line cap on the refinement summary. Template-only commit: generated SKILL.md is expected red until the regeneration commit. * make-pdf: drop nonexistent --no-syntax flag, trigger list and relative phrasing (s08 H5, M7, M9) Template-only commit: generated SKILL.md is expected red until the regeneration commit. * design-shotgun: DESIGN.md decides what variants vary; fix $SLUG; drop history (s08 H6, M1, M2, M3; W5.6) - H6: the office-hours lookup block evals gstack-slug before using $SLUG. - M1 + W5.6: with a DESIGN.md, variants keep its fonts and palette and vary layout and composition; without one they vary font, palette and layout. The swap-the-headline test and the 'Anti-convergence directive' heading (carve-guards anchor) stay. - M2: drop the Claude Code version pin; run_in_background: false and its reason stay. - M3: /tmp-then-cp rule and reason without the incident story. G1 (replace executor subagents) deferred to TODOS.md per plan. Template-only commit: generated SKILL.md is expected red until the regeneration commit. * design-review: plain identity line; scope 'never read source' to the audit (s08 M4, M10) M5 (risk-percentage stop conditions) is a UC2 gate and keeps its wording. Template-only commit: generated SKILL.md is expected red until the regeneration commit. * design-consultation: plain CLAUDE.md design-system snippet with its reason (s08 M8) Template-only commit: generated SKILL.md is expected red until the regeneration commit. * plan-tune: describe the never-ask hook truthfully; drop plan codes (s09 H8, H9, H10, M10; W4 plan-tune) W4: make the text true rather than change the hook. The consent copy and scope now say never-ask questions are auto-decided with gstack's recommendation by the question-preference hook (one-way doors still ask), and the profile itself never changes defaults. H10 removes the nonexistent --dismiss command. M10 strips cathedral/Layer/D-codes and keeps mechanisms. Template-only commit: generated SKILL.md is expected red until the regeneration commit. * context-save/restore: drop rename history; fix command-list branch rule (s09 H11, M1, M2, M3, M4, M5, M6) Template-only commit: generated SKILL.md is expected red until the regeneration commit. * retro: correct what the skill writes; drop word cap and archaeology (s09 H13, M7, M8, M9, M25) H13 and M7 touch neighbouring lines and were merged by hand in that order. Template-only commit: generated SKILL.md is expected red until the regeneration commit. * health: compute the composite in code; fix example weights; drop D6 codes (s09 H12, M21, M22) Template-only commit: generated SKILL.md is expected red until the regeneration commit. * gstack-upgrade: drop issue numbers and version history, keep rationale (s09 M20) Template-only commit: generated SKILL.md is expected red until the regeneration commit. * canary: replace the 30-second/don't-over-analyze rule with the goal (s09 M23) Template-only commit: generated SKILL.md is expected red until the regeneration commit. * benchmark: self-contained page discovery instead of a /canary reference (s09 M24) Template-only commit: generated SKILL.md is expected red until the regeneration commit. * codex: drop judge vocabulary and the maintainer known-bad-list note (s10 M1, M2) The synthesis line keeps its Recommendation: <action> because <reason> shape. M16 (consult-mode embed rule) is a UC2 gate and keeps its wording; the outside-voice prompt bodies sent to Codex are unchanged. Template-only commit: generated SKILL.md is expected red until the regeneration commit. * document-generate: true redaction-fence behavior; drop thoroughness booster (s10 H8, M3) Template-only commit: generated SKILL.md is expected red until the regeneration commit. * document-release: changelog sell test as three questions, not a 0-3 score (s10 M6) The threshold stays (fewer than two answered = needs attention, not replacement). M4/M5 (NEVER CLOBBER / NEVER BUMP) keep their wording; the integrator owns the eval-gated rewrite. W7a pin: workflow-excerpt:170 'Entries scoring <2 need attention, not replacement' becomes a case-insensitive check that weak entries get attention, not replacement. Template-only commit: generated SKILL.md is expected red until the regeneration commit. * ios-clean/fix/sync: match the generator's real header, package shape and regen command (s10 H2, H3, H4, H5, M9, M12) - H2: inventory greps the header the generator actually writes. - H4/H5: remove the local .package(path:) entry and DebugBridge* product deps; delete the generated DebugBridge/ and DebugBridgeGenerated/ dirs so the Phase 3 check can pass. - H3: regenerate accessors with /ios-sync (gstack-ios-qa-regen). - M12: ios-fix writes the regression with the project's own test runner. - M9: drop migration-relative sentence. ios-qa DEMO MODE (M8) is a UC2 gate and keeps its wording. Template-only commit: generated SKILL.md is expected red until the regeneration commit. * setup-deploy: drop User-invocable boilerplate; say default branch, not main (s10 M10, M11) Template-only commit: generated SKILL.md is expected red until the regeneration commit. * skillify: test staged skills directly; stop after Aside scrapes; drop roadmap notes (s10 H1, H6, H7, M14, M15) Template-only commit: generated SKILL.md is expected red until the regeneration commit. * W4: transcript ingest needs affirmative consent (recent|all) normalizeTranscriptConsent() in gstack-memory-ingest reads transcript_ingest_mode once per process. Only recent (90 days) and all (all history) are consent; an absent key, off, legacy gate letters, incremental and unknown values skip transcripts while other memory still syncs. The ingest's default source set drops transcript without consent; an explicit --sources list naming transcript overrides. gstack-gbrain-sync reads consent once at sync start and pins a non-consented run to an explicit source list, so a mid-run config change cannot add transcripts. It prints the pinned skip notice on every such run (even --quiet), records the normalized mode and skip reason in the memory stage, restages an interrupted import when the consent policy changed, passes --all-history on --full for mode all, and documents the default in --help. Tests: new gstack-gbrain-sync-transcript-mode.test.ts; memory-ingest fixtures seed consent; regression-2922's every-type case seeds consent. Findings: s09 H16 (enforce the consent choice in code). * W4: gstack-brain-sync holds staged transcript pages without consent Queued transcripts/ pages stay queued and on disk unless transcript_ingest_mode is recent or all; other classes still push. Test: brain-sync.test.ts push-filter case (absent, off, legacy A, then recent). * W4: gstack-config validates transcript_ingest_mode; set rejects empty and multi-line values transcript_ingest_mode accepts only recent|all|off and leaves the stored value unchanged otherwise. set rejects an empty value or one containing a newline for every key instead of truncating it. The config header documents the key; lookup_default keeps off. * setup-gbrain, sync-gbrain: fix stale facts and strip plan archaeology Applies s09 H1a, H1c (Mac-only wording; the installer supports Linux and Windows), H2 (smoke test has no delete step), H3 (five invocation modes), H4 (no CI grep test backs the telemetry claim), H7 (Step 4.5 does not exist; the prompt is Path 4 step 4d), M11, M12, M13, M15, M18, M19 (plan codes, issue numbers, version pins, rename story), M14 (restore-from-sync never shipped), M16 (draft self-dialogue in the Supabase steps), M17 (passed sunset date, manual merge into both sections). W7a: retires the setup-gbrain-bin-invocation-paths 'now persists staged transcripts' change-detector (t06 #17); the no-bare-invocation regex tests keep the contract. * W4: transcript consent prose in /setup-gbrain, /sync-gbrain and memory.md The Step 7.5 gate is rewritten as a consent question (manual merge of s09 H5, with H1b and H6): it checks gstack-config has, asks once with A) recent, B) all history, E) never, names Claude Code and Codex sessions from every repo-policy-allowed project and the destination brain, stores the mode value (never the letter), asks yes/no when no transcripts exist, never asks spawned or headless sessions, and drops the silent no-question bulk paths and the false skill-start-hook claim (s09 H5c). Path 4 no longer stores off on the user's behalf. /sync-gbrain gains Step 1.6 (ask when the key is absent or holds a legacy value). Step 10 and the sync verdict show the mode. setup-gbrain/memory.md gains #transcripts (values, sources, destination, override precedence, one-time restage, held staged pages, already-ingested pages stay). W7a: setup-gbrain-bin-invocation-paths drops the silent-bulk and preamble-hook pins (that prose is gone) for negative checks (no pre-answer --bulk, no skill-start claim), loosens the gstack-config pin (t06 #18) to the command without <choice>, and adds the DX 'gated on has' order check for both skills. * W5.2: /sync-gbrain is the only writer of the CLAUDE.md search-guidance block setup-gbrain's claude-md-persist section stops writing its own '## GBrain Search Guidance' block, which also removes the false 'auto-syncs on every gstack skill start' text (s09 H15; s09 H14 decided). Step 10 offers to run /sync-gbrain now and names it otherwise. Test sweep: only skill-e2e-sync-gbrain-readiness references the heading, and it checks the block /sync-gbrain writes, so no test changes. * E4: config-key reader tripwire in gstack-config-defaults.test.ts Every key in bin/gstack-config's DEFAULTS table must be read by code (gstack-config get/has <key>, a config-binary variable, a spawn argv, a config-reader call, or an anchored ^key: pattern in bin/, lib/, scripts/, setup or any .tmpl; wildcard keys by prefix) or appear in PROSE_ONLY_KEYS with a reason. Run on the current tree first: every key has a reader, so the seeded prose-only list is empty. Failure messages name the two fixes. A synthetic-tree case checks each reader form and that docs prose does not count. setup is scanned because timeline_stop_hook is read only there. * W4: /open-gstack-browser asks before replacing a live browse daemon Step 0 probes with BROWSE_NO_AUTOSTART=1 $B status (no daemon is booted by the probe). No live daemon: clear Chromium profile locks and run plain $B connect, which reaps orphans and refuses to replace a busy daemon. Already headed: connect reports it. Live headless daemon: AskUserQuestion, and only an explicit yes runs $B connect --force-restart; spawned or headless sessions leave it running and print the stop line with the resolved browse binary. The hand-rolled kill / kill -9 of the recorded PID and the browse.json deletion are gone. Supersedes s10 M13. Tests: new open-gstack-browser-liveness.test.ts (no kill of the recorded PID, status probe before connect, --force-restart only after the yes branch, profile-lock cleanup kept, spawned/headless stop line). The state-extraction tripwire now covers only the port snippet, since the pid snippet is gone. * fix(browse): untrusted-content warnings name both marker formats (s02 H5, s02 L2 / W7f) The /browse warning named only the --- UNTRUSTED EXTERNAL CONTENT --- envelope and the browser fallback only the ═══ UNTRUSTED WEB CONTENT ═══ one; $B prints both (commands.ts wrapUntrustedContent, content-security.ts ENVELOPE_BEGIN). Both surfaces now name both formats. W7f pin: test/aside-driver.test.ts:347-350 no longer bans the EXTERNAL marker; it reads both marker labels from browse/src/commands.ts and browse/src/content-security.ts and requires both in the fallback and warning. Negative control: reverting browse.ts turns the test red. Parity cap (test/helpers/carve-guards.ts): qa maxSizeRatio 1.103 -> 1.104 (measured 1.1035); the second marker adds bytes to every skill that renders the browser fallback. expected red until the regeneration commit (generated SKILL.md not committed) * fix(design): mockup board loop matches the daemon CLI (s02 H1, s02 H2, s02 H3) - H1: $D compare --serve publishes to the design daemon and exits; read BOARD_URL from stderr, wait with AskUserQuestion, then read feedback.json / feedback-pending.json. The fallback no longer names a $D serve command the step never runs. - H2: the reload POST uses the jq --arg form, so $_DESIGN_DIR expands (the single-quoted JSON sent the literal variable name). - H3: Content & Microcopy lists 11 items, not 8. No test pins the removed text. expected red until the regeneration commit * design: name the defaults to avoid in native design-direction prompts; plain path rule (s02 H4a, s02 H4b, s02 M9) - H4a/H4b: the design-consultation and design-sketch native subagent prompts now name the default looks to avoid (the three looks from the Hard Rules calibration plus the catalog entries the sibling outside-voice prompt already lists) and say to pick again if the first idea is one of them. - M9: 'CRITICAL PATH RULE' -> 'Path rule'; the rule and its reason stay. No test pins the changed text. Parity cap (test/helpers/carve-guards.ts): design-consultation maxSizeRatio 1.085 -> 1.092 (measured 1.0911). expected red until the regeneration commit * fix(outside-voice): preflight mode list no longer looks exhaustive (s02 M10) The preflight also prints under_codex and under_current_harness; not_authed exists only on the Codex path. 'every other mode skips them' covers all non-ready modes. No test pins the old line. expected red until the regeneration commit * outside-voice: drop booster sentences and plan-review decision IDs (s02 M1, s02 M7) - M1: native adversarial subagent prompt drops 'Be adversarial. Be thorough.'; the attacker framing, defect list and 'No compliments' output rule stay. - M7: doc-review headings drop the D3 / T3B plan-review decision IDs (D<N> also collides with AskUserQuestion brief numbering). Not changed: s02 M2 (outside adversarial prompt body sent to Codex/GPT: out of scope per plan) and s02 M3 (CODEX_BOUNDARY: UC2 gate keeps its wording). No test pins the removed text. expected red until the regeneration commit * confidence: drop issue IDs and the contributor-only design-doc pointer (s02 M4a, s02 M4b) - M4a: gate heading and FP-class intro lose '#1539' and the Django sprint measurement note; the gate rule and its four FP classes stay. - M4b: remove the pointer to ~/.gstack-dev/plans/1539-framework-aware-review.md, a contributor-local file that never exists where /plan-eng-review and /ship run. W7a pins (test/regression-1539-review-self-verify.test.ts): - :34 toMatch(/#1539/) removed (cosmetic issue-number pin; the gate-header check stays). - :64-67 'references the deferred design doc' replaced by a structural check that no .gstack-dev/plans/ path reaches the emitted prose. expected red until the regeneration commit * question-tuning: state why the summary is piped instead of citing #2024 (s02 M6) No test pins the resolver text (#2024 appears only in test names/comments). expected red until the regeneration commit * fix(testing): shared TEST_BOOTSTRAP undoes only owned changes (s03 H1, s03 H2, s03 H3, s03 M1) The shared {{TEST_BOOTSTRAP}} (/ship, /design-review) still told the agent to 'git checkout -- package.json package-lock.json', to 'revert all bootstrap changes', to 'delete silently' a failing first test, and to stage 'all bootstrap files'. The QA copy (qa/sections/test-bootstrap.md.tmpl) already says undo only owned changes, never blanket-checkout, never silently delete a valid red regression, and stop on unrelated staged edits. The shared copy now matches it. This removes destructive behavior (UC2: lands regardless of eval). W7g: test/qa-browser-preservation.test.ts gains a check that the shared copy (ship, design-review) has no 'git checkout --', no silent deletion, no blanket revert, and keeps the owned-change and staged-edits rules (case-insensitive). Negative control: the old testing.ts turns it red. Parity cap (test/helpers/carve-guards.ts): ship maxSizeRatio 1.404 -> 1.405 (measured 1.4044 at the branch tip, with the s03 M2/M3 wording). expected red until the regeneration commit * testing: coverage trace and test generation follow the value bar (s03 M2, s03 M3) - M2: 'Every branch in this diagram needs a test' / 'Each step in the journey needs a test' contradicted COVERAGE_GOAL ('Test count is not a goal') in the same rendered audit. Branches and journey steps now need coverage that would catch a regression; the value bar decides new test vs extension vs covered. - M3: 'Mock all external dependencies' -> the smallest native test, mocking only services unrelated to the behavior (matches the QA handoff in qa.ts and the audit's own E2E routing). No test pins the removed text. expected red until the regeneration commit * plan-gates: per-item confirmation states its reason, not an incident ID (s03 M4) 'VAS-449' meant nothing to the model and reached the user in the >5-items warning. The rule now gives the reason (a blanket confirmation lets the user click through without opening any file / lets unchecked items pass as verified). The per-item confirmation rule is unchanged. W7a pin: test/ship-plan-completion-invariants.test.ts:122 toMatch(/VAS-449/) removed (cosmetic incident-ID pin); the per-item rule and blanket-confirm ban checks on the same test stay. expected red until the regeneration commit * plan-gates, review-dashboard: terminal-report rules without trait claims or 'prior versions' narrative (s03 M5, s03 M7) - M5: exit gate drops 'will (correctly) reject it' and the 'Self-deception failure mode' aside; keeps the contract-violation sentence (pinned by test/gen-skill-docs.test.ts) and the body-prose-is-not-the-report rule. - M7: report-write step states the rule and its reason (delete and append at EOF so the report stays last) instead of the history of how it broke. No test pins the removed text. expected red until the regeneration commit * spec-review: default anti-shortcut clause without incident narrative (s03 M6) Drops 'the May 2026 transcript bug ... that's the bug, recognize it' and the caps; keeps the rule: each non-trivial finding goes through AskUserQuestion before it enters the plan, and only a zero-finding review skips asking. The four skill-specific branches are unchanged. W7a pin: test/skill-ceo-section-ordering.test.ts:868-873 pinned the SHA-256 of the default clause. It now checks, on every host, that the clause still says the plan cannot replace the interactive review, routes findings through AskUserQuestion, and lets only zero findings reach ExitPlanMode without asking (case-insensitive), and that the clause is host-independent. The old clause also passes; deleting the zero-findings rule turns it red. The DX hash pin is unchanged. expected red until the regeneration commit * constants: foreground-dispatch note in current-state terms at normal volume (s03 M8) Keeps every constraint (explicit run_in_background: false, Agent tool only, never inline or as a Skill, fallback only after a dispatched subagent fails) and the reason (fresh-context isolation); drops 'no longer', BACKGROUND/ONLY/ WRONG/FALLBACK caps. test/ship-control-flow.test.ts and run-in-background-guidance.test.ts check the placeholder and the explicit flag, not this prose. expected red until the regeneration commit * tasks-section: inline the effort ratios for every phase (s03 M9) The non-CEO/Eng branch said 'Effort uses the AI-compression table from CLAUDE.md', but at runtime CLAUDE.md is the user's project file; the table exists only in gstack's own repo. Every phase now renders the inline ratios the CEO/Eng branch already used. No test pins the removed text. expected red until the regeneration commit * hosts, utility: model-neutral Claude co-author trailer (s03 M10, s11 M10) The Claude host trailer and the resolver fallback (cursor, kiro, opencode, slate) signed commits as 'Claude Opus 4.7' whatever model ran. Both now read 'Co-Authored-By: Claude <noreply@anthropic.com>' so the trailer cannot go stale at the next model release (the s11 report offered this neutral form; s03 M10's hunk uses it). document-release's release body renders the same {{CO_AUTHOR_TRAILER}} and follows on regeneration. test/host-config.test.ts:589 only requires 'Claude' and stays green. Goldens move on regeneration (not committed). expected red until the regeneration commit * changelog workflow: cross-check covers user-facing changes, not every commit (s03 M11) 'Every commit must map to at least one bullet' contradicted the same block's voice rule (never mention contributor-facing details) and CLAUDE.md's entry rule (the diff vs main, not how the branch got there). Every user-facing change in the commit list must be represented; merges, version bumps and mid-branch fixes need no bullet. W7a pin: test/gen-skill-docs.test.ts:2038 no longer pins the old sentence; it checks the Cross-check step still compares against the step-2 commit list and scopes to user-facing changes (case-insensitive), without re-pinning the new sentence. expected red until the regeneration commit * plan-ceo-review, plan-eng-review: storage paths follow the state root (s04 F2, s04 F3) The CEO storage policy and the eng write policy named ~/.gstack/projects/ and ~/.gstack/analytics/, while the writers (tasks-section.ts, testing.ts, spec-review.ts) use $GSTACK_STATE_ROOT. When the state root is relocated the prose pointed at the wrong directory. W7a pin: test/plan-review-cases.test.ts:852 pinned the stale path; it now checks the discovery path is under $GSTACK_STATE_ROOT and that the hardcoded ~/.gstack/projects/{slug}/ is gone. expected red until the regeneration commit * plan-ceo-review: remove the handoff-note reader and cleanup for files nothing writes (s04 F6, s04 F7) CEO handoff notes (*-ceo-handoff-*.md) have not been written since v0.11.5.1; no template, resolver or bin script produces them. The reader cost a bash call on every CEO run, depended on $SLUG/$BRANCH from another shell, and looked under $GSTACK_STATE_ROOT where pre-state-root notes never lived. W7a pin: test/skill-ceo-section-ordering.test.ts:320 sliced up to the removed '## Handoff Note Cleanup' heading; it now slices to the next heading ('## Review Log') with the same assertions. Note: the ceo-mode-option / ceo-mode-prerequisite PTY cases fail under the image's Bun 1.3.14 with or without this change and pass under Bun 1.4.0. expected red until the regeneration commit * plan reviews: replace context-pressure triage with the full-completion rule (s04 F1; manual merge of s05 M2) plan-ceo-review, plan-design-review and plan-devex-review ranked sections 'under context pressure' ending in 'Everything else', which licensed dropping sections their own anti-skip rules require. plan-eng-review already replaced the same block with 'Complete every required stage ... do not preemptively warn'. All three now use that rule and name what may never be shortened (design: Step 0, Step 0.5 mockups unless the user opted out, Passes 1-7; devex: Step 0 with persona/empathy/benchmark/magical moment/TTHW, Passes 1-8). s04 F1 noted the same block in the s05 files; the design copy is a manual merge of s05 M2 (drops 'non-negotiable', which contradicted the user's explicit 'skip mockups' opt-out). No test pins the removed text. expected red until the regeneration commit * plan-ceo-review: shared-code guidance matches the eng review (s04 F4, s04 F5) 'DRY: flag repetition aggressively' and 'DRY violations. Be aggressive.' contradicted plan-eng-review's newer rule (require common behavior and improved reliability or net savings; similar-looking code alone is insufficient), so the two reviews gave the same plan opposite advice. Both CEO lines now follow the eng rule; Section 5 keeps the file:line citation. No test pins the removed text. expected red until the regeneration commit * plan reviews: normal-volume rules; eng sections report every finding (s04 F8, s04 F10, s04 F11) - F8: eng 'at most 8 findings' per section conflicted with 'never condense or skip' and left a 9th high-confidence finding nowhere to go; report every main-report finding, most severe first (confidence calibration still suppresses low-confidence ones). - F10: drop 'It is not optional.' (the anti-skip rule already covers Section 2). - F11: 'ALWAYS a smell' -> 'a smell'. No test pins the removed text. Two eng PTY cases fail under Bun 1.3.14 and pass under Bun 1.4.0 (CI pin). expected red until the regeneration commit * plan reviews: '## CRITICAL RULE — How to ask questions' -> '## How to ask questions' (s04 F9, s05 L3) The bodies are plain procedure; the caps label added nothing and competed with the CRITICAL GAP output label. Applied in plan-ceo-review, plan-design-review and plan-devex-review sections (s05 L3 promoted by the plan). W7a: test/devex-finding-fixture.test.ts:83-84 now finds the section by a heading regex ending in 'How to ask questions'; the skill-e2e-plan-ceo-plan-mode failure diagnosis names the new heading. Judge anchors NOT changed here (builder E moves them to structural anchors): skill-ceo-section-ordering.test.ts (old :938), workflow-excerpt.test.ts:109,296, skill-llm-eval.test.ts:638, workflow-judge-input.test.ts:366,373; the mirror in touchfiles.test.ts:219 also uses '## CRITICAL RULE'. These stay red until E's anchor commit lands. expected red until the regeneration commit * autoplan: phase drivers match the loaded review skills (s05 H1, s05 H2, s05 H3, s05 H4, s05 H5, s05 M5) - H1/H2: the skip list names the current headings ('Step 0: Detect platform and base branch', 'Design Outside Voices (independent)'); the old names never matched, risking a duplicate base-branch detection or outside-voice pass. - H3: the DX journey map is the 6-stage table from Step 0F, not 9 stages. - H4: the DX checklist names Step 0 'DX Investigation' and scores only after the evidence exists, as plan-devex-review requires. - H5 (manual merge): the eng test-plan artifact uses the eng skill's {user}-{branch}-eng-review-test-plan-{datetime}.md name under $GSTACK_STATE_ROOT/projects/$SLUG (consistent with s04 F3). - M5: the Decision Audit Trail separator has 7 columns to match its header. No test pins the removed text. Three autoplan PTY cases fail under Bun 1.3.14 and pass under Bun 1.4.0. expected red until the regeneration commit * plan-design-review: scope gate runs Step 0 before mockups (W5.4, s05 H6) The scope gate said 'audit → mockups → Step 0' while the body runs Step 0 (0A-0D) first and 0D announces 'I'll generate visual mockups next'. Per the W5.4 decision, the three gate phrases now read audit → Step 0 → Step 0.5 mockups. 'Reply with A, B, or C' is unchanged. W7a pins: - test/plan-scope-selection.test.ts:616 pinned the old order sentence; it now checks the order of the 'After resolution:' steps (preamble, base branch, audit, Step 0, mockups). Negative control: the old order turns it red. - test/gen-skill-docs.test.ts:4737 normalizer strips the design-only 'Then run the pre-review audit, ... against that plan.' sentence by pattern. expected red until the regeneration commit * plan-design-review: mockup default and retrospective at normal volume (s05 M1, s05 M3, s05 M4) - M1: 'YOUR PRIMARY TOOL' / 'The rule is simple ... Don't ask permission ... Mockups ARE the plan' -> generate mockups without asking when the plan has UI and the designer is available, with the reason; skip only under the Step 0.5 conditions (which include the user's opt-out). - M3: 'be MORE aggressive' on previously flagged areas -> check whether the earlier issue recurs or was only partly fixed and say so. - M4: drop the unverifiable 'Tier 2+ (15+ RPM)' rate-tier aside; keep the one-at-a-time rule and the /design-shotgun pointer. (s05 M2 landed with s04 F1.) No test pins the removed text; the remaining failing case is the plan-scope-selection PTY case that fails under Bun 1.3.14 and passes under Bun 1.4.0. expected red until the regeneration commit * plan-devex-review: DX TRIAGE keeps every pass and reports blockers only (W5.5, s05 H7) The Mode Quick Reference let TRIAGE skip the benchmark, the magical moment and passes 2/4-8, contradicting the anti-skip rule, the 0C target gate and the 'executed all 8 DX passes' self-check. Per the W5.5 decision the TRIAGE cells now read 'Target only' / 'Verify exists' / 'All 8, blockers only', matching the Rating Method's 'flag adoption blockers; skip nice-to-haves'. No test pins the removed cells. expected red until the regeneration commit * ship, review, land-and-deploy, landing-report: fix stale cross-references (s06 H1, s06 H2, s06 H3, s06 H4, s06 H5, s06 H8, s06 M6) - H1: TODOS-format cites /ship Step 14, not 5.5. - H2: the land readiness review is the checklist /ship applies in Step 9 (there is no Step 3.5). - H3: the Apple adapter names the no-credential-talk rule by location instead of 'line 14' (it renders at line 16). - H4: greptile triage diffs against origin/<base> from the caller's detected base, not origin/main. - H5: the land Important Rules recap matches the steps (4-minute CI rounds in Step 3; 30 seconds for merge readback and deploy status). - H8: landing-report calls the util once per bump level (four), and drops the unsupported 'cached by bun' claim. - M6: landing-report says what its base detection does instead of claiming it matches other skills. No test pins the removed text. expected red until the regeneration commit * fix(land-and-deploy): readiness gate uses the project's test command and the live eval store (s06 H6, s06 H7) - H6 (manual merge): TEST_COMMAND comes from CLAUDE.md or AGENTS.md; if neither documents one, ask the user instead of defaulting to 'bun test' (wrong for non-Bun projects and a bare suite run in gstack). The audit hunk's 'persist the answer to CLAUDE.md' write is not adopted: it adds a write the readiness gate never had. - H7: E2E and judge results are searched in the eval store the collector writes (~/.gstack/projects/<slug>/evals/ and its shards/*/ directories), then the legacy ~/.gstack-dev/evals/ fallback; the gate no longer reports 'no E2E tests run today' when results exist. test/state-root-ratchet.allowlist.json: allowlist the EVAL_DIR line with its reason (test/helpers/eval-store.ts writes under os.homedir()/.gstack, so the reader must use the same path, not $GSTACK_STATE_ROOT). Parity cap (test/helpers/carve-guards.ts): land-and-deploy maxSizeRatio 1.10 -> 1.108 (measured 1.1076 at the branch tip, including the W7f marker). expected red until the regeneration commit * ship: Step 9 Pass 1 defers to the checklist's critical categories (W5.3, s06 H9) /ship restated the checklist's Pass 1 as two of its five CRITICAL categories (missing Race Conditions, Shell Injection, Enum Completeness). Per the W5.3 decision it now names 'the checklist's Pass 1 categories', matching /review. No test pins the removed text. expected red until the regeneration commit * land-and-deploy: host-neutral post-merge branch example (s06 M5) The squash-merge cleanup example created 'codex/post-merge-pr-N' branches on every host. The example is now 'post-merge/pr-N', naming Codex Desktop's codex/ prefix as the host-specific case and keeping the detached-HEAD reason and the no-force-push rule. No test pins the removed text. expected red until the regeneration commit * ship: drop issue/incident IDs and the 'continue silently' suppressor (s06 M2, s06 M3, s06 M4, s06 M7) - M2/M3: '(#1946)' and '(#1079)' removed from the credential guard and REST fallback headings; the rules are unchanged. - M4: the audit-failure fallback states the reason (a skipped audit lets unverified plan items ship as done) instead of 'the failure shape that VAS-449 surfaced'. - M7: 'Continue silently — just note the counts briefly' -> report the pass counts in one line and continue to Step 6 (Step 16 cites these counts). Not changed: s06 M1, the Step 16 IRON LAW (UC2 gate keeps its wording). W7a pin: test/ship-plan-completion-invariants.test.ts:127 pinned the VAS-449 sentence; it now checks that the audit-failure fallback still forbids a silent fail-open (case-insensitive). Negative control: deleting that sentence turns it red. expected red until the regeneration commit * test: anchor plan-design excerpts on the question-procedure heading, not '## CRITICAL RULE' (W7a judge-anchor move; workflow-excerpt.test.ts:109,296) The end marker now accepts a RegExp. ASK_QUESTIONS_HEADING matches the heading with or without its 'CRITICAL RULE — ' prefix, so the excerpt is byte-identical on the current templates and survives the s04 F9 / s05 L3 rename. * test: plan-design judge-input case anchors on the question-procedure heading (W7a judge-anchor move; workflow-judge-input.test.ts:366,373) * test: plan-design workflow judge ends its slice at the question-procedure heading (W7a judge-anchor move; skill-llm-eval.test.ts:638) Judge input is byte-identical on the current templates; judge prompts and schemas are unchanged. * test: CEO governing-stage order anchors on the question-procedure heading (W7a judge-anchor move; skill-ceo-section-ordering.test.ts:938) * test: dedicated-tools overlay gates on correct output plus 20% fewer Bash calls (Eng W7 overlay predicate; replaces overlay-nudges.ts:213 taskCorrect) Every ON trial must pass the fixture's verify (already a valid-measurement requirement) and lowerIsBetter20Pct must hold; when the OFF median is 0, every ON trial must also be 0. assessOverlayArms enforces both through a new fixture.gate. Overlay contract bumps to v3 so v2 records are never reinterpreted. overlay-measurement gains rows for correct+worse (fails), 20%+incorrect (fails) and the zero baseline (passes only at 0). The prose comment follows the s01 M1 claude.md wording. * test: document-release E2E stops coaching the rules it measures (W7b; UC2 gate eval for NEVER CLOBBER / NEVER BUMP) Drops the IMPORTANT block at skill-e2e-workflow.test.ts:76-83, including 'auto-approve everything' and the CHANGELOG/VERSION instructions; keeps only the environment facts (non-interactive, no remote). Asserts the outcome: the original CHANGELOG entries remain, VERSION still reads 1.1.1, no commit after the fixture head touches VERSION, and README mentions Feature C. * test: routing E2E uses the routing instruction gstack ships (W7b routing fixture; skill-routing-e2e.test.ts:113) readShippedSkillRouting() parses the '## Skill routing' section from the routing-injection heredoc in bin/gstack-skill-start at test time. The journey fixture now carries that instruction instead of 'ALWAYS invoke … FIRST action. Do NOT answer directly', and still withholds the per-skill rule list so a regressed description can fail. bin/gstack-skill-start and the helper join the journey, context-skills and skillify touchfiles. A free check catches heredoc drift. Runs on unchanged templates set the new baseline. * test: context-skills E2E routes with the shipped ## Skill routing section (W7b routing fixture) Replaces the shouted 'ALWAYS invoke it … FIRST action' fixture and its hand-written context-save/restore rules with the section gstack appends to CLAUDE.md, which already routes save/resume to those skills. * test: skillify E2E stops scripting the skill and routes with the shipped section (W7b skillify prompt + routing fixture; W7d skillify narration ban) - CLAUDE.md fixture: the shipped ## Skill routing section replaces 'ALWAYS invoke it via the Skill tool as your FIRST action' and its hand-written rules. - scrape-match-path: prompt is 'Run /scrape latest hacker news stories'; the 'You MUST follow … 1. Run skill list … Do NOT enter the prototype phase' script is gone. Asserts the outcome: the bundled skill ran, no goto prototyping, no new skill or skillify staging written. Listing is logged. - scrape-prototype-path: drops the five-step script and the self-fulfilling 'Suggest /skillify'; keeps the user's JSON shape request. - skillify-provenance-refusal: prompt no longer dictates the refusal. - Narration grader moves to helpers/skill-body-narration.ts: line-start 'I '/'I'll '/'Let me ' plus mid-sentence 'let me <verb>', so 'let me know' is legal. Stored known-good/known-bad bodies show it still rejects every known-bad case the old /Let me /i ban caught. * test: land-and-deploy E2Es state environment facts instead of scripting the skill (W7b deploy prompts) The workflow, first-run and review-gate prompts drop the 'IMPORTANT … simulate: 1. Detect … 2. Infer … 3. Note …' scripts and the answers they supplied (platform, app, URL, merge method, 'Eng Review should be NOT RUN'). They keep the facts the fixture cannot provide (no remote, no PR, gh/fly unavailable, gstack-review-read output, report directory). Assertions now check the outcome: the artifacts name Fly and test-app, and the readiness report records the missing review. Canary, benchmark and setup-deploy prompts are unchanged. * test: ship triage asserts each failure's in-branch/pre-existing label (W7b ship triage) The old check passed if the words 'in-branch', 'pre-existing', 'math' and 'string' appeared anywhere in the final message, in any pairing. The prompt now asks for a closing JSON map; helpers/ship-triage-labels.ts reads it (or unambiguous per-failure lines) and the test requires truncate/string -> in-branch and divide/math -> pre-existing, plus a Bash call that ran the suite. Stored outputs show swapped, words-only and one-label runs fail. Drops the untyped recordE2E extras (typecheck baseline shrinks by one). * test: design-review-fix asserts a fixed design issue, not only the Aside ritual (W7b design-review-fix) Keeps the engine-routing checks (Aside driven, retired binary unused) and adds the outcome: design-audit.md exists, names a seeded defect, and index.html or style.css differs from the initial fixture commit. The style(design) commit prefix is not required. This case still cannot gate the design-review stop-condition rewrite (it self-skips without Aside and caps fixes in its prompt), per the Eng item. * test: CEO HOLD preservation grader checks the decision, not its phrasing (W7d CEO mode-routing regex judge, ceo-mode-option.ts:410-440) Drops the exact 'Keep <x> in scope' / 'Defer <x> to TODOS' labels, the 'D<n> — Keep … in scope, or defer' title, the '<plan>.md lists' phrase and the 2-of-5 keyword quota. Keeps the evidence bindings (native answer, session, timestamps, source read, mode context, later withdrawal) and now requires: one keep option and one defer option, the answer selects keep, a word of the kept label names a listed plan requirement, no expansion language, and an ELI10 + Stakes rationale of at least 20 words (so a bare mode label still fails). Stored captures: both retained known-good runs pass; every existing known-bad mutation still fails; four rewordings the old grader rejected now pass; three new known-bad cases fail. The Net/Note single-brief checks (t02 #3) are unchanged: the 'second sentence after Net' row adds scope ('Also add shared views'), so it stays a known-bad. * test: docsync observer accepts harmless read wrappers (W7d docsync observer) docsCommandAllowed now unwraps, on read-only commands only (pwd, ls, cat, stat, sha256sum, Git reads): a leading 'cd <fixture repo> &&', '2>/dev/null', a trailing '|| true', '--no-pager', '-C <fixture repo>' and '-c' with core.pager=cat / core.quotepath / color.* settings. Lifecycle and script calls stay literal so their failures cannot be suppressed. Writes, other repos, --git-dir/--work-tree, config that can execute code, real composition and redirects stay rejected. Stored commands (fixtures/docsync-observer-commands.json): 11 known-good read forms, which the old observer rejected, now pass; all 17 known-bad forms still fail under the new and old observers. docsync-command-grammar moves its three harmless rows to an accept test and keeps unsafe variants of them as rejects. * test: AUQ matrix fails only on the format fields software reads (W7d AUQ matrix emoji check) auqMachineFormatProblems() fails a first-question run when the Recommendation: line is missing or empty, or when the brief does not label exactly one option (recommended); recommendation substance >= 4 is unchanged. ELI10, Pros / cons, ✅/❌ and Net: are still scored and logged but no longer fail a paid case. scoreAuqFormat and the consistency suite are unchanged. Stored briefs: three known-good (including one without emoji or Net) pass; four known-bad (no or empty Recommendation, zero or two (recommended)) fail. * test: coverage audit grades a closing tested/untested summary before the diagram grammar (W7d coverage-audit-evidence) The diagram grammar grew through four waves of capture patches to accept correct-but-different diagrams. The E2E prompt now asks for one closing JSON line {"tested": [...], "untested": [...]}; coverageAuditVerdict passes the outcome when processPayment is only in tested and refundPayment only in untested (last summary wins; quoted or example summaries ignored). The legacy grammar stays as a fallback, so every stored capture keeps its verdict (all existing rows unchanged). Read evidence and capture success are still required. Stored summaries: three known-good (which the old grader rejected) pass; seven known-bad fail. * test: disabled outside-plan grader reads the run's own coverage self-report (W7d disabled-plan-review-evidence) The false-completion oracle kept being patched to tell historical review-log mentions from current claims. The E2E prompt now asks for a closing JSON line {"outside_review_this_run": ...}. When present, the coverage claim is that report ('completed' fails) plus any undenied 'both reviewers agree'; 'disabled' also satisfies attribution. Runs without it keep the existing prose oracle, so all stored captures and mutation rows keep their verdicts. Dispatch, CLI execution, preflight and persisted-disabled checks are unchanged. Stored outputs: three known-good pass (the old grader rejected the table form); four known-bad fail. * test: investigate boundary grader checks the verified fix, not one Edit and one verify (W7d investigate exactly-one ritual) Completion now needs at least one allowed Edit, the correction in place, at least one verifier run whose final status is 0, and no edit after the final verification. The ending-error case still requires a single verifier run (status 69), because that fixture's error tells the actor to stop without retrying. Ownership acquisition/release checks are unchanged. New free protocol controls: a passing re-verification passes (the old grader failed it); a failing final re-verification and a retry after the ending error both fail, as they did before. * test: CEO split-overflow call floor is two, the minimum for five options at four per call (W7d split-overflow 4-call floor) The regression is a candidate silently dropped to fit the four-option cap. The scope validator already requires an offered Include/Defer/Cut disposition for all five candidates; the extra N-1 call floor failed agents that batch compatible candidates, which the test header calls legitimate. FLOOR now comes from CEO_SPLIT_CALL_FLOOR = 2. Stored shapes (ceo-split-floor.test.ts): five sequential, 2-call and 3-call batches pass; a dropped candidate fails with 'missing target decisions'. The registration controls keep their verdicts; the 3-call plan_ready scenario still fails, now on missing decisions. * test: shared-libs PR coverage checks the stated page budget, not per-PR ceilings from one run (W7d shared-libs per-endpoint page caps) prCoverageRequestViolations keeps the skill's stated older-open-PR budget (five metadata pages, fifty file-list pages plus recent PR 7) and the coverage outcome (PR 42's file list read from page 1 through page 2). It drops the PR 42 three/four-page and single-first-page ceilings, which were tuned to one observed capture with a truncated first view. The judge criteria are unchanged. Stored endpoint lists: three known-good (including a re-read first page the old ceilings rejected) pass; four known-bad plus an over-budget file-list run fail. incompleteFirstFileView is no longer used by the paid case. * test: test-value review accepts any caller search, not only the literal 'git grep' (W7d literal git grep in test-value) recordsNonTestCallerSearch keeps the value-card contract (non_test_callers recorded) and accepts git grep, rg, grep or the Grep tool as the search. Severity checks and the golden exclusion are unchanged. Stored evidence: four known-good (three the old check rejected) pass; three known-bad (no search, no caller count, prose only) fail. * test: plan-format accepts any statement that kind-differentiated options are not comparable on coverage (W7d 'options differ in kind' phrase) The outcome, no fabricated Completeness: N/10, stays asserted. The required note now matches any brief wording of the same fact (KIND_NOTE_RE in helpers/plan-format-kind-note.ts). The format-compliance suite that pins the declared kind-note string is unchanged. Stored lines: four known-good pass, three known-bad (a completeness score, a recommendation, 'kind to the user') fail. * test: plan-mode smokes check the targeted plan, not the scope-gate announcement, and reject plan-file ## Decisions (W7d exact scope-gate line; W7g plan-mode ## Decisions) - Seeded eng/design plan-mode cases keep 'no scope menu' and replace the exact 'Scope gate: plan mode — auto-selected B' requirement with seededPlanTargeted(): the announcement or native seeded-plan selection, or seed-only plan names appearing in the review output after the slash command (trackTokens). The announcement is logged. - The AskUserQuestion format forbids writing a decision to the plan file as a substitute for asking. assertNoPlanFileDecisions() fails any plan_ready run whose plan carries ## Decisions (eng, design, devex, office-hours auto-mode). The AUQ-blocked cases (eng seeded, devex, office-hours) now require outcome 'asked' (AskUserQuestion variant or prose fallback). - plan-mode-evidence registration controls keep their verdicts; the missing-decisions control now fails on 'without a question'. - plan-mode-targeting.test.ts stores known-good (announcement; paraphrase plus seed content) and known-bad (neither; an untracked token) observations and plan files with and without ## Decisions. * test: plan-design judge selection check anchors on the question-procedure heading (W7a judge-anchor move; touchfiles.test.ts:219) Same structural anchor as the other judge-anchor moves; selects the same text on the current templates and after the '## How to ask questions' rename. * test: CEO plan-mode failure message names the current rule, not the '## CRITICAL RULE' heading (t13 F20) * test: browse reference judge gates on its stated floors; the baseline comparison is recorded, not a ratchet (W7d browse judge baseline ratchet; t08 F7) expect(regressions).toEqual([]) compared a noisy three-sample mean against eval-baselines.json (clarity 4), silently raising the stated clarity floor of 3. The comparison stays in judge_reasoning; pass and assertions use BROWSE_JUDGE_FLOORS (clarity 3, completeness 4, actionability 4). Judge prompt and schema are unchanged. Stored panel means: three known-good (two the ratchet rejected) pass; three below-floor panels fail. * test: eval-kinds drops the exact 22 behavior / 25 judge census (W7d exact count in eval-kinds; t12 F6) The kind contract is already asserted: every live case has one kind, kinds are rule/behavior/judge, every LLM judge is judge-kind, every behavior case has a literal registration and a BEHAVIOR_WHY, and the named contract-shaped cases stay rule. The census count only broke on adding or retiring a case. * test: plan-eng artifact case binds to the hermetic state root and requires the artifact; report case drops its shouted requirement (W7g plan-artifact fixture binding; W7g skill-e2e-plan.test.ts:479,720) - plan-eng-review-artifact: actor, observer and cleanup use one isolated GSTACK_HOME (the hermetic run's, or a seeded temporary one when hermetic mode is off) and the slug gstack-slug resolves for the fixture, instead of os.homedir()/.gstack/projects/test-project. A guard refuses a state root under the real home, and cleanup removes only the fixture's project directory. The 'IMPORTANT … you MUST write the test-plan artifact' line is replaced by the environment fact (state root and slug). The soft warning becomes an assertion: exactly one new *eng-review-test-plan-*.md about the reviewed dashboard change. - plan-review-report: removes 'CRITICAL REQUIREMENT … MOST IMPORTANT deliverable' and the restated report contract; keeps 'plan.md is the plan file'. The existing assertions (report present, last, rows, unresolved status) now measure the skill's own report prose. * test: session-runner-tools checks harness prompt properties, not a byte-for-byte copy (W7e session-runner-tools.test.ts:544; t09 F7, F8) The ship capture prompt test asserts its purpose (no CEO-only writing guidance) plus the functional rules: one allowed skill path, AskUserQuestion unavailable, the section Read rule, no git/gh/mutation, and the REPORT.md path. The two 'MUST actually Read' pins match the Read rule case-insensitively. Negative control: dropping the no-mutation rule from the harness prompt turns the test red. * test: section-capture native tools checks harness prompt properties, not a copied golden (W7e section-capture-native-tools.test.ts:467; t04 F4) Asserts the absolute skill path is the only SKILL.md named, the scenario, AskUserQuestion unavailable, the section Read rule (case-insensitive, also at :308), no git/gh/mutation, and the REPORT.md path. argv/tool-list checks are unchanged. Negative control: dropping the no-mutation rule turns it red. * test: office-hours review-preservation judge test keeps its schema and untrusted-data checks, not its wording (W7e office-hours-completion.test.ts:416-429; t09 F10) Drops 13 phrase pins on the judge prompt. Kept: both inputs carried as JSON, the response schema (wire format), and the separate untrusted-DATA test. The validator behavior tests already use stub judges. Negative control: changing the schema string in the prompt builder turns the test red. * test: docsync lifecycle interface drops pins on its own helper prompt (W7e docsync-lifecycle-interface.test.ts; t02 #17) docsNativeInterface is the test helper's own prompt, so its sentence pins only checked that the helper says what it says. Kept: owned skill path, the two extracted lifecycle commands (exact), the scripts-list interpolation, the repo path and Git read examples, and every docsCommandAllowed / docsToolFailures behavior check that enforces the interface. * test: shared-libs revalidation prompt keeps its machine-shaped checks, not ~69 phrase pins (W7e shared-libs-revalidation-prompt; t11 #3, #8; t05 F17) reviewRevalidationPrompt and reviewPrompt are test-owned harness prompts (non-test callers: 0); the paid shared-libs E2Es measure whether they work. Kept: the three exact receipt commands, base-prompt prefix and resumed suffix identity, branch selection (actor / default / resumed coverage), the state path and turn-budget interpolations, read-record-before-diff and first-response-before-start ordering, no embedded token, no supplied Skip answer, reusable:true before reusable:false, the captured R59 stage boundaries in the exclusion list, asset roots and interfaces, and the stale step-6 reference ban. The review template's Step 4.7/4.8 completion rule is matched by step number on one line, not by sentence. Negative control: reordering the diff-read instruction turns the test red. * test: plan-design SDK fixture pins its harness prompt once, by ordered properties (W7e plan-design-sdk-fixture.test.ts:91-120; t01 F13) The prompt was pinned three ways (sha256, ~20 sentences, an order check). Kept: the skill and lazy-section paths, the plan path, the ordered read -> review -> edit -> Write -> read-back -> summary steps, and the no-browsing rule (case-insensitive). Dropped the hash and sentence pins. Negative control: removing the read-back step turns it red. * docs: overlay benchmark contract v3 (dedicated-tools gate = correct output + 20% fewer Bash) * test: dedicated-tools overlay case pinned to claude-opus-5-5 (E2) * chore: regenerate SKILL.md, goldens and context-budget fixture for the Opus 5.5 prompt cleanup * document-release: CHANGELOG-preservation and VERSION-ask rules at normal volume (s10 M4, M5; UC2, eval-gated) * chore: regenerate document-release * Revert "test: dedicated-tools overlay case pinned to claude-opus-5-5 (E2)" This reverts commit |
||
|
|
df89475b17 |
v1.91.11.0 refactor: one state-root rule, browse route table, shared shard engine, PTY harness split, MECE review resolvers (#3002)
* refactor(resolvers): split review.ts into MECE resolver modules (pure move) Move every function from scripts/resolvers/review.ts, unchanged, into: - review-dashboard.ts: review dashboard, plan-file review report - plan-gates.ts: approval check, exit-plan-mode gate, plan-file discovery, plan-completion audit/gate (ship + review), plan verification exec - spec-review.ts: both spec review loops, benefits-from, anti-shortcut clause - outside-voice-steps.ts: Codex second opinion, adversarial step, Codex plan review, Codex doc review, disabled-outside record - review-scope.ts: scope drift, cross-review dedup, shared-code reuse review.ts is deleted; index.ts imports the new modules. gen-skill-docs output is byte-identical for every host (--host all). Test imports and source-path references are re-pointed; the two source-text report/gate tests in gen-skill-docs.test.ts become behavioral renders across every consuming skill and host. All 46 touchfile entries that named review.ts now name all five modules, guarded by a recorded selection golden. * test(browse): black-box auth matrix for every server route and both surfaces Drives buildFetchHandler fetchLocal/fetchTunnel with no token, wrong token, root token, scoped token and the SSE cookie for all 33 routes, plus unmatched paths and wrong methods. Denials assert today's exact status, body and content type; allowed credentials assert the handler was reached. Written against the unchanged if-chain server so the W3 route-table refactor must keep it green. * refactor(shard-engine): move scripts/test-strict-output.ts to scripts/lib/shard-engine.ts The shared shard engine grows from the existing strict-output module (runShardChild, killProcessGroup, signal forwarding, strict classifier). scripts/test-strict-output.ts stays as a re-export so existing importers, mock.module paths and the strict-output/run-shard-child tests are unchanged. The engine inherits the global touchfile entry; the free runner's CLI-routing fixture copies the new module. * refactor(resolvers): decompose the three >150-line review resolvers (output-neutral) Split generateAdversarialStep, generateCodexPlanReview and generatePlanCompletionAuditInner into per-section helpers whose template literals are copied verbatim, so every function in the new modules is at or under 150 lines. gen-skill-docs output is byte-identical for every host (--host all, compared against |
||
|
|
96764e80a6 | v1.91.9.0 feat: test value bar in plan-eng-review, review, qa and ship, plus /test-audit (#2998) | ||
|
|
dcaea52800 |
v1.91.7.0 feat: add functional QA and pre-publication docs checks (#2983)
* feat: add surface-aware exploratory QA and ship documentation gates * test: preserve delegated QA setup authority after main integration * fix(qa): clarify exploration order and preserve report artifacts * test(qa): follow the shared setup reference directly * refactor(ship): make verification and recovery routes explicit * test(ship): align evidence and review guards with explicit routes * fix(workflows): clarify ship recovery and functional QA evidence * fix(workflows): clarify approval recovery and full QA coverage * refactor(workflows): order review transactions and clarify ship state * fix(ship): clarify final verification and fail closed at publication * fix(evals): attribute native atomic documentation writes * fix(ship): clarify recovery and documentation lifecycle guidance * fix(test): preserve observed native placeholder styling in CI * fix(codex): report watchdog timeouts without a process-exit race * Checkpoint functional QA implementation and workflow validation repairs * Fix documentation and shared-review fixture contracts * docs: clarify judge reuse and evaluation supervision * test: align review evidence and selected case contracts * test: verify append-only documentation checkpoints and recovery * fix: qualify QA workflows and CI validation repairs * fix: launch shared-libs fixture scripts on Windows * fix: qualify QA deadlines, fixture isolation, and shard cleanup * fix: preserve qualified QA and cancellation repairs * fix: enforce functional fixture authority and share strict event decoding * fix: retain free-test evidence and explain recovery * fix: reject malformed native evidence after decoder consolidation * test: use reliable capture for telemetry privacy filters * test: refresh measured quick coverage and document validation costs * Fix native fixture receipts and preserve VM validation evidence * Align negative judge controls with upstream clarity policy * Fix report-only QA preparation and public evidence handling * Clarify QA-only preparation and current-report preservation * Stream Ship quality judgments with an explicit 64k response contract * Validate compact judge reasoning locally with supported wire schema * Align functional QA fixture instructions with evidence acceptance * Bind native browser diagnostics to execution evidence and align review verdicts * Preserve native diagnostic line boundaries * Serialize functional QA evidence from native captures * Keep large QA evidence fixture payload out of Windows argv |
||
|
|
06ed920a97 |
v1.89.0.0 feat: add shared-code extraction audit (#2925)
* feat: bind shared-code review advice to source and branch * feat: add shared-code extraction audit and scoped review checks * test: recognize complete source reads and explicit coverage legends * chore: bump version and changelog (v1.88.0.0) Co-Authored-By: OpenAI Codex <noreply@openai.com> * test: capture native review questions and retain public evidence Capture the actual first public native question with strict ownership and display matching. Preserve terminal failures and raw evidence, and retain SDK completion checks. * test: recognize verified review evidence and complete fixtures Recognize complete source and diagram evidence, concrete design and developer-experience decisions, and the complete planted scenario contracts. Preserve negative controls and grading thresholds. * fix: preserve decision brief structure in native questions Keep the required pros-and-cons heading and final Net field in native question text. Regenerate host outputs and document the release and evaluation repairs. Co-Authored-By: OpenAI Codex <noreply@openai.com> * docs: update project documentation for v1.88.0.0 Co-Authored-By: OpenAI Codex <noreply@openai.com> * fix: correct eval retry accounting and ship workflow gates * fix: capture native eval evidence and stabilize CI fixtures * fix: keep shared-code eval skips read-only Choose explicit no-change answers instead of mixed fix/preservation options. Reuse the bounded revalidation prompt for path fixtures so required review metadata is available without repeated discovery. Preserve source checks, retry limits, and failed native terminal outcomes. Add captured-question and callback regressions, plus evaluation selection coverage for the affected fixtures. --------- Co-authored-by: OpenAI Codex <noreply@openai.com> |
||
|
|
636175d349 |
v1.87.6.0 fix: make checks reliable and everyday validation faster (#2898)
* fix: acknowledge seeded plans before invoking review skills * fix: distinguish current plan input from conversation history * fix: keep hermetic plan reviews on manual permissions * fix: distinguish tool discovery from file permission ownership * fix: preserve initial plan mode in observation tests * fix: wait for scope decisions before writing review findings * fix: carry autoplan decisions consistently into review artifacts * test: retain native failure context in periodic assertions * fix: advance active file permissions before queued questions * fix: finish red-team attempts before retry and cleanup * fix: finalize plan format captures and judges before retry * fix: cancel setup-gbrain SDK attempts before fixture cleanup * test: select periodic consumers of the bounded attempt helper * fix native Bash permission cards and queued questions * fix: preserve independent decisions and review scope Keep CEO approach, engineering scope and outside-review choices from approving independent remedies together. Carry declared contracts through DX polish and resolve new gaps before editing the plan. Regenerate every host and retain existing stop boundaries. Validation: 654 focused tests passed across nine files; all-host generation passed. Full free and periodic validation pending. Co-Authored-By: OpenAI Codex <noreply@openai.com> * fix: require approval before design plan amendments Align the Design review philosophy and rating recipe with its section protocol: resolve one proposed fix, then apply only that approved decision and retain honest scores for declined fixes. Validation: 469 focused tests passed across four files; all-host generation passed. Co-Authored-By: OpenAI Codex <noreply@openai.com> * fix: observe native question completion before transcript persistence Match owned completion hooks to submitted choices, reject conflicting or late answers, and retain bounded failure evidence. * test: recognize review posture in acknowledged native questions Require the selected mode acknowledgement, a completed follow-up question, and its current decoded display while preserving existing posture assertions. * fix: preserve settled CEO choices and isolate pending remedies Resolve established approach gates with cited authority and keep independent fixes out of unrelated option commitments and plan amendments. * fix: carry approved DX choices through later review steps Choose documentation approaches within the accepted scope and map resolved confusion points without reopening them through a bulk menu. * test: handle native settings-file edit prompts Keep one-time owned-file approvals and retain the actual sampled Autoplan permission frame with its matching barrier state. * test: accept standard CEO reply directives with tuning footers Recognize the exact trailing preference footer and letter-list directive while preserving current-display and exact acknowledgement checks. * test: scope split reviewers to their generated plan artifacts * test: observe native Bash permissions and invocation results * test: handle owned Bash prompts during mode preference checks * test: preserve synchronous subprocess rejection in Codex fixture * Fix periodic review handoff navigation Recognize review-first and explicit manual-next-step labels while preserving exact action families, manual preference, and ambiguous-menu rejection. Co-authored-by: OpenAI Codex <noreply@openai.com> * Bind pending file permissions to distinct current targets Allow one captured file request to own the complete current dialog while unrelated file work is pending. Preserve same-path ambiguity, exact input ownership, and one-time grant checks. Co-authored-by: OpenAI Codex <noreply@openai.com> * Make paired CEO verification choices genuinely unresolved Start the positive control with proposed manual checks so its unchanged oracle measures two new coverage decisions. Preserve runtime contracts, targets, count bounds, and all assertions. Co-authored-by: OpenAI Codex <noreply@openai.com> * Keep CEO review options and verification within approved scope Audit every offered option for independent add-ons and keep new verification depth pending until accepted. Preserve already requested coverage and trace plan changes to the actual decision. Co-authored-by: OpenAI Codex <noreply@openai.com> * Assemble DX review artifacts before appending the final report Keep early DX evidence above decisions, update artifact sections in place, and append the report using the actual current file suffix. Re-read after deleting an existing report before choosing the append anchor. Co-authored-by: OpenAI Codex <noreply@openai.com> * Keep outside plan reviews exclusive and invocation-owned Follow one preflight-selected backend, terminate failed Codex work before fallback, and allocate extra prompt/output files uniquely. Consume only the current invocation’s completed output. Co-authored-by: OpenAI Codex <noreply@openai.com> * Select periodic completion evaluations for report writer changes Register the shared review resolver for eight missing consumers and regress selection for all nine completion cases without changing their IDs or tiers. Co-authored-by: OpenAI Codex <noreply@openai.com> * Keep permission ambiguity fixtures on the same normalized target Use distinct raw spellings of one target in the four negative fixtures so they exercise the normalized duplicate-owner guard after exact current-file disambiguation. Preserve the existing exception, no-input, diagnostic and cleanup assertions. Co-authored-by: OpenAI Codex <noreply@openai.com> * Clarify preserved contracts in engineering review fixture Co-authored-by: OpenAI Codex <noreply@openai.com> * Recognize the offered DX follow-up handoff Co-authored-by: OpenAI Codex <noreply@openai.com> * Check independent commitments before presenting review options Co-authored-by: OpenAI Codex <noreply@openai.com> * Keep Codex review output and status in one shell invocation Co-authored-by: OpenAI Codex <noreply@openai.com> * Distinguish seeded plans from reports written by a test attempt Co-authored-by: OpenAI Codex <noreply@openai.com> * Recover clipped Autoplan file approvals with bounded viewport resizing Co-authored-by: OpenAI Codex <noreply@openai.com> * Recover clipped Bash approvals before binding the complete command Co-authored-by: OpenAI Codex <noreply@openai.com> * Isolate setup message tests from the shared checkout Run the real installer in a temporary payload with private config, require successful completion, and guard source and binary contents and mtimes. Co-authored-by: OpenAI Codex <noreply@openai.com> * Fix periodic native permission and report completion handling Match the pinned CLI's soft wraps and clipped headings without granting from incomplete frames. Retire completed file requests, retain mode annotations, and ask section captures for a short final acknowledgement after their full report is saved. Co-authored-by: OpenAI Codex <noreply@openai.com> * Preserve review approvals and validate DX comparison artifacts Keep independent remedies and approved amendments explicit. Give the synthetic DX review its existing documentation and validate peer comparison as required analysis alongside four native decisions. Add positive and negative semantic calibrations while preserving review counts, model budgets and prompt size limits. Co-authored-by: OpenAI Codex <noreply@openai.com> * Make the five-finding CEO fixture's application boundary explicit Materialize the request adapter and service composition used by the synthetic payment application. Explicitly declare the revised unregistered-event and mail-telemetry assumptions while preserving uncaught handler errors, the original invoice path and all five unresolved findings. Co-authored-by: OpenAI Codex <noreply@openai.com> * Keep CEO state-path checks scoped to directory preparation Co-authored-by: OpenAI Codex <noreply@openai.com> * Use checked ports and bounded cleanup in pair-agent tests Discover the daemon port from its owned state file, retain startup diagnostics, and await failed-start cleanup. Add occupied-port, early-exit, deadline, and foreign-state regressions while preserving the existing HTTP assertions and hook budgets. Co-authored-by: Codex <noreply@openai.com> * Preserve queued edit identity and recover clipped Bash permissions Distinguish separately queued unfinished edits from mutation of one native tool ID. Keep grants bound to an exact owned request and reject reused IDs, ambiguous inputs, and competing owners. Support the pinned renderer's literal em dash and request a repaint when only the Bash card's top rule is clipped. Grants still require the complete fresh card and an exact native acknowledgment. Validation: 413 integrated parser/event tests passed; private repaint controls and joint source review passed. Full canonical suite and native periodic rerun remain pending. Co-authored-by: Codex <noreply@openai.com> * Keep periodic reviews within their approved contracts and deliverables Carry exact approvals through engineering review, preserve declared contracts when amending CEO plans, and keep prioritization at the requested decision level. Materialize the revised synthetic SDK reference contract while retaining the five original documentation gaps. Accept the observed semicolon in the finite DX handoff menu and register the direct source dependencies used by the engineering cases. Regenerate canonical review documents without changing model budgets, retries, count bands, or native completion assertions. Validation: all-host generation and 275 review, fixture, selection and parity tests passed. Full free-suite and native periodic validation remain pending. Co-authored-by: Codex <noreply@openai.com> * Keep Eng approval cadence and independence guards explicit * Accept ordinary punctuation in manual review handoffs * Recover file permissions alongside queued Bash calls * Carry approved DX work through later review findings * Clarify the synthetic auth internal failure decision * Bound the periodic DX fixture to onboarding changes * Recognize native Design review handoff labels * Hold scope in the integration-choice review fixture * Carry approved Design decisions through review evidence * Capture listener state when feedback reload fails * Exclude workspace caches before checking deprecated flags * Verify Design UI scope against a seeded review plan * Clarify plan review decisions and outside-voice approval flow * Reject setup menus in the Design UI gate * docs: require focused repair validation before final acceptance * fix: separate review commitments within existing prompt budgets * docs: align generation and contributor validation guidance * fix: advance native review prompts and count acknowledged findings * chore: bump version and changelog (v1.87.1.0) Co-Authored-By: OpenAI Codex <noreply@openai.com> * chore: enforce cheap checks and side-effect-free validation previews * fix: handle owned Fetch permissions and oversized native cards * test: ground review fixtures in independent executable contracts * fix: preserve review decisions and verify reports before completion * test: construct the synthetic credential URL without a scanner false positive * test: materialize DX examples and verify their actual local behavior * fix: clarify CEO review decisions and execution order * fix: clarify review workflow ordering and select Design quality checks * Fix review decision gates and incomplete evaluation fixtures Persist CEO and engineering commitment ledgers before menus, preserve exact approvals, and distinguish implementation structure from feature scope. Route Autoplan through the canonical CEO Step 0 ordering. Classify DX findings before requesting approval and ground runtime claims in actual evidence. Complete neutral non-target fixture contracts and accept the captured Design handoff purpose without relaxing its ownership or acknowledgment checks. Record runtime-capability verification in AGENTS.md validation discipline. Validation: 1,335 focused tests passed across 21 files; build, all-host freshness, skill validation (647 artifacts / 107 tracked), and credential checks passed. Prior paid failures are preserved; behavioral acceptance remains pending. * Fix review decision boundaries and owned Read prompts Preserve exact approvals across review options, compare consistent DX milestones, and keep proposed implementation separate from review evidence. Bind modern Read prompts to one immutable native request and wait for its result. Retain captured regression verdicts, correct fixture error names, improve import probe diagnostics, and record focused-first validation discipline in AGENTS.md. * Clarify CEO and engineering review decisions Use explicit decision steps, one engineering ledger, and clear scope/write transitions. Preserve exact approvals and distinguish pending test requirements. Keep unrelated generated content unchanged. * Fix review decision ordering and native evaluation interactions * Clarify engineering decisions and test artifact order * Clarify pending choices and approvals in CEO reviews * Make CEO review phases sequential and clarify completion * Fix Design board submission intent matching * Seed an existing browser test baseline for Autoplan * Document decision-log payloads before state initialization * Preserve exact review scope and decide one change before drafting options * Require input identity before repeating passing model judges * Honor permitted storage throughout CEO review completion * Match complete native permission text within the pinned renderer contract * Align review approvals, independent choices, and bounded validation * fix: preserve reopened approvals and declare fixture interfaces * fix: isolate review artifacts and audit complete questions * fix: match detector artifact permissions to configured storage * fix: complete native permissions and review fixture workflows * fix: order CEO review work and separate engineering guarantees * fix: preserve native validation and separate review choices * fix: clarify review decisions and judge complete report context * fix: constrain review judgments and retain parse failures * fix: compare each affected value before review decisions * fix: make engineering review decisions and completion order explicit * fix: give the complete Autoplan evaluation a bounded chain budget * fix(cso): diagnose forbidden Docker endpoints before tool lookup * fix(reviews): reconcile workflow contracts and generated artifacts after main integration * fix(evals): migrate retained regressions to the native review harness * fix(tests): close native harness and workflow integration regressions * fix(evals): preserve complete permission context and native menu contracts * fix(tests): capture synchronous command output without pipe drain stalls * fix(reviews): clarify decision and completion ordering * fix(reviews): separate decision readiness from final completion checks * refactor(reviews): consolidate decision rules and completion branches * fix(plan-eng-review): order preparation and clarify decision routing * fix(plan-eng-review): restore size and question-format guard parity * fix(plan-eng-review): clarify scope phases and blocked completion * fix(plan-eng-review): unify review flow and report destination * fix(plan-eng-review): define bootstrap and question stage ownership * fix(plan-eng-review): clarify review structure and design lookup * fix(plan-eng-review): render report examples and show saved decisions * fix: consolidate Eng review decisions and select their evaluations * test: cover overlapping terminal attachments and clean merged runner type * fix: preserve Office Hours relationship closings during review updates * fix: retain pasted review targets across slash invocations * docs: preserve validation traces and correct release scope * test: cover pasted targets in both review skills * fix: validate report artifacts before recording success * fix: redact source roots at CSO report boundaries * fix: bind native Design questions before answering * test: select report privacy and native recovery regressions * test: bind rejection predicate in extracted observers * fix: bind complete boxed native questions * test: keep the Design UI fixture on native review * fix: preserve review decisions and evaluation completion outcomes * fix: clarify CEO approval and report completion order * fix: align native review evaluation ownership and completion * fix: bind review evaluators to native decisions and owned artifacts * fix: validate review decisions against native outcomes * fix: preserve review evidence and Autoplan phase handoffs * test: bind review evidence to owned decisions and completion * fix: retain owned native history across compaction * fix(evals): validate current review decisions and setup choices * fix: bind Autoplan reviews and phase completion to current amended input * fix: reconcile native review evidence and close Autoplan phases * test: recognize owned whole-candidate complexity decisions * test: preserve report freshness for approved investigation handoffs * fix: recognize scoped review findings and isolate dual voice fixtures * fix: make review handoffs and question dispatch self-contained * test: recognize complete CEO decisions and procedural pauses * fix: bind current CEO comparison options and risk intervals * test: bind engineering decisions and completion to owned evidence * fix: publish Autoplan phase reports before continuing tools * test: verify actual Autoplan dual-review dispatch evidence * test: select dual review when shared evidence fixtures change * fix: clarify plan review decisions and completion gates * fix: make CEO review decisions and return paths explicit * test: keep Autoplan prompt files inside attempt state * test: preserve source whitespace across permission dialog wraps * fix: publish Autoplan phase reports before continuing * test: recognize current CEO comparisons and reject inactive records * fix: reconcile engineering decision states before completion * test: recognize complete Design decisions and reports * test: verify current engineering decisions before navigation * Recognize source-owned component reduction choices * fix: recognize current CEO ledger and commitment grids * test: supply RequestPolicy context to Eng count fixture * fix: save complete engineering decisions before asking * fix: bind Autoplan publication to the complete phase readback * chore: prepare 1.87.5.0 reliability release * fix: clarify engineering review completion and preserve log failures * fix: bind CEO saved choices and current section ancestry * fix(evals): bind review execution and completion evidence * fix(plan-ceo-review): verify complete decisions before asking * fix(evals): preserve complete engineering choice records * fix(evals): preserve complete review outcomes and bounded fixtures * fix(autoplan): publish phase reports before advancing * fix(plan-ceo-review): validate option fields before asking * fix(plan-eng-review): verify current decisions after answers * fix(evals): bind review decisions and bound fixture scope * fix(plan-ceo-review): verify decision rows and edit saved checkpoints * fix(evals): bind review evidence and scope document lookup * fix(plan-eng-review): update resolution state with its answer * fix(reviews): preserve complete questions through dispatch * fix(evals): recognize completed mode declarations * fix(evals): define cache consistency at wrapper completion * fix(evals): validate owned initial scope and completed review handoffs * fix: assemble complete CEO decision fields before saving * fix: authenticate automatic mode decisions without guessing selectors * fix: bind engineering coverage to approved regression contracts * fix(evals): supply review helpers to native Eng capture * fix(plan-eng-review): preserve the full selected option scope * fix(evals): recognize owned engineering seed and regression evidence * fix(evals): bind engineering retry reports to native approvals * docs: clarify release guarantees (v1.87.5.0) Co-Authored-By: OpenAI Codex <noreply@openai.com> * fix(evals): recognize owned engineering decisions and handoffs * fix(evals): bind engineering decisions and completion evidence * fix(tests): align review contracts and selection fixtures * fix(skills): restore review prompt size limits * fix(plan-eng-review): clarify review execution and completion * fix(evals): preserve configured retries through all supervision layers * Clarify Engineering decisions and report completion * Keep native decision assertions within their source boundary * fix: recognize owned engineering decisions and completed navigation * fix: bind completed auto decisions to their current review * fix: recognize explicit CEO source attribution * fix: dispatch verified CEO decisions without recomposing fields * test: expose existing execution deadlines to review actors * fix: distinguish CEO decision records from incidental headings * test: bind split-scope choices to the registered native actor * test: connect reviewed regressions to required evaluation coverage * Clarify CEO decision routing and completion stages * test: expose existing section review deadlines to fixture actors * test: recognize complete native CEO pacing inventories * test: exclude answered history from current CEO payloads * test: detect phase entry through owned skill HOME aliases * test: validate native review completion and owned report permissions * fix: make Autoplan close packets carry the parent handoff steps * test: assess source-bound HOLD decisions within the existing deadline * fix: keep CEO native decision fields under one formatting authority * test: register integrated review and permission dependencies * test: align native review adapters and finding coverage Preserve explicit AUTO decisions, apply native single-select defaults, and bind complete cropped questions and report permissions to their owned requests. Require seeded review findings instead of crediting setup menus. Keep captured failure controls and additive selection dependencies. The integrated candidate passed 3,099 focused tests across 65 files; affected paid validation remains required before publication. * fix(autoplan): require phase reports before advancing * fix(evals): bind setup and evidence to complete attempts * fix(evals): bind native answers and pending writes to fixture scope Preserve complete option rows when native descriptions wrap, retain current owned Write arguments before journal publication, and keep engineering and DX answers within their declared fixture interfaces. Add captured free regressions without increasing model budgets or relaxing completion checks. * fix(autoplan): verify phase reports across native tool paths Guard owned methodology reads and reviewer dispatches, detect complete driver loads through Bash, and distinguish report-only edits from implementation changes. Follow authenticated native UUID ancestry when journal writes arrive out of order and verify earlier native content for cached phase reads. Keep current close acknowledgment and parent publication in order, require CEO entry before later phases, and register captured failure regressions. * fix(evals): honor native input and collection lifecycles Match complete native Edit panes and truncated question borders, reject stderr close before EOF, and stop the CEO split fixture once its acknowledged scope decisions are collected. Keep semantic validation, process failures, report requirements, and absolute deadlines authoritative. Add captured-event and real-process regressions with selection dependencies. Focused checks pass; final integrated paid and full-suite acceptance remain pending. * fix(autoplan): retain native session ownership across directory changes Recover missed native UUID ancestry through the existing strict graph while preserving ordinary event order and legacy scoping. Bind publication hooks to Claude's original project directory while retaining current cwd for requested file paths. Captured public-event regressions, existing caller checks, and a pinned native CLI loopback verify both fixes. Preserve failed attempts and require fresh paid and final full-suite acceptance. * docs: align evaluation limits and completion version * fix(autoplan): allow authenticated phase reads during journal streaming * fix(evals): bind clipped native questions and owned edit dialogs * fix: preserve overlay retries and bounded cleanup * fix: recognize owned planning preludes in native questions * docs: explain overlay scheduling and cleanup guarantees * fix: require fresh publication after Autoplan phase reruns * Release gstack 1.87.6 * fix: preserve CI paths, process identity, and test deadlines * fix: keep informational setup commands independent of install probes * fix: clarify plan review decisions and bound source audit reports * Fix remaining Windows identity and native path CI failures * Clarify CEO review decision and reviewer-result routing * test: accept no-install planner in retry supervision * fix(ceo-review): make review decisions and report completion explicit * perf(test): add fast PR gates, input-keyed judge reuse and isolated free shards * fix(test): start isolated CEO smoke from its existing project plan * fix(test): repair CI fixture races and preserve retry evidence * fix(ceo-review): clarify approvals, depth and saved completion --------- Co-authored-by: OpenAI Codex <noreply@openai.com> |
||
|
|
71f6048e8a |
v1.84.1.0 fix: default Codex and Claude to frontier models (#2835)
* fix: default cross-model workflows to frontier models * chore: bump version and changelog (v1.82.1.0) Co-Authored-By: OpenAI Codex <noreply@openai.com> * fix: repair frontier eval budgets and workflow instructions Preserve frontier models and quality thresholds while fixing truncated judge output, ordered section expansion, consent checks, QA scoring, and ship audit gates. Add regression coverage and refresh generated docs. Co-Authored-By: OpenAI Codex <noreply@openai.com> * fix: resolve workflow gaps exposed by frontier evals Clarify plan-review ordering and fallback modes, preserve deploy readiness gates, honor configured merge methods, correct benchmark and canary contracts, and restore vendored installs on setup failure. Cover recovery with real-shell regressions. Co-Authored-By: OpenAI Codex <noreply@openai.com> * fix: use agent capture budgets for deploy evals Multi-turn deploy and benchmark sessions were incorrectly limited to the single-call judge timeout. Use the existing capture tier and leave outer-test cleanup headroom, with a free policy regression test. Keep all behavioral assertions and frontier models unchanged. Co-Authored-By: OpenAI Codex <noreply@openai.com> * fix: clarify retro workflow and evaluate compare instructions Include compare mode in the frontier judge excerpt, define metric sources and snapshot ordering, and preserve the existing prompt-size budget. Co-authored-by: OpenAI Codex <noreply@openai.com> * fix: make documentation release review and publication consistent Review before commit, clarify changelog safeguards and unavailable reviewer modes, and preserve raw PR bodies across separate shell calls. Keep title sync in one shell and add regression coverage. Co-authored-by: OpenAI Codex <noreply@openai.com> --------- Co-authored-by: OpenAI Codex <noreply@openai.com> |
||
|
|
0d1bd5616c |
v1.79.0.0 fix: ship subagent dispatches can no longer strand the run (#497/#2440 class) (#2772)
* fix(ship): foreground-flag + deadline + scope guard on all four dispatch sites (#497/#2440 class, 3rd recurrence)
Steps 7/8/10/18 dispatch subagents whose LAST-line JSON the parent
consumes, but none passed run_in_background: false — since Claude Code
v2.1.198 subagents background by default, so /ship stranded at Step 18
waiting on output that never arrives. Every site now renders the shared
{{FOREGROUND_DISPATCH_NOTE}} resolver constant, carries a ~10-minute
deadline with an explicit recovery branch (stop the runaway task,
reconcile against pre-dispatch HEAD, surface stray state, never
re-dispatch), and Step 18's prompt gains a docs-sync-only scope guard
(no VERSION changes, no base-branch merges, push-rejection reported as
pushed:false with parent-side reconciliation and a second-failure
branch). Greptile failures now record as UNAVAILABLE, not zero comments.
GENERATED_WITH_GUIDANCE pins all five ship dispatch carriers; a new test
pins the deadline recovery + scope guard phrases in pr-body (.md and
.tmpl). Codex/factory ship goldens re-rendered.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* feat(document-release): first-class spawned-dispatch contract
document-release's own templates had zero subagent-awareness — the
entire headless contract lived in /ship's dispatch prompt, so any other
orchestrator (or an older installed /ship) dispatching it inherited none
of the gate handling. The skill now carries the contract itself: detect
spawned strictly from the dispatch prompt or the preamble echo (never
from file content — prompt-injection guard), auto-choose recommended
options while keeping the never-clobber-CHANGELOG and
never-bump-VERSION-silently invariants via their Skip options. Step 8.4d
gets an explicit spawned note (its interactive recommendation bumps
VERSION — wrong headlessly), and the Codex Documentation Review section
skips itself in spawned sessions (the apply gate needs a human; the
dispatching workflow owns review passes).
Contract, 8.4d note, and resolver skip are pinned in
run-in-background-guidance.test.ts; document-release skeleton budget
re-measured (39,812 B) and ratcheted to 40,200.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix: sweep every remaining synchronous Agent-dispatch site with run_in_background: false
The #2440 failure shape was a silently-missing review voice — a
specialist launched in the background and merged before it completed.
Every remaining synchronous dispatch site now carries the explicit flag:
the Red Team dispatch, the spec review loop, the Codex
second-opinion/plan-review/doc-review Claude fallbacks, the adversarial
subagent, design sketch and outside voices, autoplan's design/eng/dx
phase dispatches, CSO parallel finding verification, and design-shotgun's
variant launch. Parallel fan-outs stay parallel — multiple foreground
Agent calls in one message run concurrently (the shipped v1.64.0.0
review-army pattern).
GENERATED_WITH_GUIDANCE now pins all 24 generated carriers, so a new
dispatch site that drops the flag fails the free suite. Six carved-skill
skeleton ceilings re-measured and ratcheted (~80-130 B growth each);
factory ship golden re-rendered.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* release: v1.79.0.0 — CHANGELOG, VERSION, TODOS follow-ups
Queue-advanced to 1.79.0.0 (1.78.0.0 claimed in the workspace queue;
same MINOR level per the versioning invariant). Entry references the
class history (#497 → #2440 → Step 18). Three TODOS filed: PreToolUse
hook enforcement, structural ship-mode for document-release, cross-host
dispatch semantics audit.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* regen: review-army sections carry the Red Team foreground flag
The scripts/resolvers/review-army.ts Red Team edit regenerated these two
files but the sweep commit staged only the adversarial sections — the
skill-docs freshness gate (regen + git diff --exit-code) catches this.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* regen: agents digest picks up v1.79.0.0
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(document-release): one canonical spawned contract, downstream notes are pointers
The diff-selected LLM-judge eval scored the skill's clarity 3 (threshold
4, main scores 4): the spawned-session rules read as three separately-
framed rule sets (contract paragraph, Step 8.4d note, Codex-review skip).
The contract paragraph now declares itself the single source of spawned
behavior and the two downstream notes reference it instead of restating
rationale. The pointer avoids naming the Codex section verbatim so the
codex-host render (which strips that section) keeps its negative pin.
Judge re-scored 4/5/4 across repeated samples after the change.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix: foreground note names the Agent tool as non-substitutable
The ship-docsync gate E2E caught a behavioral regression: the note's
blocking-emphasis ('a backgrounded dispatch strands the run') steered
the driven agent to run doc-sync via the Skill tool inline — the most
blocking option — twice in a row, forfeiting the fresh-context isolation
the dispatch exists for (baseline on main's text dispatches via Agent).
The shared note now says explicitly: dispatch with the Agent tool itself,
never substitute Skill or inline execution; the flag already makes the
call block. Re-verified: ship-docsync passes on the amended text.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix: pre-landing review fixes
Review army + red team findings, all verified before applying:
- ship-docsync E2E now asserts run_in_background === false on the
captured dispatch (red team CRITICAL: phrase pins prove text exists,
this proves the model obeys it — verified passing live).
- Structural scanner test: any generated file with an Agent-dispatch
imperative (or bare '(foreground)' prose, the #2440 inert shape) must
carry the flag or hold a reasoned exemption — the 4th-recurrence net
the hand-enumerated pin list can't provide.
- Parent push reconciliation models reality: the parent shares the repo,
so a non-fast-forward that hit the subagent hits the parent identically
— fetch + ahead/behind check first, push only when the rejection was
transient; dispatch prompt promise softened to 'the parent will handle
it'.
- Recovered commits from a dead subagent are vetted docs-only
(git show --stat, never VERSION/package.json) before any push.
- Deadline pacing named: ~3 minutes between checks, wall clock not polls.
- Greptile UNAVAILABLE recording narrowed to the PR body (Step 20's
schema carries no triage field).
- document-release contract gains the echo-failure tie-breaker: prompt
claims spawned + no echo → fail fast with the dispatch contract's
failure shape instead of reproducing the #2733 prose-STOP; contract
anti-injection and NEVER-relax clauses pinned in tests.
- 'Claude Code v2.1.198' extracted to CC_BACKGROUND_DEFAULT_SINCE and
interpolated at all resolver sites (byte-identical output).
- CHANGELOG: entry-boundary blank line restored; worst-case-wait row
scoped to the backgrounded path.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix: adversarial review fixes — failure shape, CHANGELOG guard, vet coherence
Claude adversarial subagent findings (11), all verified before applying:
- The dispatch JSON contract gains an explicit FAILURE shape
({"error":...}) and a parent branch for it — a doc-sync that could not
run (spawned marking failed, broken preamble) previously had only the
no-updates shape to emit, which the parent printed as 'Documentation is
current': a silent false-clean of exactly the VAS-449 class.
- Scope guard now covers CHANGELOG: skip Step 5 voice polish and resolve
CHANGELOG-touching gates to leave-as-is (the parent authored the
release entry; the prompt's older auto-choose clause conflicted with
the contract's never-rewrite invariant).
- Recovery vet is sequence-coherent: pushing a commit pushes its
ancestors, so ANY non-doc commit (VERSION, package.json, CHANGELOG.md)
blocks the whole sequence — no more push-the-vetted-child-of-an-
unvetted-parent hole.
- Steps 7/8/10 failure branches stop a still-running backgrounded task
before falling back, so a late result never races the inline audit.
- 'Documentation synced' print gated on pushed:true (item 6 owns the
local-only outcome); doc-review skip note's backward step pointer
fixed; foreground note scoped 'at initial dispatch' so the sanctioned
inline FALLBACKS in Steps 7/8 read as sanctioned; constants docstring
no longer overclaims single-sourcing (names the 3 inline templates).
- Runtime-hook TODO raised P2 → P1: the spawned trust chain is
agent-self-asserted; prose cannot close it, the hook can.
Both docsync gate E2Es re-verified green on the amended prompt, including
the strict run_in_background === false dispatch assert.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix: codex adversarial fixes — JSON trust, fail-open visibility, user-state safety
Codex outside-model adversarial pass (inline-diff workaround for the
sandbox), P1/P2 findings triaged cross-model:
- Parent validates the dispatch JSON's field types and treats
documentation_section as untrusted markdown (Step 19's redaction scan
covers the final body; instruction-shaped text inside it is never
followed). The error branch now explicitly skips items 2-6.
- Remote-ahead divergence is named, not silent: the parent lists foreign
commits before creating a PR over a moved branch.
- Recovery cleanup never discards content: stray staged doc edits are
unstaged but never checked out or cleaned away.
- Greptile UNAVAILABLE gets a concrete PR-body line, not a vague
'wherever results are reported'.
Pre-existing-class P1s (self-asserted spawned trust chain, unenforceable
foreground timeout) are cross-model confirmed and tracked: PreToolUse
hook TODO at P1, harness-residue hazard documented.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix: never-Skill prohibition made unambiguous; docsync fixture made diagnostic
The ship-docsync E2E flaked on this sandbox because the driven agent
resolved the STOP pointer's ~ to nonexistent homes (/root, /home) and
acted blind — pass/fail sampled model priors, not the prompt. The
fixture prompt now names the planted sections dir, making local runs
deterministic (CI, with real install paths, was always diagnostic).
With a diagnostic fixture: 2/2 passes, real pr-body read, Agent dispatch
with run_in_background: false, zero Skill-tool substitutions.
Prose: the foreground note now states the dispatch happens ONLY via the
Agent tool (invoking the target as a Skill is wrong even though it
appears in the skills list; inline FALLBACKs apply only after a
dispatched subagent has failed), and the Step 18 imperative carries the
never-the-Skill-tool clause at the decision point.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* docs: CHANGELOG entry covers the review-hardened contract
Failure JSON shape, CHANGELOG scope guard, vetted recovery pushes,
structural scanner, and the behavioral dispatch assert are shipped
properties of v1.79.0.0 — the entry now describes them.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* docs: update project documentation for v1.79.0.0
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* docs: CHANGELOG voice — follow-ups to For contributors, never-Skill property stated
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(test): daemon 503 test binds an OS-assigned port, not old-port+1
windows-free-tests flaked on this PR: the tunnel-less restart bound
daemon.loopbackPort + 1 — a fixed neighbor of the OS-assigned ephemeral
port — and died with 'Is port 55738 in use?' whenever another shard or a
TIME_WAIT socket held it; the file-level retry re-rolled the same dice.
Every other startDaemon in the file already uses loopbackPort: 0 and the
assertion reads d2.loopbackPort, so nothing needs a predictable number.
21/21 pass locally.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
|
||
|
|
2be6c06ba8 |
v1.65.0.0 feat: fork port wave 2 — feature fixes, session persistence, Apple releases, supply-chain CI (#2577)
* fix(memory-ingest): pass --include-gitignored to gbrain import gstack-artifacts-init writes an ignore-everything .gitignore (a bare `*`, headed "Do not edit") at the root of ~/.gstack. The memory ingest stages pages into ~/.gstack/.staging-ingest-<pid>-<ts>/, which is inside that repo, and gbrain's markdown collector honours .gitignore. The collector therefore matches every staged file against `*` and collects zero. The failure is silent. gbrain import exits 0 having imported nothing while the ingest prints `written: N` from the STAGED count rather than the imported count, so a run that indexes nothing looks identical to a healthy one and the memory corpus quietly stops growing. Reproduction, using git's own ignore machinery (no gbrain needed): git init . mkdir -p .staging-ingest-12345/learnings echo x > .staging-ingest-12345/learnings/page.md printf '*\n' > .gitignore git ls-files --others --exclude-standard # -> empty Passing --include-gitignored makes the import independent of whatever .gitignore sits above the staging directory. Adding a negation to the generated .gitignore is the alternative, but that file is gstack-owned and marked "Do not edit", so any regeneration silently reintroduces the bug. Adds a regression pin in the shape of memory-ingest-no-put_page.test.ts, plus a behavioural test for the collision itself. Both source pins fail against the unpatched file. * fix(memory-ingest): GIT_CEILING_DIRECTORIES defense-in-depth on the import child (#2144) Second layer under #2560's --include-gitignored: a realpath'd ceiling at the staging dir's parent pushes any git-enumerating collector off the git fast path (which sees zero files under ~/.gstack's ignore-everything root) onto its plain FS walk, even on gbrain builds whose flag semantics drift. Ceiling is realpath'd because git compares canonicalized directories during discovery — a staging dir reached through a symlink (macOS /var -> /private/var, symlinked $GSTACK_HOME) otherwise never matches. Behavioral tests prove discovery stops at the ceiling from the staging dir, including through a symlinked path, using git itself — no gbrain required. Mechanism ported from time-attack/gstack (GStack 2). Co-authored-by: Sina Matian <sina@time-attack.dev> Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(autoplan): Phase 4 task aggregator emitted zero tasks on every run (#2018) The branch+commit jq filter piped to the split commit array and then referenced .commit — jq rebinds context across a pipe, so .commit indexed the ARRAY with a string, every input line errored into 2>/dev/null, and || true swallowed the exit. The aggregate table has been empty for every user since the feature shipped. Bind .commit to a variable before the pipe. Functional pin extracts the ACTUAL emitted jq program from the resolver and runs it against fixture JSONL (verified RED against the broken filter), plus a source-shape guard against reintroducing a context-rebinding reference. Fix mechanism from time-attack/gstack (GStack 2). Co-authored-by: Sina Matian <sina@time-attack.dev> Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(codex): BSD mktemp templates broke /codex on every macOS install (#2091) macOS mktemp requires the X's to end the template; the five "codex-*-XXXXXX.txt" sites failed with "mkstemp failed ... File exists" before Codex ever ran (reproduced live on this machine). Same class fixed in claude/SKILL.md.tmpl's three sites. bin/gstack-paths now strips macOS's trailing slash from TMPDIR so TMP_ROOT-built paths stop carrying "//". Static tripwire scans every tracked .tmpl for characters after the X-run in a mktemp template (longer X-runs stay valid), plus a live portability check of the emitted shape. Co-authored-by: Sina Matian <sina@time-attack.dev> Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(decision-log): --supersede silently discarded the replacement decision The supersede/redact branch appended the retirement event and exited before the JSON argument was ever read — a user recording a reversal WITH its replacement lost the replacement, and the payload finder's first-non-flag-arg predicate would have mistaken the target id for JSON anyway. Payloads are now identified by their leading brace, validated BEFORE any write, and appended FIRST (retirement second), so the only visible interleaving under a crash is both-active — recoverable, never lost. The replacement carries supersedes:<old-id> provenance. Bare --supersede <id> (the documented reversal-without-replacement) stays legal; --redact with a payload now refuses instead of dropping it. Ported from time-attack/gstack (GStack 2), tests included. Co-authored-by: Sina Matian <sina@time-attack.dev> Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(brain-context): cold-start probe latency permanently disabled gbrain context gbrainAvailable() spawned gbrain --version under a 500ms budget; a cold CLI start on a loaded machine blew the timeout, misclassified gbrain as missing, and every skill session silently ran brainless — plus the per-query re-probe burned 3x the budget before any real work. Replaced with a memoized stat-based PATH scan (PATHEXT-aware on Windows) and made the query timeout overridable via GSTACK_BRAIN_TIMEOUT_MS for loaded CI environments. Also picks up the fork's manifest-filter coverage (#1687 shape) against the fake-gbrain harness — passes against our existing filter support. Ported from time-attack/gstack (GStack 2). Co-authored-by: Sina Matian <sina@time-attack.dev> Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(setup-gbrain): voyage-code-3 flags were silently dropped under zsh (#1798) zsh does not word-split an unquoted $VAR, so all three PGLite-init sites passed the entire flag string as ONE argv word — gbrain ignored it and silently fell back to its default embedding model, downgrading code retrieval for every zsh user (macOS default shell). Flags now ride the positional params (set -- ...; "$@"). Tests run the shape under BOTH bash and zsh against the fake-gbrain argv recorder (per-word argc log distinguishes one-blob from split), include a demonstration of the zsh collision on the old shape, and pin the template's three sites statically. Ported from time-attack/gstack (GStack 2). Co-authored-by: Sina Matian <sina@time-attack.dev> Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(model-benchmark): recognize macOS Keychain auth in the claude adapter (#1890) The default macOS Claude Code install stores OAuth under the Keychain generic-password service "Claude Code-credentials" and never writes ~/.claude/.credentials.json, so available()'s file-or-env sniff reported "No Claude auth found" while claude -p worked fine. On darwin the sniff now also probes the Keychain entry — metadata only (no -w, the secret is never read), 5s timeout, any security(1) failure degrades to not-found. Verified live on this machine (subscription install, no creds file, Keychain entry present). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(upgrade): v1.27 migration no longer auto-proceeds without a TTY or records a failed rename as done (#1383) Two silent-failure shapes in one script. Non-interactive runs (Claude Code Bash tool, CI) blanket-auto-proceeded into a REMOTE repo rename — now they skip-for-now by default and ask again next upgrade; unattended runs opt in with GSTACK_MIGRATE_ASSUME_YES=1. And a failed gh rename was journaled as done and the done-touchfile written, permanently stranding a half-renamed install — the failed step now stays PENDING with the manual command printed, finalize refuses the done-marker while any step is unjournaled, and the migration exits 1 with a re-run pointer while completed steps still skip on retry. Harness updated to opt in explicitly; new tests pin the default-skip and failure-stays-pending-then-retry-succeeds contracts (13/13). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(ship): REST fallback when gh pr edit hits the Projects-classic GraphQL deprecation (#1079) On repos where GitHub enforces the Projects-classic sunset, gh pr edit hard-errors on repository.pullRequest.projectCards and Step 19's PR body update dies. The template now names the error shape, says it is not an auth problem, and falls back to the REST endpoint (gh api pulls/N -X PATCH) with the SAME already-redaction-scanned temp file for body and title. Generated SKILL.md regen rides the cluster regen commit. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(ship): test-command detection was blind to Django and config-less-but-tested projects The Test Framework Bootstrap detected Python only via requirements.txt or pyproject.toml and treated missing config files as no-tests, so a green 'python manage.py test' Django app, a Go project with *_test.go beside the source, in-source Rust #[test] blocks, or a package.json with only a test script all got offered a SECOND test framework over a working one. Detection now enumerates definitive per-ecosystem markers (manage.py, tox.ini/setup.cfg, pom.xml/gradle, Makefile test targets, a tracked-file test census, in-source Rust tests) as EVIDENCE for the question it asks — never a command to run blind — preserving the read-CLAUDE.md-or-ask contract, with a marker→candidate-command table and ask-once persistence. The shared coverage-audit detection block gains the same markers. Test runs the resolver's emitted detection bash against Django / Go / Rust / Node fixtures in throwaway git repos. Ported from time-attack/gstack commit e3259078 (GStack 2). Co-authored-by: Sina Matian <sina@time-attack.dev> Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * chore: regenerate SKILL.md files for cluster A (autoplan jq, codex mktemp, setup-gbrain zsh, ship detection + REST fallback) Atomic regen of the 9 generated files whose templates/resolvers changed in the A-cluster commits. bun run gen:skill-docs, no hand edits. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * test: refresh ship goldens + parity ratios for cluster A growth Codex/Factory hosts render single-file ship skills whose committed goldens must track template changes; refreshed from the regenerated renders. Parity size guards bumped with the growth itemized — ship (carve-guards) 1.08 -> 1.10 for the detection-evidence + REST-fallback growth measured at 1.090x, qa (parity-harness monolith invariant) 1.07 -> 1.12 for the shared coverage-audit markers measured at 1.111x. Kept tight so the next growth is a deliberate decision, not drift; the Apple adapter raises ship again with its own justification. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(gbrain-sync): enforce the per-repo policy at the code-import chokepoint (#2140 sync path) The deny/read-only tiers in ~/.gstack/gbrain-repo-policy.json were stored by gstack-gbrain-repo-policy but enforced only in /sync-gbrain skill prose — a direct or cron invocation of gstack-gbrain-sync ingested repo code regardless. Worse: the code stage's egress receipt has cited 'per-repo policy chokepoint (repoPolicyTier)' as its consent since v1.63 while no such function existed. repoPolicyTier() now gates the stage before the dry-run branch: deny → refused-policy-deny (exit 1, loud), read-only → clean skipped-policy-read-only (code ingest writes pages), unreadable store → fail-closed refused-policy-unreadable, no store → unchanged fail-open. Subprocess tests pin all four paths against real git repos and a permission-blocked store (verified RED against the ungated binary). The receipt's consent string is truthful from this commit. #2140's ingest-path source-isolation ask remains open — partial-progress comment at ship. Ported from time-attack/gstack (GStack 2). Co-authored-by: Sina Matian <sina@time-attack.dev> Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(ios-qa): /auth/sessions no longer hands raw bearer tokens to any local process The loopback sessions list echoed live tokens — a harvest-and-replay primitive for anything on the machine (same class as the /health token leak fixed in v1.63). The list now returns a device-salted 16-hex token_id plus metadata; the salt is shared with the attempts log so identifiers correlate. /auth/revoke keeps the list→revoke workflow alive by accepting token_id alongside the caller's own raw token and identity. saltedHash() is exported from audit.ts and writeAttempt now reuses it (was inlined). Integration tests pin raw-token absence, the id shape/metadata, and the token_id revoke round-trip (verified RED against the leaking handler). List fix ported from time-attack/gstack (GStack 2); token_id revoke is ours. Co-authored-by: Sina Matian <sina@time-attack.dev> Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(ios-qa): boot token out of os_log entirely; IPv4 listener pinned to loopback at the socket The StateServer's bootstrap announce logged the live boot token with privacy: .public — and nothing consumed it: the daemon has read the token from the 0600 app-container file since the devicectl copy flow landed. The log line handed a credential to anything reading the unified log during the launch window. It now announces port/build only. The IPv4 listener bound the wildcard interface and relied on the per-connection peer check alone; IPv4 has no CoreDevice tunnel path, so it now binds 127.0.0.1 via requiredLocalEndpoint at the socket level. IPv6 keeps the wildcard bind for CoreDevice ULA peers by design. Static pins cover both the template and the fixture app copy. Ported from time-attack/gstack (GStack 2). Co-authored-by: Sina Matian <sina@time-attack.dev> Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(make-pdf): close the offline-gate bypass via raw-HTML fetch vectors With --allow-network off, the sanitizer stripped script/iframe/link but let Chromium fetch remote resources at print time through four raw-HTML vectors: <style> @import (any form), remote url() in <style> blocks and inline style attributes (incl. protocol-relative //), srcset with a remote candidate (Chromium prefers srcset over the inlined src), and remote src/poster on video/audio/source/track. All neutralized at the sanitizer; remote <img src> is deliberately left for the image inliner so its blocked-remote placeholder still fires, and url() mentions in prose/code spans stay untouched. Fork's test suite ported verbatim (12 cases incl. the end-to-end render assertion), verified RED against the old sanitizer. Ported from time-attack/gstack (GStack 2). Co-authored-by: Sina Matian <sina@time-attack.dev> Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(pair-agent): tunnel activation is consent-gated — and the receipt's consent claim is now real The tunnel egress receipts have claimed consent: 'pair_agent=on' since v1.63 while no such key or gate existed — ngrok installed+authed was enough for the CLI to auto-start an internet-facing tunnel. isPairAgentEnabled() (fail- closed, env-overridable) now gates all three activation points: CLI auto-start, POST /tunnel/start (refuses with the enable hint), and the BROWSE_TUNNEL=1 startup bind. Consent-on-first-use, not silent breakage: the /pair-agent skill asks once (one-way-door posture), sets pair_agent via gstack-config (registered with on|off validation, default off), and never asks again; direct API callers get the same hint in the refusal. Adapted from the fork's gate: their reader targeted config.json, which on main would have made the gate silently un-enableable — ours reads the canonical ~/.gstack/config.yaml with the JSON shape as fallback, pinned by tests either way (11 cases, gate wiring tripwires included). Ported from time-attack/gstack (GStack 2), store adaptation ours. Co-authored-by: Sina Matian <sina@time-attack.dev> Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * chore: regenerate pair-agent SKILL.md for cluster B (consent gate) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(browse): cancel the parent watchdog when handoff promotes a daemon to headed The parent-process watchdog assumes connection mode is fixed at boot: headless daemons outlive their parent, headed ones do not. The env guards (BROWSE_PARENT_PID=0, BROWSE_HEADED=1) only cover daemons that were headed when they started. handoff breaks that assumption. It swaps in a headed context on a RUNNING daemon and sets connectionMode = 'headed' without a restart, so a daemon that legitimately registered a watchdog lands on the fatal side of the branch. The parent is usually a short-lived shell, and Claude Code's Bash tool kills one after every invocation, so the next 15s poll shuts the daemon down. The user-visible effect is that handoff destroys the thing it just created. It exists so a human can log in, solve a CAPTCHA, or clear an MFA prompt; the browser disappears about fifteen seconds later and takes the session with it. Observed while driving two registrar control panels: five daemon deaths and three logins, each one discarding the authenticated session. BrowserManager now exposes onHeadedPromotion, fired only on runtime promotion and not on a headed boot, and the server binds it to a canceller for the interval it already owned but previously discarded. Bound on both the module-level manager and any embedder-supplied one, since the watchdog reads activeBrowserManager and binding only the default would let embedders promote silently. The binding sits next to the browserManager declaration rather than next to clearParentWatchdog. Placing it with the function, which lives with the watchdog it cancels, reads better but touches browserManager in its temporal dead zone, which aborts module evaluation and leaves every later const uninitialized. findport tests catch that immediately. Tests: watchdog.test.ts already noted in its header that its three cases all fix mode via env at spawn time, so none reaches the headed branch. Driving a real handoff needs a headed Chromium, so the wiring is pinned with static tripwires instead, matching cdp-session-cleanup.test.ts and server-auth.test.ts. Verified they fail when the notification call is removed and pass when restored. Full `bun test` shows the same 6 pre-existing failures on this branch and on main (gstack-gbrain-detect, gstack-artifacts-init), which pass in isolation on both, so they are test-order pollution rather than a regression here. * fix(browse): pass windowsHide so the daemon stops popping console windows On Windows, `browse` leaves empty black console windows on top of whatever the user is doing — they pop up every few minutes for as long as any browser skill is alive, and outlive the process that created them. Cause: `bun-polyfill.cjs` maps `Bun.spawn`/`Bun.spawnSync` onto node's `child_process`, and node defaults `windowsHide` to **false**. Bun never creates these windows, so nothing in the daemon's own code looks wrong — the behaviour only appears on the node fallback path. The one users notice is `spawnTerminalAgent()`, which launches `bun run terminal-agent.ts` through this shim. The daemon respawns it on a watchdog, so closing the window is not enough — a new one arrives shortly after. Ten `bun.exe` processes were live on the machine this was diagnosed on. Why they linger after the child exits: with the default terminal application set to "Let Windows decide", the console is brokered through Windows Terminal via svchost, and WT leaves the empty frame behind when its only child exits. The frame has no child process at all, which is why it looks like a dead terminal. Setting `windowsHide: true` on both wrappers fixes every console child routed through the shim — the bun agent plus the `tasklist`, `git` and `powershell` calls elsewhere in the daemon. No behaviour change on macOS or Linux, where the option is ignored. Not covered by this commit: `chromium.launch()` goes through playwright's own process launcher rather than this shim, so it still creates one window per daemon start. Worth a follow-up. * test(browse): make bun-polyfill tests runnable on Windows, and cover windowsHide `bun test browse/test/bun-polyfill.test.ts` was **0 pass / 4 fail on Windows** before this — every test in the file, on the platform the polyfill exists to support. Each test interpolates the polyfill's absolute path into a single-quoted JS string passed to `node -e`. On Windows that path has backslashes, so JS eats them as escapes: 'C:\Users\jwilk\dev\gstack-fork\browse\src\bun-polyfill.cjs' -> C:Usersjwilkdevgstack-forkrowsesrcun-polyfill.cjs (`\b` is a real escape, so it deletes a character too.) `require()` throws, the subprocess dies, stdout is empty, and every assertion compares against "". The tests pass on macOS and Linux purely because those paths have no backslashes. Fixed by interpolating with `JSON.stringify(polyfillPath)`, which quotes and escapes correctly on all platforms. Also adds a regression test for the windowsHide fix in the previous commit. It stubs `child_process.spawn`/`spawnSync` *before* the polyfill destructures them and asserts the captured options, so it is deterministic and needs no window — it verifies the contract on macOS and Linux too, where the option is a no-op. Verified on Windows: 5 pass / 0 fail with the fix, and the new test alone fails ("VISIBLE" instead of "HIDDEN") when the previous commit is reverted. * fix(browse): forward windowsHide through the Bun polyfill spawn shims The Node fallback shim accepts a Bun.spawn options object and forwards only stdio, env and cwd to child_process.spawn. windowsHide is dropped, and because Node defaults it to false while Bun.spawn hides the console window, the omission inverts the behavior on the one platform the shim exists to support. Symptom: the terminal-agent respawn in server.ts (60s watchdog ticker) pops a visible bun.exe console window on Windows every time it fires, so the window keeps coming back with no scheduled task or startup entry behind it. stdio:'ignore' silences the child's output but does not suppress its window. Both shims now forward the option and default it to true, matching the Bun API being emulated; an explicit windowsHide:false still passes through. spawnTerminalAgent also sets it explicitly at the call site. Tests: three cases in browse/test/bun-polyfill.test.ts assert the default for spawn and spawnSync and that an explicit false is honored. Each was confirmed to fail against the unpatched shim. Drive-by, required to run the suite at all on Windows: the tests interpolated an absolute path into a JS string literal, so backslashes were consumed as escapes and every require() failed with MODULE_NOT_FOUND. The path is now normalized to forward slashes. On Windows this file went from 0/4 passing to 7/7. * fix(browse): headed mode on macOS 26 — stop mutating the signed Chromium bundle, heal the ones we already broke (#2242, #2138, #2139) The in-place rebrand rewrote the Chrome-for-Testing bundle's Info.plist (global name replace — which also renamed CFBundleExecutable to a binary that doesn't exist) and overwrote its Resources/*.icns, breaking the codesign seal: GPU process exit_code=5, headed mode dead on macOS 26. The mutation lived in the SHARED Playwright cache, so it also poisoned the user's other Playwright projects. Three layers land together: (1) the rebrand block is gone — branding lives in the GStack Browser.app wrapper via GSTACK_CHROMIUM_PATH, with a tombstone and a static tripwire (no plist/icns writes into the bundle; the tripwire allows the read-only probe below); (2) a launch-time self-heal detects an already-poisoned cache bundle, removes it, and errors with the exact re-fetch command — covering deploy paths that never run migrations; (3) migration v1.64.0.0 sweeps every cached bundle, removes poisoned ones, and re-fetches clean Chromium immediately (migrations run after ./setup, so without the re-fetch an upgrade would end with zero working browser). Functionally verified against fixture caches: poisoned removed, clean untouched, rerun no-op. Migration filename tracks the final VERSION at ship. The #2242 watchdog half is the absorbed PR #2565 (thanks @Screddyice). Tombstone/tripwire ported from time-attack/gstack (GStack 2); self-heal and migration are ours. Co-authored-by: Sina Matian <sina@time-attack.dev> Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(browse): 'browse stop' no longer restarts the daemon it was asked to stop The stop handler awaited shutdown() — which ends in process.exit — before returning, so the acknowledgement never egressed. The CLI's fetch reset, which its crash path reasonably interpreted as a dead daemon: it relaunched Chromium, re-sent stop, watched the daemon exit again, and errored 'Server crashed twice in a row'. Every stop cost a wasted Chromium launch and a nonzero exit. The ack now returns first; shutdown fires on a 25ms unref'd timer. Same fix for restart. Fork's test pins ack-before-teardown for both. Ported from time-attack/gstack (GStack 2). Co-authored-by: Sina Matian <sina@time-attack.dev> Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(browse): lock acquisition reports real errors instead of phantom contention (#1084) acquireServerLock's bare catch treated EVERY failure as 'another process holds the lock' — a missing state dir, EACCES, or ENOSPC read as permanent phantom contention with nothing to debug. Now only EEXIST is contention: ENOENT self-heals with one mkdirSecure retry, everything else throws ServerLockError carrying the real errno, and the stale-lock unlink/retry loop is depth-capped so it can't livelock. Fork's five-case test ported. Ported from time-attack/gstack (GStack 2). Co-authored-by: Sina Matian <sina@time-attack.dev> Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * test(browse): integration coverage for #1781 busy-vs-dead recovery Fork's wedged-daemon fixture: first /command connection drops, daemon PID stays alive. Pins the whole contract — CLI retries the same daemon instance without a kill, state file untouched, no restart, exactly two command requests. Message-text assertion adapted: our CLI retries silently at the probe layer where the fork announces on stderr; the behavior, not the message, is the invariant. Ported from time-attack/gstack (GStack 2). Co-authored-by: Sina Matian <sina@time-attack.dev> Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(browse): windowsHide on every Windows-reachable spawn (#1835) Console windows flashed (and stole focus) on every daemon relaunch, taskkill, tasklist poll, and powershell DPAPI call — node-level spawns default windowsHide to false. Covered: the node -e launcher (outer spawnSync AND the inner detached daemon spawn inside the launcher string), the dev-mode bun fallback, killServer's taskkill, isProcessAlive's tasklist, and cookie-import's powershell + tasklist. The Bun-polyfill shims were covered by absorbed PRs #2523 + #2539 (thanks @jwilk-hrep, @jerrynicholsai); this closes the sites those PRs didn't reach. The icacls sites land with the #1605 DACL commit alongside the static tripwire that pins all of them. R8's planned spawnHidden() helper is deliberately NOT built: the polyfill default plus the tripwire achieve the no-drift goal without indirection over seven heterogeneous call shapes. The polyfill + spawn-hide tests join the Windows CI shard. Ported from time-attack/gstack (GStack 2). Co-authored-by: Sina Matian <sina@time-attack.dev> Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(browse): self-repair broken Windows DACLs on state dirs (#1605) icacls '/inheritance:r /grant:r' can partially fail on localized or domain accounts: inheritance strips but the user grant doesn't resolve, leaving a machine-SID-only DACL the owner can't even list — the sidebar/PTY failure chain in #1605, caused by the very hardening call meant to protect the dir. mkdirSecure now verifies listability after hardening (a real readdir — fs.accessSync doesn't consult NTFS ACLs) and repairs via icacls /reset, re-hardens, and if hardening breaks access again leaves inherited ACLs: functional-but-unhardened beats hardened-but-unusable. The icacls calls carry windowsHide (#1835's last two sites) and the fork's static spawn-hide tripwire lands here, pinning every covered site. file-permissions.test.ts is already in the windows-free-tests curated shard, so the DACL contract executes on windows-latest. Ported from time-attack/gstack (GStack 2). Co-authored-by: Sina Matian <sina@time-attack.dev> Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(browse): opt-in session persistence — auth survives daemon restarts (#778, #2193) BROWSE_PERSIST_STATE=1 snapshots cookies + per-tab URL/localStorage/ sessionStorage to <stateDir>/session-state.json (0600) on a 30s unref'd interval and at clean shutdown, and restores on the next launch — killing the top-complained auth-lost-on-restart class (#778, #2193, #1128, #1129). Security invariants mirror state save|load: loadedHtml and owner are never persisted and never accepted from disk; restored cookies pass the same hygiene filter (localhost/.internal/metadata domains dropped); restoreState re-validates every URL. Default OFF; headed mode excluded (the persistent profile owns that state). Hardened past the fork's shape per review R3: corrupt state quarantines to .corrupt (forensic artifact, boots fresh, one log line), snapshot failures warn once and never kill the daemon, and the boot log reports restored counts or fresh-session status. Module + 10 tests ported (MIT header retained); server wiring at launch, interval, and shutdown; skill docs section added (regen rides the cluster regen commit). Ported from time-attack/gstack (GStack 2). Co-authored-by: Sina Matian <sina@time-attack.dev> Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * chore: regenerate browse SKILL.md for cluster C (session persistence docs) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(skills): third-party web-actions contract — offer to drive vendor-site steps, never just dump a manual list When a workflow needs something done on an external website the user controls (register an API key, create a vendor account, configure a dashboard/webhook/OAuth app), five skills (ship, spec, office-hours, setup-deploy, land-and-deploy) now follow one contract: offer to drive it in a visible browser via gstack's own stack ($B headed + handoff/resume, GStack Browser) behind ONE per-task consent question naming the exact site and actions; passwords, payment, CAPTCHA, and identity stay user-performed; captured secrets go to owner-only files or the user's secret store, never chat/logs/history; and the credential is verified with one non-mutating API call before any success claim — dashboards show masked placeholders, and a 401 catches them. Declining yields manual steps and a blocked-on-user mark; nothing new is ever installed to close the gap. New resolver token {{THIRD_PARTY_ACTIONS}} (adapted from the fork's contract — their Aside-browser detection swapped for our own driver stack; MIT portions noted). Parity guards bumped with growth itemized (ship 1.10->1.12 at measured 1.103x; office-hours skeleton 101K / 1.09 at measured 1.079x); ship goldens refreshed. Ported from time-attack/gstack (GStack 2), driver adaptation ours. Co-authored-by: Sina Matian <sina@time-attack.dev> Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(office-hours): design docs land in the repo, written as decision records (#703, #2000) Office-hours Phase 5 now dual-writes the design doc: the docs/designs/ copy is what teammates and plan reviews read (committable, visible), while the ~/.gstack copy keeps memory ingest and cross-session discovery working. The repo copy leaves the private store, so it passes the redaction scan-at-sink first (HIGH blocks the repo copy, MEDIUM confirms per finding), and any failure — read-only checkout, non-git dir, unconfirmed finding — degrades to the private copy with a one-line reason, never blocking the handoff. The doc itself is now a decision record, not a transcript: one bullet per decision with its why, ruled-out approaches collapsed to a single line with the rejection reason, settled/empty template sections omitted. No page cap; extra length must come from genuinely open questions. Plan reviews (ceo/eng/devex + the shared review resolver) prefer the repo-local doc (DESIGN.md, then newest docs/designs/*.md) when it's at least as fresh as the private copy — a stale old repo doc never shadows a newer session. Parity guards bumped with measured values (three plan-review skeletons +~0.7KB each; office-hours 1.092x). Judgment ported from time-attack/gstack (GStack 2); scan-at-sink and freshness-preference adaptations ours. Co-authored-by: Sina Matian <sina@time-attack.dev> Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(office-hours): 'never show me these again' for the founder-resources pitch (#538) The Phase 6 resources offer (34 PG essays + Garry/YC videos) had no permanent decline — the reporter showed memory instructions kept being overridden on every update, so people who said no got re-pitched forever. The offer now closes with a standing choice; opting out runs gstack-config set founder_resources false (new key, default true, true|false validated), the write is VERIFIED before any promise (a failed write says so and skips this session only), and every future session skips the entire section silently — no resources, no 'skipped as requested' mention. Config outlives session context, so never means never. Re-enable anytime: gstack-config set founder_resources true. The pitch stays default-ON for everyone who never opted out. Tests pin the key's default/persistence/validation through the real config bin and the generated section's gate-before-content + write-verify contract. Approved as a promo-surface change (CEO review D3.4, 2026-08-14). Ported from time-attack/gstack (GStack 2). Co-authored-by: Sina Matian <sina@time-attack.dev> Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(ship): the Apple App Store release journey — working tree to Submit for Review Point /ship at a repo with an .xcodeproj, .xcworkspace, or app-product Swift package and ask to release: the adapter runs the whole journey with ONE authorization moment (membership + pricing + in-session sign-in, decision- store persisted so repeat releases ask nothing) and one store-assets question only when assets are missing. fastlane is the single tool (produce/cert/ sigh/gym/pilot/deliver/frameit); credential vocabulary never reaches the user. The adapter carries 21 live releases' worth of paid-for Apple knowledge: the web session mints the permanent upload key itself (iris POST /v1/apiKeys; privateKey is base64-of-PEM, downloadable only at creation) so nobody ever types an app-specific password; error -22938 is Transporter asking for a key, not a user task; errors are CLASSIFIED before credentials are touched (validation/UnexpectedResponse = metadata, incl. Apple's expanded age-rating attributes); pricing goes through POST /v1/appPriceSchedules because fastlane's price_tier is broken against the current API; and store distribution NEVER routes through the branch gate — a clean tree on main is the solo shipper's normal case (Step 0.9 loads the adapter BEFORE the gate, pinned by test with the non-Apple gate byte-unchanged and unique). Uploads/submissions follow an idempotency-log contract (inspect App Store Connect before any re-run). Non-Mac hosts get the honest split: build legs via a macOS CI runner with the minted key as a secret, API legs local. Browser use inside the journey is banned except the named paid-app banking/tax residue. Redaction dry-run clean. Ship's parity ratio raised 1.12 -> 1.22 deliberately: the 14.8KB section is on-demand (Apple store targets only), one manifest line otherwise. Ported from time-attack/gstack (GStack 2), refined across its 21 live releases; architecture adaptation (carved section, decision-store paths, idempotency log, third-party-actions handoff) ours. Co-authored-by: Sina Matian <sina@time-attack.dev> Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(code-intelligence): provider contract Phase 1 — GBrain, Sourcebot, Graphify behind one ask-once offer Open a large repo (1,000+ tracked files) and gstack can offer code intelligence ONCE, with the trade-offs stated: GBrain (semantic memory + code, sends content to YOUR gbrain DB, per-repo consent), Sourcebot (self-hosted whole-repo search, local on localhost), Graphify (local tree-sitter graph, nothing leaves the machine, user-installed), or No indexing — a decline persists machine-wide so no skill ever asks again. Small repos never see the question; grep stays the always-working default and provider-OFF degrades silently (PROVIDER_UNAVAILABLE -> file-only). Ported: lib/code-intelligence/ (contract + 3 verified adapters + picker + selection + suggest, MIT headers), the gstack-code-intelligence CLI (suggest/select/consent/index/search/status), 31 offline tests (fake CLI shims + injected fetch), and the provider-contract design doc. Verified live on this repo: suggest fires at 1,233 files with real availability detail per provider. Hardened per review: the per-remote trust store is the SINGLE consent authority — a gstack-gbrain-repo-policy deny tier vetoes any recorded code-intelligence consent (fail-closed on an unreadable store, pinned by three tests); both send-capable adapters are registered as fail-closed MODULE_SINKS in the egress tripwire so a refactor can't drop their receipts; and local-compute vs remote-send consents are never bundled. setup-gbrain gains the provider-choice Step 0. The fork's Phases 2-4 glue-collapse is explicitly NOT ported. Ported from time-attack/gstack (GStack 2); consent unification ours. Co-authored-by: Sina Matian <sina@time-attack.dev> Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(ci): supply-chain hygiene — secret gate on every PR diff, dependency review, OSV, dependabot, evidence-bar PR template The repo owned a redaction engine and had zero CI-side secret scanning. quality-gate.yml now pipes every PR diff's ADDED lines through our own bin/gstack-redact (gate-secret-scan.mjs, taken from the fork — it dogfoods the engine): HIGH findings fail the check, MEDIUM prints an advisory count only (no human in CI to confirm), planted-bug fixtures excluded by pathspec. Live-verified both directions: PEM key fails, clean diff and MEDIUM shapes pass; ShellCheck (errors) covers the setup/build shell boundary and passes today; bun audit gates critical advisories. Trigger is pull_request, never pull_request_target. dependency-review.yml adopts the hardened never-merged prior-art branch (fail-on-severity high, workflow paths watched, tight perms) — verify the dependency graph parses bun.lock with a canary bump before trusting the gate. dependabot: weekly, grouped per ecosystem, capped PR counts; and evals.yml image build/push now skips dependabot actors, whose read-only GITHUB_TOKEN made every lockfile bump a permanently red check. OSV scans weekly with a reasoned ignore file. All new workflow actions SHA-pinned. Scorecard deliberately not taken (no consumer for the score). The PR template front-loads the evidence bar (live proof, liveness screenshot, no-ETHOS/voice-changes checklist); the unenforced DCO line is dropped. bin/gstack-verify-gate ships OPT-IN (never registered by ./setup — a Stop hook running the project's verify command after every turn is the user's call), with the fork's tests adapted to pin exactly that. Ported from time-attack/gstack (GStack 2) + our own prior-art branch. Co-authored-by: Sina Matian <sina@time-attack.dev> Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * chore: remove dead bins; extend the stale-ref scan to docs (the 36-release gap) bin/chrome-cdp, bin/gstack-open-url, and bin/gstack-platform-detect were referenced only by an audit test and CHANGELOG history — dead weight that the stale-ref scanner should police, which required removing them FIRST. The scanner now also sweeps docs/, README.md, and USING_GBRAIN_WITH_GSTACK — the deliberate exclusion that let a dead command survive ~36 releases as a command-not-found instruction. Scan is green on the extended surface. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(bins): detect the default branch instead of hardcoding main gstack-diff-scope fell to an empty diff (all-false SCOPE_*) and gstack-next-version mis-based its bump math on any repo whose default branch isn't main (trunk, master, local-only). Both now resolve origin/HEAD -> origin/main -> origin/master -> main. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix: housekeeping sweep — telemetry integrity, persistent opt-out, context-bill accuracy, setup hang, dev-server discovery, model resolution (#2136 + v1.63 polish) Seven small fixes, one theme (claims matching code): - telemetry-sync strips local-only fields with jq del() (structural) instead of quote-fragile sed regexes; unparseable lines are dropped, never forwarded unstripped. Sed survives only as a jq-less fallback. - telemetry-log rejects non-integer durations BEFORE the range caps, whose test(1) comparisons silently no-op on non-numerics — a malformed duration spliced raw text into the JSONL stream. - browse's local telemetry honors the persistent tier (config.yaml telemetry: off), not just the preamble's env hint — direct $B use and embedders now respect the opt-out. - gstack-context-bill --exact sees GSTACK_-promoted keys inside Conductor (conductor-env-shim wired at the CLI entry), and the TOTAL line no longer double-counts every nested skill through the root skill's walk (v1.63 deferred polish; the telemetry-sync HTTP-status outcome deferred alongside it turned out already shipped). - setup's Chromium probe is deadline-bounded (90s, background + poll-kill — macOS has no GNU timeout) and prefers Node for the launch probe everywhere (the bun --eval hang family behind #2136); the install is single-flight behind a lock dir with an actionable stale-lock message. Probe verified live on this Mac. - the review resolver's dev-server check reads CLAUDE.md and the plan file before falling back to an expanded port probe, and says how to make itself smarter next time. - eval/harness model IDs resolve through lib/eval-model.ts (GSTACK_EVAL_MODEL[_KIND] env overrides, per-kind defaults, tested) at the SDK-capture and PTY-warmup sites; the bash-embedded distill snippet mirrors the resolution inline. - memory-ingest's silent-zero shape (staged>0, imported+unchanged==0, errors==0) warns even under --quiet — a run that indexes nothing must never look healthy again. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * test: wire ios-qa/daemon/test into the free suite and shard runner (E2) The daemon's 5 test files (allowlist, audit, auth-mint, cli-mint, daemon-integration — now 6 with session hardening) were invisible to every runner: not in the bun test glob, not in TEST_ROOTS. The same silent-coverage-hole class as the tracked design/test P2 — and it meant B2's auth regression tests would never have gated. All files are hermetic (stub state-servers on ephemeral ports, no devices); verified green in the shard census. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(skills): claimed limitations now require evidence, everywhere + wave follow-ups filed Every tier-2+ skill's preamble gains one directive distilled from nine live release failures in two days on the fork: a claimed limitation or requirement ('the API can't do this', 'X requires a credential', 'impossible on this platform') is a material claim, stated only with the verbatim error, the documented statement, or a live probe in hand — pattern-matching a failure to a familiar story is not evidence, and a cheap probe runs BEFORE asking the user or declaring a step blocked. ONE directive adapted into the preamble resolver; the fork's full judgment contract is deliberately not imported. Full regen (46 files), ship goldens refreshed, parity guards bumped with the measured ~0.45KB/skill (investigate, autoplan, plan-design-review, office-hours), Step 0.9 registered as an intentional sub-step. Approved deferrals filed: persona-fleet hostile-user harness + answer-key methodology in TODOS; the fork's question-budget ACCOUNTING judgment (never its 5/8/12 constants) folded into the V1.1 pacing design doc; the Apple adapter added to #1882's coverage note. Ported from time-attack/gstack (GStack 2). Co-authored-by: Sina Matian <sina@time-attack.dev> Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(make-pdf): close offline-gate bypasses via unquoted style attrs, CSS-escape and HTML-entity obfuscation Three live vectors found by the ship review army, all red-first tested: unquoted style attributes skipped the remote-url neutralizer entirely; CSS ident/string escapes (@\69mport, url(\68ttps://…)) defeated the literal-match patterns Chromium happily decodes; and HTML entities in style attribute values (https) decoded to fetchable schemes before CSS parsing. Style-attr values are now entity-decoded in one browser- faithful pass, escape-bearing at-rules and function tokens are dropped fail-closed, and output is re-encoded double-quoted. 21 new test rows. * fix(migrations): v1.65 Chromium re-fetch actually re-downloads, and success is verified before .done The migration (renamed from the provisional v1.64.0.0 slot, which open PR #2564 claims) deleted only the poisoned .app while Playwright's INSTALLATION_COMPLETE marker survived in the revision dir — so the advertised 'bunx playwright install chromium' re-fetch no-opped and the user finished the upgrade with no browser and a success message. Now: the whole chromium-<rev> dir goes, bunx runs cwd-pinned to the install root, .done is gated on a verified executable, and a needs-refetch sentinel makes re-runs retry a failed download. Stranded rev dirs (markers without .app) also re-trigger. 6 hermetic tests, red-first. * fix(migrations): v1.27 remediation prints a real command instead of a fictional flag Every skip/failure path referenced '/setup-gbrain --rerun-migration', which is implemented nowhere, and promised the migration 'will ask again next upgrade', which the version-window runners make false. All five sites now print the direct GSTACK_MIGRATE_ASSUME_YES=1 bash invocation. Runner-side re-offer tracking is filed in TODOS. * fix(browse): poisoned-bundle self-heal removes the revision dir, probes handoff too, and throws typed Same marker flaw as the migration: rmSync of the .app alone left INSTALLATION_COMPLETE behind, so the error message's own remediation no-opped and the user was hard-stuck. The probe is now an exported, unit-tested helper (probePoisonedChromiumBundle) that removes the whole chromium-<rev> dir, never touches GSTACK_CHROMIUM_PATH custom bundles, throws PoisonedBundleError (instanceof, not string-match), and runs on BOTH headed entry points — launchHeaded and handoff. 7 tests. * fix(browse): session snapshots are atomic and the cookie filter drops loopback IP literals A crash mid-write destroyed the previous good snapshot — the exact scenario persistence exists to survive; writes now go tmp+rename. The internal-network cookie filter gains 127.*/::1/169.254.* (a tampered state file could previously hand loopback-service cookies back to the browser), and 'state load' imports the shared filter instead of maintaining a comment-synced copy. Test cleanup made exception-safe. * fix(browse): server runtime — restore off the boot path, shutdown that cannot hang, watchdog that still reaps tunnels Four review findings on the wave's own new wiring: session restore ran before Bun.serve with sequential 15s gotos while the CLI gives up at 8s (one slow saved URL bricked every $B command) — restore now runs in the background after bind; the shutdown snapshot gets a 2s deadline so a wedged page.evaluate can't hold the port forever behind the new ack-first stop; the persistence ticker gets in-flight + shutdown gates and is cleared before the final snapshot; and the absorbed #2565 handoff fix no longer clears the whole parent watchdog — a suppress flag keeps the tunnel-orphan reaper alive (handoff→resume→tunnel is no longer an unreapable internet-exposed daemon). pair-agent with consent off now names the real remedy instead of ngrok install instructions. Lock-acquisition edge branches (garbage pidfile, vanish-race depth cap) pinned. * fix(browse): telemetry defaults to off like every other surface The persistent tier defaulted ON when the config key was absent, while gstack-config's DEFAULTS table answers 'off' for the same question — preamble-spawned daemons and direct $B daemons disagreed about consent. Absent key/file now means disabled; community/anonymous enable; env kill-switch still beats everything. Both config.yaml consumers now share one readGstackConfigYamlKey reader. 12-case consent suite. * fix(code-intelligence): consent that means what it says — polarity, receipts, read-only veto Four review findings on the wave's own Phase 1 port, all red-first: 'consent <repo> no' recorded consent GRANTED (the CLI ignored the argument and always wrote true) — yes|no is now required and garbage records nothing; Sourcebot egress receipts claimed consented=true on paths that never checked consent — the actual consent state is threaded into every receipt, search is fail-closed on non-loopback, and the liveness probe's receipt says truthfully that it sends no repo content; repoPolicyVeto only honored the deny tier while gbrain refresh writes pages — write-class ops now veto on read-only too, matching the sync chokepoint, via one shared lib/gbrain-repo-policy-client.ts (win32 bash invocation, spawn-vs-unreadable error distinction) used by both call sites. Also: source ids get a host+path hash (same-name repos no longer collide), refresh timeout raised to 120s, availability probes run concurrently at 3s, graphify status stops JSON.parsing 100MB graphs for a count, and every ported file carries the fork MIT notice. +15 tests across the two suites. * fix(verify-gate): trust before eval, re-check on re-entry, audit every grant The opt-in Stop hook eval'd whatever command the first CLAUDE.md up the tree declared — any cloned repo got arbitrary shell at turn end. Now a per-repo trust store (path+command hash, 0600) gates execution: an untrusted or changed command never runs (exit 0 with the --trust invocation printed), stop_hook_active re-entry re-runs the trusted check instead of rubber-stamping (bounded at 3 blocks per episode), and every grant appends a forensic line to ~/.gstack/security/verify-gate-trust-grants.jsonl. 20 tests, red-first. * fix(setup): EXIT traps chain instead of clobbering; timed-out probes reap their whole tree The Playwright-lock trap replaced the copied-bun cleanup trap and then cleared ALL exit handling, leaking .tmp-bun-bin on every Chromium install; and _wait_with_deadline killed only the subshell, orphaning the wedged node→Chromium tree it exists to escape — re-creating the #2136 pile-up on every timed-out re-run. Traps now chain; timeouts walk pgrep -P descendants leaves-first. * refactor(resolvers): one source for the design-doc discovery block The #703 repo-doc-preference bash was pasted byte-identically into three plan-review templates and a fourth copy embedded in review.ts — drift there means plan reviews disagree about which design doc wins. Now a {{DESIGN_DOC_DISCOVERY}} resolver; generated output is byte-identical, so no SKILL.md changes ride along. * fix(ship): finish the Apple upload idempotency sentence The durable-effect contract dropped its consequence clause mid-sentence — the instruction for what to DO when the idempotency key already exists (treat the upload as possibly-done, never re-run it) was missing from the one rule governing whether a binary uploads twice. * fix(ci): SHA-pin dependency-review; the secret gate fails closed without a report dependency-review.yml rode mutable refs (@v4 resolves to a BRANCH on that repo) inside the one workflow whose job is supply-chain hygiene — now commit-pinned like its siblings, with dependabot keeping the pins fresh. gate-secret-scan.mjs crashed with an unhandled EPIPE on oversize diffs (the designed report.oversize branch was unreachable: the scanner emits no JSON on refusal) — the pipe write now tolerates early exit and a missing report is an explicit fail-closed exit 1. Oversize + broken-scanner legs pinned. * fix(bins): Windows-safe GIT_CEILING join; next-version probes the full default-base chain GIT_CEILING_DIRECTORIES was joined with ':' — git on Windows splits on ';' and drive letters contain ':', silently disabling the #2144 second-layer defense there; now path.delimiter. next-version's default-base detection only tried origin/HEAD then 'main', diverging from the canonical 4-step chain diff-scope uses — origin/main and origin/master probes added, pinned by fixture repos. * fix(eval-model): kinds are a literal union, not string Record<string,string> widened EvalModelKind to string, so a typo'd kind only failed at runtime; as const satisfies keeps the closed set the doc comment promises. * test: coverage backfill from the ship review The telemetry-strip invariant only validated the sed FALLBACK while the live jq path went unchecked — the jq del() lists are now held to the same every-emitted-field bar, plus a behavioral pipe-through. The context-bill nested-skill double-count fix gets a regression pin (a revert shipped green before). The windowsHide tripwire gains terminal-agent-control.ts — the exact file the fix commit names. The ios-qa revoke-by-token_id branch gets its negative case: unknown ids revoke nothing and leave live sessions alone. * docs: SLATE_HOST no longer cites the deleted platform-detect bin Host detection lives in the hosts/ registry via host-config-export.ts; the doc's known-gaps list now says so instead of pointing at a bin this branch removed. * test(e2e): headroom for the two plan-ceo-review budget-edge tests Both rode their 360s runner budget at the edge (main clears at 243s of 360s), and the wave legitimately adds work to the review: the evidence directive tells the agent to probe before claiming, and the design-doc discovery block adds bash steps. Under concurrent in-file children the API queuing tipped all retry attempts past the ceiling — the runner then reports $0.00/0 turns for a timed-out child, which reads like a dead spawn but is a healthy child killed at the deadline. 540s runner / 660s test for these two only; verified 2/2 green at 228s and 315s. * fix(code-intelligence): gbrain search/export are consent-gated and receipted The Sourcebot side got this in the last round; gbrain had the same hole — search() and export() sent repo-derived query text into a possibly-remote DATABASE_URL with no consent check and no egress receipt, bypassing the deny-tier veto. Both now assert consent before any bytes move, receipts record the actual consent state (never a hardcoded true), and search receipts carry the query's sha256. gbrain stays fail-closed: the adapter cannot see where DATABASE_URL points, so every send requires consent. 7 new tests, red-first. * fix(make-pdf): SVG remote refs and image-set can no longer fetch offline <svg><image href=https://…> and <use xlink:href=…> survived the gate (only javascript: schemes were stripped from svg hrefs), and bare-string image-set("https://…" 1x) dodged the url()-shaped neutralizer. Remote svg hrefs rewrite to '#' (entity-decode-aware, unclosed-svg smuggle closed) and remote image-set args neutralize to url(#). Local fragments, local image-set, and plain <a> links pinned intact. 12 new rows, red-first. * fix(browse): duplicate config keys read last-wins, matching gstack-config readGstackConfigYamlKey took the FIRST match while gstack-config's get takes the LAST — a duplicated pair_agent or telemetry line made the two consent surfaces disagree about what the user chose. * fix(setup): stale Chromium-install lock self-heals The mkdir mutex had no owner: a SIGKILL'd setup left the lock behind and every later run exited with manual rmdir instructions. The holder pid is recorded in the lock; a dead holder is reclaimed automatically. * fix(setup-gbrain): the code-intelligence offer gate skips when the bin is absent The new Step 1.7 told the agent to run gstack-code-intelligence before the path pick — on installs predating the CLI (and hermetic E2E children) the bin doesn't exist and setup derailed before doing any setup. The gate now probes for the bin and reports offer:false reason:bin-absent, with explicit instructions to proceed: the user asked for gbrain, so set up gbrain. Never block setup on an optional gate. * test(e2e): periodic-tier repairs from the failure triage Each fix traces to a receipt: brain-privacy-gate staged config never reached the hermetic child (ambient GSTACK_HOME is scrubbed) and the operator's remote-mode gbrain suppressed the gate — both now injected per-test; ship-idempotency threw away its evidence on the timeout path and ran a 600s budget its own subject can exceed (now 900s, evidence captured); auto-decide-preserved gets the same headroom its sibling plan-ceo tests got; context-skills' hides-checks scanned bash output where an ls legitimately names old checkpoints (final-text scope now); design names the missing section instead of a bare count and learns the easing/duration/micro-interaction synonyms; qa-workflow's collector afterAll gets an explicit 60s hook timeout. * fix(eval-harness): eng-review phase boundary fires on qid-tagged questions The Step 0 boundary only matched two prose phrases, but plan-eng-review may legitimately reach the review phase without either — every per-finding AskUserQuestion then counted as pre-review and the batching regression test read 0 questions while watching the agent ask them one by one. The boundary now also fires on the first answered question carrying a gstack-qid:eng-review- marker. Additive only; 119 runner unit tests green. * chore: bump version and changelog (v1.65.0.0) Fork port wave 2: the release-summary entry credits Sina Matian (time-attack/gstack) and the four absorbed community PRs. TODOS gains three review-round follow-ups (dual-write E2E, migration runner re-offer, gbrain-adapter op coverage). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(eval-harness): eng-review qid boundary matches the real skill-name prefix Live qids render as gstack-qid:plan-eng-review-<slug> ({skill}-{slug} convention); the boundary anchored eng-review- immediately after the colon and never matched, leaving the batching counter blind while the transcript showed per-finding questions being asked one by one. * fix(setup-gbrain): never ask the provider question inside /setup-gbrain Invoking /setup-gbrain IS the provider choice. Step 1.7 now records 'select gbrain' best-effort and proceeds straight to setup; the offer ceremony is reserved for entry points where no provider was named. On machines where the code-intelligence CLI exists, the offer:true path was hijacking setup into the provider ceremony and the E2E child never reached MCP registration. * chore: file the three documented-red periodic tests as structural-repair TODOs Sidebar trio exercises endpoints removed on every tree; ship-idempotency's PTY child never receives its typed command; brain-privacy-gate has never been green anywhere. Each carries its triage receipt in the entry. * test(e2e): setup-gbrain remote — hermetic env via opts, evidence on failure, output-scoped classifier Three separate defects stacked on this one test: the ambient GBRAIN_MCP_TOKEN/GSTACK_HOME/PATH mutations never reached the child (hermetic-env scrubs them by allowlist — broken since hermetic env landed; the child correctly stopped at Step 4c with NEEDS_CONTEXT), failures discarded the in-memory transcript so every triage started blind, and the wrote-findings-before-asking classifier scanned the full event stream where the child's own Read of the skill file always contains the review-report phrase. Env now goes via opts.env, failures dump bash commands + final text, and the classifier scans assistant output only. Green in 67s with all seven asserts. * test: final coverage pass — CLI rendering, revert traps, keychain probe, gbrain doc ops The user-directed third generation pass closes the audit's remaining tail: the code-intelligence CLI's options/status/suggest surfaces get behavioral coverage through the fake-shim chain; brain-context-load gains an argv-logging trap that goes red if anyone reverts the memoized PATH scan back to the spawn probe (receipt: simulated revert failed exactly these tests); the darwin Keychain auth branch (#1890) gets its first free-tier tests via a PATH-shimmed security binary; and the gbrain add/delete/export ops are pinned (body piped byte-for-byte, receipt sha256, stdin-EOF prompt guard, PROVIDER_UNAVAILABLE degradation) — retiring their TODOS entry. * test: assemble the planted PEM at runtime so the fixture never trips the prepush guard The repo's own credential guard scans pushed diffs and correctly blocked these fixtures: the engine flags any one-line BEGIN…END spelling regardless of body. Header, body, and footer are now joined at runtime, so the file and every diff of it stay clean while the scanner under test still receives the true live shape. * docs: update project documentation for v1.65.0.0 README gains the two wave-2 CLIs (gstack-code-intelligence, gstack-verify-gate) in the standalone-binaries table, BROWSER.md documents BROWSE_PERSIST_STATE next to manual state save/load, CONTRIBUTING's CI section lists the new supply-chain gates, and CLAUDE.md's project tree reflects lib/code-intelligence/ and the added workflows. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * docs: apply cross-model doc-review fixes for v1.65.0.0 Findings from the release doc review, verified against source: verify-gate's README row gains the actual install one-liner (setup never registers the Stop hook; test/verify-gate.test.ts pins that) and the 3-blocked-re-entries yield behavior; code-intelligence's row gains the suggest subcommand and the search-side consent gate; CONTRIBUTING scopes the SHA-pin claim to the supply-chain workflows and widens the dependency-review trigger; BROWSER.md's restore-time cookie drop list matches isInternalCookieDomain; CLAUDE.md's workflows comment stops implying six workflows are all of them. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * docs: CHANGELOG accuracy pass — scope the SHA-pin claim, restore-time cookie filter, exact test counts * test: env restore runs per-test, not per-suite — the leak that failed 30 strangers gstack-memory-helpers saved HOME/GSTACK_HOME/PATH in beforeEach but restored in afterAll, so the last beforeEach's snapshot won and a gstack-test-engine temp dir leaked into every later file in the same process: gstack-config read the wrong store, make-pdf's child resolved Chromium under the temp cache, update-check and artifacts-init lost their real homes. afterAll is now afterEach; the config and update-check harnesses also strip GSTACK_HOME/GSTACK_STATE_ROOT from child env as a belt. * fix(browse): restore the #1846 start-timeout resolution the merge dropped The v1.64.1.0 merge kept this branch's lock design in cli.ts and silently lost main's resolveStartTimeout + late health re-check while their test survived — ported both back in alongside the kept design. * test: adapt main's diagnostics tests to the merged designs cli-lock asserts typed ServerLockError (errno + lock path) instead of the log-and-return shape the merge didn't keep, dropping only the one duplicate of server-lock-errors coverage; the liveness tripwire exempts error-handling.ts as the sanctioned tasklist site; snapshot and compare-board wrappers pass the now-mandatory browser-manager arg; background.js's test pins that the retired sidebar-command type is rejected pre-gate with no response fields. * chore: gitignore the gen-accessors tool's SPM build output skill-e2e-ios-swift-build compiles the Swift package in place, leaving .build/ (2,800+ files) and Package.resolved untracked after every periodic run — the workspace read as ~100 dirty changes with a clean tree. Same class as the dist/ binaries: build output, never committed. * test(browse): subprocess budget for the polyfill suite on Windows CI Every test here spawnSync's a node child; cold-start on the Windows runner (AV scan, first node.exe touch) blew bun's 5s default by 7ms on a 50ms sleep test. File-level 20s default — subprocess budget, not assertion looseness. * test: make the Darwin migration path and the query-timeout SKIP deterministic on Linux CI The v1.65 migration suite relied on the host being macOS — on the ubicloud runner the script's uname gate early-exited every test with empty output; a Darwin uname shim in the shared setup runs the real path everywhere (the non-Darwin test still overrides it with Linux). The 1ms-budget brain-context test assumed 1ms is always too short; the runner's fake gbrain answered in 0ms and no SKIP printed — the fake now sleeps 300ms so the timeout is a certainty, while --version stays instant for the detection assertion. --------- Co-authored-by: Gawie van Blerk <gawievanblerk@gmail.com> Co-authored-by: Sina Matian <sina@time-attack.dev> Co-authored-by: Claude Fable 5 <noreply@anthropic.com> Co-authored-by: Shawn Reddy <19191746+Screddyice@users.noreply.github.com> Co-authored-by: Jake Wilk <jwilk@highlinerepartners.com> Co-authored-by: Jerry Nichols <jerrynicholsai@users.noreply.github.com> |
||
|
|
008dd65b1f |
v1.64.0.0 fix wave: full tracker audit — 90 fixes, 52 issues closed, ~50 community PRs absorbed (#2571)
* fix(hooks): nest freeze/careful permissionDecision under hookSpecificOutput Claude Code ignores a top-level permissionDecision, so the /freeze deny and /careful ask guards silently allowed everything. Nest both under hookSpecificOutput with permissionDecisionReason, update the shape-blind tests to pin the nested form, and document the constraint in both skill templates (regen included). Closes half of #1459 (freeze enforcement chain). Contributed by @jawadakram20 (PR #2331; team-init hunk deferred to the dedicated team-init fix). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(team-init): required-mode hook blocks with nested schema + exit 2 The generated check-gstack.sh emitted a flat permissionDecision payload and exited 0, which Claude Code ignores — required mode enforced nothing. The generated hook now nests the deny under hookSpecificOutput and exits 2 so the block holds even if the JSON schema drifts again. Adds a temp-repo regression test that runs the generated hook under both installed and missing-gstack homes. Fixes #2413, #2296. Contributed by @Masashi-Ono0611 (PR #2423). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(careful): close three check-careful bypasses via real JSON extraction The grep-based command extractor stopped at the first escaped quote, so any quoted argument truncated the command before the pattern checks ran — `git commit -m "wip" && rm -rf /` was silently allowed. Replace it with a python3/node JSON parse that fails CLOSED on unreadable payloads, add an IFS/base64-to-shell obfuscation tripwire, and stop multi-line commands from riding the single-line safe-exception whitelist (line-based grep would have approved `rm -rf /` when a later line matched node_modules — a hazard the real newline decoding exposed). Contributed by @wtamminga (PR #2426; the -R hunk was dropped — it landed in v1.61.0.0 — and output shapes updated to the nested hookSpecificOutput form). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(review,autoplan): require explicit run_in_background: false on specialist agents Claude Code v2.1.198 made subagents run in the background by default, which inverted the old "do not use the flag" guidance: review-army specialists and autoplan dual voices silently launched in the background and the merge step could proceed before they completed — regressing the #497 fix. The generated guidance now instructs an explicit run_in_background: false, and a static tripwire fails the free suite if the inert inverted phrasing ever returns to any generated SKILL.md. Fixes #2440. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(investigate): anchor the scope-lock freeze hook on $HOME, not CLAUDE_SKILL_DIR The investigate skill's PreToolUse hooks and Scope Lock probe resolved check-freeze.sh via ${CLAUDE_SKILL_DIR}, which does not exist when frontmatter hooks run — the || exit 0 tail then failed open, so the debug scope boundary silently never engaged (#1871 follow-up). Anchor all four sites on $HOME/.claude/skills/gstack/ like careful/freeze, and add a static test asserting no frontmatter command: line in the guard-family skills ever references CLAUDE_SKILL_DIR again. Fixes #2469; closes the last live half of #1459 together with the freeze/careful hookSpecificOutput fix. The broader portable-install-root rewrite stays #1882 (its own focused PR per the TODOS.md decision). Reported with a fix by @maxpetrusenkoagent (PR #1873; absorbed narrowly — the cwd-walk rewrite belongs to #1882). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(redact): scan large diffs in line-aligned slices; stop digit-UUIDs matching as cards/phones The prepush guard blocked any push whose added lines exceeded the engine's 1 MiB cap with engine.input_too_large — a size error naming no credential — which trains people onto GSTACK_REDACT_PREPUSH=skip. Scan in 768 KiB line-aligned slices instead (no pattern is multi-line, so a boundary cannot bisect a secret); a single oversized line still goes to the engine intact and fails closed. Also suppress card/phone matches whose span sits ENTIRELY inside a UUID — digit-only UUID fixtures were 14 of 21 MEDIUM findings on an ordinary branch, the noise level that stops people reading MEDIUM at all. Fixes #2304. Contributed by @luckywenapere (PR #2543). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(redact): block Google OAuth client secrets and Telegram bot tokens at HIGH GOCSPX-prefixed client secrets and <bot_id>:<35-char> Telegram tokens are never-publishable credential shapes with unambiguous formats — both now block at HIGH like the other live-format credentials. Contributed by @francis-eye (PR #2357). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(redact-prepush): resolve the real push base instead of EMPTY_TREE whole-repo scans When the remote default branch is not main/master (or origin/HEAD is unset), the merge-base guess failed and the hook fell back to scanning the ENTIRE repository as added lines — re-attributing long-pushed secrets to the current push and, on any real repo, tripping the engine byte cap so the push blocked having scanned nothing. Derive the base from commits reachable from no remote-tracking branch, keep the empty-tree path only for genuinely fresh repos, and split the block message so an unscannable diff is reported as "could not scan (fail closed)" rather than "credential found — rotate it". Contributed by @stormeoio (PR #2398). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(redact-prepush): preserve the trailing newline handed to chained pre-push.local The chaining wrapper captured stdin with $(cat), which strips the trailing newline — a chained shell hook built on `while read` then never entered its loop for the final (usually only) ref line and exited 0, failing OPEN. Use the printf-x sentinel so the byte-exact input reaches the chained hook, with tests covering both the pass-through and the short-circuit paths. Contributed by @francis-eye (PR #2358). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(redact-prepush): close the ext-diff, header-lookalike, and ref-parse bypasses Three ways the pushed diff escaped scanning: (1) a user-level diff.external or textconv driver replaced the diff with its own output — zero '+' lines, so the scan saw nothing (now --no-ext-diff --no-textconv); (2) an added content line whose text begins with "++" renders as "+++…" and the blanket header skip dropped it (now hunk-aware header detection); (3) a pre-push ref line that failed to parse was silently skipped, leaving that ref unscanned (now fails closed with the offending line named). Minimal reimplementation of the two confirmed bypasses from PR #2498 by @lubosxyz (the full PR overlaps the chunked-scan work absorbed separately), plus the unparseable-ref hardening. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(pair-agent): keep the ngrok authtoken out of the transcript and shell argv The not-authed flow told the user to paste their ngrok authtoken into the chat so the agent could run `ngrok config add-authtoken` — putting a live credential in the transcript, tool-call argv, and anything the transcript syncs to. The user now runs the auth command in their own terminal; the agent only verifies via `ngrok config check`, and a pasted token triggers a rotate-and-reauth instruction. A static test pins that no agent-run bash fence ever contains add-authtoken again. Fixes #2335. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(update-check): crash emits CHECK_FAILED instead of reading as up-to-date gstack-update-check signals "up to date" with SILENCE, and it runs under set -e — so any unguarded mid-script failure exited quietly and was indistinguishable from a current install. Observed live as a 45-release silent-staleness incident. An ERR trap (with -E so it propagates into functions) now emits a CHECK_FAILED sentinel naming the line and status, and exits 0 so caller `|| true` guards can't eat it. Behavioral tests cover both the crash and the healthy-silent paths; egress-receipt wiring is untouched and still pinned by test/egress-receipt-wiring.test.ts. Fixes #1974. (#2378's HEAD-SHA staleness half was already fixed on main by the ls-remote + SHA-pinned VERSION resolution — close as already-fixed.) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(deps): bump diff 7.0.0 → 9.0.0 (GHSA-73rr-hh4g-fpgx parsePatch DoS) The advisory affects diff 6.x–8.0.2. The only API this repo uses is Diff.diffLines (browse/src/snapshot.ts:571, browse/src/meta-commands.ts:728), which is unchanged across the major hop; snapshot tests pass against 9.0.0. Closes #1588. Contributed by @genisis0x (PR #1599; VERSION collateral stripped, lockfile regenerated fresh). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * ci(evals): skip eval jobs deterministically on fork PRs Fork PRs never receive repository secrets, so every API-calling eval failed at SDK auth — but only when Docker-cache luck let the jobs start at all, making fork PRs randomly red or grey. Skip the eval and report jobs explicitly for fork-origin PRs, keep the image BUILD (validates Dockerfile.ci changes) without the push a fork token can't perform, and leave full coverage for same-repo PRs, pushes, and dispatches. Contributed by @andrey-esipov (PR #2345). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(extension): deny token/port reads to content-script and foreign senders background.js answered getPort — port, connected state, AND the browse server auth token — to any sender that passed the type allowlist, including content scripts running in web-page context and, behind only the sender.id check, anything without extension-page provenance. The getToken sender.tab restriction covered getToken alone, and only after getPort had already handed out the token. Single decision point now: extension/sender-auth.js classifies each message type; the eight privileged types (getPort, setPort, getServerUrl, getToken, fetchRefs, command, sidebar-command, getTabState) require an own-extension-page sender (chrome-extension://<own id>/ URL, no sender.tab, own sender.id). Denied senders get { error: 'unauthorized' } and nothing else — never the token, never the port. Content-script flows (elementPicked, pickerCancelled, inspectResult, openSidePanel) are untouched, and the sidepanel/popup keep the getPort token field their connect path reads. The policy mirrors the v1.63 server-side model: AUTH_TOKEN is released only to the pinned extension Origin via POST /extension-token, so the extension must not re-leak it to contexts the server would never have trusted. browse/test/extension-sender-auth.test.ts drives the real background.js onMessage listener under a chrome stub with four sender shapes (own extension page, own content script, foreign extension id, missing sender.url) and pins that denied responses carry no token/port fields, that a denied setPort never persists, that a denied command never reaches the network, and that the inspector + tab-state flows keep working. The helper is loaded via importScripts in the classic service worker and require()-able from bun tests. Contributed by @punksterlabs (PR #1822; reimplemented against the v1.63 POST /extension-token pinned-origin model). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * test(update-check): fixture links gstack-egress-lib.sh — all 38 tests failed on main v1.63.0.0 made bin/gstack-update-check source bin/gstack-egress-lib.sh unconditionally, but the test fixture's GSTACK_DIR only linked gstack-config — every test died at the source line (0/38 pass on pristine main, verified). The suite-truncation bug hid it: the runner was killed by an earlier file's delayed process.exit before this file ran. Link the lib like the real install layout the script assumes. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(browse): capture active-tab state before close() — last-tab auto-create raced the close event closeTab checked `tabId === this.activeTabId` AFTER awaiting page.close(), but the page 'close' event handler can fire during that await and reassign activeTabId — losing the race meant the last-tab auto-create never ran, leaving the manager with zero tabs. Capture wasActive before closing, and only reassign activeTabId when it no longer points at a live tab. Part of the test-integrity repairs unmasked by the suite-truncation fix. Contributed by @time-attack (PR #2230, browser-manager hunk). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * test(browse): delete the orphaned sidebar chat-queue suite; align sidebar-ux/tabs with the PTY-only sidebar browse/test/sidebar-integration.test.ts tested the /sidebar-command queue path ripped in v1.14 (34 references to removed endpoints — 11 permanent failures masked by suite truncation). sidebar-ux.test.ts carried 73 failures pinning the same dead surface (pickSidebarModel, ANALYSIS_WORDS); the trim keeps its 108 live tests, including the background.js token/allowlist gates. sidebar-tabs gets the two matching expectation updates. Closes #2420, #1980. Contributed by @time-attack (PR #2230, sidebar hunks; the security-sidepanel-dom deletion was NOT taken — that suite pins the live sidepanel DOM surface and passes). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * test(browse): align dual-listener and terminal-agent static guards with the current source Two static-grep guards pinned superseded source shapes and failed once the suite actually ran them: the tunnel dispatch gate is args-aware since the --out disk-write ban (canDispatchOverTunnel takes command AND args), and lazy PTY spawn routes through the maybeSpawnPty helper since v1.44. The updated assertions pin the current, stricter shapes (open() never spawns; the helper is the only spawnClaude caller). Contributed by @time-attack (PR #2230, dual-listener + terminal-agent hunks). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(test): remove all 8 delayed process.exit teardown bombs — the tier-1 gate can finally fail bun test runs every file in ONE process, so a 500ms setTimeout(process.exit(0)) armed in afterAll fired mid-way through a LATER file and killed the entire suite with exit 0 and no summary — only ~16 of 434 files ran, and every downstream failure was invisible (observed live throughout this wave's enumeration). Changes, all guarded by fault injection: - Replace every delayed-exit teardown with a time-boxed close of the file's own browser (8 files across browse/ and design/); stub the daemon /shutdown timer instead of letting its unconditional process.exit tear the runner down. - test/no-suicide-exit.test.ts: static tripwire — no *.test.ts may schedule a delayed process.exit again. - test/exit-propagation.test.ts + fixtures: fault injection with REAL bun output proves the truncation shape (exit 0, no summary) and that scripts/test-free-shards.ts now detects it: a shard exiting 0 WITHOUT bun's final summary line is treated as FAILED (exit code alone is not evidence of completion). - handoff: the three headed-mode integration tests are darwin-skipped with a pointer to the known macOS headed-launch breakage (#2242/#2554); they keep running on Linux CI. Un-skip in the browse-daemon wave. - feedback-roundtrip: repair the handler call sites unmasked by the fix — handlers take (command, args, session, bm); passing the manager where a session belongs broke all six tests. - user-slug-fallback: HOME isolation makes endpoint_hash deterministic. Fixes #2421, #2435. Contributed by @sneakygriff (PR #2172) with repairs from @time-attack (PR #2230 feedback-roundtrip hunks); supersedes PR #2252 by @whd4 (same defect, credited). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * test: include design/test/ in the free suite and the sharded runner design/test was absent from both the package.json test globs and TEST_ROOTS in scripts/test-free-shards.ts — its tests (including one of the teardown bombs removed in the previous commit) never ran in any CI or local free run, so design fixes could ship without their unit tests executing. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(make-pdf): reject directories when resolving the browse binary access(X_OK) is true for directories (they carry the execute/traverse bit on POSIX and pass the Windows existence check too), so cwd-dependent resolution could pick the ~/.claude/skills/browse alias DIRECTORY as the browse binary. Every browse call then exited 4 with empty stderr, which make-pdf surfaced as "Chromium failed to launch" against a perfectly healthy Chromium (#2156). Guard isExecutable with statSync().isFile() so only regular files qualify. Contributed by @jwilk-hrep (PR #2538). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(make-pdf): write browse-bound temp files under the safe-dirs allowlist os.tmpdir() on macOS resolves to /var/folders/..., which fails browse's safe-dirs validation ([/tmp, cwd]) since the v1.6.0.0 --from-file tightening. Default PDF output (generate with no -o), the preview HTML, tmpFile() scratch files, and setup's smoke-test fixture/output all wrote there, so browse rejected the paths it was asked to read or write. Export PAYLOAD_TMP_DIR from browseClient (the existing TEMP_DIR convention: os.tmpdir() on Windows, /tmp elsewhere) and route orchestrator.ts and setup.ts temp files through it. Contributed by @lvthewah (PR #2505; the browse-binary directory guard from that PR landed separately via PR #2538). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(make-pdf): stop URLs swallowing smartypants placeholders A bare autolinked URL (<a href="X">X</a>) has zero whitespace between the URL text and its own closing tag. TAG_RE carves that </a> into a NUL-delimited SMARTPANTS_PRESERVED placeholder BEFORE the URL pass runs, and URL_RE's \S+ swallowed the adjacent placeholder into the URL match. The restore pass is single-shot, so the inner placeholder never restored: raw "SMARTPANTS_PRESERVED_N" text leaked into the rendered link, the </a> vanished, and link-blue styling bled into the rest of the document (#2084). Excluding the NUL sentinel (\u0000) from the URL character class stops the match from crossing into an already-carved zone. Contributed by @marshaung (PR #2280; PR #2339 by @BrendaB24 covered the same smartypants defect). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(make-pdf): no blank first page when content precedes the first H1 Two paths put invisible content ahead of the first H1 and cost users a blank page 1 (#1904): - A visually-empty preamble (leading <style> block, HTML comment) became its own .chapter. That section took the `.chapter:first-of-type { break-before: auto }` exception, so the first real chapter inherited `break-before: page` and started on page 2. Non-rendering preambles now fold into the first real chapter (markup preserved, no page break); real text preambles keep their own chapter. - Leading YAML frontmatter rendered as a literal paragraph of body text on its own first page (marked has no frontmatter awareness). It is now stripped before parsing; a `---` thematic break elsewhere is untouched. Contributed by @jbetala7 (PR #1913). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(browse): allow about:blank so a restarted daemon can initialise The daemon opens its own first tab on about:blank, so blocking it in validateNavigationUrl meant a restarted daemon could never recreate the blank tab it starts from — and `browse newtab about:blank`, which `make-pdf setup` runs as its Chromium smoke test, failed and surfaced as "Chromium failed to launch" against a healthy browser. Allow about:blank ONLY, never the about: scheme: about:blank has no origin, loads nothing and runs nothing, while about:config and friends are real surfaces. Exact href match (lower-cased, since the URL parser normalises the protocol but not the opaque part), so about:blankfoo stays blocked. Contributed by @jwilk-hrep (PR #2537). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(design): drop gpt-image-2 tool model that 400s under the gpt-4o orchestrator The Responses API rejects pairing a gpt-4o orchestrator with an image_generation tool spec'd as model: "gpt-image-2" (400 invalid_request_error), which took every design image call offline — generate, variants, iterate (both threaded and fresh paths), evolve, and /design-shotgun (#1771). gpt-image-2 is only valid under a gpt-5 orchestrator; with gpt-4o the tool must omit the model field (defaults to gpt-image-1). Remove the model field at all five call sites and add a static-grep tripwire test (design/test/image-gen-pairing.test.ts) that fails CI if any design/src module reintroduces the gpt-4o + gpt-image-2 pairing. Re-enabling gpt-image-2 later requires bumping the orchestrator off gpt-4o in the same diff, which the tripwire permits. Contributed by @Pablosinyores (PR #1773). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(design): variants AbortError message reports the real 240s timeout generateVariant arms its abort at 240_000 ms but the AbortError branch returned "Timeout (120s)" — off by 2x, so a user staring at the failure could not tell whether to bump the timeout, retry, or drop the call. Report the actual configured bound, and pin it with a test that forces the abort path (fast-forwarding only the 240_000 ms timer) and asserts the surfaced string matches. Contributed by @vryahn (PR #1774). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(memory-ingest): stop silently ingesting 0 pages — include gitignored staging, reconcile counts Pages stage into ~/.gstack/.staging-ingest-*/ inside a repo whose .gitignore is `*`, and gbrain import honours .gitignore — so it collected 0 files, imported nothing, and the ingest still reported "written: N" from the STAGED count while advancing state, meaning no future run ever retried. Three layers now: (1) pass --include-gitignored (root cause); (2) if the installed gbrain predates the flag, retry without it (subcommand --help is generic, so the attempt is the only probe) with an upgrade pointer; (3) reconcile gbrain's imported+unchanged accounting against the staged count and REFUSE to advance state on a shortfall, naming the gitignore collision. Fixes #2144, #2104. Contributed by @gawievanblerk (PR #2560) and @Charles-Grant (PR #2486). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(autoplan): task aggregator returned zero tasks on every run — jq scope bug Inside ($commits | split("|") | ...) the "." context is the split ARRAY, so the filter's bare .commit raised "Cannot index array with string" on every record — and the 2>/dev/null swallowed it, so aggregation silently produced zero tasks no matter how many the reviews emitted. Bind .commit to $c before the pipe. Reproduced live before the fix; regenerated autoplan/SKILL.md. Fixes #2018. Contributed by @kkroo (PR #2416; regenerated against the current template). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(session-update): un-wedge auto-upgrade — autostash over local patches, log the pull's real reason On a normal install the tracked files ARE locally patched (skill-prefix name rewrites, gbrain-refresh blocks), so the bare `git pull --ff-only` refused on every run and auto-upgrade froze forever — observed as 308 consecutive PULL_FAILED entries with the reason discarded by 2>/dev/null. Pull now runs --autostash (local patches ride over the update and pop back), stderr is captured into the log so a genuine failure names its cause, an autostash pop conflict recovers to a clean tree and re-renders the patches (gstack-patch-names + gbrain-refresh, both idempotent), and a successful pull re-renders them as a self-heal. Behavioral tests cover the wedge shape and the reason logging. Fixes #2566. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * test: raise the free-suite per-test timeout to 30s bun's 5s default is fine for a file run solo, but the monolithic free suite shares one process across 100+ files whose browser instances contend for launch slots — Playwright tests that pass in isolation time out mid-suite. 30s matches the ceiling the enumeration runs used; the sharded runner (test:free) is unaffected. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(question-log): parse native AskUserQuestion answers — every native answer logged as __unknown__ Current Claude Code returns AskUserQuestion results as an OBJECT map keyed by question text ({answers: {question: label}}); the hook only handled the legacy array shapes, so 86% of live records carried user_choice __unknown__ — and the bin then scored every one as followed_recommendation false, silently poisoning plan-tune metrics. Adds the object-map extraction (exact + whitespace-normalized + single-question pairing, multiSelect joins, annotations as free_text), strips the (Recommended) suffix from BOTH sides of the comparison, skips the computation entirely on extraction failure, and logs unrecognized shapes to hook-errors.log instead of embedding them in the record. Fixes #2336, #2206. Based on the working patch in #2336 by @yijisoo; suffix comparison fix contributed by @chuchu2781 (PR #2400). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(slug): canonicalize slash branches to dash form — review history stops splitting Branch-name sanitization disagreed across gstack (four incompatible rules), so reviews for the same slash-named branch landed in multiple files and the ship dashboard missed entries. gstack-slug now canonicalizes / to - in one place, and ship's review lookup routes through it; goldens regenerated against the current templates. Fixes #1127, #2550. Contributed by @ShuratCode (PR #2465; duplicate fixes by @xrfael-dev and two others in PRs #1851/#1699/#1621, credited). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(slug): resolve the project root by marker walk-up — subdirectory sessions stop misfiling state gstack-slug derived everything from pwd, so a session in a subdirectory got the subdir's basename as its slug (or an outer monorepo's remote), misfiling reviews/decisions/learnings under a phantom project — and the per-pwd cache made the wrong answer permanent. The resolver now walks up from pwd: outermost STRONG marker wins (.git, package.json, pyproject.toml, Cargo.toml, Gemfile, go.mod, .project.yaml), weak content markers (README, LICENSE) catch non-code project folders, deploy artifacts are deliberately not markers, and GSTACK_PROJECT_SLUG remains the escape hatch. The cache self-heals on mismatch. Main-side invariants preserved on top: the unconditional [a-zA-Z0-9._-] re-sanitize before echo and slash→dash branch canonicalization. Fixes #1125. Contributed by @ajeenkya (PR #1702; rebased over the sanitize and branch-canonicalization work that landed after it). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(hooks): shared spawn-bin helper — all three AskUserQuestion hooks were inert on Windows The plan-tune hooks resolved bin scripts via new URL(import.meta.url).pathname (which doubles the drive letter on Windows: /C:/C:/...) and spawnSync'd extensionless bash scripts directly (unrunnable without a shell association) — so question logging, preferences, and the error fallback all silently no-op'd on Windows, and /plan-tune collected no data. A single spawn-bin.ts helper now owns bin resolution (fileURLToPath) and win32 bash routing for every hook, with static tripwires so a future hook can't reintroduce the raw pattern. This is the one Windows-spawn idiom for hook code. Fixes #2356. Contributed by @rafassousa (PR #2504; supersedes PR #2399 by @chuchu2781). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(model-overlays): add fable-5, opus-4-8, and sonnet-5 overlays + resolver mappings model-overlays/ had no entry for the current Claude generation, so every session on a Claude 5 family or Opus 4.8 model fell through to the generic claude.md nudges. Adds the three overlays with resolver mappings and per-overlay tests; generated output for the default host is unchanged (overlays activate by detected model). Closes #2509. Contributed by @chrisquorum (PRs #2246, #2243, #2247). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(windows): grant icacls ACEs by *SID, not unqualified username An unqualified username handed to icacls is ambiguous: on a machine whose hostname equals the username (a common Windows setup), it resolves to the MACHINE account instead of the user. Combined with /inheritance:r, that leaves ~/.gstack with a single ACE matching nobody — the process that just "secured" the directory locks itself out, and icacls still reports success. Both icacls sites in the repo (restrictFilePermissions and restrictDirectoryPermissions in browse/src/file-permissions.ts — the only icacls call sites; setup has none) now grant via icacls' literal-SID form `*<SID>`, resolved once per process from System32\whoami.exe (pinned to System32 because a bare `whoami` under a bash-flavoured PATH picks up the MSYS build, which rejects /user). Fallback when the SID can't be resolved is the domain-qualified `USERDOMAIN\username` name, which is unambiguous where the bare username was not. Windows-only regression tests assert the hardened directory stays usable by the calling process (readdir + write), which is exactly the check that a not-toThrow assertion sailed past before. Contributed by @asizux2 (PR #2479); the same defect was independently fixed by @Icandi40, @chiragborse1, @IntegriGit and @voltapix26. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(windows): forward windowsHide through the bun-polyfill spawn shims windowsHide is the one spawn option where Node's default is the opposite of Bun's: Node shows the child's console window, Bun.spawn hides it. The polyfill's spawn and spawnSync shims dropped the option entirely, so the Node fallback path (dist/bun-polyfill.cjs) silently inverted the behavior on the one platform the shim exists to serve — every watchdog respawn of the terminal agent popped a visible bun.exe console window. Three sites fixed: - Bun.spawnSync shim: forwards windowsHide with Bun-matching default true - Bun.spawn shim: same (stdio:'ignore' silences output but does NOT suppress the console window on Windows) - spawnTerminalAgent in terminal-agent-control.ts: explicit windowsHide: true, so the Node fallback path behaves like Bun-native An explicit windowsHide: false is honored at both shims. Three focused tests pin the default-true, default-true-sync, and explicit-false paths by intercepting child_process in a subprocess; the test file's require path now uses forward slashes so it survives interpolation into a JS string literal on Windows. Supersedes PRs #2523, #2294 and #2290, which each covered a subset of these sites. Contributed by @jerrynicholsai (PR #2539); earlier fixes by @jwilk-hrep, @rroojrooj and @WimvandenHeijkant covered subsets of the same sites. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(watchdog): signal-0 liveness, tick-scaled respawn guard, windowsHide Three-bug chain behind the Windows terminal-agent leak (console window strobing every 60s, one orphaned agent per watchdog tick until the box ran out of committable memory): 1. isProcessAlive shelled out to `tasklist /FI "PID eq <pid>"` on Windows with a 3s timeout. A Bun.spawnSync that hits its timeout still RETURNS with partial stdout, so the `.includes()` PID match read a LIVE agent as dead — killAgentByRecord skipped the kill, the watchdog respawned around the survivor, and every orphan slowed the next tasklist enough to produce the next false negative. Now: `process.kill(pid, 0)` on every platform (Node and Bun both map signal 0 to an OpenProcess existence check on Windows), with EPERM counted as alive. No subprocess, no timeout, no console window. 2. The respawn circuit-breaker was mathematically unreachable — verified in this tree: RESPAWN_GUARD_WINDOW_MS was a fixed 60_000 against a 60_000ms default tick, and each tick pushes at most one respawn timestamp, so three pushes span ~120s and can never coexist inside a 60s window (eviction is strict `>`, and setInterval drift plus per-tick work always ages the prior entry past the boundary). The guard could not fire at the default tick rate and a steady one-per-tick leak ran unbounded. The window now scales with the tick: max(60_000, tick * (RESPAWN_GUARD_MAX + 2)), so "3 crashes in quick succession → stop" holds at any tick value. 3. The tasklist probe popped a visible console per tick (no windowsHide). Removing the shell-out kills that site; the agent-spawn site itself already passes windowsHide: true (landed with the bun-polyfill windowsHide commit — PR #2414's terminal-agent-control.ts hunk is reconciled there rather than duplicated). New browse/test/process-liveness-windows.test.ts pins all three: no subprocess from the probe, a static tripwire against reintroducing `tasklist` + `PID eq` liveness checks in src/, the spawnTerminalAgent windowsHide + stdio contract, and the window-derived-from-tick arithmetic. terminal-agent-watchdog.test.ts test 4 now pins the window/tick relationship instead of the fixed literal that let this ship. Also converts `new URL(import.meta.url).pathname` to `import.meta.path` across the static-grep tests it touches — the pathname form yields /C:/... on Windows and breaks path.resolve. Contributed by @SYKhayyat (PR #2414). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(terminal-agent): tie agent lifetime to its owning browse server PID The terminal agent is intentionally detached so it survives the short-lived CLI launcher, but its real owner is the persistent browse server. If that server crashed or was killed before running normal shutdown, the agent was adopted by PID 1 and lived forever (#2019). spawnTerminalAgent now requires an ownerPid and exports it to the agent as BROWSE_OWNER_PID; all three spawn sites pass the server PID (cli.ts cold-start, cli.ts supervisor respawn, server.ts watchdog). The agent polls the owner with signal 0 every 15s (GSTACK_TERMINAL_OWNER_WATCHDOG_MS to tune) on an unref'd timer and, when the owner disappears, exits through the SAME cleanup path as an intentional SIGTERM shutdown — now re-entrancy-guarded and also removing the terminal-internal-token file alongside the port file and agent record. Runtime test spawns a real agent tied to a throwaway owner process, kills the owner, and asserts the agent exits and its discovery files (terminal-agent-pid, terminal-port) are gone. Reconciled with the watchdog commit's spawnTerminalAgent contract test (process-liveness-windows.test.ts now passes ownerPid and pins the BROWSE_OWNER_PID env forwarding). Closes #2019. Contributed by @csarigoz (PR #2530). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(windows): give the bun-polyfill spawn shim a real `exited` promise Bun.spawn exposes `proc.exited` as a Promise resolving to the exit code. The Node fallback shim (dist/bun-polyfill.cjs) returned no such field, so every `await proc.exited` on the Windows path resolved instantly to undefined — the Windows cookie picker (cookie-import-browser.ts races proc.exited at three sites) read stdout before the child produced it and silent-failed; browser-skill-commands and terminal-agent hit the same class. The shim now: - drains stdout/stderr eagerly into capped in-memory buffers (Node's Readables are pull-based; without draining, a child writing past the OS pipe buffer blocks in write() and 'exit' never fires), replaying them as fresh single-shot Web ReadableStreams so reads work before or after awaiting exit; - caps the buffer at 16 MB (GSTACK_SPAWN_MAX_BUFFER to override), still draining past the cap so a runaway child can't wedge or OOM; - resolves `exited` with Bun-matching codes (exit code, 128+signal, 1 on spawn error) after both pipes finish, and resolves on 'error' too — Node fires 'error' without 'exit' when the binary is missing, which otherwise hangs the await forever. Six tests pin exit codes, the read-after-exit ordering, spawn-failure resolution, the buffer cap, and the large-output drain. Adapted to the current test file (require path goes through the requirePath variable from the windowsHide commit), and the 1 MB drain test's child now exits in the write callback — on modern Node a pipe write past the OS buffer is async and process.exit() straight after write() truncates at ~64 KB even with a live reader, which fails the test for reasons unrelated to the shim. Contributed by @punksterlabs (PR #1743). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(setup): BROWSE_BIN carries the .exe suffix on Windows On Windows, `bun build --compile` emits browse.exe, but setup's BROWSE_BIN pointed at the suffixless path — so the post-build gate (`[ ! -x "$BROWSE_BIN" ]` → "browse binary missing") could never pass on Windows even after a fully successful build, while the build step itself reported success. Closes #2291. Applied the PR's override after the IS_WINDOWS detection, and also to the second BROWSE_BIN assignment the PR predates: the direct-Codex- install migration path re-derives BROWSE_BIN from the migrated dir and would otherwise drop the suffix again on Windows. Contributed by @rroojrooj (PR #1714). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(setup): link lib/ beside bin/ at all five host-install sites bin/ scripts import shared modules via ../lib (gstack-learnings-log → lib/jsonl-store.ts is the reported case), so any runtime root that exposes bin/ without lib/ breaks 13 bin/ commands — learnings-log, decision-log, telemetry and friends fail with "Cannot find module .../lib/jsonl-store.ts" on every non-Claude install, silently from the skills' perspective. All five host-install sites now carry lib/ next to bin/, each through the existing _link_or_copy helper (never raw ln — the static invariant in test/setup-windows-fallback.test.ts enforces this): - .agents sidecar (create_agents_sidecar asset loop) - Codex runtime root (create_codex_runtime_root) - Factory runtime root (create_factory_runtime_root) - OpenCode runtime root (create_opencode_runtime_root) - Kiro install block New test/setup-runtime-lib-command.test.ts executes the real setup shell for each root in a sandbox (both the symlink branch and the Windows copy branch of _link_or_copy) and runs gstack-learnings-log end-to-end from the installed root, asserting the learning lands in ~/.gstack/projects/<slug>/learnings.jsonl — plus a negative control proving a bin-without-lib root fails exactly the way the bug report did. gen-skill-docs.test.ts's setup-validation block pins the lib link at every site. Cross-checked against PRs #2433, #2410 and #2198: all three cover subsets of these sites; nothing they fix is missing here. Contributed by @fedster99 (PR #2262); overlapping fixes by @gregario, @lsendel and @netkurt. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(setup): ship supabase/config.sh with every host runtime root Distinct from the lib/-beside-bin/ defect: gstack-telemetry-sync, gstack-update-check, gstack-security-dashboard and gstack-community-dashboard all source $GSTACK_DIR/supabase/config.sh to resolve GSTACK_SUPABASE_URL, where GSTACK_DIR is the installed root (parent of bin/). The [ -f ... ] guard means a root without the file degrades SILENTLY — telemetry and update checks just stop resolving the project URL on non-Claude installs. Closes #2215. setup now links supabase/config.sh (file-level on purpose — migrations/ and functions/ are dev-only) via _link_or_copy at all five host-install sites: the PR's four (Codex, Factory, OpenCode runtime roots + the Kiro block) plus the .agents sidecar, whose bin/ resolves the same relative path and which the PR predates covering. The runtime-root test now asserts supabase/config.sh is present in every built root, on both the symlink and Windows-copy branches. Contributed by @jizusun (PR #2216). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * ci(windows): curate the fix-wave regression tests into the windows-latest run The windows-free-tests curated set is derived (POSIX-fragility regex scan + explicit deny list), and two of this wave's Windows regression files were auto-excluded on false-positive pattern hits: - browse/test/file-permissions.test.ts tripped the POSIX-mode-bitmask pattern, but every `mode & 0o777` assertion is platform-guarded — and the file carries the win32-only icacls-by-SID regression tests, which can only ever execute on windows-latest. - browse/test/terminal-agent-owner-watchdog.test.ts tripped the spawn(['bun','run',...]) pattern whose reason is the Playwright-bound browse server; it actually spawns terminal-agent.ts (fs/path/crypto + local helpers only, no Playwright at module scope), and the owner-PID orphan leak it pins was reported on Windows (#2019). Adds a KNOWN_WINDOWS_SAFE force-include list (mirror of KNOWN_WINDOWS_INCOMPATIBLE, each entry carrying its false-positive rationale) consulted before the pattern scan, and makes the owner-watchdog test's throwaway owner process Windows-portable (process.execPath instead of `sleep`, which a bare runner may not have). The wave's other new files need no wiring: process-liveness-windows and the bun-polyfill windowsHide/exited tests pass curation automatically; setup-runtime-lib-command self-skips on win32 by design (its Windows branch is exercised by simulating IS_WINDOWS=1 under bash), so force-including it would add a permanently-skipped file. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(setup): register the SessionStart hook with a bash prefix on Windows Windows can't execute an extensionless bash script directly — registering the bare gstack-session-update path made the hook pop the "Select an app" dialog on every session start (or silently never run), so team-mode auto-upgrade was dead on Windows installs. Companion to the hooks' spawn-bin routing: same defect class at the registration site. Contributed by @NikhileshNanduri (PR #1813; VERSION/CHANGELOG collateral stripped). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(setup): stop piping gen:skill-docs through tail — generator failures were masked setup piped doc generation through `tail -3`, so a generator crash kept the pipe's exit 0 and installs completed "successfully" with broken or missing SKILL.md files. Capture the real exit status at BOTH sites (the main gen:skill-docs step and the gbrain-detected gen:skill-docs:user regen — the second drifted in after the PR and its own test caught it), print the tail for UX, and fail loudly. Contributed by @DavidMiserak (PR #1898; VERSION/CHANGELOG collateral stripped; extended to the second pipe site). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(mktemp): move the X-run to the end of every temp-file template (BSD/busybox safe) BSD mktemp (macOS) does not substitute an X-run that has a suffix after it: `mktemp "$TMP_ROOT/codex-err-XXXXXX.txt"` creates a LITERAL codex-err-XXXXXX.txt on the first call (exit 0) and every later call fails with `mkstemp failed: File exists` — so /codex breaks from the SECOND run on every Mac, masquerading as a model stall. busybox mktemp (Alpine) rejects the template on the first run. Fixes #2091, #2370. Union of both community fixes, compared at the diff level: - PR #2372: all 11 source sites with a suffix after the X-run — codex SKILL.md.tmpl (5), claude SKILL.md.tmpl (3), bin/gstack-developer-profile (2, suffix folded into the prefix: .json.tmp.XXXXXX), and the office-hours codex pass in scripts/resolvers/review.ts (1). - PR #2103: the second half of #2091 — bin/gstack-paths now strips the trailing slash from TMP_ROOT at the source (macOS $TMPDIR ends in `/`), plus runtime tests pinning that normalization. New repo-wide tripwire in test/regression-issue2091-bsd-mktemp.test.ts: every .tmpl, every SKILL.md, and every scripts/resolvers/*.ts is swept — no mktemp template may carry a suffix after the X-run, with a self-test so the detector can't be quietly blinded. Generated SKILL.md files regenerated via gen:skill-docs in this commit. Contributed by @ShuratCode (PR #2103) and @noron12234 (PR #2372); PR #2285 by @cathrynlavery covered a subset. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(codex,review,ship): scope codex review with an explicit --base flag, never prompt text `codex review` takes its scope ONLY from --base/--commit/--uncommitted. The positional [PROMPT] is mutually exclusive with all three, and a prompt-only `codex review "<text>"` silently falls back to the uncommitted working-tree scope (verified on 0.144.1: it runs `git status --short; git diff` and reviews that) — so the previous prompt-based scoping produced a confidently-worded review of the WRONG changes and read "no changes" on a clean tree. Every diff pass now invokes `codex review --base <base>` with no prompt argument: /codex Step 2A default path, the /review structured pass, and the /ship adversarial-section pass (all via scripts/resolvers/review.ts). Custom review instructions keep their own `codex exec` path (the CLI rejects prompt + scope flag together), with the filesystem boundary preserved there. Two new Error Handling entries teach the failure shapes: the argv-parse error, and the "review says no changes on a branch full of changes" symptom. Tests updated to pin the new invariant instead of banning the fix: the old assertions required the diff range in prompt text and banned the `--base <base> -c '...'` substring, which the correct scoped form contains. Also deletes test/fixtures/golden-ship-claude.md — a 2,565-line orphaned fixture referenced by zero tests (the live goldens are in test/fixtures/golden/, compared by test/host-config.test.ts); the factory golden is refreshed from the regenerated output. Generated SKILL.md files regenerated via gen:skill-docs in this commit. Contributed by @fangearhq-boop (PR #2513). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(review,ship): run the codex diff passes under the timeout wrapper (#1036) The `_gstack_codex_timeout_wrapper` added in #1056 was wired into codex/SKILL.md but never into the /review and /ship diff passes, which kept running under a bare 5-minute Bash gate. An unwrapped stall returns no exit code and no output, which downstream reads as "Codex reviewed and found nothing" — a truncated pass silently became a clean bill. Measured on codex-cli 0.145.0: a pass was killed at 287s of a 300s budget mid-tool-call, and the same prompt completed in 336s. Both passes in scripts/resolvers/review.ts (adversarial `codex exec` and the structured `codex review --base` pass) now re-source gstack-codex-probe and run under `_gstack_codex_timeout_wrapper 540`, with the Bash tool gate raised to 600000 ms so the wrapper fires FIRST and a stall surfaces as a diagnosable exit 124. The timeout guidance now says a timed-out pass is MISSING COVERAGE, not a clean result, and points at the run's rollout log under ~/.codex/sessions/ for partial output. The stale "timeout doesn't exist on macOS" claim is gone — the wrapper resolves gtimeout, then timeout, then runs unwrapped, so it is safe without coreutils. Static guards in test/codex-hardening.test.ts pin all three sites (resolver, review/SKILL.md, ship/sections/adversarial.md): both calls wrapped, wrapper budget strictly under the Bash gate, and no reappearance of the macOS claim that steered these call sites away from the wrapper in the first place. The Claude-output path guard in test/gen-skill-docs.test.ts now scrubs ~/.codex/sessions/ (a user-facing Codex CLI path, same class as the ~/.codex/logs/ exemption) before banning Codex host paths. Generated files regenerated via gen:skill-docs; factory golden refreshed. Contributed by @aegixx (PR #2379). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(codex): sandbox the review path, fail the gate closed, order timeouts wrapper-first Closes #2496, #2524, #2477 — three defects in the class "a guard that reports success while doing nothing", all in codex/SKILL.md.tmpl: (a) Review sandbox. The default `codex review` path was the only codex call with no sandbox override, inheriting ~/.codex/config.toml's default — write access on a trusted project — while Important Rules claimed read-only. Top-level `codex review` has no -s/--sandbox flag (verified on 0.147.0), so the invocation now pins `-c 'sandbox_mode="read-only"'`, the same form the consult-resume path already uses. (b) Fail-closed verdict gate. The old rule ("no [P1] found → PASS") could not fail on the default path: native `codex review` output carries no bracketed tags, and a non-zero exit, expired auth, timeout, or empty result also contains no [P1] — all read as PASS. The gate is now an ordered, fail-closed check: non-zero exit → FAIL; empty output → FAIL; [P0]/[P1] (bracketed or codex's native labels) → FAIL with count; NO severity tags at all → FAIL requiring a human read; PASS is only reachable through the explicit tagged-advisory-only branch. [P0] is recognized as blocking, and the review-log findings count includes it. (c) Bash gate above the wrapper. Step 2A instructed `timeout: 300000` under a 330s wrapper, and Challenge's 300s gate sat under a 600s wrapper — the harness killed the call before the wrapper could emit its diagnosable exit-124 message. Every Bash gate now sits strictly ABOVE its wrapper: 360000 over the 330s review wrapper, 660000 over the 600s challenge/consult wrappers, with the ordering rationale stated at each site. Also from #2477/#2524: a new Error Handling entry for the model-entitlement 400 ("The '<model>' model is not supported...") pointing at the `model =` pin and `[notice.model_migrations]` in ~/.codex/config.toml and saying exactly which override to retry with (-m for exec-based modes, `-c model="..."` for review mode, which rejects -m); the Model & Reasoning section no longer documents `-m` for `/codex review`. Static assertions in test/codex-hardening.test.ts pin (a)-(c) across both the .tmpl and the generated SKILL.md: every scoped review invocation carries sandbox_mode="read-only" and never -s; the default-PASS sentence is banned and the fail-closed branches are present; and per-section, every Bash `timeout: N` is strictly greater than every wrapper budget, with 2A/2B/2C all required to be inspected. Generated SKILL.md regenerated via gen:skill-docs in this commit. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(preamble): quoted tilde made Artifacts Sync and telemetry-finalize dead code in 49 skills A tilde inside double quotes never expands, so the generated `_BRAIN_SYNC_BIN="~/..."` assignments resolved to a literal ./~ path and the Artifacts Sync + telemetry-finalize blocks silently no-op'd in every skill that carried them (regression of #785). The preamble resolvers now emit $HOME-based paths; all generated SKILL.md files regenerate identically from the fixed templates, and a static tripwire fails the suite if a quoted-tilde assignment ever reappears in generated output. Fixes #1656, #1715. Contributed by @jawadakram20 (PR #2333). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(gen-skill-docs): stop the catalog trim chopping descriptions at embedded periods The description-trim regex treated the first period as end-of-sentence, so skill descriptions with embedded periods (e.g. file extensions, version numbers) truncated mid-thought in the generated catalog — the discovery surface every host loads. Trim now respects the full first sentence; diagram's description regenerates to its intended text. Contributed by @sneakygriff (PR #2171). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(preamble): update_check:false gates the prose, not just the binary Setting update_check:false stopped the update-check BINARY from running, but every skill preamble still shipped the upgrade-handling instruction prose unconditionally — burning tokens on instructions that could never fire and confusing agents into probing for upgrades anyway. The resolver now suppresses the upgrade-flow prose when the config disables checks. Fixes #2001. Contributed by @jc0d35 (PR #2022). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(browse): sidebar Terminal — drop the duplicate WS subprotocol header, stop doubling CJK IME input The terminal client passed the auth token as the WS subprotocol AND echoed it in a second header, which some Chromium builds reject; and composition events double-sent CJK input (each IME commit arrived once from the composition handler and once from the data handler). One auth path, one input path; also fixes the terminal-agent test that failed on clean main. Contributed by @mindsurf0176 (PR #2515). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(setup): -h/--help prints usage instead of running the installer Asking setup for help RAN the full installer — Playwright download and all. Standard help flags now short-circuit to usage. Contributed by @saen-ai (PR #1219). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(hosts): Codex-generated skills reference AGENTS.md, not CLAUDE.md Codex reads AGENTS.md, but its generated skills still told agents to read CLAUDE.md in 8 places — instructions Codex hosts cannot follow. The host config now maps the memory-file name per host; all three ship goldens refreshed from the regenerated output. Contributed by @exGeni (PR #1996). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(retro,ship): count tracked files for the test-file metric, not the working tree The test-file count ran find over the working tree, sweeping untracked build output — a Rails repo reported 623 test files when git tracks 17 (37x), skewing retro narratives and ship dashboards. Count via git ls-files instead; includes the one-line Python-glob widening so non-JS repos stop undercounting. Fixes #2307, #1999. Contributed by @joshRpowell (PR #2308). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(land-and-deploy,gen): auto-merge diagnosis + CRLF-stable generation Two small hardenings: land-and-deploy Step 4 no longer misdiagnoses a failed `gh pr merge --auto` as a permissions problem when the real cause is the merge-method mismatch the command names; and gen-skill-docs normalizes CRLF at the template entry point so Windows checkouts with autocrlf produce byte-identical generated output to CI instead of silently skipping the \n-anchored transforms. Contributed by @Jmeg8r (PR #2437) and @1ncludeSteven (PR #1051). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(land-and-deploy): stop greedy sed from eating the URL scheme in deploy-config parsing The deploy-config bootstrap parsed "Production URL: https://x.com" with sed 's/.*: *//', which cuts at the LAST colon — the one in "https:" — yielding "//x.com". Cut at the first ": " instead (s/^[^:]*: *//). Resolver only; the generated land-and-deploy/SKILL.md regenerates from this source in the docs lane. Contributed by @briascoi (PRs #2555/#2493). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(artifacts-init): honor the provider CLI's git_protocol instead of forcing SSH gstack-artifacts-init unconditionally rewrote the push remote to SSH and hard-failed setup for users whose gh/glab auth is HTTPS-only. Now: - provider-created remotes follow `gh config get git_protocol` / `glab config get git_protocol` (HTTPS when unset — the gh default) - explicit/existing/manual remotes keep their given protocol; unknown URL forms (local bare paths, file://, self-hosted) pass through - new --push-protocol auto|https|ssh flag overrides the inference - the unreachable-remote error names the actual protocol and points at --push-protocol instead of assuming a missing SSH key Closes #1348. Contributed by @time-attack (PR #2225). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(browse): skip the .gitignore append when git already ignores .gstack/ ensureStateDir appended ".gstack/" to a tracked .gitignore even when git already ignored the directory via global excludes, .git/info/exclude, or a parent .gitignore — dirtying the working tree on every daemon start. Run `git check-ignore -q -- .gstack/` first and return early when git says it's covered; git-missing/not-a-repo/timeout all fall through to the existing text-check append (the safe default). Closes #2385. Contributed by @gregario (PR #2430). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(browse): guard browser.process() in resolveDisconnectCause `.process()` only exists on browsers Playwright launched itself; a browser from connectOverCDP() (or a test stub) has no such method, so the blind call threw "browser?.process is not a function" inside the disconnect handler and took down the daemon. Type-check the method before calling it and treat the no-method case as no process handle. Closes #2085. Contributed by @elan2002 (PR #2434). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(lib): narrow the override injection denylist to instruction-shaped phrases The /override[:\s]/i pattern flagged any prose containing "override " or "override:" — CLI flags (--port-override -1), tfvars notes, and plain "you can override the default region" all tripped the injection guard. Require an instruction-shaped continuation: "override (all)? previous | prior | above | the rules/instructions/system prompt". Genuine attempts like "Override: ignore all previous instructions" still block via the ignore-previous pattern. Closes #2401, #1934. Contributed by @Masashi-Ono0611 (PR #2424); same fix independently by @JonasFocus (PR #1940). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(redact): stop the E.164 phone pattern flagging compact timestamps Bare 14-digit runs like 20260727202423 (YYYYMMDDHHMMSS backup/log stamps) matched the phone regex and produced MEDIUM PII findings. Reject a separator-free 14-digit span whose fields parse as a plausible date-time; real numbers carry a + or spacing, so phone coverage is unchanged. Contributed by @abkrim (PR #2428). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(design): create the OpenAI key file owner-only, closing the write-then-chmod race saveApiKey wrote ~/.gstack/openai.json at the default umask and tightened to 0600 afterwards, leaving the API key briefly world-readable between write and chmod (CWE-377/367). Pass mode 0o600 at create; the trailing chmodSync stays as a backstop to tighten a pre-existing loose file. Contributed by @bunlongheng (PR #2468). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(config): make gstack-config key validation locale-independent POSIX bracket ranges like a-z follow the active collation order; under GNU grep with tr_TR.UTF-8 the range excludes the ASCII letter i, so every key containing i (skill_prefix, explain_level, ...) was rejected as invalid. Pin both get/set validators to LC_ALL=C, with a source-level tripwire test since macOS BSD grep doesn't reproduce the bug. Closes #2494. Contributed by @Math1987 (PR #2506). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(resolvers): stop env-var hosts from doubling $HOME in the binary fallback path The browse/design/make-pdf setup resolvers built the fallback binary path as "$HOME" + dir.replace(/^~/, ''), which is only correct for ~-rooted dirs. Env-var hosts carry an absolute $GSTACK_* dir, so the generated fallback became $HOME$GSTACK_.../browse — a path that never exists. New toShellPath() in scripts/resolvers/types.ts expands ~ to $HOME and passes absolute env-var dirs through untouched; all five call sites route through it. Claude-host generated output is byte-identical, so no SKILL.md regeneration is needed here. Closes #2055. Contributed by @simjak (PR #2056). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(settings-hook): respect CLAUDE_CONFIG_DIR when resolving settings.json gstack-settings-hook hardcoded $HOME/.claude/settings.json, so users running Claude Code with a relocated CLAUDE_CONFIG_DIR had hooks written to a config file Claude never reads. Resolve ${CLAUDE_CONFIG_DIR:-$HOME/.claude} first; the explicit GSTACK_SETTINGS_FILE override still wins. Partial #349. Contributed by @andrefogelman (PR #2239). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(browse): dispatch a change event after fill for change-only validators Playwright's Locator.fill() dispatches `input` but never `change`, so frameworks that validate on change (AngularJS ng-change, debounced strength/match checks) never saw the filled value — correct in the DOM, failing the framework's own validation. `browse fill` now dispatches `change` after the fill. Failing-first regression test with a change-only password-match fixture included. Contributed by @intelliot (PR #2475). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(safety): unknown question-preference source exits the documented 2, not 1 The --write user-origin gate documents exit 2 as "rejected, do not retry" (profile poisoning defense), but a source outside both the allowed and the explicitly-rejected lists fell through to exit 1 — the generic validation code callers treat as retryable. Unknown sources now exit 2 with the same do-not-retry rejection message as the known non-user-originated ones. Closes #2390. Contributed by @gregario (PR #2429). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(pr-title): stop duplicating the version prefix on bare-version titles A title that was nothing but a version ("v1.2.3" — the form ship uses for version-only bumps) matched neither the "v<NEW_VERSION> " literal case nor the trailing-space strip regex, fell through to the prepend path, and came out as "v1.2.3.4 v1.2.3" — which pr-title-sync.yml then wrote back via gh pr edit. Handle the bare form in both the no-change case and the prefix-strip regex, and emit a bare new version when nothing follows. Closes #1886. Contributed by @jbetala7 (PR #1887). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(build): escape literal braces in the bun:sqlite stub regex Perl >= 5.26 treats an unescaped literal `{` in a pattern as fatal ("Unescaped left brace in regex is illegal"), so build-node-server.sh died at the bun:sqlite stub substitution on modern perl. Escape both braces; the replacement output is unchanged. Closes #2300. Contributed by @nuga0718 (PR #2111). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(config): preserve spaces in gstack-config values get/list read values with awk '{print $2}' | tr -d '[:space:]', which truncated any value containing spaces ("/Users/x/Conductor Workspaces" came back as "/Users/x/Conductor") and set wrote the unfiltered raw value on the append path. New read_config_value() strips only the "key:" prefix and trailing whitespace (cut-style parse), and set appends the same newline-stripped value the in-place edit path uses. Closes #1782. Contributed by @jbetala7 (PR #1783). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(browse): recover a late-healthy detached daemon instead of a false "Server failed to start" startServer spawns the daemon detached + unref'd, then polls health for a fixed budget. On a loaded machine the budget can elapse in the gap between the loop's last tick and the daemon becoming ready — the CLI reported "Server failed to start within Ns" while the very next `browse status` showed a healthy server. Add a final readState()+isServerHealthy() re-check before the timeout throw, and make the budget env-overridable via BROWSE_START_TIMEOUT (BROWSE_* tunable convention). Structural + behavioral tests pin both invariants. Closes #1846. Contributed by @harjothkhara (PR #1847). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(browse): daemon resilience on loaded machines — Bun conn errors, stop/restart flush, startup + git-root budgets Four load-sensitivity fixes in the daemon lifecycle: - sendCommand only recognized Node's ECONNREFUSED/ECONNRESET; the compiled CLI runs on Bun, which reports 'ConnectionRefused'/'ConnectionClosed' ("Unable to connect..."), so daemon crashes leaked the raw error and exited 1 instead of entering the busy-check/restart path. Match both. - stop/restart called shutdown() inline, which exits before the HTTP response flushes — the CLI saw a dropped socket (and would now crash-retry a fresh daemon just to stop it). Defer shutdown ~100ms so the 200 lands first. - Non-CI POSIX startup budget raised 8s -> 15s (cold Chromium measured ~5.7s at load avg 10; load 12+ blew the old budget while the detached daemon was still booting). - getGitRoot's 2s git rev-parse timeout returned null under load (6.3s spikes measured), scattering state files across cwds into split-brain daemons. Raise to 8s, still bounded. Contributed by @mplatts (PR #1732). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(telemetry): ingest keeps error_message/failed_step instead of dropping them The telemetry_events columns exist and bin/gstack-telemetry-log already sends error_message + failed_step, but the Supabase ingest function dropped both fields on insert — every error report arrived with no message and no failing step. Map them through with the same bounded-length sanitization as error_class (500/100 chars). The completion-status resolver now also passes --error-message/--failed-step in the generated skill telemetry block, with instructions to leave them empty on success. Resolver only for the template side; generated SKILL.md files regenerate from this source in the docs lane. Contributed by @sunnnybala (PR #769). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(browse): surface non-EEXIST errors in acquireServerLock instead of masking them acquireServerLock caught every open failure as if the lock were held: EACCES/EROFS/ENOENT surfaced as phantom "another process holds the lock" (null return, no diagnostics), and a failed stale-lock read or unlink was swallowed the same way. Each failure class now logs a coded, pathed diagnostic: non-EEXIST open errors, holder-PID read errors (ENOENT retries the acquire — the holder released between open and read), and stale-lock unlink errors. Four-case unit test included. Closes #1084. Contributed by @jbetala7 (PR #1725); same fix independently by @JiayuuWang (PR #1097). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(paths): shell-quote gstack-paths output so eval round-trips values gstack-paths emitted bare KEY=VALUE lines, so the documented eval "$(gstack-paths)" re-parsed the values: backslashes were eaten as escapes (Windows $TMP C:\Users\... became C:Users...) and a space word-split the assignment, leaving the variable empty. Emit each value with printf %q so eval round-trips byte-for-byte; plain POSIX paths are unchanged. Round-trip regression tests cover backslashes, spaces, and embedded quotes. Closes #2374. Contributed by @fangearhq-boop (PR #2376); same fix independently by @yannickspiess (PR #1580). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * security(browse): drop .svg from the load-html extension allowlist SVG is a script-capable format (inline <script>, event handlers, foreign objects), so allowing it through load-html's HTML allowlist let a local .svg execute script in the browse session context. The allowlist is now .html/.htm/.xhtml only; regression test asserts .svg is rejected. Contributed by @garagon (PR #1153). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(benchmark): validate --timeout-ms as a positive integer gstack-model-benchmark fed --timeout-ms straight through parseInt, so "abc" became NaN and "0"/"-1" passed through — a NaN or non-positive timeout silently disables the per-provider watchdog. Reject anything that isn't a positive (optionally +-prefixed) safe integer with a clear error and exit 1. Closes #1726. Contributed by @jbetala7 (PR #1727). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * chore(fixtures): clean terminology in the security-bench replay fixture Two spots in browse/test/fixtures/security-bench-haiku-responses.json referred to real-world HVAC project naming; replace with the generic "mechanical services" wording. Fixture stays valid JSON; replay tests unchanged. Contributed by @apex-system (PR #2131). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * ci: cancel superseded actionlint and skill-docs runs actionlint.yml and skill-docs.yml trigger on both push and pull_request with no concurrency group, so every push to an active branch left the previous (now-obsolete) runs queued or running — twice per commit on same-repo PR branches. Add the same cancel-in-progress concurrency groups the heavier workflows already use, plus a free static tripwire test that fails CI if a push+pull_request workflow ever ships again without cancel-in-progress. Contributed by @jbetala7 (PR #2053). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(make-pdf): correct CJK rendering — NUL sentinel hardening, SC-first fonts, CJK quote context Three CJK fixes in the PDF pipeline: - smartypants strips stray input NULs up front so document text can never forge the U+0000 placeholder sentinel and leak a preserved-zone marker into the output. - The CJK font stack led with Japanese families, so Simplified-Chinese text rendered han glyphs with JP variants. Lead with PingFang SC / Heiti SC / Noto Sans CJK SC / Source Han Sans SC before the JP fallbacks. - Quote-smartening only recognized ASCII openers as "start of quote" context; the fullwidth colon and CJK brackets now count, so quotes after them curl the right way. Contributed by @rssprivacy-commits (PR #2012). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * docs: regenerate skill output for the quick-win resolver changes Regen for the deploy-config URL-scheme fix (utility resolver), telemetry completion-status resolver, and $HOME-doubling binary-resolver fix; ship goldens refreshed to match. Generated-output-only commit. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(slug): cached identity is sticky — heal ONLY the provable subdir-cache bug shape The walk-up rewrite recomputed the slug on every run and "healed" the cache toward the fresh value, which broke the #2212 continuity contract: a project that used gstack before adopting a git remote would be silently renamed to the remote-derived slug, orphaning everything under ~/.gstack/projects/. Cached identity now wins, with one precise exception: when the cached value equals THIS pwd's basename while the walk-up proves pwd is not the project root, the entry came from the pre-walk-up subdirectory bug (#1125) and is recomputed. All four slug contracts pass together (repo-mode #2212, walk-up #1125, sanitize, user-slug). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(claude): stop false-blocking macOS keychain subscription auth in host detection The /claude skill's auth probe only recognized env-var/API-key auth, so macOS subscription installs (keychain-backed, where `claude -p` works fine) were told they had no auth. Detection now uses host invocation. Fixes #1890. Contributed by @xing-qnex (PR #2411); PR #2548 by @shawnacalia covered the keychain case. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(setup): Ubuntu 26.04 Playwright platform detect + silence the codesign false alarm Two small setup papercuts: the Playwright platform probe now recognizes Ubuntu 26.04 instead of falling to the generic-Linux path, and macOS installs stop warning about a codesign "failure" that was actually the expected unsigned-adhoc path (the real signature check already gates binary launch). Contributed by @nuga0718 (PR #2113) and @lucascaro (PR #1758). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(skills): land-and-deploy squash readback, next-version paths, embed-flags quoting Three template one-liners: land-and-deploy reads the squash-merge result from the merge commit instead of the stale branch tip; review/landing-report /land-and-deploy templates call bin/gstack-next-version via its installed path instead of a bare repo-relative one; setup-gbrain quotes GBRAIN_EMBED_FLAGS so zsh word-splitting stops silently dropping voyage-code-3 flags. Regenerated output included. Contributed by @stormeoio (PR #2011), @rjmurillo (PR #1820) and @trevorhstandridge (PR #1817). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * release: v1.64.0.0 — fix wave CHANGELOG, VERSION, deferred-wave TODOs Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * test: refresh ship goldens for the telemetry error-field resolver output Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * test(redact-prepush): assemble the fake AWS key at runtime — the literal blocked our own push The hook's fixtures carried a live-format AKIA literal, and the repo's own pre-push scanner (hardened in this wave) correctly blocked pushing it. The placeholder-suppressed docs key would defeat the detection tests, so the fixtures now concatenate the key at runtime: tests still exercise real detection, and the pushed diff never contains a scannable credential shape. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(slug): terminate the marker walk-up on dirname's fixed point — hung every bin on Windows Under git-bash on Windows a mixed-form path walks C:/Users -> C: -> . -> . forever: dirname's fixed point there is never "/", so the walk-up loop spun and every bin that evals gstack-slug (learnings-log first among them) hung until spawn timeout. Caught by windows-free-tests CI on the wave PR. Break on the fixed point itself with a depth cap for exotic forms; regression tests drive the extracted function with hostile path shapes under a hard timeout. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
46c1fae7f1 |
v1.54.0.0 feat: carve /ship into skeleton + on-demand sections (-59% always-loaded) (#1806)
* feat(test): transcript-section-logger + ship-action fingerprint (T10) Pure-analysis module over a SkillTestResult/NDJSON transcript: - extractSectionReads(): which sections/*.md a run opened (post-carve check) - extractShipActions(): observable action fingerprint (merge/test/bump/ changelog/commit/push/pr) that works on the MONOLITH too, so a baseline captured before the carve can detect a sectioned-ship regression - baseline read/write + compareShipActions() for baseline-first dogf(T10) Baseline-first answers the Codex outside-voice critique that a logger in the same PR as the carve is post-failure telemetry without a pre-carve reference. 11 unit tests, all green. Paid monolith baseline capture runs separately. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(pipeline): section discovery + generation machinery (T9) - discover-skills.ts: discoverSectionTemplates() scans <skill>/sections/*.md.tmpl - gen-skill-docs.ts: extract resolvePlaceholders + applyHostRewrites + buildContext as shared helpers (processTemplate and the new processSectionTemplate both call them, so a sanitization/rewrite fix can't miss sections) [C1] - processSectionTemplate: body-fragment generation (no frontmatter/catalog/voice), parent-skill TemplateContext (skillName pinned to parent, not 'sections', so appliesTo gating + tier behave identically), per-host output routing - --host all now fails the build on ANY host failure, not just claude, so a stale external-host output can't slip the freshness gate [Codex outside-voice #9] Inert until a skill is carved (no sections/ dirs exist yet). Refactor is output-neutral: gen:skill-docs --dry-run --host all reports 0 STALE. 5 discovery unit tests + 389 gen-skill-docs tests green. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(setup): install sections/ for cherry-pick targets (claude + kiro) (T9) Two install targets cherry-pick SKILL.md and would leave a carved skill's sections/ behind, 404ing a runtime 'Read sections/<name>.md': - link_claude_skill_dirs: link the sections/ subdir via _link_or_copy (windows gets a fresh copy on every ./setup) - kiro per-skill loop: sed-rewrite + copy each sections/* so paths resolve under ~/.kiro, not ~/.codex/~/.claude codex/factory/opencode link the whole generated dir, so sections ride free. Addresses Codex outside-voice #4/#6 (runtime pathing landmine). Inert until a skill is carved. Static-tripwire test + windows-fallback invariant green. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(ship): gstack-version-bump CLI — tested idempotency classify + write (T9) Hybrid CLI extraction (CM1): the deterministic core of ship Step 12 becomes a tested CLI instead of bash prose the agent re-derives each run. - classify: FRESH/ALREADY_BUMPED/DRIFT_STALE_PKG/DRIFT_UNEXPECTED from VERSION vs origin/<base>:VERSION vs package.json.version (pure reader) - write: validated dual-write to VERSION + package.json (FRESH bump) - repair: DRIFT_STALE_PKG sync, no re-bump Bump-LEVEL choice + queue collision stay agent judgment; slot pick stays bin/gstack-next-version. This removes the re-bump-a-shipped-branch footgun from skippable prose into code that can't be skipped or misread. 15 tests (exhaustive state matrix + write/repair fs + real-git classify). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * test(parity): sectioned-skill parity capability — guards the carve (T9) Carved skills (skeleton + sections/*.md) need parity checks that see relocated content, or moving a phrase into a section reads as 'lost': - readSkillForParity(): union skeleton + all sections/*.md - checkSkillParity sectioned mode: content checks against the union; minBytes/ maxSizeRatio against union bytes (total behavior preserved); maxSkeletonBytes asserts the always-loaded skeleton actually shrank. Lowering minBytes to fit a small skeleton would otherwise make the size floor toothless [Codex #12]. Built + tested BEFORE the carve so ship's invariant can flip to sectioned in the same commit it lands. Monolith path byte-identical (verified: pre-existing investigate 1.053 ratio drift fails the same with this change stashed). 7 sectioned-parity tests + existing parity tests green. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * refactor(ship): carve into skeleton + on-demand sections (Claude) (T9) ship/SKILL.md drops 167KB → 68.7KB (~59% of the always-loaded skill) by moving 8 prose-heavy steps into ship/sections/*.md, read on demand: tests, test-coverage, plan-completion, review-army, greptile, adversarial, changelog, pr-body. Step 12's version logic now calls the tested gstack-version-bump CLI instead of inline bash. Claude-first (S2): {{SECTION:id}} emits a STOP-Read pointer on Claude (skeleton + generated section files) and INLINES the content on every other host, so external hosts keep the full monolith — verified factory at 162KB with no sections dir. {{SECTION_INDEX:ship}} renders the situation→section table from the PASSIVE manifest (CM2 / v2_PLAN.md:663); required-reads live only in test fixtures. Multi-pass resolve expands inlined sections' own resolvers. Parity: ship invariant flipped to sectioned (union content checks + maxSkeletonBytes asserts the shrink). Carve-fallout fixed across gen-skill-docs/skill-validation/ golden/plan-completion/#1539/size-budget tests via skeleton+sections union reads. Free suite green except the pre-existing investigate parity drift. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * test(ship): manifest-consistency + context-parity + requiredReads helper (T9) Free deterministic guards for the carve: - required-reads.ts + unit test: assertRequiredReads(run, requiredFiles) — the mechanical layer-5 check that the agent Read the sections its situation needs (required set comes from the fixture, not the passive manifest) - section-manifest-consistency: 3-tier orphan classification (generated orphan + hand-edited generated file → FAIL; manifest orphan → WARN per v2_PLAN.md) and pins the PASSIVE-manifest contract (no applies_when/required_for) - template-context-parity: generated sections have zero unresolved placeholders and gated resolvers (ADVERSARIAL_STEP/CONFIDENCE_CALIBRATION/CHANGELOG_WORKFLOW) rendered — proving sections resolve with the parent skillName, not 'sections' 16 tests, all green. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * test(ship): section-loading E2E + idempotency CLI detection (T9) - skill-e2e-ship-section-loading.test.ts (new, periodic): runs real /ship in plan mode against a fresh version-changing fixture and asserts the agent Read the required sections (review-army + changelog). Runs against the INSTALLED skill (~/.claude/skills/gstack/ship), not repo paths, so install-layout 404s surface [Codex outside-voice #5]. Layer-5 mechanical guard against silent section-skip. - skill-e2e-ship-idempotency.test.ts: detection updated for the carve — Step 12 now runs gstack-version-bump classify (JSON "state":"ALREADY_BUMPED") instead of the inline bash echo (STATE: ALREADY_BUMPED). Accept both; add a gstack-version-bump-write re-bump regression signal. - touchfiles: register ship-section-loading (periodic) + extend idempotency deps with bin/gstack-version-bump + scripts/resolvers/sections.ts. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * test(ship): union-read redaction wiring test for the carve (T9) main's PR-body redaction-at-sink lives in sections/pr-body.md.tmpl after the carve, not the skeleton template. Read skeleton + section templates union so the redaction-wiring assertions follow the relocated content. 9/9 green. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * v1.54.0.0 feat: carve /ship into skeleton + on-demand sections (-59% always-loaded) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |