Files
gstack/docs/troubleshooting.md
T
f67c478f05 v1.91.34.0 fix: /autoplan on current Claude Code and long sessions, quieter pre-push and sync guards, gstack-post, gstack-doctor (#3069)
* docs: Oct 7 follow-up wave plan (pre-review)

* docs: Oct 7 follow-up wave plan, reviewed through /autoplan

* docs: Oct 7 wave plan, record final gate decisions (UC1 accepted, UC2 rejected)

* docs: credit #1822 to @Mike-E-Log and #1743 to @habassa5 (v1.64.0.0)

The v1.64.0.0 CHANGELOG entry credited the extension token fix (#1822)
and the polyfill exited promise (#1743) to the wrong handle, and the
extension-sender-auth test header repeated the first slip. Commit history
is not rewritten.

Fixes #3054.

Co-authored-by: Mike Ilog <48659643+Mike-E-Log@users.noreply.github.com>
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

* fix(context-restore): offer newer checkpoints from nested repos and task worktrees

/context-restore could present an older checkpoint as the latest with no
error in two layouts:

1. A session started in a non-git parent saved from a nested repo, so the
   checkpoint went to the nested repo's bucket. Restore in the parent now
   lists checkpoints of other buckets whose project_root lies below the cwd
   (BELOW_CWD; the 200 newest are read, and from $HOME only each project's
   newest). /context-save also leaves a JSON pointer (checkpoint path, bucket,
   project_root) in the starting directory's bucket when $CLAUDE_PROJECT_DIR
   is set and files elsewhere; restore lists those as POINTER, in or out of git.
2. A plan checkpoint on the main tree's branch shadowed a newer checkpoint
   saved in a ticket worktree. When another branch holds a newer checkpoint
   for the same task (a worktree below the main tree, read from the new
   worktree: stamp; equal normalized titles; or a shared #123 / ABC-123
   token in titles or branch names), restore prints NEWER_TASK and the skill
   shows both and proposes the newer one. Generic titles (wip, checkpoint,
   save, notes, todo) never match on title alone, and the worktree rule
   applies only when restoring from the main tree, so sibling worktrees
   still never shadow each other.

The branch-first ordering itself is unchanged.

Refs #3065.

Co-authored-by: Thomas Gernhardt <t.gernhardt@softwareprojekt.net>
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

* fix(redact-prepush): list each MEDIUM finding with rule, file line and fix

The pre-push hook printed only a MEDIUM count, which nobody could review
without re-running the engine by hand. It now prints one line per finding:
rule id, file and file line, and a fix, never the matched value.

Line numbers come from the hunk headers (combined --cc headers included), so
a finding in a later hunk or past a scan-chunk boundary points at the real
file line. Findings from the per-commit catch-up path name the commit whose
file line it is, so content removed before the pushed tip is still locatable.

Plan items: CEO-7 (summary half), DX-9 (fix lines), ENG-8 (line mapping).

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

* fix(redact-prepush): stop flagging your own email and already-public authors (#3060)

The hook never passed the engine's selfEmail / repoPublicEmails, so every
push that added the pusher's address, or a commit author's, raised a MEDIUM
pii.email warning for an address the destination already shows.

The hook now reads `git config user.email` and one bounded `git log` of
author and committer addresses, raw and mailmapped (%ae %aE %ce %cE): the
history of every pushed tip (those commits become public with the push),
each remote tip being replaced, and, for a new branch or an unknown remote
tip pushed to a configured remote, that remote's own tracking refs. A URL or
unknown destination never borrows tracking refs, and another remote's
history never counts.

The read stops at 50,000 commits (the newest authors are used) and 5 seconds.
An error or the timeout passes no history and, when an email finding is
listed, prints one line saying suppression was limited for this push. The two
inputs fail independently. HIGH findings and every other rule are untouched.

Controls: a stranger's address, an address only another remote knows, a URL
push next to a matching remote, and a first push of a repo with no history
all still report.

Plan items: A3, CEO-7, CEO-23 (A3 half), DX-9 (partial history), ENG-8
(history scope, mailmap).

Co-authored-by: akmandhania <143462686+akmandhania@users.noreply.github.com>
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

* feat(redact-prepush): per-address allowlist for pii.email (gstack.redact.allowEmail)

`git config --add gstack.redact.allowEmail <address>` allows that address for
MEDIUM pii.email findings only, so a team alias or support address that has
to be in the repo stops warning without reaching for --no-verify. It never
affects HIGH findings or any other rule, and the pii.email fix line names it.

Controls: a different address still reports, and a HIGH secret next to an
allowed address still blocks.

Plan item: DX-17.

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

* docs(troubleshooting): pre-push pii.email findings, exemptions and allowlist

Plan item: DX-12 (pre-push touchpoint).

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

* test(redact-prepush): assemble fixture addresses at runtime

The email fixtures were literals, so this branch's own push listed 47
pii.email findings in its test file. Build them from a helper instead, the
way the neighbouring tests build credential and IP fixtures.

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

* fix(browse): offer the user's signed-in tab, name silent aside repl failures

Rules (rendered into every browsing skill, kept short):
- Rule 1: before the first openTab, offer the user's tabs on the target
  origin (title and origin only; the rest of listBrowserTabs() stays
  private) and attach only after the user confirms one.
- Rule 4: a second sign-in wall after a confirmed sign-in means the session
  is bound to the tab or URL; offer that tab instead of another sign-in.
- Rule 6 no longer suggests an early return; rule 7 says a fast [ok without
  GSTACK_STEP_OK is an abort.

Cookbook (browse, devex-review): a "Silent failures" list (evaluate returns
only serializable values, no top-level return, locator(sel).press(key), an
empty DOM read is about the selector until the screenshot and the request's
response say otherwise, read toggle state before clicking) and a signed-in
tab recipe that prints only target-origin tabs and never closes the user's tab.

Parity caps for design-consultation, land-and-deploy, qa and the browse
skeleton are raised to the measured sizes, with notes.

Refs #3063, #3064.

Co-authored-by: Thomas Gernhardt <t.gernhardt@softwareprojekt.net>
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

* feat(codex): run gstack-codex-probe as a command, one subcommand per probe

Generated skills now execute bin/gstack-codex-probe (select-model, check-auth,
show-sandbox, check-sandbox, probe-model, check-version, show-first-use-notice,
run-with-timeout, log-event, log-hang, help) and capture its KEY: value lines,
so no helper state crosses into the calling shell and the preflight works from
bash, zsh or sh. select-model keeps --model > GSTACK_CODEX_MODEL > config.toml
precedence; run-with-timeout supervises with the existing gtimeout/timeout/
watchdog chain and returns 124 on a deadline; an unknown subcommand exits 64.

Sourcing keeps working for skills rendered before the upgrade and prints one
deprecation line naming /gstack-upgrade; it and the zsh self-locate block are
removed in a release on or after 2026-10-21 (DX-18). Parity caps raised with
measured notes for the longer capture lines.

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

* feat(gstack-post): one argv-only, redaction-scanned write path for PR and issue text

bin/gstack-post posts a PR/MR or issue comment, a review-comment reply, a
PR title or body, a new PR/MR or a new issue from agent-written files. Every
value reaches gh/glab as its own argv element in --flag=<value> form or on
stdin, never through a shell string, and a target is a number or a URL of
this repository on the same host.

The helper owns the redaction scan: it reads each file once, scans those
exact bytes (visibility from config, then gh/glab; the pusher's own email
suppressed) and sends the same bytes. HIGH is refused with exit 1 and no
confirmation can post it. MEDIUM exits 2 with RULE:/LINE:/PART: lines and a
TOKEN: digest of the bytes, destination, operation and findings; only
--confirm <token> for those exact bytes posts, so any edit needs a new
confirmation. gh/glab failures exit 3, usage errors 64. It also carries the
projectCards REST fallback and the title read-back that /ship did by hand.

The logic lives in lib/gstack-post.ts behind an injectable runner, so its
tests run in the Windows free lane; the stub-gh executable test is POSIX.

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

* fix(autoplan): guard verifies dispatches on Claude Code 2.1.29x (schema strip, unflushed call, named causes)

Cause A: Claude Code journals the model's raw Agent input but hands
PreToolUse the schema-parsed input; with the fork-subagent gate on (the
interactive default in 2.1.292) run_in_background is dropped, so the journal
record never deep-equaled the payload. nativeToolInput now drops only that
documented strip for Agent. Agent input is bound on both paths: keys limited
to prompt, description, subagent_type (general-purpose) and
run_in_background; a model override or any other key is denied.

Cause B: when a ready journal lacks the current call, the guard evaluates
the payload placed after the last journaled record instead of polling for a
record that may arrive only with the tool result. A phase transition counts
a report only when a later journaled record follows it (and, on the journal
path, it is not in the current call's message). One batch rule covers both
paths; a background reviewer keeps the invocation armed until its
completion notice.

Every outcome now has a stable reason code, disposition and anchor
(autoplan/bin/guard-reasons.ts). UC1: Claude-Code-caused states
(unrecognized shape, journal lag, rewritten journal, oversized record,
unchecked-version schema strip) allow with a visible warning; integrity
failures stay denials. No denial advises a bare retry. Every guarded
decision appends one content-free line to analytics/autoplan-guard.jsonl,
trimmed to 1,000 lines past 256 KiB.

Co-authored-by: Jared <182125291+yolo-jared@users.noreply.github.com>
Co-authored-by: crblabs <293123359+crblabs@users.noreply.github.com>
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

* fix(autoplan): publish each phase report with a no-op call; foreground requests are conditional

Phase-close step 6 sends the report in a message whose only tool call is
the Bash no-op ': autoplan-published <phase>', and step 7 makes the next
guarded call in a later message, so the guard sees the report flushed
before the next phase entry regardless of Claude Code's journal timing.

Every site that asked for run_in_background: false now asks for it only
when the Agent schema offers it and says how to recover when a dispatch
runs in the background anyway (wait for its completion notice). Parity
caps rise by the measured bytes.

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

* feat(ship,spec,review): PR and issue text goes out only through gstack-post

/ship: Step 18 writes the title into an agent-written file. An existing PR's
title is piped from gh/glab through gstack-pr-title-rewrite.sh --stdin (new),
so neither the agent nor a shell string carries it; a new title is written
with the file tool and its version prefix checked in the file. Step 19
composes the body as before and publishes with gstack-post pr-create, or
pr-body then pr-title, which scan the exact bytes they send. The separate
title and body scans, the hand-rolled REST fallback and the title self-check
are gone (the helper does all three). Title preparation moved into the
pr-body section, so the skeleton shrinks.

/spec files its issue with gstack-post issue-create and drops the pre-issue
re-scan; Greptile replies use gstack-post reply and pr-comment.
scripts/resolvers/redact-doc.ts loses its PR and issue sinks, and a test
keeps any template from rendering one again.

Tests that pinned the old scan-then-gh wiring now pin the gstack-post wiring;
the publication tests run the real compose and publish blocks against a stub
gh with the real helper (HIGH in title or body refused, MEDIUM token flow).

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

* ci: pin Claude Code 2.1.292 in the CI image

2.1.292 is the npm latest users run. Its interactive default turns on the
fork-subagent gate, which strips run_in_background from the Agent schema
and backgrounds reviewers; CI on 2.1.284 never saw the /autoplan guard
break. The image tag is a content hash, so every eval workflow picks up
the new image without a workflow edit.

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

* fix(brain-sync): one drain lock for every writer to the artifacts repo

gstack-artifacts-init, gstack-brain-restore, gstack-gbrain-source-wireup
and the skill-start merge ran git in $GSTACK_HOME without the drain's lock,
so the drain could never tell an abandoned .git/index.lock from a live one.
They now take the same mkdir lock (bin/gstack-brain-lock.sh): the drain and
the skill-start merge skip when it is held, user-run writers wait for it
(GSTACK_BRAIN_LOCK_WAIT, default 120 s), and a child of the holder (the
drain's daily worktree advance, restore's wireup) inherits it by token.

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

* fix(brain-sync): hold back only the flagged file, keep coupled artifacts whole

One file with secret-shaped text used to block every push, silently. The
drain now attributes scanner hits to files in one pass over the staged diff
(split on its diff --git headers), holds back each flagged file with the
rest of its coupled group, unstages it, and requires the remaining staged
diff to re-scan clean; anything short of that falls back to a full unstage
and status blocked. Held files stay queued and are re-scanned every drain.

- Status file gains held_count, drainable, last_drain_at, last_push_at and
  held[] (path, scanner rule, dependents, both fixes); new status held.
- Coupled groups come from an explicit map (decisions.jsonl and
  decisions.active.json): a queued member stages the whole group, a group
  stages only when its snapshot matches its log in the index, a skipped
  member stops the whole group.
- --unskip-file undoes --skip-file and queues the path again.
- An index lock older than 10 minutes is removed under the drain lock; a
  younger one, a failed git add, a failed commit (hooks included) keep the
  queue and report status error. A verified empty staged diff is the only
  "nothing to commit". git add runs once, batched.
- The scanner reads bytes, so undecodable content no longer crashes it into
  a false clean; matched text never reaches the status file.
- An empty-queue run with unpushed sync commits stays push_failed.
- Privacy-held duplicate records collapse to one per path; --status counts
  a spool too deep for a shell glob.
- --attention prints skill start's fixed attention lines from the sync
  status table in the usage header.

Co-authored-by: v639dragoon <matthewsbrickley@gmail.com>
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

* feat(skill-start): ARTIFACTS_SYNC attention lines for a stuck sync

Skill start now prints one fixed attention line per sync-status-table row
that applies (held, blocked, push_failed, error, unknown, stale-push,
stale-drain), each naming gstack-brain-sync by absolute path. Values from
the status file are clamped (status code to the table, counts to digits,
timestamps to their shape), so the status message, file paths and scanner
output never reach the line. The preamble tells the model to surface the
line in one sentence and continue. Skill-start tests are generated from the
table in bin/gstack-brain-sync, and docs/gbrain-sync-errors.md carries the
same table.

Co-authored-by: v639dragoon <matthewsbrickley@gmail.com>
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

* fix(outside-review): severity words in label position count as findings

The Oct 7 /autoplan run's Codex CEO and DX reviews labeled seven and eight
findings High/Medium without [Pn] tags, and the classifier printed
VERDICT: clean / FINDINGS: none for both. lib/outside-review-result.ts now
reads severity words in label position (Severity: High, a leading High: /
High — / [High], **High**, a table cell): critical/high block like P0/P1,
medium/low are advisory. Words in prose (high-level, low-risk, no critical
findings) never count. A review or structured response with neither tags,
labels nor an explicit no-findings statement is unverified, never clean
(DX-11, ENG-10). Design-direction proposals move to a new proposal gate so
they keep needing only their Recommendation line, and review prompts ask
for a severity per finding. Fixtures reproduce the CEO/DX label shapes, the
eng [P1] output, a table and prose negative controls; the stored Codex panel
keeps its verdicts. docs/troubleshooting.md explains how the verdict is read.

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

* feat: gstack-doctor readiness report and setup --status Codex row (B5)

bin/gstack-doctor prints one row per check without starting a skill:
install root (the registry rows behind ./setup --status), state root, Bun
against the floor, hook parse check, Codex mode and self-locate via
gstack-codex-probe subcommands, the cached model probe with its age
(--live runs the paid probe-model), artifacts-sync health from the status
file with the status code clamped, the browse bundle, Claude Code version,
the largest session journal and the last five schema-1 guard reason codes.
Rows are ok, warn, not configured or fail; only fail exits non-zero; fix
lines name helpers by absolute install-root path.

./setup --status gains a read-only Codex row (bin/gstack-codex-status.sh,
shared with the doctor) and ends with the doctor's absolute path.

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

* docs: bug-report template asks for doctor output; README troubleshooting entry; doctor test in the Windows lane

The template gives the Claude Code doctor path and a lookup for other
hosts and names no bare gstack- command (asserted in
test/gstack-doctor.test.ts). The doctor test runs every script through
explicit bash argv, so it joins the curated Windows free subset.

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

* docs(troubleshooting): readiness check section points at gstack-doctor

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

* fix(question-tuning,plan-tune,gbrain): free text reaches helpers only through agent-written files

Question tuning no longer pipes the question summary through printf: a
registered id needs no summary, and for an unregistered id the agent writes
the summary to .gstack/tmp/qt.txt and passes --summary-file (new). The
helper resolves a relative path against the git top level, removes scratch
files under .gstack/tmp, and asks normally when the file is missing.

/plan-tune tune events carry the user's own words again: they go into a
file the agent writes and reach gstack-question-preference --write through
--free-text-file (new), still behind the injection filter and the
user-origin gate.

docs/gbrain-write-surfaces.md drops its heredoc templates for the
agent-written page file on stdin, matching the per-skill save block, and the
generic save block stops showing a quoted --content argument.

The tuning prose was tightened so every preamble stays within its size cap.

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

* test(evals): daily autoplan-schema-canary on the latest Claude Code

A periodic rule case installs the latest published Claude Code with
install scripts off, verifies registry signatures, and runs one headless
session with the fork-subagent gate on whose PreToolUse hook records an
Agent and a Read payload. Each payload must match its journal record under
the guard's own comparison, keep the journal's key set apart from the
documented run_in_background strip, and stay inside the Agent allowlist;
a background Agent's completion notice must keep the shape the guard reads.
evals-periodic.yml runs it daily on its own schedule and concurrency group
(read-only token); the weekly census also includes it. A free test proves
the comparison fails on an injected strip.

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

* fix(doctor): read the artifacts sync held count from held_count, the field the drain writes

* fix(artifacts sync): the init and restore pre-commit hooks catch Bearer/Basic/Token-prefixed JSON auth headers like the drain does

* feat(greptile-early): detect Greptile, ask once on public repos, open the early draft, wait for the review

bin/gstack-greptile-early backs /ship's early Greptile PR:
- detect: a root .greptile/ folder (which takes precedence), greptile.json,
  or a Greptile comment on the repo in the last 90 days; triggerOnDrafts from
  the effective root config; repo visibility; public-repo consent. It prints
  the signal, that /ship will push and open a draft now, the 10-minute wait
  cap and the absolute gstack-config command that turns it off.
- consent yes|no: the public-repo answer, remembered per repo in the
  project's state.
- open: opens the draft through gstack-post with a body saying /ship opened
  it early, and posts one @greptileai comment through gstack-post when the
  config does not set triggerOnDrafts: true. An open PR is left alone.
- wait: polls every 30 s for a completed Greptile check run, a Greptile
  review of the head commit or a summary naming it; each call returns within
  about 90 s and reports timeout 10 minutes after the PR opened.

gstack-config gains ship_greptile_early (default true, invalid values
rejected): on automatically whenever the repo uses Greptile, off per user.
The logic is in lib/greptile-early.ts behind an injectable runner, so its
fixture-repo tests run in the Windows lane; an end-to-end test drives the
real helper and gstack-post against a stub gh.

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

* feat(ship): early Greptile PR at Step 6.5, bounded wait at Step 10 (#3020)

When the repo uses Greptile, /ship pushes and opens a draft PR right after
its free tests pass (new lazy section greptile-early, Step 6.5), so Greptile
reviews while Steps 7-9 run. A public repo asks once, remembered per repo;
declining keeps today's order. Step 10 waits for that review with
gstack-greptile-early wait and takes the existing unavailable route on a
timeout, never a claim of zero comments. Step 18 replaces the provisional
title, Step 19 marks the draft ready unless a draft was requested, and a
/ship that stops after Step 6.5 leaves the draft open with one comment
saying why.

ship's union cap rises to 1.5275 with the measured note (B4 +836 bytes,
B2 +4,732 bytes); the step-numbering guard allows 6.5; ship goldens are
refreshed for B4 and B2.

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

* fix(autoplan): the publication no-op is 'true autoplan-published <phase>'

Measured on Claude Code 2.1.292 in a default-settings interactive session:
a bare ':' Bash command asks for approval ('This command requires
approval'), and after a background reviewer's completion notice starts a
new turn the skill's allowed tools no longer cover it, so the phase-close
no-op prompted the user. 'true' with arguments runs without approval and
prints nothing. The guard only needs a later journaled record, so the
command changes everywhere it is named.

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

* test(evals): live autoplan-guard-pty case on the pinned Claude Code

A gate-tier rule case (one trial, haiku) drives an interactive PTY
session with default settings through a project skill that carries
/autoplan's own hook block: init, Phase 1 entry, a CEO reviewer dispatch
that sends run_in_background, a prepared close packet, the report
published with the no-op, and the Phase 2 entry in a later message. It
reads outcomes from the guard decision log (three verified allows) and
fails if the no-op raises a permission card; other cards are approved and
printed.

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

* fix(autoplan): bounded, append-tolerant owned-journal read (A2)

The publication guard no longer reads the whole parent journal or refuses
it at 32 MiB. lib/claude-owned-journal.ts streams the bytes present at open
to the last complete line (pass one), keeps a content-free index per record
and decides ownership, ancestry and causal order on it with the same checks
and reason codes as before. Pass two reloads in full only what the
evaluation reads (autoplan/bin/guard-journal.ts): every literal init and its
result, the current call, and inside the invocation window (from the latest
human turn at or before the bound chain's first init) guarded and
close-packet Read results, structured Bash results, error results, assistant
text after the first close Read and active-plan mutations, plus the earlier
Read a dedup reply cites even before the window (ENG-4).

- lib/claude-journal-records.ts: per-record event extraction, record
  metadata and the causal-order checks, shared by readPlanCountTranscript
  and the bounded reader. The batch reader keeps its whole-file read and cap.
- Appends after open never fail a read; same device and inode and no
  shrink are required (identity). A second read in one hook invocation must
  hash bytes [0, first size) the same, or it is `rewritten` (UC1 unverified);
  the unrecognized-shape double read uses that prefix hash. `changing` is
  removed (REMOVED_CODES; its anchor stays).
- OWNED_RECORD_MAX_BYTES (32 MiB): only one record over it is too_large.
  OWNED_RETAINED_MAX_BYTES (32 MiB): over it is the new oversized_invocation
  fallback denial. GSTACK_TRANSCRIPT_TEST_MAX_BYTES and
  GSTACK_TRANSCRIPT_TEST_RETAINED_BYTES can only lower them.
- ENG-5: evaluation groups uses and results by tool id once (no per-tool
  rescans).

CEO-25 / ENG-5 spike (120 MiB synthetic, 31,242 records, 4 vCPU Linux,
Bun 1.4.2): a full streaming rescan (parse every line, small index, sha256)
took 254-326 ms at 147-161 MiB peak RSS; a pass-one index cache under the
state root (load cached index, verify a 64 KiB tail hash, parse the tail)
took 26-29 ms at 47 MiB. The rescan meets the 1 s budget, so the cache is
not adopted: it would add trusted persistent state and invalidation for no
budget need. A backward tail read cannot prove competing_root or cycle, so
it was not a candidate.

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

* test(autoplan): journal-read benchmark and its dispatch-only workflow (CEO-2, CEO-6)

scripts/measure-journal-read.ts builds two synthetic 120 MiB parent journals
around a real /autoplan invocation (mixed: ~28k records, most bytes inside
the invocation; large-invocation: ~89k records with a current invocation of
many small records), keeps appending while the guard reads, and measures one
hook call in a fresh process (wall ms, peak RSS). The workflow runs it on
ubuntu-latest and macos-latest on workflow_dispatch only.

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

* test(evals): autoplan-long-session, a resumed >100 MiB session enters Phase 1

CEO-15 with ENG-6's observer: a real session is compacted, its journal
padded past 100 MiB with owned records before the compact boundary, and
the resumed interactive session runs the scripted phase boundary; the
guard decision log must show a verified Phase 1 allow. A free test proves
that observer on a padded journal through the lower-only bound seams
(the PTY counting reader refuses the same journal), with a sidechain
control that must deny.

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

* v1.91.34.0: CHANGELOG, TODOS, version; harness decision for test-pr-profile; ship title-step test reads its new section

* docs(changelog): use an example.com address in the allowEmail example

* test: assemble GitHub-token fixtures at runtime in the new brain-sync and skill-start tests (CI credential scan)

* fix: integration repairs across the wave (Step 6.5 fence, printed helper paths on Windows, probe usage without a heredoc) and tests pinned to the moved title and gstack-post contracts

* test(doctor): the healthy fixture builds browse.exe on Windows, the name setup uses there

* test(shared-libs): recognize Claude Code 2.1.292's internal git probe prefix (format.pretty and per-hook disables), with its CI receipt

* fix(ship-measure): report lists v1 measurements as an older format instead of crashing

---------

Co-authored-by: Mike Ilog <48659643+Mike-E-Log@users.noreply.github.com>
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
Co-authored-by: Thomas Gernhardt <t.gernhardt@softwareprojekt.net>
Co-authored-by: akmandhania <143462686+akmandhania@users.noreply.github.com>
Co-authored-by: Jared <182125291+yolo-jared@users.noreply.github.com>
Co-authored-by: crblabs <293123359+crblabs@users.noreply.github.com>
Co-authored-by: v639dragoon <matthewsbrickley@gmail.com>
2026-10-07 23:29:05 +00:00

42 KiB

Troubleshooting gstack messages

Find the message you saw, then follow its fix. Every check gstack runs ends in one of three states:

  • ran: the check executed. It may still have found problems.
  • not run: gstack chose not to run it (you turned it off, or nothing applied).
  • unavailable: gstack tried and could not run it. This is missing coverage, never a pass.

A message that is not ran has this shape:

<check>: not run (<reason>). Fix: <command>
<check> unavailable: <reason> (<detail>). No review ran; this is missing coverage, not a pass. Fix: <command>
<check>: ran, verdict unverified (<reason>). Fix: read the output above

Search this page for the words after unavailable: or not run (. Each section has a stable link anchor; the reason codes and anchors come from lib/gate-outcomes.ts, and a free test fails if a code has no section here.

Check readiness before a skill runs

Run ~/.claude/skills/gstack/bin/gstack-doctor (other hosts: ./setup --status in your gstack checkout prints the doctor's absolute path). It prints one row each for the install, state root, Bun, hooks, Codex, the cached Codex model check, artifacts sync, the browse bundle, Claude Code, the largest session journal and recent /autoplan guard codes. Each row is ok, warn, not configured or fail with its fix; only fail exits non-zero. It makes no paid call: the Codex rows report the cached model check and its age, and --live runs that check once. Paste its output into bug reports.


Outside reviews (Codex and Claude Code)

Outside reviews send your diff, plan or question to a second AI provider. Their verdict comes from lib/outside-review-result.ts, which prints VERDICT: clean|findings|unverified|unavailable. A findings verdict with a P0 or P1 finding blocks exactly like a native P0/P1. unverified and unavailable are missing coverage: /ship and /review continue, show the gap in the readiness dashboard and the PR body, and never count it as a pass.

How the verdict is read from a review

  • Severity tags. [P0]-[P3] (or Codex's native P1: labels). P0 and P1 block; P2 and P3 are advisory.
  • Severity words in label position. Severity: High, Priority: low, a line that starts with High:, High —, [High] or **High** (after an optional heading, bullet or number), a bold **High** anywhere, or a table cell | high |. Critical and High block like P0 and P1; Medium and Low are advisory like P2 and P3. Words inside prose do not count: "high-level", "low-risk", "a medium-term follow-up" and "no critical findings" are not findings.
  • No findings. A review with no tag and no label is clean only when it says so explicitly (NO_FINDINGS, "no issues", "did not find any bugs"). Otherwise it is unverified (see below), never clean.
  • Design proposals (the design-direction voices in /design-consultation and /office-hours) are read with the proposal gate: a completed proposal needs only its Recommendation: ... because ... line.

Outside-review prompts ask the reviewer to label each finding Critical, High, Medium or Low, so most reviews land on findings or clean.

gstack: cannot load gstack-codex-probe / gstack: cannot locate <helper> (shell: ...) / CODEX_MODE: helper_unavailable

Meaning. Skills run gstack-codex-probe as a command, one subcommand per check, so the shell your agent uses does not matter. cannot load gstack-codex-probe and CODEX_MODE: helper_unavailable mean the probe file is missing or not executable in your install.

cannot locate <helper> (shell: ...) comes from a helper that is still loaded into your shell with source (gstack-egress-lib.sh, and the Codex probe when a skill rendered before the upgrade sources it). Such a helper finds its own directory from bash (BASH_SOURCE) or zsh (%x). In any other shell (dash, sh), or when the shell cannot say which file it is reading, it stops instead of guessing a path. The message names the shell it saw.

Fix. For cannot load, re-run ./setup from your gstack checkout (or /gstack-upgrade). For cannot locate, run the skill from bash or zsh (the macOS and Linux defaults), or tell the helper where gstack is installed:

export GSTACK_ROOT=~/.claude/skills/gstack   # your install dir; it holds bin/

If you see gstack: sourcing gstack-codex-probe is deprecated ..., a skill rendered before the upgrade is still sourcing the probe. It keeps working until a release on or after 2026-10-21; run /gstack-upgrade to re-render your skills now.

Expected result. ~/.claude/skills/gstack/bin/gstack-codex-probe select-model exec prints CODEX_SEL: <model> (and CODEX_MODEL: <model> (exec; source: ...) on stderr) from any shell, and preflights print a CODEX_MODE other than helper_unavailable. gstack-codex-probe help lists every subcommand.

Codex outside review unavailable: Codex's sandbox could not start here (...)

Meaning. Codex runs every command inside a Linux sandbox (bubblewrap). In many containers and devcontainers the kernel does not allow unprivileged user namespaces, so the sandbox cannot start and every command Codex tries fails. gstack reports this instead of trusting a review that read nothing. CODEX_MODE: sandbox_unavailable is the same condition found by the free preflight before any paid call.

What is kept. Nothing was sent for review, or the review was discarded. Your code and files are unchanged.

Fix. Enable unprivileged user namespaces for the container (for Docker, a seccomp profile that allows them), or, inside a container you trust, run Codex without its sandbox for this shell:

export GSTACK_CODEX_NO_SANDBOX=1

Expected result. With namespaces enabled, the next review prints OUTSIDE_STATUS: completed. With GSTACK_CODEX_NO_SANDBOX=1, every review prints WARNING: GSTACK_CODEX_NO_SANDBOX=1: ... because Codex can then read and write anything your user can. Only the exact value 1 works, and only from your shell environment.

... outside review unavailable: the reviewer could not run commands or read the diff (...)

Meaning. The reviewer tried to run commands but none succeeded, or its answer says it could not run commands or read the diff. A "no issues found" written after that is not a review.

What is kept. Nothing; the answer is shown above but not counted.

Fix. Read the reviewer's stderr above, repair what it names, and re-run.

Expected result. OUTSIDE_STATUS: completed with a VERDICT: line.

... outside review unavailable: the reviewer process failed (exit N: ...)

Meaning. The provider CLI exited non-zero. The first stderr line is in the parentheses.

What is kept. Partial output is shown above; it is not counted.

Fix. Repair the cause in the provider diagnosis (log in again, fix the model, check the network), then re-run.

Expected result. OUTSIDE_STATUS: completed.

... outside review unavailable: the reviewer hit its time limit and was stopped (exit 124)

Meaning. The provider did not finish within its deadline and was stopped.

What is kept. Partial output is shown above; it is not counted.

Fix. Re-run with a smaller scope, or check the provider's status and your network.

Expected result. OUTSIDE_STATUS: completed.

... outside review unavailable: the reviewer returned no response

Meaning. The provider exited successfully but returned nothing.

Fix. Read the stderr above, then re-run.

Expected result. OUTSIDE_STATUS: completed.

... outside review unavailable: the reviewer declined to review

Meaning. The provider answered with a refusal instead of a review.

Fix. Re-run; if it declines again, rely on the native review.

Expected result. OUTSIDE_STATUS: completed.

... outside review unavailable: the response lacks the markers this gate requires (...)

Meaning. The answer did not contain what the gate checks: a Recommendation: ... because ... line, a SCORE:/AMBIGUITIES: pair, or severity tags.

Fix. Re-run the review.

Expected result. OUTSIDE_STATUS: completed.

... outside review: ran, verdict unverified (...) / OUTSIDE_STATUS: unverified / GATE: UNVERIFIED

Meaning. The review completed but had no severity tag or label and gave no explicit no-findings conclusion, so no pass or fail can be read from it (how the verdict is read).

What is kept. The full answer is shown above.

Fix. Read the output above and decide. It is not a pass, and /ship and /review list it as missing coverage.

CODEX_MODE: model_unusable / MODEL_UNUSABLE

Meaning. Codex rejected the selected model: the account cannot use it (HTTP 400), the model is retired, or a custom provider's base_url is wrong (HTTP 404). The CODEX_MODEL: line names the model and where it came from.

Fix. Choose a model your account can use, or correct the provider:

export GSTACK_CODEX_MODEL=<supported-model>
# or edit model / base_url in ${CODEX_HOME:-~/.codex}/config.toml

Expected result. CODEX_MODE: ready.

CODEX_MODE: quota_exhausted / MODEL_QUOTA_EXHAUSTED

Meaning. Codex refused the call because the account behind it hit its usage limit (You've hit your usage limit, or insufficient_quota). The line under the marker is Codex's own message, with its reset time and where to buy more. The model choice is fine. gstack reports outside coverage as unavailable, never as a pass, and caches the result for 15 minutes, so the rest of the run (and other skills) make no Codex call. The HINT line says how many minutes remain.

Fix. Wait for the reset time in Codex's message, or add credits or a higher plan for that account. To use a different account, sign in again:

codex login

To re-check before gstack's 15-minute cache expires (for example, right after buying credits), skip the cached result for one check, or delete it:

export GSTACK_CODEX_PROBE_RETRY=1   # unset it again afterwards
# or
rm -f ~/.gstack/.codex-model-probe  # <state root>/.codex-model-probe

Expected result. After the reset (or after codex login, which changes the auth signature and re-probes at once), CODEX_MODE: ready.

CODEX_MODE: unverified (rate_limited) / MODEL_PROBE_RATE_LIMITED / unavailable: Codex rate-limited the review

Meaning. Codex answered HTTP 429 (too many requests), which usually clears within seconds. It is a different state from quota_exhausted and is never cached. At probe time the review still runs, and its own result decides. A 429 during the review itself means that review failed, so coverage is missing, never a pass.

Fix. Re-run the review in a minute. If 429s persist, check the rate limits for the account or API key on the provider's dashboard.

Expected result. CODEX_MODE: ready, and the review completes.

CODEX_MODE: unverified / MODEL_PROBE_INCONCLUSIVE

Meaning. The short model check timed out or hit a network error, so gstack could not confirm the model works. The review still runs and its own result is checked.

Fix. Nothing now. If the review then fails, its message says why.

CODEX_MODE: not_authed / AUTH_FAILED

Meaning. No Codex credentials were found: no CODEX_API_KEY, no OPENAI_API_KEY, no auth.json, and no set environment variable named by a custom provider's env_key in config.toml.

Fix.

codex login

or export the variable your provider's env_key names.

Expected result. CODEX_MODE: ready.

Codex review skipped (codex_reviews disabled) / CODEX_MODE: disabled

Meaning. You turned outside reviews off. This is not run, never a pass and never an outage.

Fix. To turn them back on:

gstack-config set codex_reviews enabled

NOTICE: gstack outside reviews send the review prompt and code to Codex (...) using ...

Meaning. Outside reviews are on by default. The first one on a machine says which provider receives your prompt and code and which login or key pays for it. It shows once (gstack records .codex-review-notice-shown in its state directory) and never blocks the review.

Fix. Nothing, if that is what you want. To stop sending code to Codex:

gstack-config set codex_reviews disabled

Expected result. Later reviews print Codex review skipped (codex_reviews disabled) or CODEX_MODE: disabled, and /ship and /review show the outside review as not run.


/ship, /review and /document-release

Shipped without a version change: no version source is configured (no VERSION file, no .gstack/version-path). ...

Meaning. /ship found no version file it owns: there is no VERSION, no .gstack/version-path, or release automation (release-please, Changesets, semantic-release, a workspace monorepo, a placeholder package.json version) owns the version. This is not run for the version bump.

What is kept. Everything else ships. No bump, no CHANGELOG version header, no vX.Y.Z PR title prefix.

Fix. To have /ship version releases, create VERSION, or pin the file:

echo package.json > .gstack/version-path && git add .gstack/version-path

Expected result. The next /ship bumps that file and writes a CHANGELOG header.

gstack-version-bump: classify: version source is broken: <path> ...

Meaning. The pinned version file does not exist, is empty, unreadable, or contains no parsable version. /ship stops instead of inventing 0.0.0.0.

What is kept. Nothing was written.

Fix. Repair that file, or correct --version-path / .gstack/version-path.

Expected result. gstack-version-bump classify prints the current version.

BASE_REFRESH: stale <rev> / Base coverage: stale at <rev>

Meaning. /review could not fetch the base branch (offline, no credentials, or a read-only .git such as Codex's sandbox), so it reviewed against your local origin/<base> at <rev>.

Fix. Run git fetch origin <base> outside the sandbox and rerun /review if the base has moved.

gstack-wtree: cannot create a temp index under <dir>; set TMPDIR to a writable directory / gstack-review-log: cannot create a private object directory under <dir>; no working-tree fingerprint. Set TMPDIR to a writable directory.

Meaning. /review fingerprints the working tree in a private temporary object directory (Codex's sandbox keeps .git read-only). The temp directory could not be created, so this review has no fingerprint and later steps cannot reuse its evidence.

Fix. export TMPDIR=<a writable directory>, then run /review again.

gstack-wtree: cannot stage the working tree: <git error>

Meaning. The fingerprint's objects could not be written. Standalone gstack-wtree writes into the repository's object store.

Fix. Run it through /review (gstack-review-log supplies a private object directory), or make .git/objects writable.

Plan completion audit: not run (no plan is bound to this branch and no docs/designs/ file matches). Fix: ...

Meaning. /ship and /review audit a PR against the plan bound to its branch (plan mode, /autoplan, or a Plan: <path> line in the PR body). With none bound and no matching docs/designs/ file, they no longer pick the newest unrelated plan; the audit is not run.

Fix. Add Plan: docs/designs/<file>.md to the PR body, or run /autoplan.

Expected result. The next /ship or /review prints the audit against that plan.

gstack-learnings-search: bun not found on PATH, so learnings could not be read. Fix: ...

Meaning. Learnings search (and gstack-timeline-read, "the timeline") needs Bun. It exits 127 instead of looking like "no learnings".

Fix. Install Bun from https://bun.sh, then cd <gstack checkout> && ./setup.

Expected result. Skills print your prior learnings again.

LEARNINGS: unavailable (<first stderr line>)

Meaning. A skill's learnings lookup failed (often Bun is missing). Skills used to show nothing here, which looked like "no learnings".

What is kept. Your learnings file is untouched; the skill continues without them.

Fix. Read the reason in parentheses. If it names Bun, install it and re-run ./setup.

gstack-learnings-log: could not create a temp file in <dir>, so the learning was not recorded. Fix: ...

Meaning. The learnings, question and preference logs write through a temp file in ${TMPDIR:-/tmp}. A sandbox that blocks it used to drop the entry silently.

Fix. export TMPDIR=<a writable directory> and log again.

CALIBRATION: not calibrated: no recorded signals

Meaning. Your logged answers matched no psychographic signal, so /plan-tune has nothing to calibrate from. It no longer says "calibrated".

Fix. Answer more registered questions, then gstack-developer-profile --derive.

gstack: earlier data for this project is in projects/<slug> (a worktree's old bucket). Merge it: ...

Meaning. Linked worktrees now share the main checkout's project identity. Learnings and checkpoints a worktree wrote under its own old bucket still exist.

Fix. Merge them (logs are de-duplicated, conflicts listed, nothing overwritten):

gstack-slug --adopt-legacy --from <slug>

Or stop the reminder: gstack-slug --adopt-legacy --dismiss <slug>.


Design skills

DESIGN_NOT_AVAILABLE: <path> --version exited <code> (or timed out after 10s, is not installed, no timeout/gtimeout/perl ...)

Meaning. Design skills now run "$D" --version with a 10-second deadline before claiming DESIGN_READY. Exit 137 means macOS killed the binary at launch (a bad code signature); setup prints the same condition as design unavailable: <bin> is killed at launch (exit 137) after re-signing.

What is kept. Nothing was generated or spent.

Fix. Re-run setup, which rebuilds and re-signs the binary:

cd <gstack checkout> && ./setup

If the message names a missing timeout tool, install coreutils (gtimeout) or perl.

Expected result. The next design skill prints DESIGN_READY.

GSTACK_DESIGN_IMAGE_MODEL="<value>" is not a gpt-image model name (expected something like gpt-image-2); fix it or unset it to use gpt-image-2

Meaning. The design binary refuses an image tool model override that is not a gpt-image-* model name, before sending any request.

Fix. export GSTACK_DESIGN_IMAGE_MODEL=gpt-image-2 (or another gpt-image model your key can use), or unset GSTACK_DESIGN_IMAGE_MODEL.

TASTE_PROFILE_UNAVAILABLE: could not resolve the project slug (gstack-slug failed). Fix: run ./setup.

Meaning. /design-consultation and /design-shotgun load your taste profile. This says it could not be found, instead of acting as if you had none.

Fix. cd <gstack checkout> && ./setup.

cannot save paid image to <path>: <code> (<cause>). Image bytes were received; saved a recovery copy to <tmp path>. Fix: ...

Meaning. The paid image generation succeeded but writing it to the output path failed (disk, permissions, or every name through -999 taken). gstack never buys the same image twice and never overwrites an existing image.

What is kept. The image, at the printed recovery path in your temp directory.

Fix. Follow the printed fix (free disk space, make the folder writable, or pick a new --output), then copy the recovery file to where you want it.


Codex and other env-var hosts

gstack: no install found (tried <path>). Fix: ./setup --host <host> from your gstack checkout; ./setup --status shows it.

Meaning. On Codex, Factory, OpenCode, Cursor, Copilot and Kiro every bash block finds gstack on its own: an exported GSTACK_ROOT (with bin/ and lib/), then a repo-local install, then the host's global install ($CODEX_HOME/skills/gstack on Codex). None existed.

Fix.

cd <gstack checkout> && ./setup --host codex   # or your host
./setup --status

Expected result. ./setup --status lists the install and says the router is a real file and its section links resolve.

Meaning. ./setup --status checks each registered install. Codex ignores a symlinked SKILL.md, and a broken section link means a carved skill cannot load its next step.

Fix. cd <source> && ./setup --host <host> (the command is printed).

saved your edited <path> to <backup> (setup rewrites gstack's SKILL.md copies on every run)

Meaning. Installed router and nested SKILL.md files are now real copies, rewritten by every setup. Your hand edit was saved first.

What is kept. Your edit, at <backup>. Move the customization into your own skill.

error: could not back up an edited SKILL.md copy under <root>; the previous runtime root is unchanged

Fix. Fix permissions on <state root>/backups, then ./setup --host <host>.

ERROR: <dir> is not a gstack checkout; nothing was changed. Re-run Step 2. / INSTALL_DIR is not set: re-run Step 2 ...

Meaning. /gstack-upgrade now checks, before any git command, that it is inside gstack's own checkout. A block ran without the path Step 2 printed, so it stopped instead of touching your project.

Fix. Run Step 2 again and prefix the block with INSTALL_DIR=<printed path>.

<host>/<skill>/SKILL.md is <bytes> bytes, over the 160,000-byte limit. Fix: carve sections with usesLazySections() for this skill.

Meaning. For contributors: hosts read at most 160,000 bytes of one SKILL.md. bun run gen:skill-docs fails in the repo and warns under --install-root.


Setup and auto-update

Meaning. Bun older than 1.3.3 silently ignores the build flags that stop gstack's compiled tools from reading a project's .env, so setup refuses it before writing anything. The same link appears on two warnings that do not stop setup: warning: gstack is tested on Bun 1.4.2 (CI pin); found <version> (1.3.3 up to 1.4.2 works but is untested) and warning: could not read the Bun version (...) (setup continued; check bun --version).

Fix.

bun upgrade
./setup

Expected result. bun --version prints 1.4.2 or newer and setup finishes with no Bun warning.

gstack auto-update: update held (bun-too-old: found Bun <version> at <path>; gstack <version> needs <floor> or newer); nothing was installed or changed. Fix now: ...

Meaning. Team-mode auto-update fetched a release whose setup needs a newer Bun than the one setup would run. It left your checkout and installed skills at the current revision and prints this line once per session start. (An older auto-updater running its first update cannot make this check.)

Fix. bun upgrade; the next session start resumes the update. To update now: bun upgrade && cd <gstack checkout> && git pull --ff-only && ./setup.

gstack auto-update: setup did not finish (<reason>); installed skills may be out of date. gstack retries automatically. Fix now: cd <dir> && ./setup

Meaning. The pull worked but setup (or migrations) failed. gstack no longer says "just upgraded". It retries after 1 hour, then 6, then 24, and prints this line once per session start. A (bun not found on PATH) reason means install Bun first.

What is kept. The previous install stays active.

Fix. Run the printed command.

gstack auto-update: git pull did not finish (exit N: ...); gstack is not updating. Fix now: ...

Fix. cd <gstack checkout> && git pull --ff-only && ./setup.

gstack setup: refusing to register hooks that do not parse (Claude Code would block tool calls with them): <file>:<line>: <error>

Meaning. Claude Code runs gstack's hook shims through /bin/sh, and a hook that does not parse exits 2, which blocks the tool call it guards in every session. Setup parse-checks every hook it registers (the shim, and the TypeScript it runs with its local imports). It registers the hooks that parse, skips the ones listed, finishes the rest of the install, and exits non-zero. Claude Code runs hooks straight from ~/.claude/skills/gstack, so a skipped hook that an earlier setup registered keeps running the broken file until it is fixed. This is a gstack bug, or a half-applied edit or merge in your checkout: report the printed <file>:<line>.

Fix.

git -C ~/.claude/skills/gstack status   # half-applied edits or merge conflicts?
git -C ~/.claude/skills/gstack checkout -- <file>   # or finish the merge
cd ~/.claude/skills/gstack && ./setup

Expected result. Setup finishes with exit 0 and no refusal line.

gstack auto-update: update held (hook-does-not-parse: <file>:<line>: <error>); nothing was installed or changed, and your current hooks keep running. ...

Meaning. Team-mode auto-update fetched a release with a hook that does not parse. It checked the incoming revision before moving your checkout, so your checkout, installed skills and registered hooks stay at the current revision. gstack checks again at the next update check and installs the first release whose hooks parse. This is a gstack bug: report the printed <file>:<line>.

Fix. Nothing to do locally. A manual git pull followed by ./setup cannot be checked before the pull; setup then refuses the broken hook (see the entry above).

CSO unavailable: its native helper was not built (windows-msvc-compile)

Meaning. Visual Studio's compiler was found but the /cso native helper did not compile. setup prints the first compiler error. It used to say "install Visual Studio".

Fix. Fix the printed compiler error, then re-run ./setup.

Docker found at <path>, but native Windows Docker transport is not supported yet; static assessment only. / docker.exe at <path> is outside the trusted install locations (...)

Meaning. On Windows, /cso looks for docker.exe only under the install folders Windows reports for Program Files, Program Files (x86) and the Windows directory, by its real path, with no symlink or junction on the way. A user-writable directory is untrusted, because the Docker child carries registry credentials; that refusal cannot be overridden.

Even a trusted docker.exe cannot run /cso's isolated containers yet: /cso admits only a local Unix Docker socket, and Docker Desktop on Windows speaks over a named pipe. /cso reports this and runs its static assessment only; no container or runtime check runs.

Fix. For runtime checks, run /cso from Linux or macOS (WSL2 counts as Linux) with a local Docker socket. On Windows, static assessment is the supported mode; if the refusal named a user directory, install Docker Desktop under Program Files.

removed the AskUserQuestion preference hook: it breaks Conductor's native AskUserQuestion (#2207). ...

Meaning. In Conductor, setup no longer installs the plan-tune question-preference hook and removes one it added before. The other plan-tune hooks stay.

Fix. To keep it anyway: gstack-config set plan_tune_hooks yes, then ./setup.

Error: browse_extension_id '<v>' is not a Chrome extension ID ... Existing value left unchanged.

Fix. Copy the 32-letter ID (letters a to p) from chrome://extensions, then gstack-config set browse_extension_id <id>.


Browser

[browse] this install's browse CLI (<root>, build <hash>) and the gstack checkout's server bundle (<checkout>, build <hash>) are from different builds, so the server was not started. ...

Meaning. On Windows a host runtime root (~/.codex/skills/gstack and the other env-var hosts) holds a copy of browse with no node_modules, so its CLI starts the server bundle in the gstack checkout recorded in .source-path. The checkout was rebuilt (or updated) without refreshing this runtime root, so the two builds differ and browse refuses rather than run a mismatched server.

Fix. Re-run setup from the checkout named in the message; it rebuilds and refreshes every runtime root:

cd <checkout> && ./setup

[browse] chain: no flow to run (stdin was empty) (or stdin is a terminal, stdin could not be read (EAGAIN))

Meaning. browse chain with no arguments runs the JSON flow piped to it. Nothing arrived, so it exits 1 before starting a browser. It used to exit 0 on Windows having run nothing.

Fix. Pipe the flow, or pass it as an argument:

echo '[["goto","https://example.com"],["text"]]' | browse chain
browse chain 'goto https://example.com | text'

Chromium at GSTACK_CHROMIUM_PATH=<path> failed to launch: ...

Meaning. Headless browse and make-pdf now use GSTACK_CHROMIUM_PATH (NixOS, macOS 13). That binary could not start.

Fix. Point GSTACK_CHROMIUM_PATH at a working Chromium, or unset GSTACK_CHROMIUM_PATH to use gstack's bundled one.

Headed Chromium profile <dir> is in use by Chromium PID <pid> (started by PID <ppid>: <name>). ...

Meaning. Headed browse keeps one profile per project (<project>/.gstack/chromium-profile). A live browser owns this one, and browse never kills it. ... is locked by PID <pid> on another host (<host>) means the lock came from another machine through a synced folder.

Fix. Close that browser, or export CHROMIUM_PROFILE=<another dir>. For the other-host case, stop it there, or remove <dir>/SingletonLock once you are sure nothing uses it.

[browse] Headed browse now keeps a profile per project ...

Meaning. First use copies your logins from ~/.gstack/chromium-profile when no browser is using it; otherwise it starts fresh and prints how to import. browse profiles lists them, browse profiles prune --days 30 removes idle ones.

Meaning. Browse refuses 169.254.0.0/16 (including container credential endpoints), 100.100.100.200, fe80::/10 and fc00::/7 in every numeric spelling, including after a redirect or a page-driven navigation. The tab is reset to about:blank and the command fails. This cannot be overridden.

forbidden origin from the sidebar terminal

Meaning. The terminal accepts only gstack's extension. A self-built or forked extension has a different ID. BROWSE_EXTENSION_ID is no longer read.

Fix. gstack-config set browse_extension_id <your 32-letter id>.

hint: "<word>" looks like a CSS selector

Meaning. Bare browse type <text> types into the focused element. The first word looked like a selector, so it was typed as text.

Fix. browse type --selector '<sel>' <text> (put text that starts with -- after --).

server-node.mjs not found. Run bun run build (Windows)

Fix. cd <gstack checkout> && bun run build.

[freeze] Hook helpers out of date (partial upgrade?)

Meaning. check-freeze.sh and hook-extract.sh come from different gstack versions.

Fix. cd <gstack checkout> && ./setup (or /unfreeze).

[browse] Could not record the Chromium PID (CDP SystemInfo.getProcessInfo: <error>); browse stop cannot reap a surviving Chromium.

Meaning. For a headless Chromium it launched, browse reads the browser's process id over CDP so browse stop can kill a browser that outlives the server. That read failed or took longer than 2 seconds, so this session's browser is not recorded. Browsers gstack did not launch are never recorded or killed.

Fix. After browse stop, check for a leftover browser with ps aux | grep -i chrom and end it with kill <pid>.


Memory and gbrain

Every message below is also in docs/gbrain-sync-errors.md with more detail, along with the per-page ingest messages (FAILED, re-queued, quarantined, reconcile progress, kept-on-this-machine notices).

database host unreachable (<code> <host>); your gbrain config is unchanged. Fix: check network or VPN, then re-run /sync-gbrain.

Meaning. A DNS or connection error reaching your gbrain database. It used to look like a broken config, and setup stripped brain features while offline.

Fix. Restore the network or VPN, then /sync-gbrain.

[memory-ingest] ERR: gbrain reported N failure(s) it did not attribute to a staged page ... Refusing to advance state.

What is kept. Nothing is marked saved, so nothing is lost. After three such runs gstack bisects the batch and quarantines the page that breaks it.

Fix. Re-run /sync-gbrain.

[memory-ingest] reconcile: not run (...)

Meaning. After upgrading, gstack re-checks transcripts it had marked saved and re-imports the ones missing from your brain. That check needs a local gbrain.

Fix. /setup-gbrain, then gstack-memory-ingest --reconcile (--dry-run to preview).

[memory-ingest] ERR: another memory ingest (pid <N>) is writing <state file>; not run.

Fix. Wait for it to finish, then re-run /sync-gbrain.

[memory-ingest] ERR: could not save ingest state <path>: <error>.

Fix. Fix permissions or free disk space under your gstack state root, then /sync-gbrain.

curated artifacts pushed to git, but gbrain source <id> has 0 indexed pages. Fix: gbrain sync --source <id>, then re-run /sync-gbrain

Fix. gbrain sync --source <id>, then /sync-gbrain.

dream: skipped — the installed gbrain cannot run only the resolve_symbol_edges phase, and the full dream cycle costs about 35 minutes ...

Meaning. /sync-gbrain --dream (and --full's auto-build) now runs only the call-graph phase. Your gbrain cannot scope it, so nothing ran.

Fix. gstack-gbrain-install, then /sync-gbrain --dream. Or run the full cycle yourself: gbrain dream --source <id>.

call graph for <source>: unknown: installed gbrain does not expose cycle_freshness

Fix. gstack-gbrain-install.

[gbrain-sync] gbrain source <id>: path unavailable (<path>); gstack skips it.

Meaning. A deleted worktree's code source. gstack never removes it on its own.

Fix. Preview, then remove the ones gstack can prove belonged to this machine:

gstack-gbrain-sync --prune-gone-worktrees --dry-run
gstack-gbrain-sync --prune-gone-worktrees

Eval reports and pass-rates

These come from the paid eval census reports and bun run eval:pass-rates. The walkthrough for a red census is docs/evals/census-red.md.

eval:pass-rates: unknown flag <flag> / eval:pass-rates: unknown case <id> (not an E2E, judge or paid test file id)

Meaning. The flag or case id is not recognized. The command exits 2 before fetching anything.

Fix. bun run eval:pass-rates --help lists the flags. --case takes a registry id from test/helpers/touchfiles-data.ts or a paid test file path.

unknown failure_cause "<x>" (written by a newer gstack; update this checkout to read it)

Meaning. A trial record names a failure cause this checkout does not know. A newer gstack wrote it.

Fix. git pull (or rebase your branch onto main), then rerun the command.

--case <id>: registered by <file>, <file>; it needs exactly one

Meaning. More than one paid test file registers the case, so test-paid-shards.ts --case cannot pick one file.

Fix. Run the red shard's file directly; the report's after a repair: line prints it: EVALS=1 EVALS_TIER=<tier> bun test <file>.

evidence: <artifact> too large to fetch (<N> MB)

Meaning. The slice artifact holding this red's transcript is over 64 MB, so eval:pass-rates --run does not download it.

Fix. Download it from the run page, or gh api repos/<owner>/<repo>/actions/artifacts/<artifact id>/zip > slice.zip.

[headroom] <case> session <key>: max <s> of <s> (<pct>) over <n> sample(s), above the 85% cap. Cut work in the skill or fixture; budgets are never raised ...

Meaning. eval:pass-rates --gate (the weekly report) found a case whose slowest session used more than 85% of the timeout it armed. It is one timeout away from a red.

Fix. Cut work in the skill or fixture. Budgets are never raised. Check the case with bun run eval:pass-rates --headroom --case <id>.

cost unknown (no billing captured for any of <N> trial(s)) / cost $<x> known + <N> of <M> trial(s) cost unknown

Meaning. PTY and Codex sessions record no billing, so their cost is unknown. Only the known sum is shown.

Fix. None needed; this is informational.

· cause provider_stall: no stream event for <N>s ...

Meaning. The session streamed partial messages, then received no event of any kind for at least 120 seconds while a model request was in flight and no tool, permission prompt, hook or subagent was outstanding. Under EVAL_POLICY v1 it is still a failed trial.

Fix. Inspect it with bun run eval:pass-rates --run <run id> --case <id>. A paid rerun happens only after a repair.

Overlay record with taskCorrect: false and answerError (contract v4)

Meaning. The trial completed with a wrong answer. Under overlay contract v4 that is a valid measurement in the comparison; only an overlay-ON wrong answer fails the case.

Fix. Read answerError in the trial JSON. No rerun is needed.

[detector-corpora] <N> stale entries (refresh from a current census when one fails)

Meaning. Some replay-corpus entries came from an older input series of their case. This is informational; the corpus still replays them.

Fix. When that case goes red in a census, add the new capture to its corpus and check it with bun test test/detector-corpus-<case>.test.ts.

recommendation substance median <x> < 4 over samples [<a>,<b>,<c>] (boilerplate/weak)

Meaning. The auq-matrix case scores each captured question's recommendation with a 3-sample judge panel; fewer than 2 of the 3 samples reached 4, so their median was below 4.

Fix. Read the logged samples and the captured question. A fix goes in the skill text, followed by one diagnostic run of auq-matrix.


Push guard (redaction)

db.url_with_password HIGH on a default postgres/postgres login whose host is a service name like @db (pre-push BLOCKED)

Meaning. The default postgres:postgres pair is exempt only on localhost or 127.0.0.1. A service name like @db says nothing about where the URL ends up deployed, so it still blocks.

Fix. Use postgres://postgres:postgres@localhost:5432/... in local and CI config, or read the URL from an env var. If the credential is real, rotate it. Bypass once: GSTACK_REDACT_PREPUSH=skip git push.

pii.ip_public MEDIUM on a four-part version number

Meaning. The line does not declare the value as a version.

Fix. Write it as version: 1.2.3.4, "version": "..." or v1.2.3.4. MEDIUM findings do not block pushes.

MEDIUM pii.email <file>:<line> from the pre-push hook

Meaning. The pushed lines add an email address that is not yours and not already in the destination's commit metadata. The hook does not report your own address (git config user.email), addresses listed in gstack.redact.allowEmail, or author and committer addresses (mailmapped too) from the history of the pushed commits, of the remote tip being replaced, and, for a new branch pushed to a configured remote, of that remote's tracking refs. An address only another remote knows still reports. If the hook also printed existing-email suppression was limited for this push, it could not read the history in 5 seconds, so known authors may be listed too.

Fix. Remove the address, or allow it for this repo: git config --add gstack.redact.allowEmail <address>. If it is your own address, check git config user.email. The allowlist only covers pii.email; it never lets a HIGH finding through.