* fix(pregate): resolve extensionless .jsx, .mts, .cts and index.jsx/tsx/mjs imports (#3340)
RESOLVE_EXT had no .jsx and no index.jsx/index.tsx/index.mjs, so on a JSX repo
every extensionless component import resolved nowhere and lanes failed with
'has no test lane'. Existing entries keep their order (it decides precedence).
Fixes#3340
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* v1.91.188.0 stamp
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
---------
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* fix(evals): plan-design focus rating in plain words, docsync own-call rule at the candidate --help, floor Create menu cropped at its rule (GSTA-65)
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* fix(evals): floor actor answers Step 0D's focus menu in the template's own words (GSTA-65)
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* fix(evals): floor actor identifies Step 0D's focus menu by its structure, not one wording (GSTA-65)
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* docs(changelog): GSTA-65 paid-side flaky repairs under [Unreleased]
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* fix(evals): the 0D focus picker moves into plan-design-finding-floor's owned fixture; no harness series reset (GSTA-65)
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* test(evals): the 0D picker rows join the floor case's owner test (test-of-test ratchet)
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* v1.91.185.0 fix(evals): plan-design focus rating in plain words, docsync own-call rule, floor Create crop and 0D menu (GSTA-65) (#3358)
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
---------
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* fix(evals): a rejected API key is INFRA, never 131 assertion reds and reruns
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* test: register the credential-rejection fixture as free
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* test: keep the credential-rejection CLI replay off the Windows subset, like paid-rerun
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* test: name the Windows run in the curation reason
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* v1.91.183.0 fix(evals): a rejected API key is INFRA, never 131 assertion reds and reruns (#3361)
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
---------
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* docs(cso): name the qualified runtime and scanner catalogs instead of 'static assessment only'
Ships with the runtime catalog promotion PR: AGENTS.md, README.md and
docs/cso-release-qualification.md said no qualified runtime or scanner
image existed. The scanner catalog is qualified since #3042; the runtime
catalog is qualified by the promotion PR this trains with.
* v1.91.180.0 docs(cso): say what the qualified runtime and scanner catalogs allow
* fix(qa): materialize names the next write; fixture states the write boundary (GSTA-63)
qa-functional-webhook-fix (gate 39646086155) and qa-functional-cli-fix
(gate 39166935531) each wrote a stray placeholder beside the fixture
right after a fail verdict that read "final for this report root".
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* v1.91.178.0 stamp
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
---------
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* ci(cso): give the promote candidate step GH_TOKEN, and fail any workflow step that runs gh without a token
Promote run 39691485727 failed after its cso-runtime-release approval:
"Generate a fail-closed catalog candidate" calls gh api .../compare with no
GH_TOKEN, and gh on a hosted runner exits 4. test/workflow-gh-token.test.ts
scans every workflow step that invokes gh for a token in step, job or
workflow env.
* changelog: promote workflow GH_TOKEN fix
* v1.91.177.0 stamp
* cso: ignore the compiled helpers' leading #! line in preparationSha256, on both sides of the release contract
S1 (#3312) rewrote only line 1 of lib/cso/verifier.ts and
lib/cso/preparation-container.ts. That shebang is an unused string in the
compiled helper binaries, but it moved preparationSha256, so qualify run
39535948607 refused the run-5 statements staged at 133cb36 with
STALE_RUNTIME_QUALIFICATION. Garry chose this narrow rule (option 2).
The release contract re-measures a recorded preparationSha256 that differs
from the checkout's at the staged commit (git history), accepts a record
that matches it under the current or the legacy rule, and compares and
records the current-rule value. Both release workflows fetch full history.
* test(cso): prove the release-contract fingerprint rule without git history
Free-suite checkouts are depth 1, so the tests no longer read 133cb36 or
6b2b5f3. They rebuild the staged commit in a scratch repository from this
tree with both helpers' S1 shebang reverted. Its legacy fingerprint equals
the real run-5 record (b069f7ef...), so it is 133cb36's image source byte
for byte, and the contract accepts that record under the current rule.
* v1.91.176.0 stamp
* fix(test-free): a flaky pass keeps the failing assertion in the epilogue and the flake ledger
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* v1.91.175.0 stamp
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
---------
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* fix(ios-qa): a restarted daemon resumes the persisted session bearer instead of relaunching the app
The boot token is one-use, so a new daemon process could only get back in by
relaunching the app with --terminate-existing, wiping its QA state (#1796).
The daemon now persists the rotated bearer to
$GSTACK_STATE_ROOT/ios-qa-session.json (0600, fresh temp file + rename),
bound to UDID, bundle ID and port, and resumes it once per process through
recoverTunnel's new pinned-address mode: devicectl-reported address only,
/healthz bundle check first. Mismatched binding, a foreign device or app, no
devicectl address, or a non-private file bootstraps without sending the
bearer. The file is cleared before every bootstrap, so a rejected bearer is
never replayed. /ios-qa Phase 0 now describes the real file.
Diagnosis and reuse scenarios from #1796 by @Bmathews721.
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* chore: take draft #3132 off the version slot (VERSION back to main's 1.91.84.0, CHANGELOG [Unreleased])
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* v1.91.171.0 stamp
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
---------
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* feat(evals): the gate reruns failed cases once and reports a flaky green (GSTA-49 F1)
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* ci: retrigger after retargeting to main
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* test(evals): a case red twice stays a real red for the report, the run, reuse and the train verdict (GSTA-49 F1)
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* fix(evals): keep a lone rerun-plan artifact off the plan manifest (run 39363705692)
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* chore: stamp v1.91.163.0
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
---------
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* fix(pregate): Dart import graph and test lanes (#3285)
Port of arlophoenix's #3285. The import graph reads Dart import/export/part
directives and resolves package: URIs through every tracked pubspec.yaml;
_test.dart files under test/ are tests. lanes runs each Dart package's
selected tests from its own root with flutter test or dart test (through fvm
when the package pins it). flutter/dart/melos/very_good CI jobs are lanes,
and run-step test paths resolve against working-directory and literal cd.
A missing Dart SDK or a .dart file outside any package leaves the lane
incomplete with a fix line; device tests (integration_test/, test_driver/)
are not unit lanes.
Co-authored-by: arlophoenix <arlo.phoenix@gmail.com>
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* fix(pregate): a --base that names a commit gets a branch fix, not an impossible fetch (#3269)
gstack-pregate --base <sha> refused with "fix: git fetch origin <sha>
--depth=1". That fetch can never create origin/<sha>: a short SHA fails
with "couldn't find remote ref", and a full SHA only reaches FETCH_HEAD.
A fork clone hits the same wall with --base upstream/main.
When origin/<base> does not resolve but <base> names a commit here and
no local branch (a SHA, a tag, another remote's ref), the refusal now
says --base takes a branch name on origin and suggests origin/HEAD's
branch. A local branch, or a name that resolves nowhere, keeps the
fetch fix: that is the unfetched or shallow checkout it repairs. Same
code, same exit 3, and pin files are still read only from the reviewed
origin/<base>.
Fixes#3269
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(pregate): Django, asset and tool-config lanes; init drafts from the repo (#3266, #3267, #3268)
- Django: settings modules, settings strings, include(), app migrations,
admin, templates, static files and project templates get framework edges
(lib/pregate/django.ts).
- Assets, stylesheets and dotfile tool config take a no-tests empty lane;
licence text under any name is documentation.
- gstack-pregate init drafts languages, runner notes and nearest-test
dependencies; never writes [].
- Review fixes on the #3285 port: untracked shell scopes leave a lane
unknown; Dart directives read to their semicolon; nested comments.
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* test(pregate): init draft API is exercised directly, so init-draft.ts has a lane
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* docs(changelog): restore the history a truncated rewrite dropped
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* fix(pregate): outside-voice review fixes for the non-JS lanes
- Tool config is an allowlist: unknown dotfiles (.rspec, .husky hooks) and
stylesheets still need a lane; snapshot, golden and fixture trees and
.gstack/ are never inert.
- Django edges need Django evidence (manage.py or code importing django)
and settings assignments in code, not strings or comments.
- A cd after ||, &, control flow or braces leaves a lane unknown.
- Dart directives allow metadata and quoted semicolons.
- init drafts at most 256 dependencies (the schema limit); the rest are TODO.
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* test(windows): pregate-dart and pregate-non-js join the POSIX-run pregate fixtures
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* chore: release v1.91.161.0
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
---------
Co-authored-by: arlophoenix <arlo.phoenix@gmail.com>
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
Co-authored-by: y$un_ <forrest.sun527@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* fix(scope,review,ship): recognise Dart and Flutter in diff scope and test detection (#3284)
Port of arlophoenix's #3284 onto current main, squashed:
- gstack-diff-scope: .dart is frontend and backend; pubspec.lock is config.
- lib/frontend-scope.ts mirrors the frontend half; gstack-design-detect
never sends .dart to the impeccable engine.
- Test bootstrap, coverage audits and review-army detect Dart/Flutter
(RUNTIME:dart, FRAMEWORK:flutter, dart_test.yaml, *_test.dart).
- Adversarial DIFF_SIZE sums git diff --numstat.
On top of the contributor's diff: shorten the bootstrap's ecosystem-marker
comment so /design-review stays at its SKILL.md size pin, lower the 31 pins
that shrank, and add the CHANGELOG entry.
Co-authored-by: arlophoenix <arlo.phoenix@gmail.com>
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* v1.91.160.0 fix(scope,review,ship): recognise Dart and Flutter in diff scope and test detection (#3300)
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
---------
Co-authored-by: arlophoenix <arlo.phoenix@gmail.com>
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* security: isolate every gstack Bun entry point from the audited checkout's .env and bunfig.toml
Bun auto-loads .env, .env.local and .env.<NODE_ENV> from its cwd and runs a
cwd bunfig.toml preload before the program starts. gstack runs inside the repo
it audits, so a committed .env could steer state roots, the claude binary or
spend caps, and a bunfig preload ran arbitrary code in every bin and hook.
Every bun shebang, shell and skill caller, TypeScript spawn and compiled build
now disables both (--no-env-file --config=/dev/null, bunIsolationArgs(),
--no-compile-autoload-dotenv/bunfig), with a ratchet and hostile-fixture
regressions.
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* test: name the directly executed scripts in the isolation ratchet
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* test: follow the isolation flags in touchfiles, copy lists, recipe assertions and the harness pin
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* fix: keep browse/src/bun-child-config.ts unchanged so the native cookie qualification still binds
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* fix(windows): the empty bunfig outlives its process so the timeline hook's detached repair child can read it
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* v1.91.159.0 security: gstack's Bun entry points ignore the audited repo's .env and bunfig.toml (S1)
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* test: plan-mode render pattern accepts the isolation flags
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
---------
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* feat(gen-skill-docs): add --plan-mode=off to omit the plan-mode preamble (#3168)
Generated skills ship a ~1.8 KB "## Plan Mode Safe Operations" +
"## Skill Invocation During Plan Mode" block on every skill, for hosts
and users who never enter plan mode. This adds a --plan-mode=off flag to
gen-skill-docs that drops that block at gen time. Default builds
(planMode undefined or true) keep the block byte-identically.
The "PLAN MODE EXCEPTION — ALWAYS RUN" markers inside skill bodies
still apply when the flag is off.
(cherry picked from commit 51edee9915)
* style(types): fix stray indentation on TemplateContext.installRoot
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* feat(config): plan_mode_preamble key wires --plan-mode=off into setup and every installed render
gstack-config plan_mode_preamble (on|off, default on) makes ./setup pass
--plan-mode=off to every gen:skill-docs render, gbrain-refresh and the
gstack-relink router render. The default Claude install, which otherwise
serves the committed SKILL.md files, gets a plain render with the key off;
the activation record notes plan_mode=off so switching back on removes the
render, including on the gbrain=unknown keep branch. With the key on or
unset every path and record is byte-for-byte unchanged.
Fixes#3168. Finishes #3213 by @xingzihai.
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* fix(setup,relink): plan_mode_preamble=off survives builds and failed renders; alias follows the router
Review findings on the plan_mode_preamble wiring:
- a build renders env hosts without --plan-mode=off, so with the key off
setup no longer skips their re-render after a build;
- with the key off the gbrain-absent branch leaves the render to the plain
re-render, so a failed render keeps the previous one;
- gstack-relink refreshes the _gstack-command alias after re-rendering the
router (also fixes the same staleness for disabled_skills).
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
---------
Co-authored-by: xingzihai <xingzihai@users.noreply.github.com>
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* perf(gen-skill-docs): stop emitting the "When to invoke" body section
The Claude catalog trim moved each skill's routing prose ("Use when...",
"Proactively invoke...", voice triggers) out of the frontmatter description
into a "## When to invoke this skill" section at the top of the body. Claude
Code reads only name + description to choose a skill and loads the body after
the skill is invoked, so that text cannot route anything and costs tokens on
every invocation. Keep the one-line trimmed description; drop the section.
The routing prose stays in each template's description, which
--catalog-mode=full and the other hosts still render. Tests that pinned the
section now assert its absence, or check the template instead.
(cherry picked from commit 707ccd6f38)
* feat(gen-skill-docs): keep voice triggers in the root routing block
The Claude catalog trim drops each skill's "Voice triggers (speech-to-text
aliases)" line from its description, and the body section that used to hold
it is gone. {{VOICE_TRIGGER_ROUTING}} renders them in the root router's
routing block instead: one "Voice trigger ... → invoke /<skill>" line per
skill, read from the templates' voice-triggers lists, so disabled-skill
filtering applies to them like any other routing line. Other hosts keep the
line in each description and render nothing here.
extractVoiceTriggers moves to the new resolver so the generator and the
resolver share one parser.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit 645e75b1e8)
* chore: regenerate SKILL.md files without the "When to invoke" section
Output of `bun run gen:skill-docs --host all` after the previous commits.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit ff4c813337)
* test: refresh context-budget ceilings for the smaller skill bodies
`bun test/helpers/capture-context-budget.ts`. Against a recapture of the base
commit, 55 skills that lost the section get a lower ceiling and the root
gstack router rises by 532 tokens for the voice-trigger routing lines (5,369
tokens lower in total). devex-review and eval-plan differ from the committed
fixture only because it already lagged main. alwaysOnTotal is unchanged: the
catalog descriptions did not change.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit d886fcecc1)
* test: refresh the claude ship golden without the "When to invoke" section
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit f1789dca5c)
* test: lower the claude design-review size pin
The size guard (#3199) asks for it: claude/design-review shrank by 437 chars
without its "When to invoke" section.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit 3a446fcc62)
* test: skip alias symlinks in the voice-routing check, keep unrelated budget ceilings, add #3129 CHANGELOG entry
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* test: rebuild the historical cookie approval with the dropped section, record the skill-fixture change as non-behavioral
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* v1.91.157.0 perf(gen-skill-docs): drop the "When to invoke" body section from Claude SKILL.md files (port of #3129)
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
---------
Co-authored-by: thanhnguyentung95 <17350411+thanhnguyentung95@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* fix(browse): /health status is daemon liveness; active tab reported apart (#3247)
/health answered `status` from a page.evaluate('1') race on the active
tab, so one page stuck in a long script made the whole daemon read as
unhealthy. The CLI gates every command on that answer, and its 2000 ms
fetch timeout equalled the server's 2000 ms race, so it could not even
read the reply: status, tabs, tab, newtab and stop all failed with
"Daemon busy" until --force-restart threw the session away.
- BrowserManager.isHealthy() is liveness only (Chromium connected).
- probeActiveTab() reports { id, responsive }, bounded by
TAB_PROBE_TIMEOUT_MS (1s); /health returns it as `activeTab`.
- `status` adds a line naming a not-responding active tab and the
commands that leave it.
- The CLI reads /health through one helper whose timeout,
HEALTH_REQUEST_TIMEOUT_MS (2s), stays above the probe.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wb6Wntntmwau19fSuSbuaL
(cherry picked from commit 322d255354)
* fix(browse): bound per-tab reads in `tabs` and `state save` (#3247)
A renderer stuck in a long script never answers page.title() or
page.evaluate(). `tabs` awaited each title in turn and `state save`
read each tab's storage unbounded, so both hung on one busy tab even
though neither needs it.
- getTabListWithTitles() reads titles in parallel, each bounded by
TAB_PROBE_TIMEOUT_MS; a tab that misses it is listed as
"(not responding)".
- saveState() skips the storage of a tab that does not answer within
5s, the same outcome as a tab whose read fails.
busy-active-tab.test.ts holds a real Chromium tab in a busy loop next
to an idle one and checks /health, tabs, status, saveState and
closetab against it. On main, /health takes 2004 ms and says
unhealthy, `tabs` hangs past the test timeout and `status` never
names the tab.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wb6Wntntmwau19fSuSbuaL
(cherry picked from commit d010d7b243)
* fix(browse): name the busy tab when a command times out (#3247)
A command that needs a stuck tab still waits out the CLI's 30s reply
timeout, and the report should say why. It never ran: Bun rejects an
AbortSignal.timeout() fetch with TimeoutError, the generic branch only
matched AbortError, so a timeout fell through to "[browse] The
operation timed out."
- isFetchTimeout() accepts both names; the cookie-import branch uses it
too.
- commandTimeoutReport() reads /health: when `activeTab.responsive` is
false it names the tab and points at `browse tab <id>` and
`browse closetab <id>`; a live daemon otherwise keeps the
busy-not-restarting line and a dead one the plain timeout line.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wb6Wntntmwau19fSuSbuaL
(cherry picked from commit ef70b7d880)
* docs(browse): /health liveness vs active-tab responsiveness (#3247)
BROWSER.md's busy-vs-dead lifecycle note, ARCHITECTURE.md's daemon
model and the REMOTE_BROWSER_ACCESS /health reference now say that
`status` is daemon liveness, that `activeTab` reports the active tab
separately, and which commands keep working while a tab is busy.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wb6Wntntmwau19fSuSbuaL
(cherry picked from commit 3dbb256796)
* fix(browse): one pending probe per stuck tab, not one per poll (#3247)
Review on #3250: the 1s race stops waiting but leaves page.evaluate()
queued in the stuck renderer, so every /health poll (the extension
polls every 10s), `status` and `tabs` call added another pending read,
all drained at once when the page recovered. Playwright cannot cancel
an in-flight evaluate, so the active-tab probe and the `tabs` title
read now join the page's pending read of the same kind instead of
starting a new one.
busy-active-tab.test.ts counts page.evaluate calls across three probes
of the stuck tab: 3 before this change, at most 1 after.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wb6Wntntmwau19fSuSbuaL
(cherry picked from commit 19fc9b9709)
* fix(browse): keep a busy tab's last good storage in saved state (#3247)
Review on #3250: with BROWSE_PERSIST_STATE=1 the periodic snapshot used
to hang on a stuck tab, so session-state.json kept its last good copy.
The 5s storage bound let the snapshot finish and write `storage: null`
for that tab over it, so a restart lost its localStorage and
sessionStorage even with persistence on.
saveState() now records the storage each page answers with, together
with its origin. A read that times out returns that last good storage
when the origin still matches, and null otherwise. A failed read stays
null, as before. The storage read also joins the page's pending read
instead of queueing a new one on every persist tick.
busy-active-tab.test.ts takes a good snapshot, hangs the tab, and checks
that persistSessionState() still writes its localStorage. Live, with
BROWSE_PERSIST_STATE=1 and a 2s interval, session-state.json kept
{"probe":"kept"} for the busy tab, and its savedAt stayed current
through 12s of busy page.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wb6Wntntmwau19fSuSbuaL
(cherry picked from commit 60cbaebee5)
* docs(changelog): busy-tab lockup fix from #3250 under [Unreleased]
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
---------
Co-authored-by: Andrei Vershinin <vershinin@intelas.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* fix(cso): withhold excluded credential files from retained history; name failed witness assertions ASSERTION_FAILED
* fix(cso): Rails verify replaces read-only copied config instead of writing into it
* fix(cso): security expectations may list several safe statuses while excluding the vulnerable marker
(cherry picked from commit aa9f45e0ece0e12152e7d02bf88e1559ee42b1eb)
* docs(changelog): safe status sets
* v1.91.154.0 stamp
* style(cso): format merged snapshot history capture
* fix(outside-review): no-findings counts only as the review's conclusion; add regressions/defects (#3294)
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* fix(outside-review): a trailing ', and' clause can conclude; elsewhere/otherwise are exceptions; real codex captures
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* fix(outside-review): whitelist the conclusion's qualifier; join wrapped Recommendation clauses; outside-review findings as controls
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* fix(outside-review): join a wrapped line only when it continues in lowercase
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
---------
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* fix(cso): record a nested repository as an explicit snapshot exclusion
An untracked nested repository makes gstack-cso start fail with
UNSAFE_PATH. Git lists it as one entry with a trailing slash, so
relativePath() rejected the empty segment and aborted the run. A
registered submodule gitlink already failed one step later for the same
reason: it is a directory, and the capture loop only reads regular
files.
Accept the boundary under its own name and record it like a host
dependency tree: one manifest entry, originalHash not-read, an
excluded: transformation that discloses the omission in the report's
snapshot-inputs coverage, no payload copied, and an identity check
during capture so a swapped or symlinked boundary fails closed. Nested
repository contents are never audited with the outer repository because
Git never descends into them.
* fix(cso): keep the newest whole commits when history exceeds the 1 MiB bound (#3255)
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* test(cso): regressions and negative controls for nested repos, long history and macOS sockets (#3255, #3248)
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* docs(changelog): blank line before 1.91.149.0
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* test(cso): set GSTACK_HOME in beforeAll, not at module scope
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* chore: stamp v1.91.152.0
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
---------
Co-authored-by: Chao Chen <209239467+surlymochan@users.noreply.github.com>
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* fix(plan-eng-review): register the structure question id so never-ask can fire
The complexity gate's Original/Smaller arrangement question had no
registered question_id, so each run invented a per-plan slug (one install
logged ten different ids for it). --check matches ids exactly, so a stored
never-ask could never fire and the question was asked every time.
- Register plan-eng-review-structure as a two-way routing question.
- The template names that exact id and says AUTO_DECIDE picks the
recommended arrangement; the preamble's check-first rule does the rest.
Wording is byte-neutral against the parity budget and the per-host pins.
- --check falls back to $GSTACK_HOME/global-question-preferences.json, the
file the question-preference hook already reads, when the project file
has no entry for the id, so one setting covers every repo and both
layers agree. A project entry still wins, the one-way and split-chain
guards run first, a malformed or non-object global file is ignored with
a NOTE, and --write stays per-project.
- Tests for the fallback and the template contract; two wording pins
updated.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit fb0334eb5c)
* fix(context): pin English provenance markers and ask when branch cannot discriminate
Addresses three gaps from #3217:
1. context-save: markers must stay the five English literals; translated
tokens break restore sorting. Decisions Made claims about file contents
need evidence or an assumed Remaining Work item.
2. context-restore: unknown/translated parenthesised markers sort under
Verify first as (path assumed).
3. When CURRENT_BRANCH cannot discriminate and recent candidates have
different titles, ask instead of silently loading newest.
Pins the new contract in the provenance prompt-byte test.
Fixes#3217
(cherry picked from commit 2a02647fd4)
* docs: add a neutral how-to for bringing up a first deploy (#3150)
/setup-deploy stops at "Nothing is deployed yet" and writes nothing,
because it has no production URL or deploy trigger to record. The docs
said nothing about the step in between. The maintainers chose to keep
that stop vendor-neutral and asked for a neutral docs page instead
(#3150, option 2).
docs/howto-bring-up-a-first-deploy.md walks through the first deploy:
where the app runs and how deploys start, the services it needs, env
and secrets, DNS and TLS, checking that it works, and running
/setup-deploy again. It names no hosting provider or deploy tool. The
README Docs table and the /setup-deploy section of docs/skills.md link
to it. The stop's own text is unchanged.
Fixes#3150
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit 5c5d8ec96f)
* docs(changelog): contributor fixes from #3249, #3244, #3264 under [Unreleased]
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
---------
Co-authored-by: Trevor Bezdek <bezdekt@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Chao Chen <209239467+surlymochan@users.noreply.github.com>
Co-authored-by: y$un_ <forrest.sun527@gmail.com>
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* feat(evals): Opus 5.5 is the paid gate's model under test; judge stays Fable (GSTA-49 TM1)
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* test: re-baseline old-model paid pins onto the current generation (TM3)
Repair the prosons capture prompt that tripped the reasoning_extraction
classifier, and restructure /setup-browser-cookies so the Aside probe,
decisions and target order are unambiguous.
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* docs: CHANGELOG entry for the TM3 re-baseline under [Unreleased]
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* chore: stamp v1.91.150.0
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
---------
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* feat(evals): Opus 5.5 is the paid gate's model under test; judge stays Fable (GSTA-49 TM1)
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* v1.91.140.0 chore: stamp TM1 (Opus 5.5 model under test) and CHANGELOG
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* feat(evals): Codex host adapter, real Codex cost, two-arm weekly and monthly census (GSTA-49 TM2)
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* chore: TM2 CHANGELOG under [Unreleased]; re-pin codex-host-runner in the harness manifest after the main merge
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* ci: retrigger PR workflows (base changed to main after open; no pull_request run fired)
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* v1.91.149.0 chore: stamp TM2 (Codex host, census arms) and CHANGELOG
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
---------
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* ci(seed): seed PRs also dispatch Quality gate, and say which runs are their checks
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* chore: stamp v1.91.148.0
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
---------
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* test(seed): refresh the free-suite duration seeds from the weekly recording
* test(seed): allowlist brain-sync and setup-codex-scope with the split todo; drop host-renders-prune
The 2026-10-19 weekly recording put test/brain-sync.test.ts at 89s (#3254's
case-collision suite) and test/setup-codex-scope.test.ts at 64s, over the 60s
ceiling; test/host-renders-prune.test.ts fell to 57s, so its entry is stale.
Both new entries carry the existing TODOS.md split todo.
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* test(seed): classify the seed allowlist as free-only so editing it selects no paid case
Only test/free-seed-ratchet.test.ts reads scripts/free-test-seed-allowlist.json,
but eval selection derived all 246 gate cases from it, so #3289's allowlist
repair paid for a full gate. The new free-fixtures case shows seed data and the
allowlist select nothing, with a negative control: design/src/evolve.ts still
selects its derived dependents. Non-behavioral HARNESS_VERSION decision.
Gate integrity: scripts/test-pr-profile.ts FREE_ONLY_PR_FILES gains one entry.
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* chore: stamp v1.91.147.0
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
---------
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: garrytan <garrytan@gmail.com>
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* fix(evals): qa-only opts in to the read-only grammar; stderr-to-null forms accepted
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* fix(evals): qa-only checkpoint rule counts guarded "$B" stop as browser cleanup, like closetab
PR #3272's Opus gate 39217387697 (slice 8) failed qa-only-no-fix when the agent
stopped the browser inside the guard; the rule counted stop as a third probe
that needed a checkpoint. The prompt now names both cleanup children. A free
replay of the captured three guarded calls passes (and fails on the old rule);
stop before a probe, a disguised probe, stop --all and stop outside the guard
still fail.
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* test(evals): register the qa-only stop-cleanup replay fixture in qa-only-no-fix touchfiles
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* fix(evals): docs-sync observer ignores an unexecuted candidate call that exited 127 at a missing path
PR #3295's gate (run 39223031058, slice 2) failed ship-docsync-current after Opus
called gstack-docs-candidate compare at a path missing the fixture-home segment,
got exit 127 (no such file or directory), and reran it at the declared path. The
observer now ignores such a call only when the absolute binary path does not
exist, the recorded result is exactly the shell's exit-127 not-found message for
that path, and a later call runs the same command at the declared path without
failing. A free replay of all 43 captured tool calls passes and fails on the
previous observer; negative controls cover an existing wrong-path binary, other
exits and messages, a missing/earlier/failing/changed retry, and non-candidate or
relative binaries. GStack Minder-approved.
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* v1.91.145.0 chore(release): stamp qa-only read-only opt-in, stderr forms, stop cleanup and docs-sync exit-127 exception
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
---------
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* feat(evals): Opus 5.5 is the paid gate's model under test; judge stays Fable (GSTA-49 TM1)
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* v1.91.140.0 chore: stamp TM1 (Opus 5.5 model under test) and CHANGELOG
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* fix(evals): repair the five Opus gate reds from run 39161502626
- coverage audit: owned cd; followed by one && chain, wc -l on in-cwd files
- shared-libs: Claude Code 2.1.292 git --version probe, exact argv only
- ship hook: accept 'integrate the hook yourself' / 'combine ... by hand'
- qa-only: deadline starts only after the charter Write completes
- plan floor: tool-only parent session identifies the owned Create grant;
floor runner fails fast with PERMISSION_DIALOG_UNANSWERED
Each repair replays its captured run as a free regression with negative
controls. HARNESS_VERSION stays 6 (re-recorded from main's 5).
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* fix(evals): ship hook manual-integration sentence rule; qa-only inspection uses Read/Glob; tool-only permission tests join their owner file
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* fix(evals): tpa-absent-linux accepts instructions the user follows themselves as the manual option
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* fix(evals): owned plan Edit menu binds to exactly one pending Edit, including unpublished ones
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* fix(evals): shared-libs read evidence counts a literal path loop whose output names each file
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* chore: restamp to v1.91.142.0 (queue slot moved)
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* fix(evals): investigate boundary fixture identifies the owner question by its continue/abort options
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* fix(evals): plan-design-with-ui focus check accepts the skill's design-completeness rating in the question body
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* fix(evals): phrasing sweep across the 108 gate rule cases; replay captured Opus paraphrases with negative controls
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* fix(evals): command-detector sweep — exact-count anchor, label slot, 2.1.292 worktree probe
- coverage audit (gate 39207508651 plan-eng-coverage-audit): output of exactly
known length (wc -l over literal in-cwd files) anchors the next owned read
at that line; a label's slot counts from the end and a later complete copy
voids the credit (review/specialists/testing.md prints '---')
- shared-libs-unsupported-git (39161502626): Claude Code 2.1.292's askPass-first
'worktree list --porcelain -z' cwd probe is Claude's own; the read-only Git
check is shared (repoGitRequests, isReadOnlyRepoGitRequest) and the free
regression replays every Git request of that capture
Each fix replays its captured command with negative controls. No threshold,
trial count, case kind or judge changed. HARNESS_VERSION stays 6.
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
---------
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>