Commit Graph
231 Commits
Author SHA1 Message Date
Garry Tanandcapy-ai[bot] 8018ba52fe v1.91.243.0 fix(capy): source the launch environment from a 0600 file; no skill fence may print the environment (GSTA-104) (#3449)
* fix(capy): the launch environment is sourced from a 0600 file and no skill fence may print the environment (GSTA-104)

During GSTA-96's unattended /ship on #3425, the Step 14.5 docs-audit child
re-typed the GSTACK_LAUNCH assignments as `export $(...)`; the substitution
came back empty, the bare `export` printed the whole environment, and three
API key values landed in its tool output.

- bin/gstack-capy-install writes the assignments to <verdict>.env (mode 0600,
  idempotent PATH prepend) and prints GSTACK_LAUNCH_FILE: <path>.
- ship Step 14.5 prompt, document-release's spawned note, autoplan/DRIVER.md
  and gstack-skill-start's capy-asks block say: source the file; never
  export $(...), env, printenv or bare set in a tool fence.
- test/launch-env-echo-ratchet.test.ts greps every generated SKILL.md,
  sections, DRIVER.md, the Capy skill and bin/ shell helpers, with negative
  controls under test/fixtures/launch-env-echo/.
- lib/redact-engine.ts: env.value (HIGH) finds the value of any *_KEY,
  *_TOKEN, *_SECRET, *_PASSWORD variable in the writer's environment, so a
  leaked value cannot reach a PR body, receipt, report or Drive file.
- scripts/lib/shard-engine.ts scrubs the Capy launch markers from shard
  environments (GSTA-99's finding).

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

* fix(ci): DRIVER.md back to 159 lines; shard-engine recorded non-behavioral at HARNESS_VERSION 6; verdict-count test lists .json only

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

* test: register test/fixtures/launch-env-echo as a free fixture

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

* test(redact): env-value fixtures stay out of pregate's env.kv net

A credential-named assignment with an entropic literal on one line is what
the repo's own pregate secrets scan refuses in added lines, so the fixture
values move into plainly named constants and the scanned text is built from
them. The scan under test still sees the same values through processEnv.

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

* test(windows): the late-completion remote-caller case gives its second attempt a 2s budget (run 40263984171)

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

* fix(redact): env.value trims the value, keeps a prefix value's longer match, hides the preview and skips PUBLIC names

/ship Step 9.4 review fixes for the GSTA-104 env-value scan:

- a value whose environment copy ends in a newline (a key read from a file)
  is searched for trimmed, so its leaked form still matches (the length
  check already trimmed; the search used the raw value);
- when one credential value is a prefix of another, the dedup key carries the
  span length, so the longer value is redacted whole in either order instead
  of leaving its tail in the text and dropping its finding;
- the env.value preview is a fixed mask instead of the first characters of a
  live credential;
- framework-public names (NEXT_PUBLIC_*_KEY, *_PUBLIC_KEY) are not scanned,
  since those values are committed on purpose;
- the header and the processEnv docstring state the name rule (KEY, TOKEN,
  SECRET, PASSWORD, PASSWD, any case) and that process.env is the engine's
  one ambient input.

Step 7 coverage extensions and a regression test for each fix in
test/redact-env-value.test.ts; docs/capy-install.md names the full rule.

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

* refactor(capy-install): write_launch_file quotes through shq; header names the launch file

Review finding: _sq duplicated shq() from the same file. The launch file's
assignments now go through shq, and the header comment describes the
GSTACK_LAUNCH_PATH and GSTACK_LAUNCH_FILE lines beside GSTACK_LAUNCH.

Step 7 coverage: the verdict test sources the launch file and runs --check;
the single-quote path test sources the file too; a new case makes the
launch-file directory unwritable and checks the run says so, prints no
GSTACK_LAUNCH_FILE line, leaves no temp file and keeps its exit code and
trailer (linux, non-root).

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

* test(preload): strip the Capy launch markers so a direct bun test matches the shard sandbox

Red Team finding: the launch-marker scrub lived only in createShardSandbox,
so a direct `bun test <file>` run under an unattended Capy session inherited
GSTACK_SESSION_KIND, GSTACK_CAPY, GSTACK_EPHEMERAL and GSTACK_INSTALL_VERDICT
(GSTA-99's symptom). The preload now deletes the same four names, and
test/shard-engine.test.ts imports CAPY_LAUNCH_MARKERS instead of re-typing them.

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

* test(ratchet): the launch-env echo scan catches backtick and $VAR exports, typeset -p, env flags and a set in any segment

Review findings 11 and 12: export-subst also trips on `export \`...\`` and
an unquoted `export $VAR` (both splat a NAME=value file; `export "$name"` is
one word and stays allowed); the export rule adds `typeset -p`; env trips
with only flags (`env -0`) and as `/usr/bin/env`; set is judged per segment
and with redirections, allowed only when piped straight into an anchored
grep filter. Quoted strings are blanked offset-preserving so a `#` inside
quotes no longer hides the rest of the line. The negative control covers
each new form on both sides.

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

* docs(ship): Step 14.5 names where the launch-file placeholder comes from

The /document-release prompt line read `. <file>` with no word on what fills
it. It now says the placeholder is the GSTACK_LAUNCH_FILE: path the parent's
own install check printed. Worded to stay under the ship skill's pinned
parity size (313,279 of 313,281 union bytes).

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

* fix(redact): env.value admits *_PASS, *_PAT, *_APIKEY and *_PWD, and skips path values and gstack's lock token

/ship Step 11 adversarial findings. The name rule also covers DB_PASS,
GITHUB_PAT, *_APIKEY and *_PWD (the shell's own PWD and OLDPWD stay out). A
credential-named variable whose value is a filesystem path (SSL_KEY=/etc/ssl/
private/x.key) names where the credential lives, so the path is no finding,
and GSTACK_BRAIN_LOCK_TOKEN (pid-random-epoch, written to the lock owner file)
is not a secret. Without these, an ordinary mention of that path or token in a
PR body, receipt or CSO report was a HIGH fail-closed blocker.

The adversarial pass also recommended an entropy floor so dev defaults such as
DB_PASSWORD=postgres stop matching prose; declined here on purpose, because the
scan's promise is to catch a leaked value whatever its shape, and a weak real
password is still a credential. Recorded as an open decision in the ship report.

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

* fix(capy-install): a failed launch-file write removes only its own temp file

The failure path ran rm -f "$LAUNCH_FILE".tmp.*, which also deleted the temp
file a concurrent installer run (two threads on one machine, same verdict
path) was about to mv, so that run lost its launch file too. write_launch_file
now cleans up its own $tmp and returns 1; the caller only reports. The file's
comment no longer embeds the state-root path (a newline in GSTACK_STATE_ROOT
could have ended the comment).

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

* test: the launch markers come from one list, and the echo ratchet scans setup and scripts/*.sh

The preload imports CAPY_LAUNCH_MARKERS instead of re-typing the four names,
and the ratchet asserts the installer exports every name on that list, so a
fifth marker cannot be added to one place only. The shell scan also covers
./setup and the scripts/*.sh install scripts callers run first.

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

* docs(changelog): the Unreleased entry states the review-fixed env.value rules, the ratchet forms and the one test-budget change

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

* fix(redact): the env.value path exemption asks whether the file exists instead of matching a shape

Second review cycle: a path-shaped rule also matched a base64 secret that
starts with "/" and holds another "/" (about 1 in 150 AWS-shaped keys), which
then went unscanned. The exemption now fires only when the value names a file
that exists on the scanning machine (SSL_KEY=/etc/ssl/private/x.key). The
module header names that stat as the engine's one filesystem read.
NON_SECRET_ENV_NAMES is exported so the test can prove bin/gstack-brain-lock.sh
still exports every name in it; the PWD/OLDPWD fixtures hold plain words so only
the name rule keeps them out.

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

* test: the installer's own-temp-file cleanup is exercised, and the ratchet coverage test names setup and scripts

An mv shim first on PATH refuses only the launch-file target, so the temp
file exists when the move fails: the run removes that file alone (a sibling
from a concurrent run survives), says so, prints no GSTACK_LAUNCH_FILE line
and keeps the earlier file, exit code and trailer. The ratchet coverage test
asserts setup, scripts/build.sh and scripts/sandbox-doctor.sh are scanned.

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

* fix(redact): env.value has no path exemption and matches the value in normalized form

Second adversarial pass: the existence check let the scanned agent exempt
any "/"- or "~/"-prefixed secret by creating a path of that name, a silent
bypass of the one guard this branch adds, so the exemption is gone; a
path-valued SSL_KEY that must appear in a text goes through the allowlist.
The engine is back to no I/O. The value is now normalized the way the text
is before indexOf, so a credential NFKC rewrites (fullwidth digits) still
finds its leaked ASCII form.

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

* fix(capy-install): the launch file is written only on a passing run; the ratchet checks the marker list both ways

A gstack: fail run no longer writes or announces a sourceable file whose PATH
prepend points at an unverified Bun. The ratchet also asserts every variable
the launch file exports is a CAPY_LAUNCH_MARKERS entry or GSTACK_STATE_ROOT,
so a sixth export cannot reach fixture children unstripped. The Capy skill's
prose no longer offers re-typing the assignments as an alternative to sourcing.

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

* fix(capy-install): the launch file is part of the ok verdict

Third adversarial pass: a passing run whose launch file could not be
written still said gstack: ok with exit 0 and printed only the GSTACK_LAUNCH
line, leaving a shell-evaluating caller with the re-typing path the branch
exists to remove. The file is now written before the verdict is recorded;
a write failure on a passing run turns the run into
"gstack: fail launch-file — could not write the launch file <path>; fix: …
(LAUNCH_FILE_UNWRITABLE)" with exit 1 and an ok:false verdict, and every
failing run removes the launch file an earlier passing run left beside the
verdict. The two failure tests assert the new contract, the doctor-FAIL
tests assert no launch file is announced or left, and the docs, Capy skill
and CHANGELOG say the same.

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

* fix(redact): the env.value allowlist and placeholder rules see the normalized value too

An allowlisted value in NFC did not suppress a match on its NFKC-composed
form, and a value that is a placeholder only after normalization was still
searched for. Fixture nits from the third review cycle: fixed directory
string instead of process.cwd(), the fullwidth assertion guards its own
vacuity, a zero-width-padded value under the floor is pinned, and the
ratchet comment names the one kept export and the PATH case line.

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

* fix(capy-install): LAUNCH_FILE_UNWRITABLE is a registered result code; a kept profile verdict keeps its launch file

Fourth review cycle. The new trailer code now has its row in
lib/result-codes-capy.ts (the owner module is at its size cap) and its
docs/troubleshooting.md anchor, so the install-side code scan resolves it.
A failing profile check (--for review-voice) that keeps a fuller ok verdict
for this HEAD no longer deletes that verdict's launch file: the fail branch
removes the file only when the run rewrites the verdict, and setup_failed
removes it too. The docs say "a failing run that rewrites the verdict",
and that an unwritable directory keeps both the old file and the old
verdict. A new test runs a passing install, then a failing profile check,
and asserts the file and the ok verdict both survive.

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

* test(redact): the normalized allowlist and placeholder rules each have an assertion that fails without them

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

* test(capy-install): a setup failure after a passing install removes the earlier launch file

Fifth review cycle: setup_failed's cleanup had no assertion. The setup
failure test now installs once (launch file present), commits a new HEAD so
setup runs again, fails that setup, and asserts the launch file is gone and
the verdict says ok:false before the next run sets up again and ends ok.

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

* docs: state-root, contributor reference and capy-install follow the launch-file contract (Step 14.5 audit)

The /document-release audit (docs-3449-1792992068) corrected three authored
docs: docs/state-root.md shows the GSTACK_LAUNCH_FILE line and says to apply
it with ". <that file>" instead of prefixing or exporting the assignments;
docs/CONTRIBUTOR_REFERENCE.md names scan()'s one ambient input instead of
calling it pure; docs/capy-install.md's bun-shim bullet and exit table name
the launch file and the fail launch-file trailer.

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>
2026-10-26 12:00:20 +00:00
Garry Tanandcapy-ai[bot] 021d817c36 v1.91.240.0 feat(proactive): default to the four core skills (core | all | off), with docs, prompt and tests (GSTA-99) (#3432)
* feat(proactive): default to the four core skills (core | all | off); docs, prompt and tests (GSTA-99)

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

* fix(proactive): gstack-skill-start falls back to core, not true, when gstack-config is missing

The PROACTIVE status line's fallback literal still said true (= all) after the default moved to core. Found by /ship's coverage audit; the regression test fails at the previous head and passes at main.

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

* test(proactive): STATUS line carries the stored value; every surface names the same core four and three values

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

* fix(proactive): off gates the office-hours handoff; the preamble and router scope the setting to unasked suggestions

The office-hours next-review handoff gated on the legacy false only, so an off user was still asked to launch /plan-eng-review. The per-skill preamble now says the setting governs unasked suggestions and this skill's own steps still run (a handoff, a review gate, a dispatched /document-release is not a suggestion), at exactly the previous length, so the 89 size pins return to main's values; the router says the same in full. Found by /ship's specialist and red-team review.

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

* fix(skill-start): unknown proactive values read as core; first-run tips name core skills and stay quiet under off

A hand-edited config can hold any value and only set validates, so STATUS normalizes anything but core|all|off|true|false to core. The first-run and first-loop tips named /spec, /investigate and /plan-eng-review whatever the setting; they now name the core four and are skipped under off. The fallback regression test folds into test/proactive-skill-start.test.ts, the duplicate header assertion leaves proactive-config, and the frozen-length preamble test is retired (the size guard enforces it).

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

* docs(proactive): scope note, the unknown-value rule, and the CLAUDE.md routing follow-up; CHANGELOG

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

* fix(skill-start): no CLAUDE.md routing offer under proactive off

The one-time offer to append ## Skill routing to the project's CLAUDE.md fired whatever the proactive value, and its rules are the auto-invocation an off user turned off. The offer is skipped under off and the legacy false; a section already in CLAUDE.md is left alone. Under core the offered section still routes every skill, which docs/proactive.md records as the follow-up. Found by /ship's red team.

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

* fix(proactive): STATUS says only core | all | off, and the preamble carves out only named skills

gstack-skill-start reads the legacy true/false as all/off and tolerates a quoted, upper-case or commented hand edit (proactive: OFF # mine read as core before), so every surface sees one of three values and the off gating lives in one flag. The per-skill preamble drops the legacy spellings and replaces 'This skill's steps still run', which licensed any in-skill offer under off, with 'Skills the user or this skill's steps name run', at the same length, so the 89 size pins stay at main's. Tests run the script for the tips and the routing offer under core and off instead of grepping the source. Found by /ship's fix-range repeat (red team, testing, maintainability).

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

* fix(config): set proactive is case-insensitive; follow-up #3446 cited; test hygiene

set proactive OFF warned and stored core, the opposite of the opt-out typed; the value is lowercased first, as gstack-skill-start does for a hand edit. The fixture git init carries the tripwire timeout, the source-shape assertion on the private _PROACTIVE_OFF line goes (the routing-offer test proves the behavior), and the CHANGELOG Tests bullets collapse to one per file with the known gap pointing at #3446. Found by /ship's full repeat pass and its tests lane.

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

* fix(proactive): a stored off answers the one-time prompt; the router names /browse and /investigate under core instead of invoking them

Red team, full repeat pass: with proactive: off stored before the prompt, skill-start still emitted the opt-in prompt; it now writes the marker and asks nothing. The router's Route-first browser step and the Failures branches invoked non-core skills under core while the core block said to answer directly; one sentence in the core block resolves it, pinned by the core-set tests. CHANGELOG and docs/proactive.md say both.

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

* docs(proactive): the full-catalog skill descriptions are the second surface that still routes every skill under core (#3446)

From /ship's document-release audit: a --catalog-mode=full build and the non-Claude hosts keep the "Proactively suggest when ..." description sentences, which do not follow the setting; the default Claude build trims them.

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

* test(proactive): the tips test runs skill-start as an interactive session

GitHub CI sets CI/GITHUB_ACTIONS, which gstack-session-kind reads as headless, and a headless start skips first-task detection, so the first-run tip never fires there (Free Tests shard 16 red at 0d7dbe6; green locally and on the Ubicloud pregate, which have no CI marker). The fixture now sets CLAUDE_CODE_ENTRYPOINT=cli, the positive interactive signal; reproduced locally with CI=1 and fixed.

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

* fix(proactive): an opt-out never fails open; the preamble carves out invocations, not offers

The YAML yes/no family (yes/on/1, no/none/0/disabled/never) reads as all/off in both gstack-skill-start and gstack-config set, so a typed or hand-edited opt-out cannot land on core. The per-skill preamble says "Skills the user names or this one invokes run" (same 168-char length, pins unchanged): a skill dispatching another is not a suggestion, an in-skill offer to run one still is under off. The stored-off prompt marker is written before STATUS prints PROACTIVE_PROMPTED, so the line is true. docs/proactive.md's table row carves out in-skill handoffs.

Fourth /ship fix cycle, past the skill's three-cycle cap by GStack Minder's decision (2026-10-26) under Garry's "do it" on the proactive core set; items from the Step 11 adversarial pass.

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

* v1.91.240.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>
2026-10-26 10:05:57 +00:00
Garry Tanandcapy-ai[bot] 7ee7e8bdd4 v1.91.238.0 fix(ship): gate-diff base in single-branch clones, gstack-post release versions and receipt ids, evidence tests_ran, restamp duplicate headings and stamp prompt (GSTA-102) (#3439)
* fix(gate-diff): resolve the base to origin/<base> when no local branch exists; the no_base fix line fetches into the tracking ref

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

* fix(post): the repo's release versions and a receipt's tree hash / run ids are not pii.ip_public or pii.phone.e164

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

* fix(evidence): parse the shard runner's and the remote pregate lane's test counts into tests_ran

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

* fix(restamp): CHANGELOG_DUPLICATE_HEADING refuses only duplicates the PR introduces

checkChangelog takes the base branch's CHANGELOG: a heading the base already
carries N times may appear N times on the PR (inherited released history), and
only a further copy is a duplicate. gstack-restamp passes origin/<base>'s
CHANGELOG at the plan's base sha, so a base like gbrain's master, which heads
one version twice, can be stamped; a duplicate the PR adds is still refused
with nothing written.

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

* fix(restamp): the stamp-at-merge next: line no longer asks the lane to edit the CHANGELOG

Under a train the stamp commit may hold only restamp's own output: the train's
verify accepts the CHANGELOG only as the base's plus the PR's own entry
re-headed, and a lane that followed the old "write the CHANGELOG entry text"
prompt froze the queue. The claim-at-open wording is unchanged.

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>
2026-10-26 09:33:07 +00:00
Garry Tanandcapy-ai[bot] 6665e20d56 v1.91.236.0 feat(ship-queue): require a current /ship receipt before READY, train build and train merge (#3438)
* feat(ship-queue): require a current /ship receipt before READY, train build and train merge

Garry, 2026-10-25: use the gstack ship skill before merging PRs. ready printed
SHIP_RECEIPT: none as information and returned ok, so the pool was READY'd without
/ship. Now ready, train build (dry run included) and verify --train refuse with
QUEUE_RECEIPT_MISSING or QUEUE_RECEIPT_STALE from the PR body alone; verify accepts
the receipt written for the pinned head when the current head is its stamp commit.

gstack-ship-receipt result is /ship's terminal step (GSTACK_RESULT: skill=ship,
run dir with the head and receipt), unattended /ship may update an existing PR's
body, and INSTALL_UNVERIFIED with no verdict names the project directory to
install from (GSTA-85).

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

* test(ship): pin Step 22 in workflow-required-steps and tighten the ship skeleton

Gate edit: test/fixtures/workflow-required-steps.json pins the new
"## Step 22: Terminal line (every run)" heading (strengthens: a later
edit that drops the terminal line turns the required-steps test red).
Steps 14, 16, 18, 20 and 22 prose tightened so the carve-guards raise
shrinks to 81,600 / 1.6740 (measured skeleton 81,535, union 314,027).

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

* fix: pre-landing review fixes (ready at the stamp commit, screen on a stale merge ref, ship-run.json)

- ready accepts the pin's receipt at a slot's stamp commit (plus the slot-2
  merge of main), as verify --train already did; a real edit stays STALE
- ready --screen refuses QUEUE_SCREEN_STALE_REF when refs/pull/<n>/merge is
  missing or merges an older head; a finished screen that planned 0 cases is
  never followed, and train build seats that slot unscreened (GSTA-95 on #3431)
- gstack-ship-receipt result writes ship-run.json (a /ship record, not a run
  v1 manifest), validates --head, and prints status=incomplete on gh failures
- bindReceipt takes an optional pr and renders one-line SHIP_RECEIPT lines
- tests for every state-matrix cell the review named; the train fixture's
  merge ref is a real merge commit

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

* fix: cycle-2 review fixes (private screen scratch, no bare-merge receipt reuse, 12-hex --head, one receipt scrub) and align the unattended hook text with the GSTACK_RESULT writers (GSTA-91)

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

* fix: adversarial-review fixes (result refuses an unpushed HEAD, resolves --out and reports unwritable records; ready names a failed merge-ref fetch; unattended /ship never runs gh pr ready)

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

* fix: close review-cycle gaps (a stamp must change a stamp file, result requires a full --head and fails closed without headRefOid, digits-only --pr) with tests for every cycle-3 branch

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

* test: give the fsck-rejected merge-ref spawns a timeout (spawnsync tripwire)

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

* fix: detect a hollow screen from the plan it uploads (no paid-plan artifact or nothing planned), correct two troubleshooting facts, and normalise result --pr

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

* fix(hollow-screen): read manifest.json by name, list artifacts by name, say when the plan is unreadable; lowercase result --head

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

* test(hollow-screen): expired and unreadable run rows, uppercase --head; docs name every SCREEN: literal

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

* fix(train build): say when the repository slug is unreadable instead of counting the screen silently

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

* fix: name an ls-remote failure in ready --screen, refuse result --pr with --body, cap echoed receipt errors, tell an unreadable install verdict from a missing one

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

* test: ls-remote refusal, --pr with --body, capped receipt errors, unreadable slug and verdict (both twins); docs name the new literals

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

* fix: scrub Unicode line separators from receipt text, report an unreadable --body as incomplete, refuse unsupported verdict schemas in the bash twin

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>
2026-10-26 09:03:59 +00:00
Garry Tanandcapy-ai[bot] 75badf306c v1.91.234.0 feat(releases): weekly tagged releases and a stable update channel for team mode (GSTA-98) (#3429)
* feat(releases): weekly tagged releases and a stable update channel for team mode (GSTA-98)

Every Monday morning .github/workflows/weekly-release.yml tags the newest green
commit on main as v<VERSION>, publishes a GitHub Release from the CHANGELOG
headlines and fast-forwards refs/heads/stable to it (scripts/release-cut.ts;
workflow_dispatch with dry_run, and ref for a hotfix). Team-mode installs
follow stable through the new update_channel key (auto: stable when
team_mode, else main) in bin/gstack-session-update, bin/gstack-update-check,
bin/gstack-upgrade and /gstack-upgrade. Per-merge stamping and the merge
train are unchanged. docs/releases.md explains it.

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

* ci(weekly-release): check out the dispatched ref so a dry run exercises that branch's script

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

* ci(weekly-release): a PR touching the release tooling runs the dry run against main (read-only); the cut job checks out the dispatched ref

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

* fix(gstack-upgrade): no $1 in the rendered skill (Claude Code rewrites $N), trim Step 4 under the size budget, pin the new floor-check literal

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

* chore: leave setup untouched (its channel note moves to README and docs/releases.md); the release workflow and script are free-only PR files

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

* test: record the pr-profile free-only classification as non-behavioral (HARNESS_VERSION 6)

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

* fix(releases): gate the cut on a main-backstop check run, finish half-done cuts, keep team installs on their channel

/ship review fixes (GSTA-98, PR #3429):

- The weekly release read the Main backstop gate run's conclusion, but
  scripts/train-backstop.ts exits 0 when main moved past the push before it
  was gated (superseded), so a never-gated tree read as green. The backstop
  workflow now publishes its verdict on the commit as the `main-backstop`
  check run from a code-free checks:write job (success for covered|gated,
  neutral for inert|superseded|deferred, failure for frozen|failed), fed by
  the new `--outputs` flag; release-cut.ts requires that check run and
  free-tests to succeed and holds a commit whose check runs are still going.
- A cut that pushed the tag and stable but failed to create the release can
  be finished: stable itself stays a candidate until its release exists, the
  notes compare against the release tag behind it, and a dry run with nothing
  green exits 0 with the skips named (the real cut still fails).
- weekly-release.yml: the cut job runs only from refs/heads/main, `ref` must
  be a full 40-hex SHA, and actions:read is dropped (no workflow-runs call).
- /gstack-upgrade: the destructive fallback requires the carried UPSTREAM_REF
  instead of defaulting to origin/main in a fresh shell; a team install past
  the release stops with AHEAD_OF_STABLE instead of FF_REFUSED and a reset
  backwards. gstack-update-channel.sh --fetch-ref keeps origin/stable on any
  fetch failure other than a missing release; dead gstack_update_channel_ref
  removed. gstack-update-check follows main before the first release instead
  of caching CHECK_FAILED; gstack-upgrade treats a failed ls-remote as a
  fetch failure, not an empty answer.
- Tests: release-cut (check-run fixture, partial cut, neutral/pending),
  weekly-release-workflow (new), evals-train-workflow and train-backstop
  (backstop-check job, --outputs), update-channel (no-release fallback,
  offline --fetch-ref), template pins.

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

* fix(releases): the upgrade skill's ahead-of-stable exit works in depth-1 clones; update-check keeps its cache identity on the pre-release fallback

/ship second-pass review of 73e278d4 (GSTA-98, PR #3429):

- gstack-upgrade/SKILL.md.tmpl: README installs are depth-1 clones, where
  origin/stable never connects to HEAD, so the AHEAD_OF_STABLE shortcut now
  also fires when HEAD is not behind stable and stable's VERSION is not
  higher than the installed one (the order bin/gstack-upgrade uses). The
  fence prints the channel helper's warnings instead of discarding them, and
  a 'fetching origin/stable failed' line is a STOP. Prose trimmed to stay
  under the skill size budget. New behavioral test runs the Step 4 fence on
  a depth-1 install past the release (AHEAD_OF_STABLE, nothing moved) and on
  one behind it (goes on to the fast-forward).
- bin/gstack-update-check: the no-stable fallback keeps #stable in the
  cache identity (so the outcome replays within its TTL instead of two
  ls-remote calls per skill start), triggers only when ls-remote answered
  with no such branch (a failed ls-remote stays CHECK_FAILED on stable), and
  the branch-pinned URL derives from REMOTE_RAW_BASE so tests stay offline.
- scripts/release-cut.ts: a cancelled check run is red; the previous-release
  lookups check .ok before using git output. docs/releases.md says which
  pushes get no main-backstop check run at all.

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>
2026-10-26 07:22:44 +00:00
4216bbb223 v1.91.233.0 fix(plan-tune): bound each UTC day's pull on a dimension, record contributing_events (GSTA-79) (#3397)
* feat(plan-tune): per-dimension contributing_events in the inferred profile

Port of the provenance part of #2625 by @v639dragoon: --derive records, per
dimension, how many unique log rows moved it, so "no evidence" and
"balanced evidence" no longer both print a bare 0.5. Also ports the test
harness fix: logQuestion sets GSTACK_QUESTION_LOG_NO_DERIVE=1 so the
background derive cannot race the test's own --derive.

The registry-independent autonomy signal from #2625 stays parked, as the
Oct 8 review decided.

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

* fix(plan-tune): clamp each session's pull on a dimension to one strong answer; trace shares derive's accumulation (#2623)

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

* fix(plan-tune): shared accumulation JS as a quoted string, not a heredoc (heredoc pipe-deadlock guard)

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

* fix: pre-landing review fixes

Own-property signal lookups so a logged choice such as constructor cannot
break every later --derive; --trace replaces control characters in the
fields it prints; tests for the negative clamp, empty session_id, an
unmatched answer and both fixes; docs and CHANGELOG say the bound keys on
the writer session_id.

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

* fix: pre-landing review fixes (pass 2)

A session_id that is an unsubstituted placeholder such as SESSION_ID
counts as its own session instead of collapsing every agent-logged
answer into one bound; a log line that parses to null is skipped; the
accumulation tolerates a dimension it did not seed; the trace test covers
DEL and C1 controls; docs and CHANGELOG wording match the code.

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

* fix(plan-tune): bound each UTC day instead of each session_id

The per-session bound trusted whatever session_id a writer logged. The
PostToolUse hook and the agent log different ids for one review, and an
unsubstituted SESSION_ID placeholder either merged every review into one
bound or left each answer unbounded. Bucketing by the UTC day of ts
stops one burst from pinning a dimension without trusting any id;
several reviews on one day count as one. Decision A from GStack Minder.

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

* fix: pre-landing review fixes (day bound)

Rows without a parseable string ts share one undated bucket instead of
each escaping the bound; a ts without a zone is read as UTC, so the day
does not depend on the machine's zone; days_span counts the same UTC
days; question_id and user_choice must be strings. Tests pin a zone west
of UTC. CHANGELOG says contributing_events is recorded in the profile
JSON and not displayed yet (#3436).

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

* fix: pre-landing review fixes (strict ISO ts)

Only an ISO 8601 date or date-time ts counts as dated; Date alone parses
strings such as "junk 1" as real days, which let junk rows escape the
bound and inflate days_span. A zoneless time, T- or space-separated, is
read as UTC. Tests cover array-valued fields, an offset ts in days_span,
the space separator and every undated row reaching the shared bucket.

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

* docs: narrow the #2623 TODOS entry to its remaining Gate 1 work (#3442)

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

---------

Co-authored-by: v639dragoon <matthew@dohma.ai>
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
2026-10-26 07:08:52 +00:00
Garry Tanandcapy-ai[bot] be402d695c v1.91.231.0 fix(setup): install production dependencies only (~520 MB smaller install) (GSTA-97) (#3426)
* fix(setup): install production dependencies only; contributors and CI keep the full install (GSTA-97)

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

* docs(changelog): production-only install (GSTA-97)

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

* fix: pre-landing review fixes (GSTA-97)

- setup/gstack-browser-ensure: Bun treats --production as frozen, so the
  old fallback could never recover a stale lockfile; refresh it with
  --lockfile-only, then install production-only again.
- gstack-browser-ensure failure line and the README tip no longer suggest a
  full bun install to users.
- Tests: real-Bun stale-lockfile and keep-devDeps contracts, behavioral
  browser-ensure install tests, relative-import closure scan, multi-line and
  re-export negative controls; CHANGELOG matches.

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

* fix: pre-landing review fixes, pass 2 (GSTA-97)

- gstack-browser-ensure's failure line and the README tip suggest the
  frozen production install (./setup skips the install when the build is
  current, and a bare --production install is frozen anyway).
- setup install guard catches quoted and unquoted $BUN_CMD spellings.
- Replace the exact-text gstack-browser-ensure grep with an invariant: no
  full bun install; behavior is covered by gstack-browser-ensure.test.ts.

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

* fix(setup): keep a contributor checkout's dev packages on bun.lock's versions (GSTA-97)

/ship Step 11: a production install over a full install reports "no
changes" and leaves devDependencies at old versions after bun.lock moves,
so the session updater's ./setup left contributors on stale TypeScript,
Prettier and Agent SDK. A checkout with node_modules/typescript gets the
full frozen install. The import-closure scan now follows shipped code into
test/helpers (gstack-model-benchmark loads the providers).

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

* fix: pre-landing review fixes, ship invocation 2 (GSTA-97)

- Tests: contributor-branch failure row; bun.lock production closure never
  reaches the dev-toolchain marker; real-Bun rows isolate HOME,
  XDG_CONFIG_HOME and BUN_CONFIG_*; the import walker follows side-effect
  imports; file header matches the two install branches.
- BROWSER.md quick start uses the frozen production install.
- CHANGELOG: the reclaim recipe also drops the Agent SDK's cache entries
  (Bun hardlinks node_modules into its cache); Tests list is current.

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

* docs(changelog): stub-bun wording matches what the fixtures check (GSTA-97)

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

* v1.91.231.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>
2026-10-26 06:41:22 +00:00
Garry Tanandcapy-ai[bot] 4814b3d8a4 v1.91.230.0 feat(design-shotgun,design-html): archetype handoff and persisted craft exceptions (GSTA-92 phase 2) (#3422)
* feat(design-html): craft section and free craft check, adapted from Elaya design skills (GSTA-92)

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

* fix(GSTA-92): restore the full CHANGELOG under the Unreleased entry; drop a $-digit literal from craft.md; count only craft-check executions in design-html-slop-gate

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

* feat(design-shotgun,design-html): carry the chosen archetype into approved.json; persist accepted craft exceptions in finalized.json (GSTA-92 phase 2)

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

* test(touchfiles): design-html-slop-gate selects on the craft check it runs (GSTA-92)

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

* fix: pre-landing review fixes (GSTA-92)

A malformed or missing --accepted exceptions file now gets the full craft
check with nothing accepted, after a warning, still exiting 2. Before, it
printed nothing, and the craft section treats a missing CRAFT_RESULT as
'check unavailable', so one bad finalized.json switched the gate off
(found by both adversarial passes). The header comment and the craft
section now document the accepted-count suffix on CRAFT_RESULT. Tests
cover duplicate carried-forward exception ids and the slop-gate touchfiles
selection.

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

* fix: scope --accepted to the same page; record current counts (GSTA-92)

The craft section passed --accepted whenever a finalized.json sat beside
the page, so a same-day Start fresh build inherited the old page's
exceptions. It now passes them only when evolving or rerunning a page it
finalized, records the count the check reports now, and writes [] when
nothing was accepted instead of the template's placeholder entry. A test
pins exit 2 for a clean page with a broken exceptions file.

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

* docs: note the archetype handoff in the shotgun and design-html loops (GSTA-92)

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>
2026-10-26 06:07:07 +00:00
Garry Tanandcapy-ai[bot] e622075a18 v1.91.228.0 fix(gstack-lease): take the lease lock with mkdir, not ln -s, so Git Bash on Windows stops waiting out 300 tries (#3330) (#3424)
* fix(gstack-lease): take the lease lock with mkdir, not ln -s, so Git Bash on Windows stops waiting out 300 tries (#3330)

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

* test(gstack-lease): count lock retries instead of timing them

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

* fix(lease): a dead lock that cannot be renamed aside no longer spins lock()

break_lock ignored drop()'s failure and returned 0, so lock() continued past
the tries limit forever when a dead holder's .lock could not be renamed aside
(a Windows file in use). It now removes the lock in place under .lock.break,
and returns 1 when that also fails so the try is counted. touch no longer calls
a dead holder 'live' in its exit-3 message.

Adds the /ship coverage-audit tests for the lock's failure paths.

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

* fix(lease): pre-landing review fixes

- drop() reports a lock released once it is renamed aside, so a failed
  delete of the renamed copy never makes unlock or break_lock rm -rf a new
  taker's lock at .lock.
- An ownerless lock is broken only once it is 2 s old, so a live taker whose
  claim landed after a breaker read the claims keeps the lock it is about to
  publish. An empty owner, an invalid pid or a stray file counts as ownerless.
- touch's prune stays out of the lock (top-level lease files only) and clears
  leftover renamed-aside locks; a failed claim write leaves no claim.
- Docs and CHANGELOG state the mixed-version and dead-break-lock limits
  instead of claiming exclusion that does not hold.

Tests pin each fix (each fails against the previous bin), make the exclusion
case wait on a release file, prove the parallel case is mutually exclusive,
and check the Git Bash holder keeps its lock.

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

* fix(lease): second /ship review pass

- orphaned() reads the clock before the lock's mtime and requires the mtime
  unchanged at the end, so a lock made meanwhile is never removed; an mtime
  a minute or more ahead (clock skew) counts as old.
- Claims older than 2 minutes are abandoned and pruned by touch.
- A waiter skips the claim and mkdir while the lock is visibly held; the
  wait is bounded at 300 tries or 30 s; a non-numeric tries value falls back
  to 300; ten quick retries (not three) before a lock nobody holds is
  reported as untakeable; pid 1 is a valid holder again.
- Tests: deterministic young-lock clock, skew and non-numeric owner shapes,
  abandoned claims, the deadline, explicit 30 s timeouts.

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

* fix(lease): third /ship review pass

- A claim is honored while its claimant lives; only a claim with no start
  identity expires after 2 minutes, so a stalled live taker keeps its lock on
  hosts that record identities (the remaining case is a documented limit).
- A late taker never overwrites an owner already in its directory, and does
  not remove a directory that has one.
- A lock whose owner file exists but cannot be read is never ownerless.
- Zero-padded owner pids count as dead; --help prints the whole usage;
  touch prunes only claims whose claimant is gone or recorded no identity.

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

* docs(lease): scope 'never by age' to locks that name a holder

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>
2026-10-26 04:46:42 +00:00
Garry Tanandcapy-ai[bot] 9dc1dfb729 v1.91.227.0 fix(windows): shell bins pass NUL as Bun's empty config when MSYS path conversion is off (#3415) (#3431)
* fix(windows): shell bins pass NUL as Bun's empty config when MSYS path conversion is off (#3415)

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

* fix(windows): detect Git Bash/MSYS2/Cygwin from OSTYPE, as gstack-doctor does, so uname stubs in setup fixtures stay untouched

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

* fix: pre-landing review fixes (#3415)

- Choose NUL only when cygpath is also on PATH, so an OSTYPE=msys inherited
  by a POSIX bash keeps /dev/null instead of a cwd-relative NUL file.
- gstack-developer-profile --log-session reports a Bun failure instead of
  skipping it as invalid input, like gstack-timeline-log.
- learnings-injection tests check that their anchored bun -e block exists.
- CHANGELOG names the callers this fix does not cover; curation reason fixed.

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

* fix: pre-landing review fixes, cycle 2 (#3415)

- timeline-log and developer-profile validators print their verdict; any
  non-zero Bun exit is a reported failure (a Bun crash on Windows exits 3).
- timeline-log keeps the original event when ts stamping fails.
- The definition ratchet scopes a local GSTACK_BUN_CONFIG to its function.
- The shell-bins fixture clears its preload marker per test.
- CHANGELOG states the callers that discard the report and the Bun 1.3.x case.

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

* docs: CHANGELOG notes Bun 1.3.10 evidence and remaining literal /dev/null callers (#3415)

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>
2026-10-26 04:32:27 +00:00
Garry Tanandcapy-ai[bot] 7bd3fb4a40 v1.91.225.0 feat(design-html): craft section and free craft check, adapted from Elaya's design skills (GSTA-92) (#3420)
* feat(design-html): craft section and free craft check, adapted from Elaya design skills (GSTA-92)

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

* fix(GSTA-92): restore the full CHANGELOG under the Unreleased entry; drop a $-digit literal from craft.md; count only craft-check executions in design-html-slop-gate

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>
2026-10-26 00:29:01 +00:00
2853304d17 v1.91.224.0 fix(context): record the save commit and flag work that landed after it (port of #3316) (#3396)
* fix(context): record the save commit and flag work that landed after it

/context-save recorded the branch, a timestamp and the modified files, but not
the commit, so /context-restore could not tell that work landed after the save.
A checkpoint saved before a step was committed came back with that step still
listed as the plan.

- gstack-slug --stamp-checkpoint also writes head:, the commit the save
  describes, next to remote:/project_root:/worktree:.
- context-restore compares head: with HEAD before presenting. When commits
  landed after the save it prints how many and lists them, and moves Remaining
  Work under Verify first; a saved commit that left the branch history is
  reported the same way. Checkpoints saved before this keep their old sorting
  with a one-line note.
- test/context-restore-head.test.ts runs the real save-path, stamp and
  head-check blocks in fixture repos: unchanged, moved, diverged after a reset,
  pre-tracking, and a malformed head: that must never execute.

* fix(context-restore): name another branch as a cause when the saved commit is not in history

Cross-branch resume (Conductor handoff) is a supported restore path, and there
the checkpoint's commit is usually not an ancestor of HEAD. The diverged line
said only 'rebased, reset, or not fetched'. It now names 'saved on another
branch' first; the new test case covers a save on a feature branch restored
from main.

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

* docs(changelog): unreleased entry for the context head check (port of #3316)

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

* fix(context-restore): head check passes the fence lint, reads frontmatter only, prefers worktree:, and overrides provenance sorting

- The block used a free-text placeholder, <the chosen file>, which the
  generated-bash lint (test/env-host-fence-conformance.test.ts) refuses on
  every host. It now uses the allowlisted <the FILE path printed above>, with
  prose naming the Step 2 choice.
- head:, worktree: and project_root: are read from the leading frontmatter
  only (sed, no positional $N tokens Claude Code would rewrite), so a head:
  line in a checkpoint's notes no longer counts.
- Outside git, the comparison uses the saved worktree: before project_root:,
  so a save from a nested linked worktree is not compared with the main tree.
- The provenance rules now say they are skipped after HEAD_MOVED,
  HEAD_DIVERGED or HEAD_UNKNOWN, instead of contradicting the head check.
- The re-stamp test checks the exit status and that the head moves.

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

---------

Co-authored-by: Yao Amevi A. Sossou <47247401+soyames@users.noreply.github.com>
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
2026-10-26 00:14:08 +00:00
Garry Tanandcapy-ai[bot] 1c28c09b76 fix(plan-tune): map the clicked AskUserQuestion label to the registered option key at capture (#2623) (#3394)
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
2026-10-25 23:54:50 +00:00
Garry Tanandcapy-ai[bot] c23d7d48fe fix(session-update): the starting session's own lease never skips its update; autostash fixtures own their lease dir and state root (GSTA-91) (#3412)
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
2026-10-25 22:45:05 +00:00
Garry Tanandcapy-ai[bot] 956a3f5484 v1.91.220.0 fix(office-hours,plan-reviews): write the plan-mode copy through to the design doc and warn when it is newer (#542) (#3408)
* fix(office-hours,plan-reviews): write the host plan-mode copy through to the design doc and warn when it is newer (#542)

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

* docs(changelog): #542 under [Unreleased]

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

* test(instruction-facts): drop the office-hours allowlist row for the removed cross-team sentence

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>
2026-10-25 22:31:08 +00:00
Garry Tanandcapy-ai[bot] d73b547066 fix(team-init): register the required Skill hook with onFailure: block (#3319) (#3393)
Without node on PATH the node -e launcher exits 127, and with check-gstack.cjs
missing it exits 1; Claude Code treats both as non-blocking and lets the Skill
call through. Register the handler with onFailure: block (Claude Code 2.1.295+),
upgrade existing registrations in place, and tell the agent and the developer
what the two block reasons mean.

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
2026-10-25 21:21:10 +00:00
64d82101ca v1.91.216.0 fix(sync-gbrain): refuse an active autopilot before touching the source list (#3360 part 2, port of #3362) (#3386)
* fix(sync-gbrain): refuse on an active autopilot before the code stage registers a source

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* docs(changelog): sync-gbrain autopilot refusal before source registration (#3360, port of #3362)

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

* test(sync-gbrain): the early autopilot refusal check matches #3377's pid-naming wording (#3360)

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

---------

Co-authored-by: Evgenii Lopatin <e75533@gmail.com>
Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
2026-10-25 16:35:45 +00:00
Garry Tanandcapy-ai[bot] 5ac0f56934 fix(ship): unattended handoff writes the receipt to a file the parent reads (GSTA-88) (#3409)
Step 18-H's receipt write gains --out .gstack/tmp/ship-receipt.md; skill-start
prints a ship-only UNATTENDED_SHIP line; docs/unattended.md sends the parent to
read --body on that file and to compare head to the branch SHA.

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
2026-10-25 16:22:14 +00:00
Garry Tanandcapy-ai[bot] 08301328e9 v1.91.214.0 fix(brain-sync): --publish-removals cleans up its temp list and lock without 'list: unbound variable' (GSTA-87) (#3405)
* fix(brain-sync): --publish-removals trap expands its temp paths at install, so cleanup and lock release run after the function returns (GSTA-87)

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

* v1.91.214.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>
2026-10-25 16:09:15 +00:00
Garry Tanandcapy-ai[bot] cb99df4a2f fix(diff-scope): count Python test_*.py and nested test dirs as tests (#3335) (#3382)
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
2026-10-25 12:39:53 +00:00
3b6502f79c v1.91.207.0 fix(global-discover): count removed Claude worktree sessions with their repository (port of #3334) (#3387)
* fix(global-discover): attribute sessions of removed Claude worktrees to their repository

Claude Code creates worktrees at <repo>/.claude/worktrees/<name> and they are
removed once the branch merges, but the sessions recorded under
~/.claude/projects stay. The scanner now discovers those sessions (#1315), but
gitProject() returns null for a cwd that no longer exists, so
resolveAndDeduplicate drops them: they are in tools.claude_code.total_sessions
and in no repository's count.

When the cwd is gone and sits at <repo>/.claude/worktrees/<name>[/...], resolve
the project from the repository it was created from. Any other missing
directory is still unattributed, and a worktree that still exists resolves
exactly as before.

Fixture with 6 sessions in a main checkout and 40 in a removed worktree
(46 discovered): repository count before 6, after 46.

* docs(changelog): global-discover counts removed Claude worktree sessions with their repo (port of #3334)

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

---------

Co-authored-by: Shweta-Mishra-ai <shwetam242@gmail.com>
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
2026-10-25 11:48:39 +00:00
669e59d65b v1.91.206.0 feat(pregate): user-owned policy file and local lanes (port of #3286) (#3398)
* feat(pregate): user-owned policy file and local lanes

A repo that keeps .gstack/ out of git can hold its pregate policy at
<GSTACK_HOME>/projects/<slug>/pregate.json. gstack-pregate run reads it only
under --policy-from base when the base carries no .gstack/pregate.json, so a
reviewed base copy always wins and a PR still cannot reach the policy. A
missing file falls back; any other read error, and a non-null sealed or
price_file (read only from the git copies), refuse with PREGATE_CONFIG_INVALID
and a fix that names the file.

A new `local` key lists <workflow.yml>/<job> lanes a local run satisfies: a
platform lane named there runs here (tier unit) instead of becoming a
requires-remote obligation. Lane ids share one LANE_ID check with selection.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs(pregate): the user-owned policy file and local lanes

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(pregate): policy fixes name the user-owned file when it is the active policy

With no .gstack/pregate.json on the base, an edit to that path is never read,
so the no-lane, runner, stall_timeout_s, remote_gate and strays fixes now name
ctx.configFile: the user-owned path when it is the active policy.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(pregate): the remote_gate, strays and stall fixes name the active policy file

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(pregate): pre-landing review fixes for the user-owned policy

Ignore a user policy path that is relative or inside the checkout, fingerprint
the active user file into check inputs, name the user file in its JSON errors
and the refusal lead, add --explain lines for an absent user file and an
unknown local id, and document the state-root location and slug lookup.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(pregate): containment edges and explain wording for the user policy

A state root under an in-repo directory named ..something passed the
inside-the-repo check, and realpath kept the caller's casing on macOS.
The unknown-local explain line now says workflow lane, and the state-root
chain in explain and docs names every source.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(pregate): a base copy git cannot read, or a dangling user symlink, refuses

Any git show failure read as no base policy, which now hands the run to
the unreviewed user file; it is absent only when cat-file says so, and a
spawn error (stdout cut at the buffer limit) counts as a failure. A user
pregate.json that is a dangling symlink refuses instead of reading as
absent.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(pregate): decide a missing base policy from the tree, and catch dangling parent links

cat-file -e checks the blob, so a missing or corrupt object still read as
no base copy; ls-tree reads the tree entry, and any failure refuses. The
dangling-symlink check now covers every path component, not just the file.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(result-codes): POLICY_SOURCE_UNAVAILABLE names an unreadable committed pin file

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs(pregate): the active policy may be the user-owned file; new refusals

Guards, remote_gate, stall timeouts and lane fixes name the user-owned file
when it is the active policy; the trust boundary says run alone reads it;
the dangling-symlink and unreadable-base-copy refusals are documented, and
state-root lists the bucket's pregate.json.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(pregate): a checkout's .env cannot pick the user policy; verify re-checks it

Bun loads .env from the working directory, so a branch could set
GSTACK_PROJECT_SLUG or a state root and choose which pregate.json governs
it. The shebang now passes --no-env-file, scriptInvocation carries that
flag over when it launches the bin as bun <script>, and a bare bun launch
beside any dotenv file refuses (POLICY_SOURCE_UNAVAILABLE).

A recorded pass also outlived its policy: pregate.json now records the
user file's digest (or, under the defaults, the path that was absent), and
verify and the ship receipt treat an edited, deleted or newly present user
file as PREGATE_STALE, reading both paths from the record.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(pregate): keep the POLICY_SOURCE_UNAVAILABLE summary's pin-files wording

The added '(or .gstack/pregate.json)' broke describeRefusal's rewrite to
'the reviewed pin files cannot be read', which pregate-wtree-refusal pins.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(pregate): pick the host-foreign lane in the user-policy local-lane test

On a Windows host the windows lane is native, so it never became
requires-remote and the test failed on the Windows shards.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(pregate): Rust crate lanes and nested JS package runners

lanes reported PREGATE_NO_LANE for every touched .rs file and ran a nested
pnpm+vitest package's tests as `bun test` from the repo root.

- Rust: a .rs file of a crate (nearest Cargo.toml with [package]) is a test
  under <crate>/tests/ or when it holds a #[cfg(test)]; every .rs file of a
  crate and its Cargo.toml select the crate's tests and those of every crate
  depending on it (lib/pregate/rust.ts). Selected tests run per crate as
  `cargo test -p <name>` from the workspace root, judged on the summed
  `test result:` lines (doc-tests included); zero tests stays ZERO-RUN and a
  missing cargo is incomplete, like a missing Dart SDK.
- JS/TS: a test owned by a nested package.json that declares vitest or jest
  runs from that package with package-relative paths, through the package
  manager its packageManager field or nearest lockfile names (pnpm exec,
  yarn, bun x, else npx). Root detection and a declared runner are unchanged.
- init notes that Rust tests run per crate; docs/pregate.md updated.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(pregate): parse Cargo manifests as TOML; follow workspace membership

The line scan of Cargo.toml missed dependents (a false green) and picked the
wrong workspace:
- an inherited renamed dependency (`base = { workspace = true }` with
  `[workspace.dependencies] base = { package = "alpha", path = ... }`);
- dotted keys (`alpha.path = "../a"`), `[dependencies.<name>]` tables and
  target-specific tables, matched by key name only;
- a crate the workspace excludes ran `cargo test -p` from the workspace root.

Manifests are now parsed with Bun.TOML. A dependency resolves by path
(relative to the manifest declaring it, or to the workspace root when
inherited), else by package name. A crate runs from the workspace it is a
member of under Cargo's rules (package.workspace, the nearest [workspace],
members globs, exclude, literal members, path dependencies of members),
else from its own directory. Nothing runs cargo.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(pregate): the Rust fixture's cargo shim wins PATH on Windows and beside a real cargo

windows-free-shard (5) failed 'a touched src file runs its crate...' and
'zero tests is ZERO-RUN...' deterministically: the shim PATH was joined with
':' so Git Bash never saw the shim dir, and the runner's real cargo ran
against the fixture (exit 1, no test result lines). On POSIX hosts where
Bun and cargo share a directory (/usr/local/bin on Capy's image), the lane
puts Bun's directory first and the real cargo wins the same way.

Join with path.delimiter, add cargo.cmd for Bun.which on Windows, and on
POSIX put the shim first again in the lane's own bash through BASH_ENV.

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

* fix(pregate): quote the crate name in cargoRunner

The crate name is read from the repo's Cargo.toml and was interpolated into
'cargo test -p <name>', which the lane runs through bash. A name like
'evil;touch pwned' ran the second command. Quote it like every file and cwd
the lane already passes; a new end-to-end case checks the name reaches cargo
as one argument and runs nothing.

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

* test(pregate): the nested-package pnpm shim wins PATH beside a real pnpm

'a touched file in a nested pnpm+vitest package runs vitest there' ran the
host's real pnpm wherever Bun and pnpm share a directory (/usr/local/bin on
Capy's image): the lane puts Bun's directory first, so pnpm installed vitest
from the network, dirtied the fixture tree and the check came back
incomplete. Put the shim first again in the lane's bash through BASH_ENV,
as the Rust fixture does.

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

* docs(changelog): pregate Rust crate lanes and nested package runners (#3344)

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

* fix(pregate): a crate name Cargo would reject never reaches the lane's shell

Port review (outside voice): quoting alone was not enough, because the lane
expands {files} after the runner string is built, so a crate named
'evil{files}' spliced the quoted file list into the quoted name and a file
name like tests/$(cmd).rs then ran. A name outside Cargo's grammar
(letters, digits, _ and -) now gets no runner: its files are unclaimed and
the lane is incomplete, as Cargo itself would refuse the manifest. A valid
name, Unicode included, still runs quoted.

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

* fix(pregate): match Cargo members globs per path component, without a backtracking regex

Port review (outside voice): members globs went through ship-policy's
matchesAny, which escapes [...] classes (crates/[ab] matched nothing, so the
crate was no member, lost its inherited [workspace.dependencies] path and its
dependents' tests were never selected: a false green) and backtracks
exponentially on a hostile glob such as '*a*a...*ab' (over 5 s for one crate,
before any runner deadline). A small dynamic-programming matcher handles *,
?, [..] with negation and ranges, and ** across components, in
O(pattern x path).

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

* fix(pregate): a plain #[test] in src is a Rust test; bound the cfg attribute scan

Port review (outside voice): only #[cfg(test)] modules and tests/ files
counted, so a dependent crate whose tests are plain #[test] or #[tokio::test]
functions in src never ran when its dependency changed (a false green). The
cfg scan also went quadratic on a run of unclosed '#[cfg(' (about 3 s on
120 KB). Test attributes now count, and the cfg predicate and its strings are
length-bounded so the scan stays linear.

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

* docs(pregate): Rust test attributes, workspace membership and rejected crate names; changelog for the port review fixes

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

* fix(pregate): match members globs by code point in one pass, and admit every crate past a size cap

Second port review round (outside voice): the matcher counted UTF-16 units, so
'crates/?' missed a crate directory named with a supplementary character and
dropped its inherited dependencies (a false green); and each unmatched '['
rescanned the rest of the glob (5 s for one crate with a million of them).
Tokenize by code point with a precomputed next-']' table, and past 4096
characters of members globs admit every crate under the workspace: a wrong
member fails loudly in cargo, a dropped one is silent. The new supervised
probe kills a super-linear matcher at 30 s instead of hanging the file.

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

* fix(pregate): scan cfg attributes once without truncating them; an unclosable one counts as a test

Second port review round (outside voice): the length bound from the first
round dropped a valid cfg(any(test, feature = "<257 chars>")), so its crate's
tests were no longer selected (a false green). Read each attribute once to
its closing ']' and resume after it, which keeps the scan linear without a
bound; an attribute the scan cannot close (an unterminated string, no ']')
counts as a test, which selects more, never fewer.

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

* docs(changelog): second port review round (code-point member globs, unbounded linear cfg scan)

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

* fix(pregate): past the members size cap, a crate's run root is undecided and its tests unclaimed

Third port review round (outside voice): admitting every crate past the cap
made a non-member run as 'cargo test -p <name>' from the workspace root,
where a same-named member answered and its passing tests stood in for the
orphan's failing one (a false green). Keep the conservative membership for
dependency edges, but leave such a crate's workspace undecided (null):
planRuns leaves its files unclaimed and the lane incomplete.

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

* test(pregate): the Rust end-to-end runs put the cargo shim under Windows' own Path key

windows-free-shard (5) still failed both end-to-end cases at fe99b200 with
the same symptom (the runner's real cargo, exit 1, no test result lines).
runBin merges process.env, whose Windows key is 'Path', with the shim's
'PATH', so the child got two path variables and read the original. Write the
shim under the key process.env actually uses, as test/helpers/unreachable-gbrain.ts
and sync-gbrain-readiness-fixture.ts do.

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

* test(pregate): run the cargo stand-in cases on POSIX only

windows-free-shard (5) still failed both end-to-end cargo-lane cases after the
delimiter, cargo.cmd and Path-key repairs: the lane's Git Bash exits 1 with an
empty log, and that log is not visible from CI. Skip the two cases that run the
bash stand-in on win32 (test.skipIf, as other POSIX-shim pregate fixtures are
handled) and drop the unproven cargo.cmd and Path-key changes. Selection,
planning and the hostile-name case still run on Windows.

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

* test(pregate): TODO(#3380) on the two win32-skipped cargo-lane cases

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

* fix(pregate): refuse a user policy whose link chain passes through the checkout

userConfigRejection checked the lexical path and the final realpath only. A
state root linked into the checkout, or a link hop inside it, let a branch pick
which existing out-of-repo policy governs it. Every hop of the resolution chain
is now checked. Found by the security review of #3286.

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

* fix(pregate): verify goes stale when a link on the way to the absent user policy dangles

A fresh run refuses a user policy path with a dangling parent link, but verify
looked only at the leaf, so the parent's ENOENT read as continued absence and
an old defaults pass stayed publishable. The loader and verify now share one
absence check. Found by the security review of #3286.

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

* docs(pregate): CHANGELOG entry for #3286 and the link-chain and dangling-parent rules

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

* v1.91.206.0 stamp

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: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: vilano93 <244097767+vilano93@users.noreply.github.com>
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
2026-10-25 10:50:18 +00:00
Garry Tanandcapy-ai[bot] 072d073b12 v1.91.205.0 feat(ship): Step 9 fix-range re-review, with a mandatory final full pass (#3339) (#3404)
* feat(ship): Step 9 fix-range re-review with a mandatory final full pass (#3339)

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

* fix(review-log): fix-range base is the commit the fixing pass started from; follow Step 9.4's real order (#3339 review)

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>
2026-10-25 10:37:48 +00:00
Garry Tanandcapy-ai[bot] 7b6d189b67 fix(skill-start): unattended block promises GSTACK_RESULT only to skills that write it (#3281, GSTA-83) (#3392)
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
2026-10-25 10:24:44 +00:00
Garry Tanandcapy-ai[bot] 5278207dbe v1.91.203.0 feat(router): verified cause → fix, bounded triage for unknown causes (#3157, #3158) (#3395)
* feat(router): verified cause → fix, bounded triage for unknown causes (#3157, #3158)

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

* test: count the failure-routing eval file in the paid allocation pin (#3157/#3158)

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

* test: classify the failure-routing gate cases as skill-tool Claude-only (#3157/#3158)

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

* fix(ship): trim the T4 root-cause line so ship fits its parity cap beside #3372 (#3158)

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

* v1.91.203.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>
2026-10-25 10:09:02 +00:00
Garry Tanandcapy-ai[bot] e3566ab141 fix(session-update): skip the team-mode auto-update while another session holds a workflow lease (#3363) (#3385)
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
2026-10-25 09:55:56 +00:00
Garry Tanandcapy-ai[bot] f344103a3d v1.91.194.0 feat(memorable): per-remote recall opt-in for read-only repos; recall veto fails closed (#3359) (#3381)
* feat(memorable): per-remote recall opt-in for read-only repos; recall veto fails closed on a corrupt policy store (#3359)

Closes #3359

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

* fix(repo-policy): reserve only the two metadata keys, so the _unattributed sentinel stays a valid policy key

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

* fix(memorable): recall lookup is a pure snapshot read that fails closed on malformed, dangling or unsearchable stores, and keys on the configured origin URL (security review)

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

* fix(memorable): check every configured origin URL, and confirm a missing store through its ancestors (security review, second pass)

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

* fix(memorable): read origin URLs NUL-delimited, and keep a dangling ancestor symlink off the no-store fast path (security review, third pass)

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

* fix(memorable): use each NUL-delimited origin URL verbatim (security review, fourth pass)

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>
2026-10-25 05:06:30 +00:00
Garry Tanandcapy-ai[bot] e66c43c572 v1.91.191.0 fix(version-bump): write --changelog re-heads [Unreleased]; /ship Step 12 acts on an incomplete queue (GSTA-70) (#3372)
* fix(version-bump): write --changelog re-heads [Unreleased]; /ship Step 12 acts on an incomplete queue (GSTA-70)

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

* v1.91.191.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>
2026-10-25 02:04:24 +00:00
Garry Tanandcapy-ai[bot] 225a210378 v1.91.189.0 fix(ci): bounded Playwright install with one infra retry, PR runs get main's local branch, Bun install default 1.4.4 (GSTA-68) (#3368)
* fix(ci): bounded Playwright install with one infra retry, PR runs get main's local branch, Bun install default 1.4.4 (GSTA-68)

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

* test(runtime-pins): read the install-default job's pin from GSTACK_BUN_DEFAULT (GSTA-68)

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

* v1.91.189.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>
2026-10-25 01:00:05 +00:00
Garry Tanandcapy-ai[bot] 632bde77de v1.91.187.0 docs: unattended contract, gate reply grammar, session-kind override and voice triggers match the code (GSTA-69) (#3367)
* docs: unattended contract, gate reply grammar, session-kind override and voice triggers match the code (GSTA-69)

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

* v1.91.187.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>
2026-10-24 23:55:21 +00:00
Garry Tanandcapy-ai[bot] b04cc35832 v1.91.182.0 fix(capy-install): verify Bun 1.4.4 archives so a 1.4.4 project pin installs instead of RUNTIME_PIN_CONFLICT (#3345)
* fix(capy-install): verify Bun 1.4.4 archives so a 1.4.4 project pin installs instead of RUNTIME_PIN_CONFLICT

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

* v1.91.182.0 stamp: fix(capy-install): verify Bun 1.4.4 archives (#3345)

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>
2026-10-23 05:47:18 +00:00
Garry Tanandcapy-ai[bot] a59b5e4097 v1.91.179.0 test(docsync): cut per-case spawn cost in the fault interface and split its outer run (flaky pass 2026-10-22) (#3342)
* test(docsync): cut per-case spawn cost in the fault interface and split its outer run (GSTA-63)

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

* changelog: docsync-fault-interface per-case cost (GSTA-63)

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

* v1.91.179.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>
2026-10-22 20:10:23 +00:00
Garry Tanandcapy-ai[bot] e7a1df9e18 v1.91.172.0 feat(capy): approvals per thread, Captain plan briefs and before/after PR images (#3328)
* feat(capy): approvals per thread, Captain plan briefs and before/after PR images (GSTA-61)

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

* fix(capy): mark whole rewrites in PR images, point the ship guide at the skill folder, refresh ship goldens (GSTA-61)

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

* test(context-budget): raise the Capy skill ceiling for "Working under a Captain" (GSTA-61)

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

* revert(capy): keep wrapper 2; owners copy the skill folder by hand (GSTA-61)

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

* v1.91.172.0 stamp (#3328)

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>
2026-10-20 23:55:41 +00:00
5ebda5999f v1.91.161.0 fix(pregate): Django, Dart, asset and tool-config lanes; init drafts from the repo (#3266-#3269) (#3306)
* 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>
2026-10-20 01:38:26 +00:00
71b007ae7c v1.91.160.0 fix(scope,review,ship): recognise Dart and Flutter in diff scope and test detection (#3284) (#3300)
* 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>
2026-10-20 01:06:17 +00:00
Garry Tanandcapy-ai[bot] 4c6fa89cdc v1.91.159.0 security: gstack's Bun entry points ignore the audited repo's .env and bunfig.toml (S1) (#3312)
* 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>
2026-10-20 00:24:28 +00:00
cb9ccf2e80 v1.91.158.0 feat(config): plan_mode_preamble=off drops the plan-mode preamble from installed skills (port of #3213) (#3308)
* 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>
2026-10-19 23:31:59 +00:00
afac8dfd87 v1.91.151.0 fix: contributor ports — plan-eng-review structure id, context checkpoint gaps, first-deploy how-to (#3299)
* 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>
2026-10-19 19:44:10 +00:00
Garry Tanandcapy-ai[bot] 6a4ab69dcd v1.91.146.0 feat(ship-queue): merge trains with pattern bisect and train-record verify (GSTA-49 T1) (#3272)
* feat(ship-queue): merge trains with pattern bisect and train-record verify (GSTA-49 T1)

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

* test(windows): curate ship-queue-train.test.ts out of the Windows subset (gh shim not resolved on windows-latest)

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

* v1.91.145.0 feat(ship-queue): merge trains with pattern bisect and train-record verify (GSTA-49 T1)

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

* v1.91.146.0 restamp: #3295 takes v1.91.145.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>
2026-10-19 10:29:44 +00:00
Garry Tanandcapy-ai[bot] a59734fed6 v1.91.132.0 feat(pregate): a remote gate for gstack's own pregate (Ubicloud or keyless local shards); concurrent next-version fetches (#3258)
* feat(test-free): --files-from runs exactly the listed planned files

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

* feat(pregate): declare scripts/ubicloud/pregate-remote.sh as gstack's remote gate (keyed Ubicloud VM, keyless local shards)

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

* perf(next-version): fetch PR VERSIONs concurrently; a timed-out fetch is an unknown claim

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

* docs(pregate): gstack remote-gate example and the small-machine pregate row

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

* fix(pregate): remote gate leaves paid test files to the paid gate, adds reason codes and the keyless backend line

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

* fix(next-version): unknown claims make the pick provisional (complete: false)

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

* docs(pregate): remote-gate reason codes, paid-file handling and measured keyed/keyless timing

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

* fix(pregate): remote gate finishes before pregate's deadline

pregate kills the remote gate at --timeout with a 5 s grace, which could cut a
VM teardown short. pregate-remote.sh now reads the enclosing deadline
(PREGATE_TIMEOUT_S, else the gstack-pregate --timeout it runs under, else 600),
reserves 120 s for teardown plus 15 s margin, and tells the VM to stop at the
earlier of that point and the cap's wall; a deadline that leaves under 300 s
refuses before any VM (PREGATE_REMOTE_TIMEOUT_TOO_SHORT). It names the VM up
front and prints the exact ubi-runner.sh down line, and the body is one braced
block so an edit to the script mid-run cannot change what the run executes
(keyed run 5 died after teardown with '$2: unbound variable' for that reason).

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

* fix(restamp): an incomplete claim queue is never reported as read

gstack-next-version now returns complete: false with unknown_claims when a
PR's VERSION fetch timed out or failed. restamp's claim-at-open path took any
usable JSON as 'read'; it now says 'incomplete', names the unknown PRs and
calls the version provisional, the same warn-not-refuse stance as the
existing 'unverified' fallback.

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

* fix(pregate): only a gstack-pregate process sets the remote gate's deadline

The ancestor walk matched any command line that mentioned gstack-pregate, so a
shell line running pregate beside the tests (bash -c '...; bin/gstack-pregate
...') gave the stub gate a 600 s deadline. It now matches only a process whose
program, or the script its interpreter runs, is gstack-pregate.

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

* docs(changelog): A3 remote gate, --files-from, concurrent next-version

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

* v1.91.132.0 chore: 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>
2026-10-18 02:13:29 +00:00
8f103baa71 v1.91.131.0 feat(pregate): Python import lanes (#3201) and a missing-dependencies regen diagnostic (#3252)
* feat(pregate): lanes follows Python imports and mock.patch targets

The import graph only parsed JS/TS imports, so in a Python repo every
touched module failed `lanes` with PREGATE_NO_LANE even though
SOURCE_EXT, TEST_FILE_RE and the pytest runner/summary already
understood Python.

Python sources now contribute `import a.b`, `from a.b import c`
(c may be a submodule), parenthesised multi-line lists and relative
`from ..pkg import x` edges, resolved to `a/b.py` or `a/b/__init__.py`
from the repository root. Package `__init__.py` files on the path get
edges too, since Python executes them on import. Modules outside the
repo resolve to nothing. In test files, dotted string literals such as
`'pkg.mod.Class.method'` (mock.patch targets) resolve to the longest
dotted prefix that is a repo module.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(regen): report missing dependencies as REGEN_DEPS_MISSING, not REGEN_STALE

When package.json declares dependencies and node_modules is missing, the
scratch worktree runs the generator without them. A generator that degrades
and still exits 0 then looked stale, and the fix line sent people to rerun
the generator. gstack-regen check and the pregate regen check now report
"dependencies missing; run <install>" with its own result code and
troubleshooting anchor; the install command follows the lockfile. The
fixture reproduces the shape GSTA-31 saw in gbrain.

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

* fix(pregate): keep Python import scanning linear and conservative

Review of #3201 on the integrated tree found two quadratic paths on
untrusted Python source and three ways a test could be left unselected:

- The from-import module spec let two repetitions share a dot, so a
  failed match on a long run of dots backtracked quadratically. The spec
  is now dots followed by an optional word-led name.
- Dotted literals and imports resolved every prefix with repeated
  split/join. Names are capped at 32 segments, deeper than any module path.
- A literal that also named a repo file skipped its mock.patch reading;
  both readings now add edges.
- A package and a same-named module both map (Python loads the package).
- Imports after `;` or a compound statement's `:` are recognised.

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

* fix(pregate): linear Python string blanking, non-empty from-spec, repo-bounded depth

Second review round on the Python import scanner:

- An empty from-spec let the whitespace on both sides of it compete, so
  `from` followed by a long run of spaces backtracked quadratically. The
  spec now starts with a dot or a word character.
- Strings and comments are blanked by a single linear scanner that honours
  backslash escapes, so an escaped triple quote no longer ends a docstring
  early and swallows the imports after it.
- The fixed 32-segment cap dropped real deep modules. Dotted names are now
  bounded by the repo's deepest path, which no module can exceed.

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

* docs(changelog): unreleased entry for Python import lanes and REGEN_DEPS_MISSING

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

* v1.91.126.0 stamp

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

* release slot 1.91.126.0: back to unstamped (CHANGELOG entry unreleased)

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

* fix(changelog): resolve the main-merge conflict (Unreleased entry above 1.91.127.0)

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

* v1.91.131.0 stamp

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

---------

Co-authored-by: Scott Gigante <scott@gethubflow.ai>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
2026-10-18 01:54:14 +00:00
Garry Tanandcapy-ai[bot] 77d125e0bc v1.91.127.0 fix(evals): paid-gate reliability round 2: docsync candidate protocol, scope-block detection, qa fix-loop budgets, lettered consent (#3260)
* test(qa-callers): bind QA section names in the caller prompt instead of teaching helper forms (W3-4)

The caller session prompt taught full capture/checkpoint syntax but only
mentioned materialize, so two ship-exploratory-small-cli sessions (paid
gate runs 38973329985, 38993076767) skipped qa/sections/exploratory.md and
system-functional.md and guessed a bare materialize. The prompt now
declares only fixture bindings (DEADLINE_TOOL, EVIDENCE_TOOL, PROBE_DIR,
DEADLINE_FILE, --public approval, --timeout-ms 10000 for plan probes) and
points to the section for every form. The read requirement is unchanged;
both captured transcripts are kept as negative controls.

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

* fix(review-army): run the state-machine detector in the scope block and check its exit status (W3-5)

Specialist 9 named gstack-state-matrix detect only in selection prose, and
small diffs keep only State Machines, so agents ran an undeclared command
(paid gate run 39002269281). The generated scope block now runs the
detector with its exit status checked: on failure it keeps the detector's
diagnostics and sets SCOPE_STATE_MACHINE=unknown, and selection dispatches
State Machines when the signal is true or unknown. The check step becomes a
declared heredoc block. The clarity test requires every non-diff-scope
signal to come from a detect helper the scope block runs; a free test runs
the block against a failing detector stub; the captured standalone call
stays outside the caller interface.

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

* fix(third-party-actions): letter the absent-Aside consent options B/C/D (W3-7)

The contract fixed letters only when Aside is detected, so a tpa-absent-linux
agent numbered its consent question 1/2/3 (paid run 39011101113), which the
lettered-offer inspection cannot read. The absent branch now offers
B) gstack's own visible browser, C) manual instructions, D) defer and omits
A). The paid test's assertion is unchanged; the captured numbered answer is
a documented fail example in the unit test.

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

* fix(plan-reality): say tier 1 rows are never n/a (W3-8)

The plan-review reality block offered pass|finding|n/a without saying tier 1
rows may never be n/a, so a plan-mode-no-op session wrote
`REALITY: deferred-asks n/a ...`, check returned repair, and the repair loop
ran past the 300 s capture (paid run 39004466422). The block now says tier
1 rows are never n/a and that with no linked issue deferred-asks is pass
with a receipt showing none. The captured line is pinned through check as
missing=deferred-asks verdict=repair.

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

* test(qa-functional): webhook fix runs re-verify the repaired invariant and rerun superseded captures (W3-6, W3-14)

W3-6: stage 2 of the fix-run prompt asked only for the original, happy,
cancel and dependency probes while verifyQANativeRegression reruns all
eight, so a status-only repair that left partial broken reached the verifier
(paid run 38986120559). The webhook fix prompt now states the capture budget
up front (at most 5 pre-repair captures, then the 7 post-repair scenarios:
duplicate, partial, concurrent-ab, concurrent-ba, happy, cancel, dependency;
12 in total; report incomplete if 5 cannot establish the defect). That list
feeds the capture nudge (GSTACK_QA_REQUIRED_PROBES);
QA_WEBHOOK_REQUIRED_SCENARIOS and the verdict are unchanged.

W3-14: the fix prompt now says to rerun every pre-repair capture the
verdict relies on after the final repair, and that a superseded row stays
open until its command reran on the repaired source (paid run 39053431488
labelled a pre-repair duplicate superseded and never reran it). The
detector and its negative test are unchanged; the captured shape joins them
as a negative control.

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

* fix(qa): tie the exploratory stop rule to per-rule input probes; fix runs close superseded captures (W3-9, W3-14)

W3-9: two qa-functional-cli-report sessions stopped at 5-7 of 12 probes
without probing a non-digit amount (paid runs 38983306684, 39001107574),
though system-functional.md requires one probe per documented input rule.
The single stop rule shared by both exploratory templates now says each
documented input rule needs its own probe breaking only that rule, and
rules left uncovered at a probe or time cap go in limits.

W3-14: for fix runs (not qa-only), every capture taken before a fix is
superseded even when its client prints no snapshot; before completion the
agent reruns each one its verdict relies on or reports it as an open
remainder. Production superseded/open semantics are unchanged.

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

* test(qa-functional): keep the captured webhook-report cat attempts as denied negative controls (Q3)

Paid run 39006896632 (qa-functional-webhook-report) attempted
`cat package.json probe.ts .gitignore` and `cat src/*.ts test/*.ts`; the
PreToolUse hook denied both. This is a model miss, not a detector defect, so
both commands are pinned as denied by the registered hook and outside
qaCommandAllowed. No detector change.

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

* fix(docs-candidate): per-subcommand --help and a compare reject that names the fix (W3-2)

snapshot --help and compare --help print the usage and exit 0 before any side
effect; the docsync observer admits exactly those two forms. compare names the
record path, each missing or wrong field (schema_version, base, hashes) and
the snapshot command that writes a valid record, keeping exit 1. The captured
ship-docsync-stale-after commands from PR gate run 38990111230 replay inside
the interface; help with any other argument stays rejected.

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

* fix(docsync-fault): prepare writes the production candidate through the installed snapshot (W3-1)

The fault actor prepare wrote its own {base_sha, content_hashes, ...} record,
which the installed gstack-docs-candidate compare rejects, while
ship/sections/documentation.md requires running compare; 79 of 198 docsync
captures (and the red in PR gate run 38990111230) were pushed off the
documented path by it.

prepare now runs the installed snapshot under a 30 s timeout for the same
selection, returns its exact command as snapshot_command so the post-child
rerun is literal, publishes prompt and event only after a valid record
exists, and removes a record its own failed snapshot left. dispatch observes
the candidate with the same installed snapshot; inspect returns base, index
and per-file git blob ids in the record representation; the legacy re-audit
check reads selected/hashes. The fault prompt names snapshot_command and the
new field names.

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

* docs(todos): file the qa-quick capture headroom follow-up (fix wave 3, T)

qa-quick timed out once at 300 s (paid run 38980005793) against passing
runs of 164-272 s. Trimming what the Quick tier loads is a product call, so
it is filed, not repaired; no budget change.

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

* test(docsync): keep the two docsync model misses as negative controls (W3-3)

The Bash grep of ship-docsync-missing-asset (PR gate run 38983306684,
bash#13) and the composed cat; cat of ship-docsync-failure (38984977679,
bash#12) were correct rejections. They join the stored known_bad commands
(with a {home} placeholder for the fixture home); no detector change.

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

* test(golden): refresh the ship goldens for the W3-5 scope block and W3-7 consent letters

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

* fix(third-party-actions): state the absent-Aside letters in fewer characters (W3-7)

Same contract (B), C), D) when Aside is not detected; the next sentence
already omits the Aside option), 67 fewer characters on the pinned
land-and-deploy and office-hours skills.

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

* test(skill-md-size): re-pin 31 over-limit skills grown by fix wave 3 prompt repairs (owner sign-off needed)

CREW rule 11: these skills were already over 100,000 characters and grow
by the minimum text each repair needs; none crosses the limit for the
first time.
- gstack-review on cursor, factory, kiro, opencode, slate (+657..+676):
  W3-5 runs gstack-state-matrix detect inside the scope block with its exit
  status checked and declares the check step as a heredoc block; the new
  fence carries the env-var-host runtime prelude (about 360 characters).
- gstack-plan-eng-review on 12 hosts (+104): W3-8 says tier 1 reality rows
  are never n/a (paid run 39004466422 looped on a rejected n/a row).
- gstack-land-and-deploy on 12 hosts and gstack-office-hours on 4 hosts
  (+21): W3-7 letters the absent-Aside consent options B), C), D).

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

* fix(skills): fit the W3-5, W3-7 and W3-8 prompt repairs inside the parity budgets

The parity suite left 2-42 bytes of headroom on ship, review,
plan-eng-review, plan-ceo-review and land-and-deploy, so each repair now
pays for itself in the same resolver:
- W3-5: the scope block runs `source <(gstack-state-matrix detect --base
  <base> || echo SCOPE_STATE_MACHINE=unknown)` (a failed detector's
  stdout is overridden by the later unknown assignment; its stderr still
  shows), the check block uses a short heredoc, and DIFF_LINES comes from one
  `git diff --numstat | awk` sum instead of two --stat greps (same count:
  insertions plus deletions, binary files zero).
- W3-7: the absent branch reads "offer only options B), C) and D)."
- W3-8: "never n/a" joins the tier 1 parenthesis, the no-linked-issue rule
  joins the receipt sentence, and the deep header says the same in fewer
  words; the redundant "(it prints the PLAN_REALITY: line)" is dropped.
Size pins: land-and-deploy (12 hosts, -20), office-hours (4 hosts, -20)
and plan-eng-review (12 hosts, -10) are lowered to their new sizes;
gstack-review on cursor, factory, kiro, opencode and slate stays above its
main pin (+355..+374) because the declared check block is a new fence that
carries the env-var-host runtime prelude. Raising those five pins needs
owner sign-off.

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

* fix(design-e2e): the shim-dom scan check resolves the command's literal _REPORT/_RUN (W3-10)

PR gate run 38980005793 went red because the agent ran the generated DOM
block, scanning "$_REPORT/dom/$_RUN" with _RUN="run1" assigned in the same
command, and the check looked for the literal dom/run1 anywhere in a command.

domScanOfRun substitutes that command's own literal _REPORT=/_RUN=
assignments and credits a scan only when one of its own arguments is the run
directory. scan --changed, another directory, quoted heredoc report text and
non-literal or conflicting assignments stay negative controls. All 15
captured shim-dom runs in the window pass the new check (14 passed the old).

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

* test(pty): keep the last non-blank viewport for failure dumps (W3-11)

autoplan-guard-pty (PR gate run 38976253774) stalled at 489 s and its screen
dump, the tail of visibleText(), was only spinner redraws. Live PTY sessions
now remember the last non-blank viewport a screen read returned
(session.lastScreen()) and the harness wait-timeout dumps append it.
Diagnostic only: visibleText(), matching and verdicts are unchanged.

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

* test(autoplan): explicit 30 s timeout for each answer-invalidation test (W3-12)

The eng-only override test ran 4.1-4.4 s on a 4-core machine against Bun's
5 s default (7.8-9.4 s per file). Each test now names a 30 s budget.

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

* test(review-army): size the nested bun test budgets for windows-latest (W3-15)

windows-free-tests run 39053431430 (shard 5): the nested bun test exited 1 in
5.58 s because Bun's default 5 s hook timeout fired on the real Red Team
beforeAll (8 git spawns plus skill-section copies). Both nested argvs pass
--timeout 30000, which raises only the nested default; the registrations
under test keep their explicit timeouts. Spawn timeouts move from 8 s to
40 s and outer test timeouts to 45 s, including the consensus test that had
none. With a 6 s beforeAll injected, the lifecycle still matches; the old
argv reproduces the Windows hook timeout.

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

* docs(todos): close the createQAFunctionalFixture entry and file D6 (W3-13)

The git auto-maintenance entry was fixed by 0bf9d50 (#3190). D6: nested bun
test output pollutes free-runner failure attribution in
scripts/lib/shard-engine.ts (windows-free-tests 39053431430).

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

* test(free-fixtures): declare the W3-4 captured caller transcript as a free-only fixture

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

* test(pty): strip cursor-visibility escapes and print the last screen on autoplan-guard-pty failure (W3-11)

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

* fix(review-army): keep the state-machine detector's output only on exit 0 (W3-5 confirm round)

A failing gstack-state-matrix detect prints SCOPE_STATE_MACHINE=false before
exiting non-zero. The scope block now captures its output and sources it only
when the detector succeeds; otherwise it discards the output, sets
SCOPE_STATE_MACHINE=unknown (dispatching the State Machines specialist) and
leaves the detector's stderr visible. Free tests cover false-output/non-zero,
empty-output/non-zero and a late failure after printing true. The stack
comment is shortened so gstack-review stays 4 chars under its pin; the five
host pins are lowered to match.

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

* test(qa-functional): reserve webhook post-repair reruns first inside the unchanged 12-capture ceiling (W3-6/W3-14 confirm round)

The fix prompt now states precedence: post-repair reruns (the 7 stage-2
scenarios plus each distinct pre-repair command the verdict relies on, such as
a reject probe) are reserved first, pre-repair exploration gets what remains,
and reruns that cannot fit are reported as open. A free test fits a pre-repair
reject plus the reproduced defect and its replay within 12 (11 captures).

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

* fix(docs-candidate): the compare reject never sends an agent to re-snapshot after the child audit (W3-2)

A post-audit snapshot over the candidate replaces the before-audit baseline, so
the ownership check would compare current files with themselves. The fix line
now says the candidate must be the record the Prepare snapshot wrote, that the
Prepare snapshot is rerun only before dispatching the child audit, and that the
post-child snapshot uses a different --out (<audit-id>-post.json). The helper
test pins the text.

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

* test(docsync-fault): diagnosable prepare snapshot failures with one fresh-id retry (W3-1)

A failed installed snapshot now returns exit 25 with a stable reason code
(snapshot_exit, snapshot_timeout, snapshot_spawn_error, snapshot_invalid_record),
the helper's stderr tail redacted through lib/redact-engine with the fixture
root as <HOME> and bounded to 800 characters, and keeps the attempt's files: the
candidate the helper left, if any, and prepare-<audit-id>-failure.json. The
failure is a prepare-failed event, not a rejected interaction. One retry is
allowed under a fresh audit id; reusing the failed id, dispatching it or a third
prepare is rejected. Tested before and after candidate creation. The two
snapshot tests get an explicit 30 s timeout: the compare/inspect agreement
test runs 4.3-4.8 s alone and timed out at Bun's 5 s default under load.

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

* fix(qa-evidence): count required post-repair probes only after the fixture-declared final source edit (W3-6/W3-14 confirm round)

GSTACK_QA_REQUIRED_PROBES reminders counted any complete capture, so a
pre-repair duplicate or partial capture looked like post-repair coverage. The
caller may now declare GSTACK_QA_REQUIRED_AFTER_EDIT with absolute paths and a
clean-fixture time: only captures started after the last edit under those
paths count, none count before the first edit, and a malformed declaration
counts none. Without the declaration every capture counts as before. The
webhook fix fixture declares its src/ directory. A free test covers
pre-repair-only coverage (nudges), complete post-repair coverage (no nudge)
and a second edit (nudges again).

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

* test(harness): bump HARNESS_VERSION to 5 for the W3-11 stripAnsi change

stripAnsi now removes cursor-visibility escapes (ESC[?25h/l). Every PTY case
matches patterns against its output, so a pattern spanning one of those escapes
can now match where it did not before: a verdict can change, so this is a
behavioral bump, not a non-behavioral entry. The launch.ts change (last
non-blank screen in failure messages) is diagnostic only.

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

* fix(review-army): pass the state-machines output to check as a written file, not a heredoc placeholder (W3-5)

The generated-bash lint (env-host-fence-conformance INV-3) rejects free text in
a heredoc body: <its complete output> reached the shell. The agent now writes
the specialist's full output to .gstack/tmp/state-machines.md and the fence
runs gstack-state-matrix check on that file. Two stack-detection comments and
the parenthetical are trimmed to stay inside the review/ship parity ratios;
gstack-review shrinks by 33 on the five pinned hosts and their pins are
lowered to match.

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

* test(autoplan-guard-pty): the scripted actor ends its turn after a background CEO dispatch before reading the close packet

PR gate 39070276914 (dc739be) failed at the close-packet check while gate
39069171159 (b789898) passed with byte-identical autoplan inputs. The evidence
differs only in order: the failing actor read the close packet in the dispatch
turn, so the first tool call after the CEO notice was the publication. The
product guard allowed all four guarded calls in both runs. The fixture step 5
told the actor to end its turn only conditionally; step 5 now ends the turn as
soon as the result says the agent runs in the background, and step 6 reads the
close packet only after the reviewer finished. The close-packet check moves to
closePacketReadAfterNotice, replayed in a free test against both captured
orders; a second free test pins the actor's steps 5 and 6.

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

* chore(release): v1.91.127.0 fix wave 3 paid-gate reliability round 2

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

* fix(qa): input-rule probes are per field, and a value breaking two rules covers neither (W3-9)

PR gate 39073934326 (93449bc) failed qa-functional-cli-report with no observed
unannounced defect, the same class as 38983306684 and 39001107574. The agent
read 'allowed characters' as the ID rule only, counted -5 as its sign probe
and never probed the amount's digits-only rule with an otherwise valid value,
so the seeded parseInt defect stayed hidden. The stop rule now says each
field's documented input rule needs a probe breaking only it, and that a
signed amount breaks sign and digits-only and covers neither; the functional
table row names input rules per field. The qa union stays inside its parity
ratio.

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

* fix(qa): state the input-rule principle instead of a worked example (W3-9)

The signed-amount example in 0fe616e was misread in the focused rerun as 'a
signed amount breaks only the digits-only rule', and the run again missed
the seeded non-digit defect. The stop rule now states the principle: each
field's documented input rule needs a probe that breaks only it and satisfies
every other rule.

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

* test(tpa): the Darwin pitch detector accepts the canonical phrase anywhere, short forms only at a sentence start

PR gate 39075977549 (d6d82fb) failed tpa-absent-darwin on a correct pitch:
'One-time note: you can download Aside (macOS 15+) at aside.com, then open it,
sign in, and re-run'. The contract is to deliver the pitch once; the
sentence-start anchor exists only so narration is not counted. The full
canonical phrase now counts anywhere unless a negation precedes it, and the
short forms (download it / download Aside at aside.com) keep the
sentence-start rule. The detector moves to test/helpers/third-party-actions.ts
with a count helper. Free tests replay the captured red (counts once), the
four captured passing Darwin pitches (once each), five Linux and five READY
narrations plus both recovery-public responses (zero), a mid-sentence short
form and negated phrases (zero), and a duplicated phrase (two).

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

* test(qa-callers): only a required plan check may swap the deadline for --timeout-ms; reruns keep the deadline

PR gate 39077524802 (0f71a55) failed ship-exploratory-late-input: after the
late input change the actor reran captures 001 and 002 with --timeout-ms
10000, reading the fixture's 'explicit plan probes outside the smoke budget'
clause as covering revalidation reruns, and the verifier reported both as
smoke probes missing a trusted deadline run. The binding now says only an
explicit plan check this phase requires may use --timeout-ms, and every other
capture, including a rerun or revalidation, uses the deadline.

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

* test(third-party-actions): count the Darwin pitch only at a recommendation boundary

/review delta on 51f1e02 (Claude and GPT-6.1 Sol voices): the 0f71a55
detector accepted the canonical phrase anywhere unless the adjacent word was
a negation, so a Darwin refusal or a quoted policy mention ('I will not ask
you to download Aside ...', 'The Darwin policy says to "download Aside ..."',
'You cannot download ...') satisfied the paid case's positive match, and a
free assertion even expected "You don't need to download ..." to count.

The canonical phrase now counts only where a recommendation can start: a
sentence, clause (, : ; dash), bullet, line or bold start, optionally led by
then/please/just/so/first/now and you can/could/may/should/need to, with no
negation directly before it across a line break. Quoted, parenthesised,
reported and clause-negated mentions fall outside that boundary. Every probe
from both voices is a free test; the five captured Darwin pitches (including
the 39075977549 red) still count 1 each and the Linux/READY captures 0.

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

* fix(review-army): write the state-machines output to a fresh private file from gstack-state-matrix outfile

/review delta on 51f1e02 (Claude F2): dc739be wrote the specialist output to
a fixed .gstack/tmp/state-machines.md with no exclude. On a second review
Claude Code's Write refuses an existing unread file, so check could validate
the previous run's output, and git add -A could commit it.

gstack-state-matrix outfile creates .gstack/tmp (0700) at the repo top,
adds /.gstack/tmp/ to info/exclude once, and prints a new empty 0600 file
per call; the selection prose has the agent Read that file, write the full
output there and check that path. The prose moved the check command inline,
so gstack-review shrinks and its pins are lowered (cursor 141010->140637,
factory 144792->144416, kiro 140816->140449, opencode 143258->142872,
slate 140913->140543).

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

* test(qa-evidence): pin the post-repair edit's mtime to capture 002's completion

The GPT voice's /review run on 51f1e02 failed qa-evidence.test.ts:547 once:
capture 003 reported no required probe remaining, so capture 002 (started
before the edit) counted as post-edit. The test wrote the edit right after
002 returned, a gap of one child run (7-27 ms measured here; the voice's
machine ran this test in 59 ms against 183 ms here, so a few ms there),
while an ext4 mtime comes from the kernel's coarse clock and lags
Date.now() by up to a tick (0.5-4.5 ms measured here).

The edit's mtime is now set to 002's recorded completedAt, so 002 started
strictly before the boundary and 003 starts after it on any machine. The
product boundary is unchanged: tick-scale imprecision only matters when a
capture starts within milliseconds before an edit, and a stricter margin
would refuse a real edit && capture chain.

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>
2026-10-18 00:37:43 +00:00
4e4f2959cd v1.91.126.0 fix(brain-sync): hold case-colliding artifact paths instead of reporting ok (#3220) (#3254)
* fix(brain-sync): verify requested index entries after add

Refuse missing or unrelated staged paths and stale indexed content without consuming the queue or resetting the index.

* fix(brain-sync): refuse new casefold path collisions

Check component prefixes in requested/index and outgoing trees, including retained commits and fetch/merge retries. Preserve historical aliases, local commits, index, and queue on refusal.

* fix(brain-sync): hold case-colliding paths instead of halting the sync (#3220)

Builds on Kkkkkuro's staged-index and casefold checks (#3245, from #3223).
A queued path whose spelling differs only by letter case from another path
in the index or the batch is held before staging: it stays queued, every
other file keeps syncing, and the status is "collision" rather than "ok".
An established spelling keeps syncing; only the new alias waits.

Skill start prints one ARTIFACTS_SYNC attention line naming the colliding
names and the repair anchor (docs/troubleshooting.md#artifact-case-collision);
gstack-doctor warns the same way. The outgoing check is cached by HEAD so
the empty-queue drain at every skill boundary stays near main's cost.
gstack-slug and skill start's slug path are unchanged.

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

* fix(brain-sync): close review gaps in the case-collision hold

- Windows: write the held-path list with LF and strip CR from helper output,
  so CRLF cannot let a held path through or blank case_names.
- --publish-removals runs the outgoing case check before it pushes.
- The cached outgoing verdict is keyed by every ref, not HEAD alone, so a
  moved remote baseline is re-checked.
- The repair snippet folds only JSONL logs and lists snapshots and other
  files for a manual decision.

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

* fix(brain-sync): second review pass on the case-collision hold

- The repair loop also folds the dropped spelling's untracked (held) files
  and says to check for leftovers before removing the folder on Linux.
- The cached outgoing verdict also keys on HEAD's symbolic target.
- The outgoing check no longer needs a temp file, so a failed mktemp can
  no longer skip it.

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

* test(brain-sync): give the retry-merge fixture a git identity

The retry merge commits with the ambient identity. CI and Ubicloud runners
have none under the test HOME, so the merge failed before the outgoing case
check ran and the drain reported push_failed instead of error.

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

* docs(changelog): unreleased entry for the #3220 case-collision hold

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

* v1.91.126.0 stamp

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

---------

Co-authored-by: KuroChen <Kkkkkuro@users.noreply.github.com>
Co-authored-by: Kkkkkuro <180382840+Kkkkkuro@users.noreply.github.com>
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
2026-10-17 21:17:39 +00:00
82530ac143 v1.91.117.0 fix: pregate bats lanes, WTREE_UNAVAILABLE, doctor rows, SKILL.md size guard, planMode hosts (fix wave PR 5) (#3236)
* fix(pregate): recognise bats test files so lanes does not fail in bats repos (#3161)

gstack-pregate never treated a .bats file as a test, so in a repository whose
tests are bats files the lanes check failed every run with PREGATE_NO_LANE: a
.gstack/pregate.json dependency pointing at tests/foo.bats selected nothing, a
selection manifest printing .bats paths yielded 0 files, and a bats run that
executed no tests could not trip the zero-run rule.

Recognise .bats in the test-file pattern and in the manifest-output parser, and
read the bats TAP plan and pretty summary (as a last resort, after every
existing runner's summary) so a run that reports zero tests is a failure.

Fixes #3161.

Cherry-picked from #3177 (d355c116b). Conflicts resolved as in the author's own
merges (b09476beb, 444f4e768): lane-selection.ts keeps main's isTestFile-based
manifest parser, and lanes.ts carries the bats summary on main's supervised runner.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

* test(pregate): a nested .bats file is selected and run end to end (#3161)

Reviewer test for #3177: touched itself with no declaration, and through a
declared dependency; a failing nested bats run reports the bats counts.

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

* fix(pregate): a wtree failure is WTREE_UNAVAILABLE, not an unresolvable base (#3197)

The pregate consumers of the working-tree fingerprint (buildContext,
withFingerprint, verifyPregate) move to lib/script-invocation.ts's
{tree}|{error} form, and lib/regen.ts drops its string-returning wrapper.

- A fingerprint failure was reported as "policy source unavailable:
  origin/<base> is not a resolvable commit" with git fetch as the fix. It is
  now WTREE_UNAVAILABLE: a launch failure lists each Windows bash candidate
  with its rejection reason and names GSTACK_GIT_BASH; a gstack-wtree exit
  keeps the repair it printed (set TMPDIR to a writable directory).
- A fingerprint failing after a check is incomplete with that cause, not
  "working tree changed"; verify says the tree cannot be fingerprinted.
- #3190's gstack-config read (test_backend) goes through runScriptSync, so
  Windows reaches it through Git Bash. The tripwire learns the bin-constant
  shape that let it through.
- Under --json every context refusal is one envelope on stdout
  ({ok:false,status:'refused',code,detail,fix,docs}, exit 3); the run object
  carries ok and status too, and GSTACK_RESULT goes to stderr.

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

* ci(pregate): gstack-pregate joins the Windows pregate job; pregate tests return to the Windows lane (#3197)

- windows-pregate-e2e runs gstack-pregate run (preflight: regen, secrets
  through the gstack-redact bun bin, literals, guards) in both journeys, and
  asserts a missing GSTACK_GIT_BASH refuses with one WTREE_UNAVAILABLE --json
  envelope.
- test/helpers/pregate-fixture.ts and restamp-fixture.ts runBin launch bins
  through lib/script-invocation.ts (bun bins as Bun argv, bash bins through
  Git Bash on Windows), which removes the "shebang via CreateProcess" reason
  for the pregate tests; they move from KNOWN_WINDOWS_INCOMPATIBLE to
  KNOWN_WINDOWS_SAFE.

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

* doctor: list rescue refs and warn about CLAUDE.md skill lists

gstack-doctor's plain report gains two rows. 'rescue refs' lists each
refs/gstack/rescue/* commit in the install checkout, newest first, with its
date, a needs-manual-resolution label when the rescue held unmerged files, and
the shell-quoted inspect and delete commands bin/gstack-ff-update's banner
prints. 'CLAUDE.md' warns when a gstack section in ~/.claude/CLAUDE.md or the
project's CLAUDE.md enumerates skills (#3169) and prints the short block to
paste instead; docs/troubleshooting.md#claude-md-skill-list explains it.

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

* doctor: name the Windows bash gstack resolves and what chose it

On Windows (OSTYPE msys/cygwin) gstack-doctor adds a 'windows bash' row from
lib/script-invocation.ts resolveWindowsBash, run through the new thin
bin/gstack-windows-bash.ts with the bun on PATH: the resolved bash and the
override or discovery step that chose it, a warn when GSTACK_GIT_BASH and
GSTACK_BASH differ, and a fail carrying the BASH_UNAVAILABLE line (every
candidate with its rejection reason) when none qualifies. Tests inject the
platform (GSTACK_DOCTOR_PLATFORM) and the discovery inputs
(GSTACK_DOCTOR_BASH_PROBE) so the row runs on every OS.

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

* doctor --check: name the held-install recovery on Windows, not bin/gstack-upgrade

bin/gstack-upgrade compares a pwd -P path (/c/...) with git's toplevel
(C:/...) and refuses on every Git Bash install, so on Windows the revision
row's upgrade fix (REVISION_BELOW_MINIMUM, the Capy-wrapper variant and
REVISION_UNMET) names the held-install recovery forms from
docs/troubleshooting.md#auto-update-held-install-recovery instead: the Git
Bash form and the Windows PowerShell 5.1 form, each labelled by shell with the
install path quoted for that shell. Other platforms keep bin/gstack-upgrade.
A test pins both printed forms to the documented ones.

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

* fix(skills): omit the plan-mode section on hosts without plan mode (#3168)

generatePlanModeInfo now honours capabilities.planMode. Hosts that declare
planMode:false (codex, agy, hermes, openclaw, and define-host's default:
factory, kiro, slate, cursor, gbrain) no longer render the
'## Plan Mode Safe Operations' and '## Skill Invocation During Plan Mode'
preamble sections. Claude, copilot and opencode are unchanged.

The codex, factory and agy ship goldens are regenerated. The clarity test that
pinned the plan-mode entry-order prose on every host now pins it on planMode
hosts and asserts its absence elsewhere (intentional contract change).

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

* test(skills): guard generated SKILL.md size at 100,000 chars on every host (#3199)

A free test renders every host and fails when a generated SKILL.md exceeds
100,000 characters, naming host, skill, actual size, limit and the fix. The 89
current offenders are pinned at their exact size in
test/fixtures/skill-md-size-allowlist.json: growth past a pin fails, and a
shrink fails until the pin is lowered. The shared-preamble hoist is deferred
(owner Garry, revisit 2026-11-15).

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

* test(pregate): keep #3177's bats tests beside the nested-directory test

Moves #3177's bats describe block verbatim from test/pregate-selection.test.ts
into test/pregate-bats-nested.test.ts, so pregate-selection.test.ts is
unchanged on this branch and the in-flight field-list-2 pregate lane
(capy/fl2-L6-pregate, which edits that file's imports and adds cases) merges
without a conflict. No assertion changes.

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

* doctor --check: keep bin/gstack-upgrade as the Windows fix (path spelling fixed on main)

Reverts 37bac07f8. Plan item D named the held-install recovery forms on
Windows only until bin/gstack-upgrade stopped refusing Git Bash installs.
bin/gstack-upgrade:63-67 now compares with gstack_path_same from
bin/gstack-path-spelling.sh, pinned by test/gstack-upgrade-path-spelling.test.ts,
so bin/gstack-upgrade is the right fix on every platform again.

* test(skills): measure the size-guard pins on v1.91.109.0

#3225 (merged into this branch) grew 53 pinned skills: 3 characters each from
the Aside driver resolver, and about 340 more in plan-design-review and
plan-devex-review from the #3219 summary sections. The pins are this PR's
starting measurement, so they are taken on the merged base; no skill this PR
renders grew.

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

* ci(windows): only the pregate tests that pass on windows-latest join the lane

windows-free-tests run 38984824731 (this branch at 8ed6da402) ran the moved
files. gstack-regen and the new pregate-wtree-refusal passed. pregate-guards,
-remote, -regate, -invalidation, -selection and -bats-nested launched
gstack-pregate fine but fail on Linux-host expectations: their fixture lanes are
Linux workflow jobs, which a Windows host records as requires-remote
obligations. They go back to KNOWN_WINDOWS_INCOMPATIBLE with that reason
instead of "shebang via CreateProcess".

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

* windows curation: keep CI run ids out of reason prose (pii.phone false positive)

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

* skill-md size pins: follow #3231's review-army line (+21 chars on five inlining hosts)

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

* doctor test: drop the reverted Windows upgrade-fix wording from item D's describe

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

* skill-md size pins: follow #3226 (plan-devex-review +3324) and #3234 (autoplan +59)

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

* WTREE_UNAVAILABLE moves to lib/result-codes-release.ts (#3235 split; result-codes.ts stays at 796 of 800 lines); doctor fixture copies the release table; size pins follow #3235's review +1

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

* docs(changelog): fix wave PR 5 release notes

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

* v1.91.117.0 fix: fix wave PR 5 (pregate bats + WTREE_UNAVAILABLE, doctor rows, SKILL.md size guard, planMode hosts)

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

* fix(qa): an invalid-input probe breaks only the rule it tests

The paid gate's qa-functional-cli-report failed again on #3236 (run
39001107574): the agent probed the digits-only amount rule with -5, which
the positivity check rejected first, so the planted parseInt defect stayed
hidden and it reported no defect. The contract map's invalid-input row now
asks for each input to break only the rule it tests, so another check
cannot reject it first.

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

---------

Co-authored-by: harjothkhara <48686985+harjothkhara@users.noreply.github.com>
Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
2026-10-17 04:36:00 +00:00
cadbd0af15 v1.91.116.0 feat: field list 2 wave B: gstack-ship-queue, approval classes, state-machine review, pregate timing check, update-check main moves (#3235)
* fix(capy): the install trailer says behind=<n> and its fix, so a stale install shows in every relayed trailer (field list 2 item 13, #2378)

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

* fix(capy): a lease records its holder's start identity, so a pid reused after a wake is reclaimed; a skipped session-start upgrade ends fail behind, never ok (GBRA-105, GBRA-106)

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

* fix(lease): stale needs proof (unreadable or malformed identities keep the lease), ps identity is timezone-independent, and reclaim never deletes a concurrent renewal

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

* feat(autoplan): enforced round cap and spend budget, deferred close, public export without machine paths, opt-in durable checkpoints (field list 2 W5-W7)

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

* fix(lease): one directory lock for publish, prune and reclaim; reclaim rereads the record judged stale, so a renewal is never deleted or hidden

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

* fix(lease): the directory lock names its holder; broken only when the holder is dead or its pid reused, unlock removes only its own lock, touch exits 3 under a live holder

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

* fix(lease): breakers serialize on .lock.break and reread the lock before removing it; gstack-skill-start reports a lease the held lock kept out

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

* docs(lease): name the double-crash limit of breaking a dead lock

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

* feat(capy): --for review-voice install profile, no-Drive bootstrap command, reviewer prompt trailer (field list 2 W11)

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
(cherry picked from commit 89bf556c11)

* test: pin review-voice row, helpers row and the no-Drive fallback in existing install tests

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
(cherry picked from commit 7f6c70459a)

* test: the DRIVER template names the reviewer trailer relay (pregate lane)

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
(cherry picked from commit 2545d9a6d2)

* test(context-budget): capy skill eager ceiling for the review-voice subagent paragraph (W11; ratchet protocol, capture value)

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

* feat(eval-plan): prereg diagnostics dry run (W8) and .gstack/eval-models.json lint (W9), WIP

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
(cherry picked from commit b509096218)

* feat(eval-plan): W8/W9 tests, docs/eval-models.md, troubleshooting anchors, /eval-plan steps

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
(cherry picked from commit 9335e45de2)

* test(eval-plan): eval-plan/SKILL.md names only defined codes and accepted flags

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
(cherry picked from commit a7a974d268)

* test(review-voice): the launch line points at the verdict's bun-only directory (#3233's launch contract)

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

* test(third-party-actions): the Aside drive-offer detector matches the product name, not the English idiom (paid run 38988927989 tpa-absent-linux false positive)

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

* fix(autoplan): export refuses artifact paths outside the run before writing; a deferred close keeps earlier undecided findings; reopening CEO grants Eng its rerun (review of #3234)

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

* fix(restamp): gate-ahead keeps the lane CHANGELOG entry when both PRs took the same tentative stamp (GBRA-102 diagnosis fixture)

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
(cherry picked from commit 43c75a6bb6)

* test(tree-receipt): a real gbrain stamp pair is same-modulo-stamps under gbrain committed policy (W1 acceptance)

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
(cherry picked from commit 6cf53f9fed)

* feat(ship-policy): gate_command, full_e2e and min_gstack_version; unknown keys stay fail-closed; validate prints introducing versions

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
(cherry picked from commit 878a7123d7)

* feat(ship-queue): gstack-ship-queue ready, stamp, verify and gate-ahead (WIP, tests next)

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
(cherry picked from commit 409a5957ad)

* test(ship-queue): stamp journal and resume, verify from a separate clone, gate-ahead attempts, ready

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
(cherry picked from commit ade554087b)

* docs(ship-policy): Run a shared merge queue; new keys and compatibility rule; troubleshooting anchors; /ship pointer

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
(cherry picked from commit 7fc73e8892)

* fix(ship): shared-queue pointer rides the show-effective ship: line (ship skeleton has 2 bytes of parity headroom)

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
(cherry picked from commit 5b33d4a015)

* test(windows): curate the shared-queue tests out of the Windows subset (shebang spawns)

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
(cherry picked from commit 8deee14728)

* test(fixtures): register the gbrain stamp-pair fixture as free-only

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
(cherry picked from commit 563a07aa03)

* test(ship-queue): receipt digest contract names lib/ship-queue.ts for its pregate lane

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
(cherry picked from commit 501637c258)

* feat(pregate): timing check flags elapsed-time bounds and sleep-then-assert in added test lines (W4)

lib/pregate/checks/timing.ts prints TIMING_ASSERTION: <file>:<line> <pattern>
and a TIMING_ASSERTIONS summary; gstack-timing-ok: <reason> in any comment
syntax on the line or the line above escapes one. It warns
(PREGATE_TIMING_ASSERTION): measured on gstack (9 of 47 test-touching
commits, 263 hits in the tree) and gbrain (37 of 95 commits; 31 of 38 sites
of the five timing-flake fixes), blocking would stop a fifth to two fifths
of test changes. Adds the /review checklist rule and the /investigate
timing-flake playbook.

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
(cherry picked from commit a3b86b10b2)

* feat(update-check): an equal VERSION with a moved main is an upgrade, via the compare API (W12)

When VERSION matches but ls-remote's main SHA differs from this checkout's
HEAD, bin/gstack-update-check asks the GitHub compare API (status, ahead_by)
through the existing receipted curl and prints
UPGRADE_AVAILABLE <ver> <ver>+<n> <sha>; a diverged or unreadable compare is
CHECK_FAILED (unknown), and behind/identical is up to date. Never fetches.

The cache gains an IDENTITY line (head, compared remote SHA, upstream): an
outcome replays only for the same HEAD and upstream, a same-remote refresh
reuses it without another compare, and a git checkout ignores a legacy
one-line cache (non-git installs keep the one-line format). The snooze is
keyed by the SHA. gstack-upgrade's question, snooze note and What's New step
handle the four-field line; gstack-skill-end reports it.

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
(cherry picked from commit 421e66d2a4)

* fix(pregate): treat planning and evidence markdown as an empty lane

A docs-only diff with no .gstack/pregate.json failed lanes as
PREGATE_NO_LANE. Planning and evidence Markdown is now a docs/no-tests
empty lane. Unmapped source files still fail closed.

Co-authored-by: Nick <surlymo@apache.org>
(cherry picked from commit e8793e59e8)

* fix(pregate): keep test-read markdown on its test lane

Unmarked Markdown that no test depends on is a docs/no-tests empty lane.
A file a test reads, including a fixture, still selects that test through
the import graph. Unmapped source and unreferenced test-tree files still
fail closed.

Co-authored-by: Nick <surlymo@apache.org>
(cherry picked from commit 7aabbddf3d)

* test(pregate): reviewer edges for the docs/no-tests empty lane (#3215)

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
(cherry picked from commit 2fb3fa8613)

* test(pregate): requires-remote on a gbrain-shaped repo (ci:ubicloud, UBI_OWNER) (W10)

A base pregate.json with gbrain's dependencies and remote_gate
"bun run ci:ubicloud" (a package script whose stub needs UBICLOUD_API_KEY,
names VMs from UBI_OWNER and prints PREGATE_REMOTE). Under 8 cores the test
tier is one obligation naming the command; --remote passes the caller's
UBI_OWNER and key through and clears it on the report; without the key the
gate's own error shows and the obligation stays uncleared. #3190's path
needed no change.

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
(cherry picked from commit 4d69ff02df)

* test(pregate): the ship receipt counts the timing check (7 checks)

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
(cherry picked from commit cb03ef67e7)

* test(context-budget): gstack-upgrade eager ceiling for the W12 equal-version upgrade question (ratchet protocol, capture value)

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

* feat(approval-classes): gstack-approval-classes and ship-receipt citation enforcement (W2, WIP)

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
(cherry picked from commit 847c7dae3d)

* feat(approval-classes): tests, docs, /review and /ship wiring (W2)

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
(cherry picked from commit 269bd5b8b5)

* feat(review): state-machines specialist and gstack-state-matrix round-trip check with a captured #6792 regression (W3)

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
(cherry picked from commit 1a634979c6)

* fix(review-army): keep the --simplification force flag pinned; approval test timeouts

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
(cherry picked from commit 789d392bce)

* test(free-fixtures): register the state-matrix-6792 fixture

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
(cherry picked from commit 1bacef296d)

* test(state-matrix): cover state-machines NEVER_GATE in gstack-specialist-stats

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
(cherry picked from commit 859063d56f)

* test(fixtures): register the pregate-timing fixtures as free-only (W4)

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

* fix(ship-queue): full_e2e needs bound gate evidence in ready and verify, CI reads every check-run page and commit status, next: quotes its paths (review #3235 P1-1, P1-5, P2-1)

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

* fix(pregate): timing reads a native diff and never passes an unread one

The timing check's git diff honored diff.external, textconv and
diff.noprefix, so a configured driver hid every added line and the check
passed. Run it with --no-ext-diff --no-textconv and explicit a/ b/
prefixes, and report a git diff that fails or does not finish as
incomplete (PREGATE_INCOMPLETE), which a warn downgrade does not soften.

Review of #3235, sibling of P1-4 (lib/pregate/checks/timing.ts:189).

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

* fix(approval-classes): only affirmative APPROVED evidence counts, explicit tokens are authoritative, native diffs fail closed (review #3235 P1-2, P1-3, P1-4)

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

* fix(state-matrix): detect reads the native diff, ignoring diff.external and textconv (review #3235 P1-4)

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

* test(e2e): review-army-state-matrix behavior case on the #6792 fixture (field list 2, W3)

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

* fix(review-army): tighten the State Machines dispatch line so /ship stays under its parity size ratio with main's #3231 review-army text

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

* fix(approval-classes): only top-level approval lines count; quoted, fenced, indented-code and commented text never does (review #3235 r2)

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

* fix(approval-classes): approval evidence is the first non-blank line only, at column 0 (review #3235 r3)

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

* test(autoplan-budget): bind the reservation expectation to lib/autoplan-budget's default estimate (test-of-test ratchet)

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

* fix: keep gstack-upgrade under its size budget, exempt state-matrix's fetch hint, fit the periodic plan's 37 slices

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

* fix(evals): the docs observer accepts the spawned-prefixed gstack-skill-start --help read

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

* fix(windows): curate capy-review-voice out of the Windows subset (POSIX launcher contract)

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

* docs: changelog entry for field list 2 wave A

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

* chore: stamp v1.91.115.0

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

* test: the scope-signal clarity check follows helper-printed signals; the hermetic update-check fixture writes the identity cache

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

* refactor: move the release-path result codes to lib/result-codes-release.ts (module-size ratchet); fit the periodic plan's 38 slices

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

* docs: changelog entry for field list 2 wave B

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

* chore: stamp v1.91.116.0

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

* test: copy lib/result-codes-release.ts wherever a fixture copies lib/result-codes.ts; the scope-signal check requires the helper's computed emission

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

* test: state-matrix stats test resolves its slug under its own state root (home containment)

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

* fix(windows): curate pregate-docs-lane-review out of the Windows subset (runBin shebang, like the other pregate fixtures)

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

* fix(windows): curate pregate-remote-gbrain out of the Windows subset (runBin shebang, like pregate-remote)

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>
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Nick <surlymo@apache.org>
2026-10-17 03:32:16 +00:00
Garry Tanandcapy-ai[bot] c71d764ba4 v1.91.115.0 feat: field list 2 wave A: autoplan round cap and public export, eval-plan diagnostics and model rules, --for review-voice (#3234)
* fix(capy): the install trailer says behind=<n> and its fix, so a stale install shows in every relayed trailer (field list 2 item 13, #2378)

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

* fix(capy): a lease records its holder's start identity, so a pid reused after a wake is reclaimed; a skipped session-start upgrade ends fail behind, never ok (GBRA-105, GBRA-106)

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

* fix(lease): stale needs proof (unreadable or malformed identities keep the lease), ps identity is timezone-independent, and reclaim never deletes a concurrent renewal

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

* feat(autoplan): enforced round cap and spend budget, deferred close, public export without machine paths, opt-in durable checkpoints (field list 2 W5-W7)

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

* fix(lease): one directory lock for publish, prune and reclaim; reclaim rereads the record judged stale, so a renewal is never deleted or hidden

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

* fix(lease): the directory lock names its holder; broken only when the holder is dead or its pid reused, unlock removes only its own lock, touch exits 3 under a live holder

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

* fix(lease): breakers serialize on .lock.break and reread the lock before removing it; gstack-skill-start reports a lease the held lock kept out

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

* docs(lease): name the double-crash limit of breaking a dead lock

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

* feat(capy): --for review-voice install profile, no-Drive bootstrap command, reviewer prompt trailer (field list 2 W11)

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
(cherry picked from commit 89bf556c11)

* test: pin review-voice row, helpers row and the no-Drive fallback in existing install tests

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
(cherry picked from commit 7f6c70459a)

* test: the DRIVER template names the reviewer trailer relay (pregate lane)

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
(cherry picked from commit 2545d9a6d2)

* test(context-budget): capy skill eager ceiling for the review-voice subagent paragraph (W11; ratchet protocol, capture value)

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

* feat(eval-plan): prereg diagnostics dry run (W8) and .gstack/eval-models.json lint (W9), WIP

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
(cherry picked from commit b509096218)

* feat(eval-plan): W8/W9 tests, docs/eval-models.md, troubleshooting anchors, /eval-plan steps

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
(cherry picked from commit 9335e45de2)

* test(eval-plan): eval-plan/SKILL.md names only defined codes and accepted flags

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
(cherry picked from commit a7a974d268)

* test(review-voice): the launch line points at the verdict's bun-only directory (#3233's launch contract)

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

* test(third-party-actions): the Aside drive-offer detector matches the product name, not the English idiom (paid run 38988927989 tpa-absent-linux false positive)

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

* fix(autoplan): export refuses artifact paths outside the run before writing; a deferred close keeps earlier undecided findings; reopening CEO grants Eng its rerun (review of #3234)

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

* test(autoplan-budget): bind the reservation expectation to lib/autoplan-budget's default estimate (test-of-test ratchet)

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

* fix(evals): the docs observer accepts the spawned-prefixed gstack-skill-start --help read

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

* fix(windows): curate capy-review-voice out of the Windows subset (POSIX launcher contract)

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

* docs: changelog entry for field list 2 wave A

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

* chore: stamp v1.91.115.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>
2026-10-17 02:40:45 +00:00
Garry Tanandcapy-ai[bot] bc2ed0cee7 v1.91.114.0 feat(dx): score every error for an AI agent reader (agent-ready error contract + gstack-error-capture) (#3226)
* feat(dx): agent-ready error contract for the DevEx review skills

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

* feat(dx): gstack-error-capture runs a CLI with no terminal and prints a redacted record

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

* fix(dx): CI-clean fences, runtime-root-safe contract location, path-agnostic launch-check test

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

* test(dx): preregistered behavior case for the agent-ready error contract; README skill rows

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

* test(dx): grader items come from the contract; paid-file count includes the new case

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

* feat(dx): outside DX voice uses the agent-ready contract; DX_CAPTURE_* codes join RESULT_CODES with troubleshooting anchors

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

* fix(dx): review fixes for gstack-error-capture and the paid grader

- every helper diagnostic passes the record's redaction, fail-closed
- forwarded values are masked before and after sanitizing, plus JSON-escaped forms
- the final secret check reads the record's string fields with byte-based scan limits
- mise/asdf config, trust and env-loading variables need --pass-env; only data locations pass by default
- launch reports whether the target started; --launch-check's probe result is exit_code
- Step 3 captures --help first; help states the containment lifetime limit
- the paid grader validates the bound result with parseReviewResult

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

* fix(dx): mask forwarded values before argument validation; protocol strings never match literals; Step 3 captures stay read-only

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

* v1.91.114.0 feat(dx): score every error for an AI agent reader (agent-ready error contract + gstack-error-capture)

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>
2026-10-17 02:10:12 +00:00
Garry Tanandcapy-ai[bot] 73f1394b0a v1.91.113.0 fix(capy): trailers say behind=<n> with a fix, a skipped upgrade is never ok, and a reused pid no longer holds the upgrade lease (#3232)
* fix(capy): the install trailer says behind=<n> and its fix, so a stale install shows in every relayed trailer (field list 2 item 13, #2378)

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

* fix(capy): a lease records its holder's start identity, so a pid reused after a wake is reclaimed; a skipped session-start upgrade ends fail behind, never ok (GBRA-105, GBRA-106)

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

* fix(lease): stale needs proof (unreadable or malformed identities keep the lease), ps identity is timezone-independent, and reclaim never deletes a concurrent renewal

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

* fix(lease): one directory lock for publish, prune and reclaim; reclaim rereads the record judged stale, so a renewal is never deleted or hidden

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

* fix(lease): the directory lock names its holder; broken only when the holder is dead or its pid reused, unlock removes only its own lock, touch exits 3 under a live holder

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

* fix(lease): breakers serialize on .lock.break and reread the lock before removing it; gstack-skill-start reports a lease the held lock kept out

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

* docs(lease): name the double-crash limit of breaking a dead lock

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

* test(third-party-actions): the Aside drive-offer detector matches the product name, not the English idiom (paid run 38988927989 tpa-absent-linux false positive)

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

* docs(changelog): field list 2 F1 entry; capy/support.env GSTACK_MIN_REVISION to this release

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

* chore: stamp v1.91.113.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>
2026-10-17 01:52:40 +00:00
Garry Tanandcapy-ai[bot] f592d67826 v1.91.111.0 fix(evals): four PR-gate background reds were a detector, an interface, a helper and a workflow-block defect (#3231)
* fix(evals): three PR-gate background reds were a detector, an interface and a helper defect

- docsync-fault-eval: a conditional mention (until/unless/once/when/before/if) of
  'Documentation is current' is not a false current claim; replay corpus for
  ship-docsync-launch-failure from PR run 38336400566 with derived refusals.
- gstack-skill-start/gstack-skill-end: --help prints usage and exits before any
  side effect (it used to start a session); the docsync interface admits the
  lifecycle helpers' own --help; captured run 38969924945 replays clean.
- qa deadline policy: listing the owned qa-reports/screenshots directory is
  bookkeeping (qa-only-no-fix, run 38969924945).

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

* test(evals): the launch-failure corpus is a row in the docsync corpus test that owns docsync-fault-eval

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

* test(evals): register the launch-failure corpus and docsync-store capture in their contracts

The B1 corpus contract wants its own detector-corpus-<case> replay test, so the
launch-failure corpus keeps one, listed in the test-of-test baseline beside its
sibling corpora; FREE_FIXTURES names both new fixtures' free consumers.

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

* fix(review-army): the scope block prints the SCOPE_* signals specialist selection reads

The Detect stack and scope block sourced gstack-diff-scope silently, so an
agent that needed SCOPE_FRONTEND to pick specialists appended its own echo and
left the declared caller interface (ship-exploratory-plan-checks red in run
38981523623). The block now lists the sourced signals itself.

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

* v1.91.111.0 fix(evals): four PR-gate background reds were a detector, an interface, a helper and a workflow-block defect

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>
2026-10-17 00:56:33 +00:00
Garry Tanandcapy-ai[bot] 83f15043ac v1.91.110.0 fix(capy): install the project's pinned Node; launch Bun through a bun-only directory (#3233)
* fix(capy): install and launch the project's pinned Node; launch Bun through a bun-only directory

gbrain pins node 24.18.0 in CI while the Capy image ships 24.21.0 in
/usr/local/bin, so a full install still ended with fail pins (GBRA-117).
The installer now resolves the project's exact Node pins the way it resolves
Bun's (required non-CI pins, CI pins in different jobs as alternatives): the
node first on PATH, then gstack's store copy and nvm's copy of each wanted
version, then any other node on PATH. With no match, install mode downloads the
pinned release into <state root>/runtimes/node and checks it against
SHASUMS256.txt. The resolution is recorded in the install verdict
(node_path, node_version, node_rule), and --check reuses it.

The launch PATH no longer prepends a shared bin directory. When the resolved
Bun was /usr/local/bin/bun, the image's node there shadowed every other node
(GBRA-122). A store Bun still launches from its own version directory; any
other Bun, and a Node the launch needs, launch through a gstack-owned
directory of symlinks beside the verdict. The pregate's lanes use the
recorded launch directory.

The doctor's pins row names the node it read and, for a Node mismatch, the
installer command that installs the pinned Node (or nvm install <v> off
Capy).

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

* chore: v1.91.109.0

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

* test(egress): exempt the Node installer's two release downloads with their reason

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

* chore: restamp v1.91.110.0 (1.91.109.0 is #3225's slot)

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

* test(doctor): make the Node pin case platform-neutral (an any-lane CI pin, bash's spelling of the stub path)

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>
2026-10-17 00:41:41 +00:00