Files
gstack/docs/designs/fork-port-residual-2026-09/REPORT.md
T
Garry TanandClaude Fable 5.1 c241216637 v1.80.0.0 fix: setup survives a failed Chromium install, hooks share one state root, gstack never clobbers a skill it did not create (#2802)
* fix(freeze): hook reads the same state root /freeze writes — fails closed under GSTACK_HOME (#1459, #1509)

check-freeze.sh resolved its state dir as ${CLAUDE_PLUGIN_DATA:-$HOME/.gstack}
while every writer (/freeze, /guard, /unfreeze, /investigate) resolves through
bin/gstack-paths, GSTACK_HOME first. With GSTACK_HOME set, /freeze wrote
freeze-dir.txt under GSTACK_HOME, the hook read $HOME/.gstack, found no file,
and allowed everything — a deny-tier boundary failing open.

One resolver now: gstack_hook_state_root() in careful/bin/hook-extract.sh
(already sourced by both check-freeze.sh and check-careful.sh) implements the
exact gstack-paths chain, including the CLAUDE_PLUGIN_ROOT guard that keeps a
CLAUDE_PLUGIN_DATA leaked from another plugin from redirecting our state.
check-freeze.sh and gstack_hook_log_fire both call it; nothing spawns
gstack-paths from a hook.

Tests: the GSTACK_HOME deny regression, GSTACK_HOME-over-CLAUDE_PLUGIN_DATA
precedence, plugin-root guard both ways, and a byte-parity check against
bin/gstack-paths across six env combinations. Existing freeze tests now pass
CLAUDE_PLUGIN_ROOT like a real plugin install would.

Idea from PR #1509 (@NikhileshNanduri); implemented natively against the shared
resolver rather than a second fallback chain.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* fix(relink): never delete or link over a skill gstack does not own (#2119)

gstack-relink runs on every ./setup. Its cleanup did `rm -rf` on any same-name
entry whose SKILL.md was a symlink, with no readlink check, and its link step
did `mkdir -p` then `ln -snf` onto any existing SKILL.md — on Linux that
replaces a user's real file with a symlink into gstack (macOS refused by
accident). setup's Windows mode-flip cleanup deleted any real dir whose name
matched a gstack skill. A personal `qa` skill, or a fork installed under
another path, was destroyed by the installer of a tool it never asked for.

Ownership is now proven, never assumed. An entry is ours when it is a symlink
resolving into INSTALL_DIR or RENDER_DIR, a real dir whose SKILL.md is such a
symlink, or a real dir carrying the .gstack-owned marker setup now writes for
Windows copy installs (legacy copies count when byte-identical to the source
or carrying gen-skill-docs' AUTO-GENERATED header). Anything else — including
an entry whose readlink fails — is foreign: left untouched, reported on
stderr, and listed in relink's summary line. The same rule replaces setup's
Windows name-match deletion; setup:1040 and gstack-uninstall:204 already
gated on readlink, so this closes the last unguarded deleter of the class.

Tests: foreign real dir in flat mode, foreign flat entry on a prefix flip,
foreign directory symlink, RENDER_DIR-targeted entry (ours), marker-carrying
copy (ours), marker-less copy (foreign); the Windows cleanup test now proves
provenance three ways and keeps the user's own same-name skill.

Idea and two regression cases from PR #2119 (@smblight); implemented on the
destination entry, not only the symlink target.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* fix(setup): Chromium bootstrap is best-effort and bounded — skills always register (#1900, #1901, #1902, #913, #2233)

setup runs under `set -e`, and the Chromium bootstrap in section 2 sat ahead
of skill registration in section 4 with a bare `bunx playwright install
chromium`, an unbounded download, and an explicit `exit 1` after the
post-install launch probe. On an offline, proxied, or AppArmor-restricted box
the user ended with ZERO skills registered and a re-run that died at the same
line; a wedged download hung setup indefinitely.

Every browser failure now records a reason code in _PW_FAIL_REASON and setup
continues: skipped (GSTACK_SKIP_PLAYWRIGHT=1, #913), chromium-install,
chromium-install-timeout (the download is bounded by the existing
_wait_with_deadline helper, default 600s, env GSTACK_PLAYWRIGHT_INSTALL_TIMEOUT,
process tree killed via _kill_tree), chromium-install-locked (another setup
holds the lock: this one registers skills and re-probes next time instead of
exiting), windows-no-node, windows-node-modules, post-install-launch (with the
GSTACK_CHROMIUM_NO_SANDBOX=1 hint for Ubuntu 24.04's userns policy, #2157).
The daemon font refresh is skipped when Chromium is unavailable. The final
summary names the skills that need the browser (/qa, /qa-only,
/design-review, /browse, make-pdf, /pair-agent) and the fix for the recorded
reason, and logs the reason code (never a path) through gstack-telemetry-log
when telemetry is on.

Tests: static invariants over the anchor-sliced block (no exit, every reason
code, deadline helper, trap chaining, guarded refresh, summary contents) plus
an integration harness that executes the real block with a stubbed probe and
installer: install failure, hang killed at the deadline with the tree kill
recorded, non-numeric knob fallback, live lock (continues, installer not run,
lock preserved), stale lock reclaimed, post-install probe failure, and the
skip flag.

Credit @DavidMiserak (PR #1900) for the best-effort shape; re-implemented on
the current block.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* docs(designs): preserve the time-attack fork-port residual evaluation

The read-only evaluation of what remains portable from time-attack/gstack
(583 raw candidates, 415 canonical, 287 with a residual, 48 adversarially
refuted, 14 standing) lived only on a throwaway VM. This records the report,
the lite residual index, the absorbed/superseded ledger, the refuter
verdicts, and SHAS.md with the fork tip, upstream HEAD, merge-base, and a
sha256 per file, so every scheduled fix in this wave series traces to its
evidence.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* docs: file the fork-port residual deferrals and document the Chromium bootstrap knobs

TODOS.md gains the seven items the CEO and eng reviews of the fork-port
residual plan deliberately deferred (shared ownership helper, config-key
reader tripwire, "pre-existing" vocabulary, opt-in reply_language, .auth.json
writer removal, the fork-derived-change rule for CONTRIBUTING, hook slug
parity audit), each with rationale, and updates the two residual bullets for
PR #2232 and PR #2233 with their dispositions. README's Troubleshooting
section explains the best-effort Chromium bootstrap and its three knobs.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* fix(relink): canonicalize link targets before the ownership check

Pre-landing review finding: the ownership gate compared readlink output
textually against INSTALL_DIR and RENDER_DIR, so two shapes of gstack's OWN
entries read as foreign and were left behind on a mode flip — a legacy
relative link (`gstack/qa/SKILL.md`, resolved against $PWD instead of the
link's directory) and an entry linked against the real path of a symlinked
install dir (~/.claude/skills/gstack -> checkout). Both now resolve: relative
targets anchor at the link's directory, the directory part is canonicalized
with pwd -P (the basename stays verbatim so a dangling managed target is not
misread), and both spellings of each root are accepted. Two regression tests.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* fix(telemetry): one-shot setup events never sweep other sessions' pending markers

gstack-telemetry-log finalizes every .pending-<session> marker that is not
the caller's own as outcome:unknown and deletes it. setup's onboarding
events (_setup_welcome, _setup_playwright) have no session of their own, so
a Chromium bootstrap failure during a live skill session recorded a false
unknown for that session and removed its marker.

New --no-sweep flag skips the stale-marker pass; both setup call sites use
it (the synthetic --session-id did not prevent the sweep). Surfaced by the
Codex adversarial pass.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* fix(hooks): partial upgrades fail closed for freeze and fall back for careful

A hook script and its sourced helper can be copied at different times. With
an older careful/bin/hook-extract.sh that lacks gstack_hook_state_root:

- check-freeze.sh now emits a deny ("fail closed, re-run ./setup or
  /unfreeze") instead of dying under set -e with no decision JSON.
- check-careful.sh falls back to ${GSTACK_HOME:-$HOME/.gstack} so project
  rules under the plain chain still load and a decision is always emitted
  (a warn hook must never break on a stale helper).

gstack_hook_state_root prints its root without a trailing newline and both
callers capture it with a printf-x sentinel, so a GSTACK_HOME ending in a
newline round-trips byte-for-byte with the writer's %q form.
gstack_hook_log_fire stays on ${GSTACK_HOME:-$HOME/.gstack}/analytics, the
same two-step chain every other analytics writer and reader uses, so the
usage log remains one file under a plugin install.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* fix(setup): never link over, copy over, or reap a skill gstack does not own (#2119)

The relink gate alone left three destructive sites open:

- link_claude_skill_dirs runs BEFORE relink on every ./setup and used
  `ln -snf` (Linux replaces a user's real SKILL.md with a symlink into
  gstack) or, on Windows, rm -rf + cp followed by a marker that made the
  user's directory "ours" on the next flip. It and _install_alias_skill_md
  now consult _claude_entry_is_ours first and skip loudly.
- cleanup_prefixed_claude_symlinks kept a bare name-match deletion and a
  `*gstack*` substring match. Symlink arms use anchored `gstack/` segment
  patterns; the Windows real-file arm proves provenance (marker,
  byte-identity with our source, or the full two-line gen-skill-docs banner
  within the first 40 lines, never a one-line substring another generator
  could emit). cleanup_old_claude_symlinks uses the same banner rule.
- gstack-relink's fast path judged absolute targets before canonicalizing,
  so `/x/gstack/../foreign/SKILL.md` counted as ours; dot-segment targets
  now canonicalize first. Its banner rule matches setup's.

The `.gstack-owned` marker records the owning payload's realpath. Entries
skipped by setup or relink are listed in the final setup summary.

Chromium bootstrap refinements from the pre-landing review: an INT/TERM
trap kills the installer's process tree; the Windows npm chain no longer
masks an install failure; GSTACK_SKIP_PLAYWRIGHT=1 is reported as a choice
rather than a failure and sends no telemetry; the timeout knob is
normalized (0, 000, non-numeric, or more than nine digits fall back to the
600s default instead of killing on the first poll or never killing).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* docs: README Chromium note outside the CLAUDE.md fence; report banner stripped; deferrals name the four gate sites

- README: the Chromium troubleshooting paragraph sat inside the CLAUDE.md
  snippet code fence, so copy-paste put it into users' CLAUDE.md. Moved to
  the troubleshooting list.
- docs/designs/fork-port-residual-2026-09/REPORT.md: the scratch-run
  preamble banner is gone; SHAS.md re-hashed.
- TODOS: the ownership-gate deferral names the four sites and the
  marker-path idea for the fork-with-banner residual.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* docs(todos): the bootstrap block coverage gap is pinned except the quarantine helper

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* fix(setup,relink): ownership proof has two strengths; weak proof never deletes a directory or discards a differing file

The first #2119 gate treated a byte-identical or banner-bearing real-file
SKILL.md as full ownership, so a prefix flip could rm -rf a user's directory
(their own qa skill started from a gstack SKILL.md, plus my-templates/) and
the link pass could replace their customized file with a symlink. Two
strengths now:

- STRONG: the .gstack-owned marker (we created the directory), or a
  directory holding nothing but symlinks and the marker (deleting it loses
  no data). Only strong proof removes a directory whole.
- WEAK: byte-identity with our source or the two-line gen-skill-docs banner
  on a real file. Weak proof covers that SKILL.md and our runtime-asset
  links only; a differing file is moved to
  ${GSTACK_HOME:-~/.gstack}/backups/skills/<ts>/<skill>/ before we link
  over it, and setup/relink print one summary line naming what moved.

The marker is written on every platform now (path-independent proof for
Windows copies and for checkouts whose path carries no gstack segment), but
only for a directory gstack creates: a directory we merely link into
(unclaimed, or a legacy install) never becomes deletable whole. A directory
with no SKILL.md at all is unclaimed: the link pass may add our file, the
cleanup pass has nothing to remove.

Also from the review passes: the banner check reads 8192 bytes, not 40
lines (investigate, office-hours, plan-ceo-review and design-consultation
carry the banner past line 40 and were left "foreign" on pre-marker
Windows installs); a link into a checkout named without a gstack segment
(git worktree add ../gstack-<branch>) is ours when that tree carries
setup + VERSION + bin/; relink's fast path is gone so both files
canonicalize before judging; relink's root alias (_gstack-command) is
gated and stamped like every other entry; relink reports the bare entry
name with setup's wording and setup dedupes when forwarding
(_run_relink_quiet); the summary names the browser skills as examples.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* fix(setup): Chromium-install lock reclaim is atomic and pid-validated; abandoned locks expire; the tree kill walks /proc without pgrep

- A pid file holding "", "-1" or "0" counted as a live holder (kill -0 -1
  signals every process and succeeds), locking Chromium out for good. A pid
  must be a positive integer; anything else is stale.
- Two setups judging the same lock stale raced on rm -rf + mkdir and the
  loser deleted the winner's fresh lock. The stale dir is renamed first
  (atomic), so exactly one reclaims.
- A lock dir with no pid file (killed between mkdir and echo) was never
  reclaimed; it now expires once older than the install bound.
- _kill_tree needed pgrep; debian-slim and git-bash ship none, so the bound
  killed only the wrapper subshell and the installer kept running. Without
  pgrep the children are found by walking /proc/*/stat.
- The timeout knob is normalized in one place with one comment; the trap's
  exit 130 is the only exit the block may contain.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* fix(freeze): an unexpected non-zero death denies via an EXIT backstop instead of exiting with no decision

set -e plus a failing pipeline (a tool on PATH exiting non-zero, a deleted
cwd) ended the deny-tier hook with no JSON, which Claude Code treats as
non-blocking: the edit outside the boundary proceeded. The EXIT trap now
prints a deny for any non-zero exit that happens before a decision was
written; every deliberate output sets _FREEZE_DECIDED first so a late
failure never prints a second object.

Tests also pin careful's state-root precedence (GSTACK_HOME over
CLAUDE_PLUGIN_DATA, plugin data when CLAUDE_PLUGIN_ROOT names gstack) and
the specific "out of date" deny for a helper without gstack_hook_state_root.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* refactor(telemetry): guard the stale-marker sweep with an if, not a break inside the loop

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* docs(todos): the ownership gate lives in six sites, and the cleanup arms inline their own chain

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* test: the two remaining linker harnesses extract the ownership helpers; the marker is the one allowed dotfile

setup-claude-skill-assets and user-render-out-dir-install slice
link_claude_skill_dirs out of setup without the helpers it now calls, so
the extracted function died with "command not found" (or, inside an if,
degraded into "foreign, skipped"). Both harnesses now carry the full helper
set and the globals. The hidden-files census allows .gstack-owned, which
the linker writes for directories it creates rather than copying from the
skill source.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* fix(setup,relink): weak proof never costs the user a file — assets, flips, failed backups, foreign dir links, alias markers

Third review cycle on the ownership model, every item reproduced against a
fixture before the fix:

- Runtime assets (sections/, templates/, checklist.md, ...) were refreshed
  with rm -rf regardless of who owned the directory, so an unclaimed or
  weakly-owned directory lost the user's same-named real files. Real assets
  are now replaced only in a directory gstack created or strongly owns
  (marker, or SKILL.md symlink into gstack), plus the legacy Windows
  real-copy shape; elsewhere they are kept and reported. Symlinks are never
  content and are always refreshed.
- The prefix-flip cleanup deleted a customized banner-bearing SKILL.md that
  the link pass would have backed up. Both cleanups now compare the file
  against the source (raw, or with its name: line rewritten to the entry
  name, which is how alias and prefixed copies legitimately differ) and
  move a differing file to the backup root.
- A failed backup (unwritable root) returned success and the caller linked
  over the file anyway. It now fails, and the entry is left untouched and
  reported.
- A foreign DIRECTORY symlink whose target had no SKILL.md fell through to
  the "unclaimed directory" rule and was replaced by a real directory. A
  symlink that does not resolve into gstack is foreign, full stop.
- The alias installers stamped .gstack-owned into pre-existing directories;
  they now follow the same created-or-already-marked rule.
- A directory counts as "only links" only when every link resolves into
  gstack: a user's own symlink makes it mixed, so their link survives.
- The gstack-tree heuristic requires bin/gstack-relink, not just a VERSION
  file, a setup script and a bin/ directory.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* fix(setup): lock reclaim hands a fresh lock back; a live holder past the bound is stale; /proc walk strips through the last paren

- Reclaim renamed the lock by path after judging it stale, so a second setup
  that had already reclaimed and re-created it lost its fresh lock and two
  installers ran. After the rename the moved directory's pid is re-read: a
  new live holder, or a fresh lock whose pid is not written yet, is moved
  straight back.
- A pid file whose process is alive but whose lock is older than the install
  bound is stale too (the holder is past its own deadline, or the pid was
  recycled to an unrelated long-lived process); it was locked forever.
- The /proc fallback stripped the comm field to the FIRST ") ", so a comm
  containing ") " hid a child from the kill. proc(5) says the last paren.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* fix(freeze): mark the decision written after the helper prints, not before

If gstack_hook_decision ever failed between the flag and its output the
backstop would have stayed silent; setting the flag after the print keeps
the deny backstop armed until a decision is actually on stdout.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* chore: bump version and changelog (v1.80.0.0)

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* docs: update project documentation for v1.80.0.0

README troubleshooting + manual uninstall cover the skill ownership gate
(.gstack-owned marker, ~/.gstack/backups/skills/<ts>/, foreign same-name
skills left untouched). CLAUDE.md and CONTRIBUTING carry the ownership and
best-effort Chromium bootstrap invariants for people editing setup and
gstack-relink. PROJECT_STRUCTURE gains careful/, freeze/, guard/, unfreeze/,
gstack-upgrade/, gstack-relink, and the setup/relink/hook test files.
TESTING_INTERNALS documents the anchor-sliced setup harness convention.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* fix(setup): the final summary reports customized SKILL.md files moved to the backup root

The linker moved a weakly-proven, customized SKILL.md aside before linking
over it but never said so; only relink printed a "Moved N" line, and by the
time relink runs the file is already a symlink. The summary now names each
moved file and where it went, next to the foreign-entry report.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* test: run assembled setup harness scripts from a temp file, not `bash -c` argv (Windows MSYS2 8 KB truncation)

windows-free-tests (run 33907177851) failed in
test/setup-alias-name-uniqueness.test.ts with
  bash: -c: line 178: unexpected EOF while looking for matching `'
The harness slices functions out of `setup` and passed the joined script as
one `bash -c` argv element. The ownership gate grew that script from 6.7 KB
to 15.7 KB, and on Windows bash is an MSYS2 program: when its parent is a
non-MSYS process (bun), msys-2.0.dll's build_argv() runs any argument
containing `?*["'(){}` through globify()/glob(), which copies the pattern
into a fixed `Char patbuf[8192]` and silently stops after 8192 - MB_CUR_MAX
(8186 chars under C.UTF-8); GLOB_NOCHECK then returns the truncated text as
the argument. Character 8186 lands inside the single-quoted sed token on
line 178. Rebuilding the exact script with CI path shapes and cutting it at
8186-8190 characters reproduces the identical message locally; cmd.exe's
8191-UTF-16 cap and CreateProcess's 32767 do not fit the evidence.

Fix: test/helpers/bash-script.ts writes the script to a temp file and runs
`bash <path>` — a short glob-free argument that never enters globify. Every
setup harness that assembled a script for `bash -c` (11 files, 22 sites)
uses it; timeouts and env are preserved verbatim, spawn/timeout errors are
appended to stderr, temp cleanup is best-effort. `spawnSync('bash',
[<Windows absolute path>])` already passes on windows-latest in setup-help,
uninstall-windows-copies and the migration tests. The Windows-curated list
is byte-identical before and after.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* docs(test-free-shards): the rerun-refresh harness spawns bash <tempfile> via test/helpers/bash-script.ts, not bash -c

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-05 14:45:28 -07:00

62 KiB

Is there anything else worth taking in from time-attack/gstack?

Evaluation of the time-attack/gstack fork (Sina Matian, MIT; fork main 0aca1f77, tip of the "GStack 2 public beta" release merged as PR #53 at 41927ca8, 2026-08-10; merge-base 7c9df1c5) against upstream garrytan/gstack main v1.79.0.0 (2026-09-01). Sources: /tmp/ta-eval/summary.json, residual-index.json (287 items, read in full), records-other.json (128 items), records-residual.json (per-item lookups), refute-batch-1..6.json (48 items) and the appended adversarial refuter verdicts. Upstream claims below were re-checked by grep at HEAD where cited as file:line.

1. Bottom line

After the seven directly merged fork PRs, the iOS suite, port waves 1 and 2 (v1.63, v1.65) and the security sweep (v1.67.1), the fork's 583 raw candidates collapse to 415 canonical items, of which 128 are already absorbed, superseded or not applicable and 287 carry some residual. The panel rated 269 of those 287 as take or take-partial, but when the 48 top-ranked candidates were put through adversarial refutation only 14 survived, and two of those are the same defect counted twice, so the honest answer is about a dozen genuine ports plus a cluster of ten small grep-verified bin/browse fixes, with a further 39 panel-P1 items that were never refuted and are listed in section 5a so nothing is silently misfiled. Almost none of it is "GStack 2": the surviving items are correctness bugs and consent gaps in upstream's own code that the fork happened to notice, and roughly a third of the shortlist is really "merge a stale community PR the fork carried" (#1509, #2119, #1900, #1777, #1096). The top five by user impact are: (1) ./setup aborting before any skill is registered when Playwright's Chromium download fails (setup:3 set -e, setup:806, setup:850 vs skills at setup:1657); (2) the /freeze deny hook reading a different state dir than /freeze writes, so the boundary fails open whenever GSTACK_HOME is set (freeze/bin/check-freeze.sh:35); (3) /ship Step 5/6 still hardcoding bin/test-lane, Rails and a Ruby eval runner (ship/sections/tests.md.tmpl:9-113); (4) the question-preference PreToolUse hook keying project preferences by cwd basename while the writer keys by owner-repo, so the "never ask again" enforcement layer never fires for any repo with a remote (hosts/claude/hooks/question-preference-hook.ts:300-306); (5) transcript_ingest_mode=off being a documented consent switch that no binary reads (bin/gstack-config:181 is the only occurrence). Safely ignorable: the entire GStack 2 architecture (six dispatchers, fifteen-vector scale system, SHARED-JUDGMENT and VERIFICATION contracts, zero-question budget), the web-data provider stack, PRIVACY.md, issue forms and labels, the multi-OS CI lane, the Node .cjs team hook, and the persona-fleet methodology already parked in TODOS.md.

2. Ranked shortlist — worth taking

Sorted by panel priority, then user_value_score + fit_score. Every item here either survived refutation (refuted=false) or is a cheap fix whose upstream premise I re-verified by grep at HEAD; the latter are flagged as such and clustered.

Footnote on the survivor count: the 14 survivors reflect the final (appended) refutation pass. The residual-index.json prior_refuter field records an earlier pass with a different boundary on 7 of the 48: s1-prewave-transcript-ingest-mode-off and s1-prewave-terminal-ws-extension-id-pin were refuted=true in the earlier pass and refuted=false in the final one; s1-prewave-setup-install-deadline-and-continue, s1-prewave-readme-update-check-disclosure, s1-prewave-review-loosened-inputs-stale-strings, s6-branches-web-data-official-api-first-rule and s1-prewave-auth-json-file-write-removal had no earlier verdict (prior_refuter is null, not false) and are refuted=true in the final pass. This report follows the final pass throughout.

1. /freeze hook state-dir chain (gap2-skillwave-hooks-envelope-and-state-dir-chain-1509), P0, val 8 / fit 10. The hookSpecificOutput envelope half of #1509 is absorbed, but freeze/bin/check-freeze.sh:35 still reads STATE_DIR="${CLAUDE_PLUGIN_DATA:-$HOME/.gstack}" while every writer (freeze/SKILL.md.tmpl:61-64, guard/SKILL.md.tmpl:70-73, unfreeze/SKILL.md.tmpl:31-35, investigate/SKILL.md.tmpl:131-134) resolves through bin/gstack-paths GSTACK_HOME-first. GSTACK_HOME is a documented production knob (bin/gstack-config:16, setup:2135) and the same hook's analytics helper already honours it (careful/bin/hook-extract.sh:71). Result: with GSTACK_HOME set, /freeze prints "boundary set" and the hook hits the no-file allow branch, contradicting its own header ("a boundary that fails open is not a boundary"). Fork refs: bbad1bab (check-freeze.sh hunk), c896016b (test pin); upstream PR #1509 and issue #1459 both OPEN. Port: one line, STATE_DIR="${GSTACK_HOME:-${CLAUDE_PLUGIN_DATA:-$HOME/.gstack}}", mirroring hook-extract.sh; do not port the fork's 12-line probe-and-eval of gstack-paths inside a hook that fires on every Edit. Update test/hook-scripts.test.ts:690-893 to pass {GSTACK_HOME, CLAUDE_PLUGIN_DATA} and add the missing regression (GSTACK_HOME dir with freeze-dir.txt, HOME elsewhere, must deny). Credit @NikhileshNanduri, close #1459/#1509. Effort: human 2-3 hours, CC+gstack 15 min. Refuter's strongest counter: only the fork's shape (subprocess in a hot hook) is wrong; the defect itself could not be refuted.

2. /ship hardcoded test lanes (s9-skills-ship-hardcoded-test-lanes), P1, val 8 / fit 10. ship/sections/tests.md.tmpl:9-22 is still a literal bash block that runs bin/test-lane 2>&1 and npm run test 2>&1 and warns about RAILS_ENV=test bin/rails db:migrate; Step 6 (:44-113) matches app/services/*_prompt_builder.rb, runs EVAL_JUDGE_TIER=full bin/test-lane --eval, and ships a Haiku/Sonnet/Opus price table; ship/SKILL.md.tmpl:381 hardcodes the tests/vitest label pair for the evidence check. This is a direct violation of upstream CLAUDE.md "Platform-agnostic design" and survived both port waves because v1.66.1 wrapped the hardcode in evidence labels instead of removing it. Fork refs: 8bc0a04f, d72133e1 (0aca1f77:skills/ship/references/sections/ship/tests.md); upstream issue #1069 and PR #1102 (@JiayuuWang) OPEN. Nothing from the fork transfers (its version is a 2.0 legacy overlay); this is upstream's own deletion-heavy rewrite: Step 5 consumes the command Step 4 ({{TEST_BOOTSTRAP}}) resolved from CLAUDE.md or asked and persisted, one evidence label per resolved command; Step 6 conditional on a CLAUDE.md-declared eval command; Step 16 evidence check becomes repeated --label/--expect-cmd pairs; same fix at land-and-deploy/sections/readiness-gate.md.tmpl:111-116. Regenerate the three ship goldens; extend test/ship-test-detection-markers.test.ts to reject bin/test-lane|RAILS_ENV|_prompt_builder|EVAL_JUDGE_TIER in rendered output. Effort: human ~2 days, CC+gstack 1-1.5 hours. Refuter's counter: TEST_BOOTSTRAP already says "use CLAUDE.md's command"; does not hold because the literal block still runs afterwards regardless.

3. Playwright Chromium failure becomes a named warning, not a mid-setup abort (gap3-setup-playwright-best-effort-not-fatal), P1, val 9 / fit 9. setup:3 set -e plus the bare bunx playwright install chromium at setup:806 (and the explicit exit 1 at :850) sit under "# 2." (setup:764), ahead of "# 4. Install for Claude" (:1657), migrations (:1943) and hooks (:1990). An offline, proxied, AppArmor-restricted or Bun-on-Windows box ends with zero skills and a re-run that dies at the same line, even though every later step is already best-effort (emoji font :853-864 is warn-only). Playwright already retries the download five times internally, so what reaches this path is real unavailability, exactly when the 35+ non-browser skills matter most. Issues #1900, #1901, #1902, #913, PR #2233 all OPEN on this behaviour. Fork ref: ce9f0f9a (David Miserak; reachable only from origin/backup/pre-isolated-wave-review-2026-07-14). Port: re-implement on upstream's block, not cherry-pick: wrap the install in if ! ( ... ) so set -e cannot abort, a _PW_FAIL_REASON accumulator (chromium-install | windows-no-node | windows-node-modules | post-install-launch), keep _PW_LOCK contention fatal (setup:815-819), keep _clear_playwright_quarantine and the EXIT-trap chaining, loud stderr naming the unavailable skills (/qa, /design-review, /browse, make-pdf, /pair-agent) repeated in the final summary, optionally honour GSTACK_SKIP_PLAYWRIGHT=1 (#913). Add the static test the TODOS.md:199-202 entry asks for. Credit @DavidMiserak, close #1900/#1901/#1902/#2233. Effort: human 2-3 hours, CC+gstack 15-20 min plus one free-suite run. Refuter's counter: the fork's message text is false in upstream ordering and its block-wide "no exit 1" test would flag the lock exit; both are port corrections, not reasons to keep the abort.

4. gstack-relink ownership guard (gap3-relink-ownership-guard-on-prefix-flip), P1 (refuter says P2), val 8 / fit 9. bin/gstack-relink:46-53 _cleanup_skill_entry does rm -f on any symlink and rm -rf on any dir with a symlinked SKILL.md with no readlink check, is called on every flat name in prefix mode (:93) and every gstack-* name in flat mode (:99), and setup runs relink as a self-healing step on every install (setup:1679-1685). Upstream already adopted the opposite policy at every sibling site (setup:1040-1057 readlink gate, bin/gstack-uninstall:204-230), so this is the one remaining unguarded deleter of a class fixed three times. Fork refs: 5ed18036 (smblight, upstream PR #2119 MERGEABLE), 4d5f0309. Port: re-implement, gating on the resolved target being inside $INSTALL_DIR or $RENDER_DIR (relink:43, the fork's */gstack/* pattern misses the render dir and lexically matches non-owned paths); port smblight's two regression tests plus a RENDER_DIR case; decide the flat-mode collision policy at relink:104 (loud skip rather than silent overwrite). Credit @smblight. Effort: human ~2 hours, CC+gstack 15-20 min. Refuter's counter: blast radius is narrower than the panel implied (only prefix-mode users with foreign symlinked skills sharing a name, and only the registration link is lost); accepted, hence P2, but the bug stands.

5. Merge two stale community PRs the fork carried: taste direction-aware confidence (s8-codediff-taste-direction-aware-confidence, val 6 / fit 10) and setup-deploy Render key bytes (s1-prewave-setup-deploy-render-key-bytes, val 6 / fit 10), both P1 (refuter: P2/P3 housekeeping). bin/gstack-taste-update:199-201 computes confidence = approved_count/(total+1) for both buckets, so a rejected preference is pinned at 0 forever; cmdShow's rejected sort at :243 is a no-op and the drift warning at :204 is unreachable; upstream's own test hand-seeds confidence 0.8 to dodge it (test/taste-engine.test.ts:269-270). Fork 944dcab2 is a strict subset of upstream PR #1777 (@jbetala7, MERGEABLE). setup-deploy/SKILL.md.tmpl:104 still says echo $RENDER_API_KEY | head -c 4; fork fc8dded2 is byte-identical to upstream PR #1096 (@JiayuuWang, MERGEABLE). Port: merge #1777 (rebase, line moved 196 to 201) and #1096 as-is, regen SKILL.md, close #1776/#1078. Skip the fork's executable snippet test and tree-wide regex (false positives on test/evidence.test.ts:117). Effort: human 1 hour total, CC+gstack 15 min. Refuter's counter: nothing here is from the fork and severity is overstated (Render keys carry a fixed prefix, unverified); true, but both fixes are correct and free.

6. make-pdf preview fires remote image GETs despite the offline promise (s8-codediff-make-pdf-preview-offline-gate), P1, val 6 / fit 10. make-pdf/src/orchestrator.ts:361-362 writes rendered.html verbatim to the preview path; opts.allowNetwork is read only on the generate path (:170); make-pdf/src/cli.ts:197 parses --allow-network into PreviewOptions where it is dead; render.ts:256-258 deliberately leaves remote <img src> for the inliner that preview skips by design (:331-345). So $P preview beacons once per hotlinked image with no flag passed, and shows images the PDF will not contain. Fork ref: 8fae162d (~30 lines, reuses IMG_TAG_RE/SRC_RE and buildBlockedRemotePlaceholder in diagram-prepass.ts, unit test included). Port: cherry-pick, keep upstream's browseClient.PAYLOAD_TMP_DIR path, export blockRemoteImages, gate in preview() unless allowNetwork === true, add an orchestrator-level assertion. No egress receipt needed: it removes a fetch. Effort: human 2 hours, CC+gstack 10 min. Refuter's counter: "the user's browser fetching the user's own images is not gstack egress" is answered by the documented promise and the dead flag.

7. browse click on a missing selector waits twice (s8-codediff-browse-click-missing-selector-no-second-wait), P1, val 7 / fit 9. browse/src/write-commands.ts:349-369: after .click({ timeout: 5000 }) throws, the catch calls locator.evaluate(el => el.tagName === 'OPTION') on the same locator; Playwright auto-waits, browse sets no default timeout (grep setDefaultTimeout in browse/src = 0), so the 30s library default collides with the CLI's 30s abort at browse/src/cli.ts:828 and the user sees "Command timed out" instead of the precise selector error at ~5s. Fork ref: a84a6e23 (~12 lines: optionLocator.count().catch(() => 0), evaluate only when count === 1, plus a regression test). Port: cherry-pick only that hunk and test; bump the test's own timeout to 10s; reproduce pre-fix timing once with Playwright installed for the CHANGELOG line. Effort: human 2 hours, CC+gstack 10 min. Refuter found no counter beyond CI timing tightness.

8. ios-qa proxy hangs forever on a suspended app (s8-codediff-iosqa-proxy-timeout-hardening), P1, val 7 / fit 9. ios-qa/daemon/src/proxy.ts:57-84 passes timeout: 30_000 to httpRequest but per Node docs the 'timeout' event only notifies; the request must be destroyed manually. No outer deadline exists (grep setTimeout|headersTimeout in ios-qa/daemon/src/index.ts = none), so shouldRefreshTunnel's 504 branch (index.ts:245-256) is unreachable and /ios-qa, /ios-fix wedge with no typed error. Upstream has zero behavioural tests of proxyToDevice. Fork ref: 2c487305. Port: adapt, do not cherry-pick: add the settled guard, req.on('timeout') to 504 upstream_timeout then req.destroy(), res.on('aborted') to 503, opts.timeoutMs; KEEP reject(err) for unmapped codes so index.ts:213-243's ECONNRESET/EPIPE to 503 device_disconnected re-bootstrap still fires; leave out the bundled x-gstack-expected-bundle-id header. Port the bounded-504 test. Effort: human ~2 hours, CC+gstack 15 min. Refuter's counter: a verbatim port would defeat upstream's recovery mapping; adaptation resolves it.

9. transcript_ingest_mode=off is a dead consent switch (s1-prewave-transcript-ingest-mode-off, clustered with residual (d) of s8-codediff-bin-lib-absorbed-batch), P1, val 7 / fit 9. bin/gstack-config:181 (transcript_ingest_mode) echo "off") is the only non-prose occurrence in the repo; bin/gstack-memory-ingest.ts:541-545 gates transcripts solely on --sources (default ALL_TYPES at :234) and bin/gstack-gbrain-sync.ts:1211-1265 never reads the key. Yet setup-gbrain/memory.md:138 advertises it as "Disable entirely" and setup-gbrain/sections/transcript-gate.md.tmpl:47-53 offers "E) Never ingest transcripts (off)" and then runs gbrain-sync --full unconditionally, so a user who declined gets every transcript ingested on every /sync-gbrain. Fork refs: 6249074d (PR #2232 slice, /tmp/ta-wt/memory-trust:bin/gstack-memory-ingest.ts:523-538), fork PR #7 commit 7260b8e2 (CLOSED; not in the local clone, verified via gh pr diff 7; exempts --probe). Port: natively, not cherry-pick: decide semantics (honour only an explicit off; either change the :181 default to empty and update test/gstack-config-defaults.test.ts:139, or read the raw key with a comment explaining why); pass memory-ingest's existing --sources minus transcript from gbrain-sync when off; exempt --probe; one stderr line when the skip fires; fix transcript-gate.md.tmpl:52 so option E does not run --full; port the "honors off mode before invoking gbrain" test. Effort: human ~1 day, CC+gstack 30-45 min. Refuter's counter: the fork regex-parses config.yaml directly, bypassing gstack-config's GSTACK_STATE_ROOT resolution, and PR #2232 is CONFLICTING; both are shape objections, and this slice is independently shippable because it only removes writes.

10. question-preference hook slug mismatch (s2-runtime-question-pref-hook-slug-mismatch), P1, val 8 / fit 8. hosts/claude/hooks/question-preference-hook.ts:300-306 slugFromCwd returns path.basename(cwd), fed at :395 to lookupPreference (:149-159) which reads projects/<slug>/question-preferences.json; the only writer, bin/gstack-question-preference:36-38, buckets by gstack-slug which is owner-repo for any repo with an origin remote (bin/gstack-slug:17-20). Additionally, no writer of global-question-preferences exists anywhere (only the hook reads it and the test seeds it), so the Layer 2 deny is effectively inert outside the no-.git test fixture. Fork ref: b6572ebb (mechanism is GStack-2 runtime/identity.js, not portable). Port: replace slugFromCwd's body with slugFromEnvironment(stateRoot(), stdin.cwd) from lib/bin-context.ts:150 (git-spawn-free on cache hits, writes the same slug-cache as gstack-slug), keep 'unknown' for missing cwd; add a fixture that pre-seeds the slug-cache and asserts a deny; measure first-miss cost inside the 3s hook budget; confirm the Windows lane. Effort: human ~4 hours, CC+gstack 20 min. Refuter's counter: Layer 1 (gstack-question-preference --check) already honours project prefs so users mostly see correct behaviour; does not excuse a documented enforcement layer that never fires.

11. Chromium dead on Ubuntu 24.04 default kernel (s8-codediff-browse-userns-sandbox-probe), P1, val 8 / fit 8. browse/src/browser-manager.ts:88-99 shouldEnableChromiumSandbox checks only win32, GSTACK_CHROMIUM_NO_SANDBOX, CI, CONTAINER and root; repo-wide grep for apparmor|userns hits only xprotect-heal.ts NEGATIVE_SIGNATURES; #2157 is OPEN and describes the Ubuntu 24.04 LTS default (apparmor_restrict_unprivileged_userns=1). Fork ref: 4a8833cc (sysctl probe, isNoUsableSandboxError, relaunch-once, warnSandboxUnavailableOnce, 8 unit tests). Port: take the relaunch-once fallback first, composed inside the doLaunch closure at all three sites (browser-manager.ts:528, :744, :1821) so launchWithXProtectHeal stays the outer layer; emit SANDBOX_UNAVAILABLE once and surface it in browse status; append the GSTACK_CHROMIUM_NO_SANDBOX=1 hint to the startup-error text; treat the sysctl probe as optional (it saves one failed launch but can downgrade where a setuid helper would have worked). Port the unit tests; extend the static tripwire to all three launch sites. Cite #2157 as closing; #2101 is not fixed by this. Effort: human 1 day incl. an Ubuntu 24.04 VM check, CC+gstack 30-45 min. Refuter's counters: the fork author asked to close #2157 as resolved by the env var; upstream deliberately chose opt-in (CHANGELOG.md:3960); the fork's warning goes to the daemon log. None outweighs a dead daemon on the current LTS default, and a relaunch on the literal zygote fatal has zero false positives.

12. Terminal-agent WS Origin pin is never armed (s1-prewave-terminal-ws-extension-id-pin), P1, val 6 / fit 9. browse/src/terminal-agent.ts:41 const EXTENSION_ID = process.env.BROWSE_EXTENSION_ID || '', :602 gated compare; browse/src/terminal-agent-control.ts:77-79 spawns the agent with only BROWSE_STATE_FILE/BROWSE_SERVER_PORT/BROWSE_OWNER_PID, so production never sets it, while the comment at :583-584 says the Origin gate is "required, not optional". Fork refs: 7b3f391b, d947d2e1 (PR #2226). Port ONLY the pin: split GSTACK_EXTENSION_ID into a small browse/src/extension-identity.ts (new file) re-exported from server.ts, make the /ws Origin compare unconditional (403), update the fake-origin test sites (terminal-agent-integration.test.ts:120-135) and add a foreign-ID case; before merge confirm GStack Browser.app / GBrowser's baked extension carries the same manifest key or gate the strict compare behind isCustomExtensionBakedChromium(); never copy the fork constant (hjcdll... is NOT upstream's dgbkdb..., server.ts:306). Effort: human 2 hours, CC+gstack 15 min. Refuter's counter: defense in depth only, since AUTH_TOKEN is already released only to the pinned origin via POST /extension-token (server.ts:1886-1897); accepted, which is why this is last, but the code comment and the code disagree and the fix is two lines.

13. Cluster: ten small bin/browse/template fixes (grep-verified at HEAD, panel TAKE P1, not adversarially refuted). Each is a few lines with the upstream defect re-confirmed by me at the cited line; none carries voice or design risk.

  • gh pr checks field names (gap2-skillwave-land-gh-checks-targeturl-bugfix, val 6 / fit 10): land-and-deploy/sections/first-run-validation.md.tmpl:112 --json name,targetUrl and land-and-deploy/SKILL.md.tmpl:154 --json name,state,status,conclusion; the panel verified live with gh 2.83.2 that both field sets are rejected ("Unknown JSON field"). Fix: name,link and name,state,bucket, regen. Fork ref: bbad1bab.
  • Codex/Factory /ship skips plan verification (gap5-hygiene-codex-ship-qa-only-path, val 6 / fit 9): scripts/resolvers/review.ts:1178 cat ${CLAUDE_SKILL_DIR}/../qa-only/SKILL.md; hosts/codex.ts has no qa-only pathRewrite (grep = 0). Fix: pathRewrite to $GSTACK_ROOT/../gstack-qa-only/SKILL.md for codex and factory, regen goldens, test. Upstream PR #1772 (@spacegeologist) OPEN since 2026-05-28.
  • Detached HEAD emits BRANCH=HEAD (s8-codediff-slug-detached-head-branch, val 6 / fit 10): bin/gstack-slug:274-276 passes git rev-parse --abbrev-ref HEAD through, so CI runs and bisects pool review/evidence logs under HEAD-reviews.jsonl. One-line map to unknown, plus a test. Fork ref: b6572ebb.
  • brain-cache hardcodes ~/.claude/skills/gstack/bin/gstack-config (s8-codediff-brain-cache-gstack-config-hardcoded-path, val 6 / fit 10): bin/gstack-brain-cache:614-616 silently falls back to defaults on every non-Claude host. Fix: join(import.meta.dir, 'gstack-config').
  • gstack-config ignores inline # comments (gap3-config-inline-comment-stripping, val 6 / fit 9): bin/gstack-config:321-329 read_config_value has no # strip, so update_check: false # temp fails to disable; browse/src/config.ts:208-229 (the "single source of truth" TS reader) does strip. Fix: one sed clause ordered before the key strip; two tests.
  • welcome.html loads Google Fonts and Fontshare (s8-codediff-browse-welcome-offline-fonts, val 6 / fit 9): browse/src/welcome.html:7-8; server auto-navigates the headed browser there on launch, unreceipted. Fix: system font stack (+9/-7) and a no-remote-asset tripwire. Fork ref: e1cd3096.
  • Dangling symlink passes validateOutputPath (s8-codediff-browse-path-security-dangling-symlink, val 6 / fit 9): browse/src/path-security.ts:43-54 shares one try for lstatSync and realpathSync with if (e.code !== 'ENOENT') throw e, so a dangling link under /tmp pointing outside falls through to the parent-dir check. Fork ref: f14445bb (restructure + test).
  • v1.27 artifacts rename uses bare repo names (gap3-migration-v127-owner-qualified-gh-rename-and-heal, val 8 / fit 8): gstack-upgrade/migrations/v1.27.0.0.sh:219-224 gh repo rename NEW --repo OLD with stderr discarded; gh requires OWNER/REPO so the GitHub-side rename fails every time. Take the owner-qualification hunk (fork 5c5b8c4e, Brandon Pugsley) and consider the heal migration (56425c9a); keep upstream's #1383 failure semantics.
  • GSTACK_HOME not honoured as state root in four bins (s8-codediff-gstack-home-state-dir-consistency, val 5 / fit 10): bin/gstack-update-check:25 and bin/gstack-telemetry-log:27 read only GSTACK_STATE_DIR; bin/gstack-detach writes ~/.gstack/locks and its test cleans the real home (test/gstack-detach.test.ts:141). Six one-line edits.
  • buildGbrainEnv passes a project's DATABASE_URL into a PGLite gbrain (s8-codediff-gbrain-exec-strip-caller-database-url, val 7 / fit 8): lib/gbrain-exec.ts:117-125 returns the caller env untouched when config has no database_url; issue #1917 OPEN. Adapt (strip only when config exists and lacks database_url; keep GSTACK_RESPECT_ENV_DATABASE_URL), flip the three pinning tests. Effort for the cluster: human ~2 days, CC+gstack ~1.5 hours across two or three bisected PRs.

3. Contested — take only if the maintainer agrees

  • Self-contained questions (s3-judgment-self-contained-questions). Refuted as a universal preamble rule (doubles output tokens at every gate; hardcodes host UI collapsing behaviour); the office-hours-specific failure in issue #879 is real. Tie-break: add one sentence at office-hours/sections/design-and-handoff.md.tmpl:168 printing the saved path plus the doc body as assistant text before the Approve/Revise gate (credit @RyanAlberts, close #879/#1116), and nothing universal?
  • /review small-diff coverage (s9-skills-review-small-diff-sweep-hole). Refuted as a Pass 3 sweep (upstream runs an adversarial subagent on every diff, review.ts:509); what remains is one inaccurate sentence at review/checklist.md:10 and no Access Control bullet in Pass 1 CRITICAL. Tie-break: is an Access Control bullet (missing auth middleware, IDOR, path traversal) worth its bytes in a forced-read file under the context-budget ratchet?
  • Reply in the user's language (s9-skills-match-user-language, #679). Refuted as an always-on Voice-section line (community-PR guardrail, chat-only scoping leaves reports English). Tie-break: an opt-in reply_language config key rendered into Writing Style, with the mixed-language tests time-attack asked for on #679?
  • AskUserQuestion compact payload (gap2-skillwave-askuserquestion-compact-payload-1241). Refuted: shrinking question to 80 chars strips what question-log-hook and question-preference-hook hash and classify (hosts/claude/hooks/question-log-hook.ts:288-313, question-preference-hook.ts:404-457) and re-legitimises prose-then-tool. Tie-break: reproduce #1208 on the VSCode host first; if real, decide payload-vs-prose as a deliberate AUQ resolver design with an A/B receipt.
  • Catalog trim on external hosts (gap3-catalog-trim-external-hosts, #1972). Refuted: on Codex the description is the only routing signal and the fork's own maintainer refused this on #1972 without routing evals. Tie-break: run a Codex routing eval; if it passes, merge #1972 with @hadrien-de-march credit.
  • $B record (s6-branches-browse-record-cdp-screencast, fork PR #54). Refuted as a drop-in: record in SCOPE_READ, spawnSync ffmpeg in the daemon request path, frames never cleaned, cap evaluated only on frame arrival, VP9 into .mp4, and #2497 is already queued at TODOS.md:43. Tie-break: CDP screencast (no context rebuild, works headed, no-ffmpeg fallback) versus #2497's recordVideo .webm; decide once at triage and land one.
  • Codex sandbox canary (s8-codediff-codex-sandbox-canary), panel TAKE_PARTIAL P1, val 8 / fit 7, xref PARTIAL, not among the 48 refuted. Fork ref 8abb08e5 (bin/gstack-codex-probe:110-142 in the fork tree). Upstream has a real round-trip model probe (bin/gstack-codex-probe:39-142) and typed CODEX_MODE degradation (scripts/resolvers/constants.ts:118-160) but no sandbox/userns signature (grep bwrap|bubblewrap|landlock|user namespace across bin/ lib/ scripts/ codex/ review/ ship/ = 0), so a Codex that exits empty because bwrap cannot create user namespaces reads as a clean pass in /review, /ship and /autoplan on devcontainers, Flatpak and hardened Ubuntu. Issue #1892 is OPEN. The appended refuter for s8-codediff-bin-lib-absorbed-batch directs this to be handled under #1892 on its own merits, not as a fork port. Tie-break: does Garry want a fail-closed sandbox_unavailable CODEX_MODE with the mandated one-liner "codex skipped: sandbox unavailable" plus the same signature in review.ts:383-385/548-550/689-691 and codex/SKILL.md.tmpl:310, or the GSTACK_CODEX_NO_SANDBOX opt-in that #1892 asks for, or both? Effort: human ~1 day, CC+gstack ~45 min (+15 min for the env gate).
  • Privacy page (s7-docs-method-privacy-data-flow-doc). Refuted as a fork port (Context.dev-flavoured, drifts). The real defect surfaced: gstack-egress grants enumerates four grants (bin/gstack-egress:155-190) while bin/gstack-config defines update_check, codex_reviews, pair_agent. Tie-break: fix the grants under-enumeration (upstream bug, no fork lineage) and stop there, or also write a short table in README's Privacy section?
  • Codex dispatch consent and scan-at-sink (s4-fleet-codex-consent-and-redact-at-sink). Refuted: Codex runs locally with -s read-only in the repo and reads any file, so scanning a materialised diff is scan-then-re-render, and codex_reviews is already the master switch. Tie-break: an egress receipt (fail-open) at each codex dispatch, since test/egress-receipt-wiring.test.ts:138-143 exempts skill prose, plus one disclosure line in the preflight?
  • Untrusted-data authority rule (gap6-s15-hostadv-untrusted-data-authority-rule). Refuted as a preamble generator (generate-evidence-directive.ts:3-12 records the decision not to import SHARED-JUDGMENT). Concrete gap: qa/qa-only use {{BROWSE_SETUP}} which does not carry UNTRUSTED_CONTENT_WARNING (scripts/resolvers/browse.ts:58-60). Tie-break: wire the existing warning into BROWSE_SETUP and take the two per-skill lines from PR #2186 (@danlayfield), nothing else?
  • Delete ~/.gstack/.auth.json writer (s1-prewave-auth-json-file-write-removal). Refuted for now: browser-manager.ts:638-640 says the component-baked GBrowser extension reads it, and the token is already on disk 0600 in the browse state file. Tie-break: does Garry know whether GBrowser's baked extension bootstraps via POST /extension-token? If yes, delete plus a migration.
  • Loosened inputs and stale strings (s1-prewave-review-loosened-inputs-stale-strings). Byte-for-byte upstream PR #2141 (@aversini, MERGEABLE), not a fork item. Tie-break: does Garry want two more anecdote-derived categories in a forced-read checklist? If yes, merge #2141 keeping the "Enum & Value Completeness" heading (test pin at test/skill-validation.test.ts:893-897).
  • Base detection for local-only repos (s9-skills-base-detection-local-only). Refuted: the fork ladder adds git remote set-head -a (network) and a merge-base guess, and upstream pins "fall back to main" (test/gen-skill-docs.test.ts:649-651). Two literal-main sites bypassing BASE_BRANCH_DETECT are upstream bugs: scripts/resolvers/utility.ts:111-112 and design.ts:83. Tie-break: fix those two now; revisit a "no remote" terminal state only on field reports.
  • "Pre-existing" requires evidence (s9-skills-pre-existing-requires-base-run). Refuted as an automatic base-branch run (deps, DB side effects, minutes per ship). Tie-break: rewrite T1 vocabulary to verified/unverified with base-branch CI status (gh run list --branch <base>) as default evidence and a failing-files-only worktree run as an opt-in option?
  • CLAUDE.md ## Review section (s9-skills-review-claudemd-review-section, #452). Refuted: the host already loads CLAUDE.md, and free-form suppression rules cannot be told from tightening. Tie-break: a docs/skills.md paragraph documenting the convention (credit @evb87-tech) and close #452?
  • /scrape evidence gate (gap2-skillwave-scrape-evidence-gate-1717). Refuted as a 40-line step (coached E2E, breaks skillify's final-attempt slice). Tie-break: two sentences in Step 4 ("if the intent names a filter, count or ordering, the rows must demonstrate it"), credit @orendi84, close #1717?
  • gen-skill-docs single-host failure exits 0 (s2-runtime-gen-skill-docs-single-host-fatal). Refuted as P1: setup:727-731 renders codex unconditionally under set -e, so flipping :1150 would let a Codex render error abort the Claude install. Tie-break: P3, only with a setup decision to warn-and-continue for unselected hosts and a throwing-template test.

4. Defer

  • Codex prompt over stdin (s7-docs-method-regression-pr2370-stdin-prompt-dispatch; also s4-fleet-codex-mktemp-portable's residual). Blocked on a real bug: bin/gstack-codex-probe:194 runs "$@" & in the no-gtimeout watchdog, and bash redirects an async command's stdin from /dev/null, so codex exec - < file would dispatch an empty prompt on stock macOS. Unblocks: change :194 to "$@" <&0 & with a PATH-masked test, then convert exactly review-mode.md.tmpl:87 and review.ts:373.
  • Time constraint as a ceiling (s3-judgment-time-constraint-caps-scale). Contradicts autoplan/SKILL.md.tmpl:150-159 "MUST NOT compress"; docs/designs/PACING_UPDATES_V0.md:97-110 already owns chain-scoped accounting. Unblocks: fold "a time box caps question rounds, never review completeness" into PACING V1.1.
  • Transcript source-id isolation and current-repo scoping (s1-prewave-transcript-source-id-isolation, s1-prewave-transcript-current-repo-scope; PR #2232 remainder, issue #2140). Fork passes --source-id to gbrain import with no support probe (gbrain docs spell it --source); two-pass split must be re-derived across resume, reconciliation (bin/gstack-memory-ingest.ts:1803-1812, :2316-2330) and BulkResult merging. Unblocks: the never-double-store review TODOS.md:3947 gates on, a flag probe in the #2662 pattern, and confirmation that gbrain can exclude a source from dream; sequence after the off fix.
  • Defensive framing for red-team and security specialists (gap2-skillwave-review-defensive-framing-shared-resolver-1921). No reproducible denial trace exists for those two prompts (time-attack's own #1921 review asked for one). Unblocks: an E2E trace; then frame only the red-team prompt via a shared constant.
  • Recording caps/fallbacks, QA recording prose, shutdown flush and tunnel-denial pins (s6-branches-record-caps-and-fallbacks, s6-branches-qa-recording-specialist-judgment, s6-branches-record-shutdown-flush, s6-branches-record-tunnel-denial-and-scope). Nothing to govern until a record primitive lands. The fourth item (panel TAKE_PARTIAL P1, val 4 / fit 9, fork ref 76c89d56) is two one-line negative pins in browse/test/dual-listener.test.ts:109-120 and browse/test/tunnel-gate-unit.test.ts:41-53 plus the scope decision; upstream's closed TUNNEL_COMMANDS allowlist (browse/src/server.ts:315-344) already denies any unlisted command, so the pins are documentation of a property, not a behaviour change, and its earlier refuter said exactly that. Unblocks: the #2497/#54 decision; then wall-clock cap, byte ceiling, zero-frame screenshot, .webm for VP9, the tunnel pins with the "a continuous capture is a larger exfil surface than a single screenshot" rationale, the scope classification (panel recommends SCOPE_WRITE over the fork's SCOPE_READ or #2497's SCOPE_CONTROL), and four neutral prose rules written against the real semantics.
  • Web-data task taxonomy (s6-branches-web-data-task-taxonomy-and-rankings). Points at providers upstream does not ship; the authenticated-to-own-browser rule already lives in scripts/resolvers/third-party-actions.ts:35-37. Unblocks: the Aside Phase 2 product call at TODOS.md:462-486; then two sentences in scrape's "does NOT do" list.
  • ios-qa local package wording (gap5-hygiene-ios-qa-local-package-wording-1735). Not a fork item; an upstream #1735 close-out: ios-qa/SKILL.md.tmpl:124/:255 and ios-clean/SKILL.md.tmpl:82's wrong .package(url:) line. Unblocks: whoever closes #1735; decide bearer persistence separately against #1796.
  • make-pdf entity-obfuscated javascript: href (gap3-makepdf-parser-backed-sanitizer-not-recommended, skeptic flip, never panel-judged). Skeptic reports make-pdf/src/render.ts:243-246 rewrites only a literal javascript: and fork PR #6 commit f1a92355 (CLOSED; not in the local clone, verified via gh pr diff 6) has a test for java&#x73;cript:. Exploitability is low (output is a PDF rendered by Chromium), so not a shortlist item. Unblocks: port that one test; if it fails, extend the existing decode step, not the parser-backed sanitizer.
  • ios-qa per-mutation bundle guard (s8-codediff-iosqa-active-bundle-assertion, skeptic flip). Needs StateServer.swift.template changes; decide with the ios-qa maintainers alongside #1796/#1975.
  • Panel DEFERs: parallel /batch across tab groups (s6-branches-browser-batch-multitab-plan; blocked on the global switchTab pin at browse/src/server.ts:1063-1072); fast-path gate E2E (s4-fleet-fast-path-gate-e2e; presupposes a trivial-change fast path upstream has not adopted); Windows console-flash residue (gap1-s10-browsewave-windows-console-flash-suite; #1784's claim that Bun.spawnSync ignores windowsHide is unverified, needs a Windows tester); iOS XCUITest evidence schema and flow runner (gap4-s13-ios-xcuitest-evidence-schema, gap4-s13-ios-xcuitest-flow-runner; contradict ios-qa/SKILL.md.tmpl:41 "No simulator, no XCTest", needs an architecture decision); /cso --fix (gap2-skillwave-cso-fix-mode-safe-autofixes-1053; inverts cso's report-only contract at cso/SKILL.md.tmpl:44, needs a safe redesign); Windows network lockdown CI (s6-branches-windows-network-lockdown-ci; no offline CI phase exists to use it).

5. Skip

Item Reason
Bound Playwright install + continue (s1-prewave-setup-install-deadline-and-continue) Duplicate of gap3 wearing a 300s deadline that would kill legitimate slow downloads; Playwright already retries 5x. Close #2233 once gap3 lands.
Setup probe deadline (s1-prewave-setup-probe-deadline) Headline absorbed (setup:417-466 _kill_tree/_wait_with_deadline, #2136 CLOSED); install bound better served by upstream PR #2359.
Official-API-first rule (s6-branches-web-data-official-api-first-rule) Truism already in the tier-3 reuse ladder; motivated by one planning anecdote; targets /scrape, which is one-shot by contract.
PR mutations via REST (s9-skills-pr-mutations-rest) Ship Step 19 rewrites the body with the #1079 REST fallback after document-release; #1079 CLOSED; standalone exposure is warn-and-continue.
Team hook as Node .cjs (s1-prewave-team-hook-cross-platform-cjs, #2229) Claude Code on Windows requires Git Bash; the real fix is the bash prefix convention (setup:2060-2071) on bin/gstack-team-init:183-189; fork re-introduces the #2500 false-block and a Node dependency.
Matcher Skill|skill (s1-prewave-team-hook-matcher-lowercase-skill) No-op on every supported host; Copilot CLI does not read .claude/settings.json.
Issue forms and labels (s7-docs-method-issue-templates-and-labels) Fork never implemented them either; upstream triage is wave-based, not label-based. Do fix the dangling "The evidence bar" link at .github/PULL_REQUEST_TEMPLATE.md:4 (grep 'evidence bar' CONTRIBUTING.md = 0); that fix is record s7-docs-method-contributing-evidence-bar-section, see section 5a.
Multi-OS static tree lane (s7-docs-method-multi-os-static-tree-gate) Static budget tests have zero platform dependence; a macOS lane is an upstream CI decision (mktemp/keychain files), not a fork import.
README update-check disclosure (s1-prewave-readme-update-check-disclosure) Fork line is now inaccurate (ls-remote plus raw fetch, two cache windows); rewrite natively from bin/gstack-update-check:157-243 if wanted.
Hardcoded origin/main residue (s4-fleet-hardcoded-origin-main-residue) Three descriptive prose sites with near-zero impact (review/checklist.md:5, review/greptile-triage.md:86, office-hours/SKILL.md.tmpl:87); fix as housekeeping when touching those files, do not adopt the fork's banned-regex tripwire.
/plan-pm-review (gap2-skillwave-plan-pm-review-1666) Explicitly declined by Garry on #1412 ("The user IS the PM").
Conductor implicit AUQ hook opt-in removal (s1-prewave-conductor-implicit-auq-hook-optin) Upstream deliberately narrowed rather than removed it (setup:2294-2307) and pins the contract in test/setup-plan-tune-hooks-noninteractive.test.ts:91-95.
QA evidence-per-finding layout (gap5-hygiene-1484-qa-evidence-per-finding) Panel SKIP: judgment already upstream; #1484 folder layout adds nothing.
patchright stealth backend (gap1-s10-browsewave-patchright-stealth-backend) Panel SKIP: ongoing per-Playwright-bump cost for a CDP leak upstream's Layer C already addresses.
BACKLOG-MAP generator (s7-docs-method-backlog-map-triage-generator) Panel SKIP: 810-line offline triage generator for a fork process.
browse-migrate legacy extension IDs (s1-prewave-browse-migrate-legacy-extension) Panel SKIP: path-derived legacy IDs are not an upstream state.
CLAUDE.md node -e snippet (s1-prewave-team-claude-md-node-snippet) Panel SKIP: same Node dependency argument as #2229.
/setup-search-mcp with Exa (gap2-skillwave-setup-search-mcp-exa-2168) Panel SKIP: adds a third-party MCP endpoint to Search Before Building.
GStack 2 judgment contracts: verification contract, total-verification ledger, execution depth profiles, build-scale classification, zero-question budget, proportional planning, trivial-change fast path, one-inspection-command-per-call, dispatcher precedence (s9-skills-, s3-judgment-) Depend on the six-dispatcher architecture and the fifteen-vector scale system; PACING_UPDATES_V0.md:97-110 already recorded what to fold in.
Fleet harness sub-ideas: tEnd bookend, rage taxonomy, abandonment contract, metric provenance (s4-fleet-*) Methodology for the persona-fleet harness TODOS.md already defers.
Docs/method artifacts: ADR series, audit-backlog ledger, bloat ledger, host compat evidence tiers, routing scenario fixtures, install-matrix artifacts, skills.sh distribution, devcontainer, upgrade/rollback doc, egress audit doc (s7-docs-method-*) Contributor-facing process artifacts in fork voice; upstream keeps the equivalents machine-readable (receipts, ratchet fixtures).
New skills: /yc-review, /diagnose, /fanout, /community-review, /land merge queue, /plan-status, /pr-prep audit (s6-branches-yc-review-skill, gap2-skillwave-*) Each adds catalog bytes under the enforced 1,150-token discovery ceiling for a niche. #1815 is Garry's own still-OPEN /land proposal (gh: OPEN, author garrytan) and should be decided there, not via the fork; #1949 (/fanout) is a CLOSED community PR by @sohmn.
Host adapters: Grok Build, Pi/Antigravity/Vibe/Qoder, Codex ~/.agents migration, request_user_input rewrite, native image_gen (gap3-hosts-*) Host-config refactor is blocked (docs/designs/SLATE_HOST.md); declarative configs alone ship untested surfaces.

Beyond the items above, 39 panel-P1 and roughly 130 P2/P3 take or take-partial items (170 P2/P3 in total: P2 126, P3 44 per summary.json) were neither adversarially refuted nor grep-verified for this report. The P2/P3 leftover (browse env knobs, retro/health additions, ios-qa docs, test-infra pins, Windows PowerShell bootstrap, statusline, learnings refine, design round artifacts) is individually small and can be picked up opportunistically from /tmp/ta-eval/residual-index.json. The 39 P1 items are listed next so they are not mistaken for P2/P3.

5a. Unrefuted panel-P1 take / take-partial items (39)

Scores are user_value / fit from residual-index.json. "Panel only" means no evidence beyond the panel record was gathered for this report; the panel was generous, so treat these verdicts as unproven until grep-verified or refuted. Two entries carry an earlier prior_refuter refuted=true and are marked.

Id Panel Val/Fit Xref Disposition
s8-codediff-browse-absorbed-batch TAKE_PARTIAL 7/9 PARTIAL Mis-titled batch; 12 of 13 absorbed. Panel only for the one residual.
s9-skills-ship-breaking-change-over-linecount TAKE_PARTIAL 7/9 PARTIAL Overlaps CLAUDE.md "Scale-aware bumps" prose; panel only.
s9-skills-eng-review-pre-mortem-and-data-model TAKE_PARTIAL 8/8 MISSING Adds a pre-mortem step to plan-eng-review; voice-bearing template change, needs Garry's read. Panel only.
gap3-codex-probe-custom-provider-env-key TAKE 7/9 MISSING Codex auth probe for config.toml env_key providers; panel only.
gap2-skillwave-office-hours-codebase-surface-premise-verify-1738 TAKE_PARTIAL 8/8 MISSING Verify codebase-fact premises before AUQ in office-hours; template change, panel only.
gap2-skillwave-retro-persist-recommendations-followthrough-1834 TAKE 8/8 MISSING /retro persists recommendations and scores follow-through; panel only.
gap2-skillwave-retro-language-agnostic-test-detection-2037-2013 TAKE 8/8 PARTIAL Python/Terraform/Bats test-file detection in retro metrics; consistent with platform-agnostic rule, panel only.
gap2-skillwave-cso-mini-shai-hulud-tier3-1523 TAKE 8/8 MISSING /cso Tier 3 known-campaign IOC rules; panel only.
gap6-s15-hostadv-fixture-set-and-schema TAKE 8/8 MISSING Injection-resistance fixture set; sibling of the persona-fleet harness TODOS.md defers, panel only.
s8-codediff-iosqa-drop-resolve6-unicast-dns TAKE 6/9 MISSING Drop dns.resolve6 fallback leaking device hostname; panel only.
s8-codediff-memory-ingest-default-secret-gate TAKE 7/8 MISSING Default-ON redact scan over staged ingest bytes; panel only.
s8-codediff-iosqa-devicectl-typed-errors-hwudid TAKE 7/8 MISSING devicectl typed errors + hardware-UDID match; same fork commit as shortlist item 8, panel only.
s8-codediff-codex-sandbox-canary TAKE_PARTIAL 8/7 PARTIAL Listed in section 3 (Contested); handle under OPEN issue #1892 on its own merits per the bin-lib refuter.
gap2-skillwave-reviewer-discipline-gate-884 TAKE_PARTIAL 7/8 MISSING Requested human review as merge gate (#884); panel only.
s2-runtime-workflow-hardening-tripwire-test TAKE 6/9 MISSING Free test for mutable action refs / permissions / concurrency; pairs with the sha-pins item below, panel only.
s4-fleet-spec-execute-host-neutral-spawn TAKE 6/9 MISSING /spec --execute spawns claude -p on every host; panel only.
s4-fleet-review-precision-answer-key-corpus TAKE_PARTIAL 7/8 PARTIAL Answer-key review corpus; TODOS.md already defers answer-key evals, panel only.
s7-docs-method-contributing-evidence-bar-section TAKE 6/9 PARTIAL The record behind the dangling PULL_REQUEST_TEMPLATE.md:4 link (verified: grep 'evidence bar' CONTRIBUTING.md = 0). Write the section in house voice or re-point the link; do not paste fork prose. Human 2-3 hours, CC+gstack 15 min.
s8-codediff-make-pdf-render-sentinel-invariant TAKE 6/9 PARTIAL Strip U+0000 at render entry, throw on surviving placeholder; panel only.
s9-skills-system-functional-qa TAKE 8/7 MISSING QA for APIs/CLIs/jobs/webhooks; new skill-sized surface under the catalog ceiling, needs a product call. Panel only.
s9-skills-debug-bisect-and-nonreproduction-taxonomy TAKE_PARTIAL 7/8 MISSING /investigate bounded bisect + non-reproduction classes; panel only.
s9-skills-review-finding-validation-gate TAKE_PARTIAL 7/8 PARTIAL VALIDATED/REJECTED/UNCERTAIN per finding before Fix-First; panel only.
gap1-s10-browsewave-chromium-path-setup-probe TAKE 6/9 MISSING setup launch probe honours GSTACK_CHROMIUM_PATH; natural rider on shortlist item 3, panel only.
gap1-s10-browsewave-cookie-import-cdp-pipe TAKE 7/8 MISSING Windows cookie import over --remote-debugging-pipe; panel only.
gap1-s10-browsewave-untrusted-content-hardening-prose TAKE_PARTIAL 7/8 MISSING Skill-prose hardening across investigate/browse/qa/canary/ship; overlaps the BROWSE_SETUP tie-break in section 3, panel only.
gap3-codex-jsonl-parser-bin TAKE 7/8 MISSING bin/gstack-codex-jsonl-parser replacing inline python in /codex (#1329); panel only.
gap2-skillwave-land-and-deploy-configurable-merge-method-726 TAKE 7/8 MISSING Configurable merge method via CLAUDE.md Deploy Configuration; fits utility.ts:65 idiom, panel only.
s9-skills-ask-only-uninferable TAKE_PARTIAL 7/7 MISSING SHARED-JUDGMENT clause 10; preamble growth under the ratchet, panel only.
s3-judgment-session-start-code-intelligence-offer TAKE_PARTIAL 6/8 PARTIAL Session-start indexing offer; panel only.
s7-docs-method-workflow-sha-pins-and-permissions TAKE 5/9 PARTIAL SHA-pin actions + top-level permissions; CI hygiene, panel only.
gap5-hygiene-chromium-path-status-correction TAKE 5/9 PARTIAL GSTACK_CHROMIUM_PATH for headless launch; panel only.
s9-skills-external-effects-no-blind-retry TAKE_PARTIAL 7/7 PARTIAL EXTERNAL-EFFECTS semantics; the PR-mutations sibling was refuted (section 5), panel only.
s1-prewave-uninstall-preserve-unowned-codex-dirs TAKE_PARTIAL 6/8 PARTIAL Prior refuter refuted=true: bug is real but is upstream's own TODOS.md:258-262 backlog item; do it there, not as a port.
s5-beta-human-era-anchors TAKE_PARTIAL 7/7 MISSING Retire file-count/human-day proxies; touches ship/review heuristics Garry wrote, panel only.
s9-skills-ship-smallest-release-convention TAKE_PARTIAL 7/7 MISSING Honour the repo's release convention at its smallest; panel only.
gap3-codex-global-discover-ssh-url-form TAKE 5/9 MISSING normalizeRemoteUrl ssh:// slash form; small, panel only.
s6-branches-record-tunnel-denial-and-scope TAKE_PARTIAL 4/9 MISSING Prior refuter refuted=true (already guaranteed by the closed TUNNEL_COMMANDS allowlist); folded into the record cluster in section 4.
s5-beta-hung-child-stall-guards TAKE_PARTIAL 5/8 PARTIAL E2E idle-stall/wall-clock guards; overlaps v1.77 test-infra wave, panel only.
gap2-skillwave-office-hours-artifact-outcome-1049 TAKE 6/7 MISSING Verify design doc exists before outcome:success (#1049); panel only.

6. Already absorbed / superseded / not applicable

From records-other.json (100 ABSORBED, 25 NOT_APPLICABLE, 3 SUPERSEDED), grouped:

Cluster Status Evidence
Apple App Store release journey (10 items: core roll-up plus adapter before branch gate, classify errors before credentials, browser credential ban + escalation ladder, deck editor, macOS CI runner split, pricing in authorization, session-minted upload key, sticky assets, two permitted interactions) ABSORBED ship/sections/apple-release.md.tmpl (header credits time-attack; wave 2, then hardened with allAppsVisible:false)
Judgment rules: design docs repo-local (#703), decision-record concision (#2000), evidence-before-claimed-limitations, third-party web actions contract, founder-resources opt-out (#538), prose fallback when no question tool ABSORBED office-hours/sections/design-and-handoff.md.tmpl:20-42, :343-366; scripts/resolvers/preamble/generate-evidence-directive.ts:15-17; scripts/resolvers/third-party-actions.ts:29-45; generate-ask-user-format.ts:13-37
Egress receipts, gstack-egress CLI, context-bill, discovery-surface ceiling, distill/eval model host-neutral ABSORBED (wave 1) bin/gstack-egress, lib/egress-receipt.ts, test/egress-receipt-wiring.test.ts; test/catalog-budget.test.ts; lib/eval-model.ts
Browse daemon: bun-polyfill spawn parity, disconnect-cause null browser, numbered-profile cookies (#2139), Playwright ^1.62 pin, Windows ACL SID (#2170), closeTab race, handoff SingletonLock, stop-ack before shutdown (#2020), live-but-busy daemon preservation (#2219/#2231), lock errno honesty (#1084), extension getPort withhold (#1822), CJK IME (#1272), WS subprotocol echo, /health token removal, stealth tests, telemetry config tier ABSORBED browse/src/browser-manager.ts:194,986-1020,1796-1798; cli.ts:608-641,684-745; meta-commands.ts:424-436; file-permissions.ts:45-87; server.ts:1870-1915; extension/sender-auth.js; browse/src/telemetry.ts:47-91
Setup/install: --help side-effect free, gstack-paths shell quoting, settings-hook fail-closed, mirror support files (#1502), absolute bin path (#1820), investigate hook paths (#1873), freeze symlink resolution (#781), careful compound-command guard (#2039), Codex/setup wave set, codex web-search flag ABSORBED setup:16-49, :871, :964-967; bin/gstack-paths:16-22; bin/gstack-settings-hook:70-223; freeze/bin/check-freeze.sh:100-116; careful/bin/check-careful.sh:186-189; scripts/resolvers/constants.ts:50-65
CI / supply chain: bun-version pin, dependency-review + osv-scanner, OSV config, fork-PR secret guards, gate secret scan, workflow concurrency, basic-ftp pin, diff ^9 bump ABSORBED 9 workflows pin bun 1.3.13; .github/workflows/{dependency-review,osv-scanner}.yml; .osv-scanner.toml; evals.yml:78-107; .github/scripts/gate-secret-scan.mjs; test/workflow-concurrency.test.ts; package.json:59
gbrain / memory: policy tier chokepoint, ingest staging, brain-context-load cold probe, brain-sync tilde paths (#1656), decision-log --supersede, setup-gbrain provider Step 0, codex-session-import xargs -r, deprecated brain-consumer scripts deleted ABSORBED bin/gstack-gbrain-sync.ts:45,839-880; bin/gstack-brain-context-load.ts:72-200; bin/gstack-decision-log:57,97; setup-gbrain/SKILL.md.tmpl:107-142; bin/gstack-codex-session-import:67-71
ios-qa: boot token out of os_log, loopback bind, hashed session tokens, touch selectors out of Release ABSORBED ios-qa/templates/StateServer.swift.template:100-105,154-167; ios-qa/daemon/src/index.ts:365-397; DebugBridgeTouch.m.template:23
make-pdf: invisible preamble, sibling browse before PATH, absorbed fix batch (render/smartypants) ABSORBED make-pdf/src/render.ts:69-70,213-265,487-515; browseClient.ts:10-20
Test infra: hermetic skills config dir, no-suicide-exit guard, handoff test isolation, sharded paid tier, CI gate binaries + poppler, codex E2E MCP isolation, multi-ecosystem ship test markers, Xvfb argv0 match, design timeout/count validation, benchmark timeout validation, catalog lead periods, artifacts-init push protocol (#2225), telemetry error mapping (#769), context-recovery slug (#1851), autoplan jq bind (#2021), land-and-deploy squash readback (#2011), one-way-door reset/revoke (#2024), document-generate (#1477), Opus 4.7 migration, codex openai.yaml, Windows spawn/DACL, question-format hook outputs ABSORBED test/helpers/hermetic-env.ts:278; test/no-suicide-exit.test.ts; scripts/test-paid-shards.ts; free-tests.yml:92-128; codex-session-runner.ts:211-214; scripts/resolvers/testing.ts:8-50; browse/src/xvfb.ts:144-170; bin/gstack-artifacts-init:67-82,218-256; scripts/one-way-doors.ts:66-72
Nine further ABSORBED items not named in a row above: s1-prewave-sidebar-chat-era-test-deletion (#1984), gap3-test-infra-wave-fully-absorbed (14 community PRs), gap2-skillwave-env-var-resolver-paths-1704, gap1-s10-browsewave-merged-pair-blocklist-and-brain-trust-ids (#2228), gap1-s10-browsewave-update-check-hardening (#1982), gap1-s10-browsewave-spawned-session-helper-and-telemetry-state (#1188), s6-branches-investigate-brokenness-v035, gap5-hygiene-bac5d9a5-unreachable-gate-tests-closed, s5-beta-body-floor-guard ABSORBED One-line evidence per id in records-other.json; with these the 100 ABSORBED reconcile against the rows above
GStack 2 runtime internals: execution-result envelope, release pipeline/attestation, shard heuristics, dispatcher headers and alias tables, runtime/cli main guard, deepeval/platform bakeoff, PostHog, demos fixture, Windows isolated gates harness, web-data egress rails, browser-provider eval, telemetry domain hashing, ios executor planner NOT_APPLICABLE No runtime/, evals/, demos/ or executor/ directories upstream; TODOS.md:460-505 and PACING_UPDATES_V0.md:97-110 record what was folded in instead
Deliberate upstream decisions: generic gpt-5.6 overlay (#2245; Sol is exact-only, scripts/models.ts:26-41), Slate first-class arm (docs/designs/SLATE_HOST.md), Hermes install arm/frontmatter (hosts/hermes.ts:32-37), careful text-segment filter (#1110, a destructive-command bypass), gstack-paths --get, extension storage.session auth (POST /extension-token chosen), Apple intermediate browser-drive states (s3-judgment-apple-intermediate-browser-drive-and-asc-cli), beta-first eval recalibration, slop-diff generated exclusion, windows-setup-e2e runtime lane NOT_APPLICABLE As cited per row in records-other.json
design daemon reset timers; Claude Desktop AUQ pre-empt (#2146); memory-ingest total-files tripwire (#2144) SUPERSEDED design/src/daemon.ts:206,489 (different shape); question-preference-hook.ts:21-23 never emits 'defer'; bin/gstack-memory-ingest.ts:2303-2346 reconciliation check

7. Open time-attack PRs on upstream

All eight upstream PRs are OPEN and CONFLICTING as of 2026-09-02 (gh); none can merge as-is.

PR Title Disposition Grounding
#2235 /bug-report skill Port-the-residual, then close Panel P2 val 7 / fit 5: a new tier-2 skill overlapping /investigate under the 1,150-token catalog cap. The independently useful piece is the redact-doc retain argument: scripts/resolvers/redact-doc.ts:85-88 deletes $REDACT_FILE right after the scan while the prose says "pass the SAME file downstream" (s1-prewave-bug-report-skill). Take retain, decline the skill.
#2233 Bound Playwright bootstrap, preserve skill registration Close-superseded Probe deadline absorbed (setup:417-466, #2136 CLOSED); install bound better served by PR #2359 (30 min, GSTACK_PLAYWRIGHT_INSTALL_TIMEOUT); the continue-after-failure half lands via the gap3 re-implementation crediting #1900. Update TODOS.md:3951-3953.
#2232 Transcript trust, scope, source isolation Port-the-residual Take the transcript_ingest_mode=off slice natively now (shortlist item 9); defer scope and --source-id behind the never-double-store review (TODOS.md:3947) and a gbrain flag probe. Close the PR after the off fix with a pointer to the deferred design.
#2231 Preserve live daemon during busy loads Close-superseded browse/src/cli.ts:684-686 daemonPidAlive + probeHealthWithBackoff, :733-745 "#2219 IRON RULE"; issue #2219 CLOSED 2026-08-17 (records-other: s1-prewave-live-daemon-preservation ABSORBED).
#2230 Test fixes for 182 failures unmasked by #2172 Close-superseded 5 of 6 fixes absorbed or superseded plus the bunfig PATH-restore preload (s1-prewave-hermetic-test-fixes). Optional 15-minute residual: one shared codex --version availability helper replacing seven which codex gates.
#2229 Team-init cross-platform hook + Copilot lowercase skill Close-not-wanted Fork re-introduces the #2500 false-block (checks only ~/.claude/skills/gstack), adds a Node dependency, Copilot does not read .claude/settings.json, README hunk is a voice rewrite. Native fix instead: bash "$CLAUDE_PROJECT_DIR/.claude/hooks/check-gstack.sh" on Windows at bin/gstack-team-init:183-189, or route through gstack-settings-hook.
#2226 Browse local auth leak + legacy profile migration Port-the-residual Take only the terminal-agent WS Origin pin (shortlist item 12). auth.json deletion gated on a GBrowser source check; bin/gstack-browse-migrate, bin/gstack-browse-repair and the whole-surface Host check (#1324) stay separate decisions. Never copy the fork's extension ID constant.
#2225 artifacts-init HTTPS push remotes Close-superseded bin/gstack-artifacts-init:11, :67-82, :218-256 --push-protocol incl. preserve and gh config get git_protocol; test/gstack-artifacts-init.test.ts:277-380 carries the same test titles.

Fork's own open PRs:

PR Title Disposition
time-attack #54 $B record (CDP screencast) + iOS session poller Do not merge as-is. Treat as the design alternative when triaging #2497 (TODOS.md:43): fix SCOPE_READ to a stricter scope, no spawnSync ffmpeg in the daemon, wall-clock cap, frame cleanup, .webm for VP9, tunnel-denial pins (s6-branches-record-tunnel-denial-and-scope), then land one implementation with @Topherhindman and @itstimwhite credit. The iOS poller (s6-branches-ios-record-session-poller) follows the same decision.
time-attack #40 Web-data provider contract (Firecrawl/Exa/Context.dev/Aside) Not wanted now (CONFLICTING on the fork too). Revisit only at the Aside Phase 2 product call (TODOS.md:462-486); upstream deliberately ships no off-machine scraping providers (s6-branches-web-data-provider-cli, s6-branches-web-data-task-taxonomy-and-rankings).
time-attack #55 AGENTS.md Cursor Cloud notes Do not put in AGENTS.md (upstream's is the generated catalog). The useful residual is routing raw git commit calls in test/diff-scope.test.ts:21-31, gstack-version-bump, team-mode, worktree and gstack-repo-mode tests through test/helpers/scratch-repo.ts (gpgsign/fsmonitor neutralised) plus one CONTRIBUTING.md:175-177 sandbox sentence (s7-docs-method-agents-md-cursor-cloud-gotcha).

8. Suggested port order

  1. Wave A: setup and hook correctness (three bisected commits, one PR). Playwright best-effort block (credit #1900), check-freeze STATE_DIR line plus test pins (credit #1509), gstack-relink ownership gate plus tests (credit #2119). Effort: human ~1.5 days, CC+gstack ~1 hour plus one free-suite run.
  2. Wave B: stale community PR merges and contributor-doc fixes. #1777 (taste confidence), #1096 (Render key), optionally #2141 if the checklist growth is accepted, the #1772-equivalent Codex/Factory qa-only pathRewrite with golden regen, and the CONTRIBUTING "The evidence bar" section or link re-point (s7-docs-method-contributing-evidence-bar-section). Effort: human ~half a day, CC+gstack ~45 min.
  3. Wave C: browse, make-pdf, ios small fixes. make-pdf preview offline gate, click no-second-wait, ios proxy timeout (adapted), terminal WS Origin pin, welcome.html system fonts, dangling-symlink validateOutputPath, Ubuntu userns relaunch-once (closes #2157). One commit each. Effort: human ~2.5 days incl. an Ubuntu 24.04 check, CC+gstack ~2 hours.
  4. Wave D: /ship platform-agnostic purge. tests.md.tmpl Steps 5/6 rewrite, ship/SKILL.md.tmpl:381 evidence pairs, readiness-gate default, gh pr checks field fix, goldens regen, marker test extension (credit #1102, close #1069). Effort: human ~2.5 days, CC+gstack ~2 hours.
  5. Wave E: consent and state hygiene. transcript_ingest_mode=off honoured plus transcript-gate prose; GSTACK_HOME in update-check/telemetry-log/detach; brain-cache config path; gstack-config inline comments; detached-HEAD slug; v1.27 migration owner qualification; gbrain-exec DATABASE_URL strip (adapted); gstack-egress grants under-enumeration (update_check, codex_reviews, pair_agent). Effort: human ~2.5 days, CC+gstack ~2 hours.
  6. Wave F: contested items, only after Garry's calls in section 3. Office-hours #879 one-sentence fix, Access Control bullet, opt-in reply_language, UNTRUSTED_CONTENT_WARNING into BROWSE_SETUP, egress receipt at codex dispatch, Codex sandbox canary under #1892, literal-main fixes at utility.ts:111-112 and design.ts:83, codex stdin dispatch after the gstack-codex-probe:194 wrapper fix. Effort: human ~3 days, CC+gstack ~2.5 hours, plus one paid AUQ/ship eval run for anything touching the preamble. The 39 unrefuted P1 items in section 5a are not scheduled; each needs the same grep-verify-or-refute pass the 48 received before it earns a wave.