mirror of
https://github.com/garrytan/gstack.git
synced 2026-10-31 22:14:25 +01:00
v1.91.279.0 eval:select exemptions and ETHOS.md registration (#3499), below-floor review rulings (#3504, #3485), Windows curation, GSTA-121 follow-ups (#3515)
* Eval selection exemptions, review-floor rulings and the GSTA-121 follow-ups Release metadata (NOTICE.md, LICENSE, licenses/**) no longer restores the full paid gate and ETHOS.md is registered under the two prompts that read it (#3499). docs/review-policy.md records the six finding classes #3485 and #3504 ruled below the floor and the review army's lead judgment applies the table; the floor itself is unchanged. The Windows curation moves test/pregate-remote-stamp-only.test.ts to KNOWN_WINDOWS_INCOMPATIBLE (#3485). From GSTA-121: Blast radius in docs/skills.md and the merge-ask template, the 11-digit run-id exemption from pii.phone.e164 after a run word, relative string literals as import-graph edges, and a ubi-runner pack that never tars the live .git. Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix: pre-landing review fixes Red Team pass 1 (all below the floor): an 11-digit span with a leading 1 after a run word is a US or Chinese national number, not a run id, and reports again; ubi-runner pack removes its temporary repository when it fails and replays branch.* with remote.* so a branch's upstream resolves on the VM; the conservative-refusal example in docs/review-policy.md reads in the refusing direction; the touchfiles header names why ETHOS.md sits only under the two prompts that quote it. Step 6's selection found captain/templates/merge-ask.md as the one unknown dependency of this PR and registered captain/templates/** in FREE_ONLY_PR_FILES (#3499). Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix: pre-landing review fixes (pass 2) The cmd_pack EXIT trap bakes the temp path in: a set -e failure unwinds the function before the trap runs and a local $tmp is unset there, so the old form leaked the snapshot. The leak test creates TMPDIR where the pack reads it and asserts the failure is the missing index. CHANGELOG examples no longer spell a full national number. Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix: adversarial review fixes The pack snapshot carries the index's staged blobs as a pack of their own, so a VM write-tree (gstack-wtree) can read a staged file the way the old .git tar allowed; git init runs with --template= and gc.auto=0 and a valueless remote/branch key replays as true. scripts/harness-version.json re-pins scripts/test-pr-profile.ts after the captain/templates/** entry. The run-id exemption reads the same way in the scanner and gstack-post: an 11-digit span after a run word is a run id (the leading-1 exclusion from the Red Team pass contradicted gstack-post's tested contract and the ruling that a run id is not a phone number), with the residual named in the comment, the docs and the CHANGELOG. The #3499 pins move to their own test so the boundary test stays under its default budget. Docs audit: TESTING_INTERNALS names RELEASE_METADATA_FILES and captain/templates/**, docs/captain.md names Blast radius. Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix: pre-landing review fixes (pass 4) The pack's staged-blob list comes from the copied index, holds only blobs staged against HEAD (not every tracked blob), skips gitlinks whose commit the superproject lacks, and dies with its own message when pack-objects fails. The live-checkout test covers a gitlink entry and a valueless remote key replayed as true. CHANGELOG names the staged-blob carry. Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix: pre-landing review fixes (pass 5) The pack's blob list is the copied index's blobs that HEAD's tree lacks, so an intent-to-add entry's empty blob and the stages of an unmerged entry travel too (the HEAD-only diff list dropped both); gitlinks still skip. The live-checkout test adds an intent-to-add entry, which the VM fsck assertion covers. 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>
This commit is contained in:
1 parent
1f246e3505
commit
b3e94a3f8d
33 files changed
+350
-87
No files matched your search
@@ -1,5 +1,30 @@
|
||||
# Changelog
|
||||
|
||||
## [1.91.279.0] - 2026-10-27
|
||||
|
||||
**A licence line or a changelog entry no longer buys a full paid gate, the review loop stops re-reporting findings the owners have already ruled below the floor, and three follow-ups from the Blast radius ship land.** #3499: every PR that touched `NOTICE.md`, `LICENSE`, `licenses/` or `ETHOS.md` restored every gate case and judge because no paid case was mapped to them (#3493's docs-only PR planned 82 cases, about $30, for a notice and a changelog entry). #3504 and #3485: the same classes of finding (a schema lagging its writer, a helper nobody calls, a re-dispatch, a refusal one step late) reached the owners again on every ship after being judged below the floor once. GSTA-121's ship left three gaps. The Captain's merge-ask template and `docs/skills.md` lacked the `## Blast radius` item the PR body now carries, an 11-digit GitHub Actions run id after a run word was redacted from an approval record as a phone number, and the remote free gate failed with `PREGATE_REMOTE_INFRA` when a reviewer ran git in the checkout while the gate tarred `.git`.
|
||||
|
||||
### Added
|
||||
- **`RELEASE_METADATA_FILES` in `scripts/test-pr-profile.ts`** (`NOTICE.md`, `LICENSE`, `licenses/**`): release metadata no paid case reads, exempt like `CHANGELOG.md` and `VERSION` already were in `knownNonBehaviorFile`; a PR of only these files selects no paid case and keeps mode `pr`; `captain/templates/**` (the crew templates no paid case reads, found as the one unknown dependency of this PR's own selection) joins `FREE_ONLY_PR_FILES`. `ETHOS.md` is registered under the cases and judge of the two prompts that read it (`/office-hours`, `/plan-ceo-review`) in `test/helpers/touchfiles-data.ts`, so an ETHOS edit selects those cases (three gate cases and one judge today) instead of the whole gate; `SECURITY.md` replaces it as the tests' example of an unregistered file. The "unknown dependency restores the full gate" rule itself is unchanged.
|
||||
- **`docs/review-policy.md#rulings-below-the-floor`**: six finding classes put to the floor by #3485 and #3504 and ruled below it (own-artifact hardening, unwired or unread, bounded repeat work, conservative refusal, artifact presentation, test gap), each with what it looks like and why it fails the floor; a finding in one is a note named by its ruling from pass 1 on. The review army's lead judgment (`scripts/resolvers/review-army.ts`, rendered into both `review-army.md` sections) gains the one-line **On record** ground that applies the table; its other grounds were tightened by the same byte count so the generated review SKILL.md stays at its pins. The floor ("would fail a test, break production, or lose data or secrets") is unchanged.
|
||||
- **`docs/TESTING_INTERNALS.md` Windows free lane**: the command that shows whether one test is in the curated Windows subset and why not, and which list (`KNOWN_WINDOWS_SAFE`, `WINDOWS_PROBE_SAFE`, `KNOWN_WINDOWS_INCOMPATIBLE`) moves it.
|
||||
- **`docs/skills.md` `/ship` "The PR body"**: the four Capy sections in order (What changed, Why, Blast radius, Approvals), how to write the Blast radius item, and what `gstack-prose-lint` gates. `captain/templates/merge-ask.md` carries `## Blast radius` between Why and Approvals; `bin/gstack-pr-shots`'s example caption and the `capy-pr` section heading name it too.
|
||||
|
||||
### Fixed
|
||||
- **`lib/redact-patterns.ts` `isCiRunIdentifier`**: an 11-digit GitHub Actions run id right after a run word on the same line (`screen 40477638211 GREEN`, `gate=…`, `rerun passed …`, `run #…`) is not `pii.phone.e164`, in every scanner (`gstack-approvals add`, `gstack-decision-trail`, the pregate secrets check, `gstack-post`); the run-word regex moved there from `lib/gstack-post.ts`, which imports it for its chained-id allowlist. A 10-digit national number after a run word, a bare 11-digit number, an E.164 `+` number and `job`, `id`, `workflow` or `attempt` before one still report; a national number written without its `+` right after a run word reads as a run id, the accepted residual of the ruling that a run id is not a phone number (`gstack-post` has read it that way since GSTA-121).
|
||||
- **`scripts/ubicloud/ubi-runner.sh pack`**: the live `.git` is never tarred. The refs are fetched into a fresh repository through git's transport (`--update-shallow`, so a shallow CI or Capy checkout packs), the `remote.*` and `branch.*` config is replayed so `origin/<base>` and each branch's upstream resolve on the VM, a detached HEAD gets a `detached-<sha12>` branch, the index is copied with the blobs staged against HEAD (a VM `gstack-wtree` can read a staged file), and a pack that fails removes its temporary repository; a review subagent's `git diff` or a trial merge's `worktree add` during the first minute of a remote gate no longer makes tar exit 1 (`PREGATE_REMOTE_INFRA`). The linked-worktree path uses the same snapshot.
|
||||
- **`lib/pregate/import-graph.ts`**: a relative string literal (`'../ship/sections/pr-body.md.tmpl'` through `new URL(…, import.meta.url)` or `readFileSync`) is a dependency edge from the file that names it, like a relative import; `test/ship-publication-gates.test.ts` is now selected when the pr-body template changes (the remote free gate missed it in GSTA-121's ship). A literal that climbs out of the repository names nothing.
|
||||
- **`scripts/lib/windows-curation.ts`**: `test/pregate-remote-stamp-only.test.ts` is `KNOWN_WINDOWS_INCOMPATIBLE` (it expects the Linux-host lanes verdict and skips itself on win32), as #3485's follow-up asked.
|
||||
|
||||
### Tests
|
||||
- `test/paid-free-boundary.test.ts`: release metadata alone and beside `CHANGELOG.md` keeps mode `pr` with no paid case; `ETHOS.md` with `NOTICE.md` selects the two skills' gate cases and judge and nothing else. `test/pr-dependencies.test.ts`, `test/paid-pr-profile.test.ts`, `test/exercised-map.test.ts`: `SECURITY.md` as the unregistered example.
|
||||
- `test/redact-context-exemptions.test.ts`: seven run-word rows with run 40477638211 and five controls; `test/approval-record.test.ts`: an approval whose words cite `screen 40477638211 GREEN` is recorded verbatim, a phone number still refuses the row; `test/pregate-allowlist.test.ts`: its run-id fixture reads `job <id>`, which the engine does not exempt, so the allowlist mechanism it tests still has a finding to silence.
|
||||
- `test/ubicloud-runner.test.ts`: pack of a plain checkout under twenty seconds of index, worktree and ref churn, three times; the unpacked snapshot has the same HEAD, `origin/main`, remote URL, status and `gstack-wtree` fingerprint, a detached HEAD lands on `detached-<sha12>`, a pack with no index exits 1 and leaves nothing in `TMPDIR`, and a branch's upstream survives the snapshot.
|
||||
- `test/pregate-import-graph-paths.test.ts`: relative literals through `new URL` and `readFileSync`, one and two directories up, and a literal that escapes the repository.
|
||||
|
||||
### Gate integrity
|
||||
- No threshold, timeout, trial count or case kind changed. The paid selection narrows only for `NOTICE.md`, `LICENSE`, `licenses/**`, `captain/templates/**` and `ETHOS.md`; every other unmapped file still restores the full gate. Generated SKILL.md sizes stay at their pins and the ship parity union stays under its 1.7500 cap; no allowlist or cap moved.
|
||||
|
||||
## [1.91.278.0] - 2026-10-27
|
||||
|
||||
**The merge train's tooling stops failing the lanes for things the tooling itself could see.** A head whose only conflict with main was the CHANGELOG got its Free Tests through the dispatch route and the paid screen still called it NOT-RUN; the receipt-versus-prose check read the canonical `REVIEW_LOOP` line as a false mismatch; a PR that writes a new `*_KEY_VERSION` constant as a tentative next-minor version (the convention #3511 introduces) could not clear the train's stamp-only receipt; a pooled pin learned that main had moved onto its files only when the train was built; a watcher had no way to leave a stuck non-required check out of its verdict; and a lane that passed the slot call's remaining order to `gstack-restamp` without its own PR got the same stale list back as the fix. Backlog items from GSTA-119, #3485, #3495 and the 2026-10-27 slot calls.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# gstack digest v1.91.278.0 — regenerate/re-copy after upgrading gstack
|
||||
# gstack digest v1.91.279.0 — regenerate/re-copy after upgrading gstack
|
||||
|
||||
Behavioral rules from gstack (https://github.com/garrytan/gstack), compressed
|
||||
for agent hosts without a full skill install. The full skills add workflows,
|
||||
|
||||
+1
-1
@@ -33,7 +33,7 @@ const HELP = `gstack-pr-shots — before/after image of a change for a PR body (
|
||||
[--title <text>] [--name <slug>] [--out <dir>] [--json]
|
||||
|
||||
Example:
|
||||
gstack-pr-shots --path ship/SKILL.md --heading "Step 19: Create PR/MR" --caption "PR body opens with What changed, Why and Approvals under Capy"
|
||||
gstack-pr-shots --path ship/SKILL.md --heading "Step 19: Create PR/MR" --caption "PR body opens with What changed, Why, Blast radius and Approvals under Capy"
|
||||
|
||||
${exitTableHelp()}`;
|
||||
|
||||
|
||||
@@ -10,7 +10,10 @@ it before training the PR.
|
||||
<one to three images, highest-signal first; a collapsed text version under each>
|
||||
|
||||
## Why
|
||||
<the cause and the user-visible effect, two or three sentences>
|
||||
<the problem in the person's words first, then the cause and the user-visible effect, two or three sentences>
|
||||
|
||||
## Blast radius
|
||||
<what else the change touches and the one fact that makes that safe, with the evidence-ladder rung reached>
|
||||
|
||||
## Approvals
|
||||
<the table from gstack-approvals render>
|
||||
|
||||
@@ -126,9 +126,12 @@ Every PR you send READY on, and the READY report itself, has:
|
||||
With no browser that line says `unavailable`: screenshot the printed `shot.html` with your own browser.
|
||||
Upload each PNG with Capy's PR asset tool (`tools.pr.uploadAsset`), only for public repositories, and embed
|
||||
the URL it returns with the caption as alt text.
|
||||
2. **Why.** The cause and the effect the user will see, in two or three sentences.
|
||||
3. **Approvals.** `gstack-approvals render`.
|
||||
4. **Evidence.** Checks green at the head, gate or train results, flaky cases.
|
||||
2. **Why.** The problem in the person's words first, then the cause and the effect the user will see,
|
||||
in two or three sentences.
|
||||
3. **Blast radius.** What else the change touches and the one fact that makes that safe, with the
|
||||
evidence-ladder rung that fact reached.
|
||||
4. **Approvals.** `gstack-approvals render`.
|
||||
5. **Evidence.** Checks green at the head, gate or train results, flaky cases.
|
||||
|
||||
The READY report attaches the key image (Capy message attachment), so the Captain can show it inline.
|
||||
/ship does all of this when `GSTACK_CAPY=1` (its `capy-pr` section). Outside Capy, the PR body is unchanged.
|
||||
@@ -1097,8 +1097,10 @@ PR selection (`scripts/test-pr-profile.ts`) classifies every changed file. A fil
|
||||
mapped by a paid case's touchfiles selects that case. These files are exempt and
|
||||
select nothing: docs, any non-paid `*.test.ts(x)`, `FREE_ONLY_PR_FILES` (the
|
||||
free and paid duration seeds, `scripts/ubicloud/**`, every workflow that runs no
|
||||
paid evals, and free-only tools), and the fixtures in `FREE_FIXTURES`
|
||||
(`test/helpers/free-fixtures-data.ts`, each fixture with its free consumers).
|
||||
paid evals, `captain/templates/**`, and free-only tools), the release metadata
|
||||
in `RELEASE_METADATA_FILES` (`NOTICE.md`, `LICENSE`, `licenses/**`), and the
|
||||
fixtures in `FREE_FIXTURES` (`test/helpers/free-fixtures-data.ts`, each fixture
|
||||
with its free consumers).
|
||||
Unmapped files under `DERIVABLE_PREFIXES` (bin/, lib/, scripts/, browse/,
|
||||
design/, make-pdf/, hosts/, extension/, model-overlays/, agents/, test/helpers/,
|
||||
capy/) are not unknown: `scripts/pr-dependencies.ts` derives the cases whose
|
||||
@@ -1506,6 +1508,20 @@ summary). Files a probe proved safe despite a `/tmp` literal are force-included
|
||||
`WINDOWS_CURATED_FLOOR`; when Windows-safe tests are deleted on purpose, lower it
|
||||
in the same PR with a reason.
|
||||
|
||||
To see whether one test is in the subset, and why not when it is excluded:
|
||||
|
||||
```bash
|
||||
bun run scripts/test-free-shards.ts --windows-only --list | grep test/<name>.test.ts
|
||||
```
|
||||
|
||||
A test a content pattern excludes wrongly joins the subset through
|
||||
`KNOWN_WINDOWS_SAFE` (with its reason) or, for a `/tmp` literal a probe proved
|
||||
safe, `WINDOWS_PROBE_SAFE`. A test that fails on `windows-latest` for a reason
|
||||
the patterns cannot see (it expects the Linux-host lanes verdict, skips itself
|
||||
on win32, drives a POSIX signal path) leaves it through a
|
||||
`KNOWN_WINDOWS_INCOMPATIBLE` entry naming the reason; rerun `--list` to confirm
|
||||
the file moved.
|
||||
|
||||
Native qualification campaigns (Windows cookie extraction, Windows launch
|
||||
diagnostics, Dia on macOS) run only on dispatch of `native-qualification.yml`:
|
||||
|
||||
|
||||
+1
-1
@@ -232,7 +232,7 @@ recommendation at full scope. `gstack-owner-brief render --captain` prints it fr
|
||||
autoplan run.
|
||||
|
||||
**C. The merge ask** ([template](../captain/templates/merge-ask.md)): captioned
|
||||
before/after images, why, the Approvals table, and the evidence. /ship writes the PR
|
||||
before/after images, why, blast radius, the Approvals table, and the evidence. /ship writes the PR
|
||||
body in this shape on Capy; the controller's merge report shows the key image inline.
|
||||
|
||||
## The digest
|
||||
|
||||
+4
-1
@@ -566,7 +566,10 @@ that reads them ships. The worked gbrain configuration is in
|
||||
|
||||
The `secrets` check cannot be downgraded, and that is right for a key. It also
|
||||
refused 1,800 per-trial dummy tokens in eval receipts (`"key": "sk-trial-<id>"`
|
||||
reads as `env.kv`), an 11-digit Actions run id read as a phone number, and
|
||||
reads as `env.kv`), an 11-digit Actions run id read as a phone number (one
|
||||
after a run word such as `run`, `screen` or `gate` is exempt in the scanner
|
||||
since the GSTA-121 follow-up; the allowlist still covers one after any other
|
||||
word), and
|
||||
excalidraw.com's public Firebase key that every rebuild of a bundle re-adds
|
||||
(harness-evals #81/#83, GSTA-102, GSTA-106). `allowlist` in the policy names
|
||||
those, each with its scope and its reason:
|
||||
|
||||
+26
-1
@@ -39,6 +39,31 @@ floor above; everything else is INFORMATIONAL. Data loss, a trust-boundary
|
||||
bypass and uncapped spend stay forced CRITICAL (the sideways sweep rule); no
|
||||
lane, specialist, dedup or saved decision downgrades them.
|
||||
|
||||
<a id="rulings-below-the-floor"></a>
|
||||
### Rulings below the floor
|
||||
|
||||
The floor is a test, and these classes have been put to it, each more than
|
||||
once, and failed it (#3485 from the /ship of #3484, #3504 from the /ship of
|
||||
#3487). A finding in one of them is a **note** from the first pass on, named
|
||||
by its ruling in the PR body's `## Review notes`; it never earns a fix round
|
||||
or another review pass, and the lead judgment stage drops it from the defect
|
||||
count with the pointer. The floor itself does not move, so a finding that also
|
||||
loses data, crosses a trust boundary or spends without a cap stays CRITICAL
|
||||
whatever class it resembles.
|
||||
|
||||
| Ruling | What it looks like | Why it is below the floor |
|
||||
|---|---|---|
|
||||
| **own-artifact hardening** | A check missing between two things the repository's own tooling writes: a screen verdict not compared with the receipt it belongs to; a receipt schema that does not declare a key the writer records; a policy loader that defaults when the base branch's own file is invalid; a size read that follows a symlink inside the repository. | The only writer is the repository's maintainers through its tools; no caller inside the intended case reaches the gap, and no test fails. A writer outside that trust boundary makes it a bypass, and CRITICAL. |
|
||||
| **unwired or unread** | A helper with no caller, a field written and never read, a duplicated helper, a header comment naming a step that does not run it, a key-version constant that moved before any release carried the old value. | Nothing takes the path, so nothing breaks; it is dead code to delete or wire in its own change. |
|
||||
| **bounded repeat work** | A re-dispatch on every call, a redundant screen after main moves, an orphaned scratch branch, a refusal that could come one step earlier in the sequence, a settle that could wait one more check. | The operation completes, nothing is lost, and the cost is one more bounded run; uncapped spend is the sideways sweep's CRITICAL, not this class. |
|
||||
| **conservative refusal** | Stale state scoped wider than its cause: a RED verdict that keeps refusing a head after main moved, a pending result that keeps refusing after it settled. | A refusal costs a rerun and never passes something wrong; a wrong pass would be the floor. |
|
||||
| **artifact presentation** | A string from an artifact printed verbatim into a parsed line (whitespace, control characters); a classifier that reads a non-zero exit code beside a non-RED verdict as RED. | Display and classification of the repository's own output; it forges no parsed field. |
|
||||
| **test gap** | A state-matrix cell without a test, a coverage percentage, a weak or flaky test, a fixture that shares the coordinator's environment. | Category `testing`, below the floor by definition above; a gap is work for the test audit, not a defect in the diff. |
|
||||
|
||||
A reviewer that wants a class promoted argues it against the floor with a
|
||||
reaching input, in the finding, and the owner rules; the table records the
|
||||
rulings, it does not close them.
|
||||
|
||||
## `NO FINDINGS` is a result
|
||||
|
||||
A reviewer that finds nothing answers `NO FINDINGS` and nothing else. That is a
|
||||
@@ -171,7 +196,7 @@ trailers, appended }`.
|
||||
|
||||
## Where this is enforced
|
||||
|
||||
- `scripts/resolvers/review-army.ts`: the floor and `NO FINDINGS` in every specialist and Red Team prompt (rendered into `review/sections/review-army.md` and `ship/sections/review-army.md`); in `/ship` every specialist and Red Team prompt it renders carries its diff base as a literal sha the parent writes in (`DIFF_BASE=<diff-base>`: the merge base on pass 1, `RANGE_BASE` from pass 2 on), because a reviewer is its own process and never sees the parent's shell variables; the fan-out batches above.
|
||||
- `scripts/resolvers/review-army.ts`: the floor and `NO FINDINGS` in every specialist and Red Team prompt (rendered into `review/sections/review-army.md` and `ship/sections/review-army.md`), and the lead judgment's "On record" line that turns a [ruled class](#rulings-below-the-floor) into a note; in `/ship` every specialist and Red Team prompt it renders carries its diff base as a literal sha the parent writes in (`DIFF_BASE=<diff-base>`: the merge base on pass 1, `RANGE_BASE` from pass 2 on), because a reviewer is its own process and never sees the parent's shell variables; the fan-out batches above.
|
||||
- `ship/sections/review-army.md` Step 9: passes, `RANGE_BASE`, classify, defer, render, the cap check before any edit, the Step 11 late-defer row, scratch files under `.gstack/tmp/`, the cap decisions; `ship/sections/adversarial.md` Step 11: the outside voice reads the same range and scopes queued fixes the same way.
|
||||
- `lib/review-scope.ts`, `bin/gstack-review-scope`: `classify`, `defer`, `render`, `summary`; refusals are `REVIEW_SCOPE_INPUT` and `REVIEW_SCOPE_RANGE` ([troubleshooting.md](troubleshooting.md#review-scope-input)).
|
||||
- `lib/ship-policy.ts`: the `review_scope` key and its `ship: review_scope …` consequence line.
|
||||
|
||||
@@ -735,6 +735,17 @@ Every `/ship` run builds a code path map from your diff, searches for correspond
|
||||
|
||||
A lot of branches die when the interesting work is done and only the boring release work is left. Humans procrastinate that part. AI should not.
|
||||
|
||||
### The PR body
|
||||
|
||||
`/ship` writes the PR body from the diff, the receipt and the review, and a reader who was not in the session decides the merge from it. On Capy (`CAPY_PR: on`) the body opens with four sections, in this order, before the standard Summary, Test plan and Documentation sections:
|
||||
|
||||
1. **`## What changed`**: one to three captioned before/after images from `gstack-pr-shots` or a UI screenshot, highest-signal first, with a collapsed text version under each.
|
||||
2. **`## Why`**: two or three sentences. The first states the problem in the person's words (the issue, the message, the failing run), then the cause and the user-visible effect.
|
||||
3. **`## Blast radius`**: what else the change touches and the one fact that makes that safe, with the evidence-ladder rung that fact reached (a test that exercises the shared path, a grep over every caller, a measured run). A change to a shared helper, a generated file, a selection list or a policy default always has one; a leaf fix says so in one line.
|
||||
4. **`## Approvals`**: the table from `gstack-approvals render`, verbatim.
|
||||
|
||||
Before the body is composed, `gstack-prose-lint` reads the Why, Summary and Blast radius drafts (`--sections 'Why,Summary,Blast radius' --gate`) and the CHANGELOG entry (`--base origin/<base> --gate`), and a `PROSE_LINT_FOUND` line names the sentence to rewrite; the rest of the body is advisory. The Captain's merge-ask template (`captain/templates/merge-ask.md`) carries the same four sections, so a READY report and its PR say the same thing.
|
||||
|
||||
### Versions: where `/ship` looks, and when it ships without one
|
||||
|
||||
`/ship` bumps a version only when the project says where the version lives. It checks, in order:
|
||||
|
||||
@@ -2373,7 +2373,12 @@ exact tokens it finds in these positions:
|
||||
`gates`, `backstop`, `bisect`, `rerun`, `dispatched`, `dispatch`,
|
||||
`failed`, `passed` (`failed 40269992077 | rerun passed 40287239206`,
|
||||
`gate 40323989742`, `run #19368712345`); a generic word such as `id`,
|
||||
`job`, `attempt` or `workflow` exempts nothing;
|
||||
`job`, `attempt` or `workflow` exempts nothing. This position is exempt in
|
||||
the scanner itself (`isCiRunIdentifier` in `lib/redact-patterns.ts`), so
|
||||
`gstack-approvals add`, `gstack-decision-trail` and the pregate secrets
|
||||
check accept it too, not only `gstack-post`. A national number written
|
||||
without its `+` right after a run word reads as a run id there; that is the
|
||||
accepted residual of the ruling that a run id is not a phone number;
|
||||
- every 11-digit id chained after an allowlisted one through `, `, ` and `,
|
||||
` & `, ` or ` or ` / `, optionally with a `(#1234)` between
|
||||
(`screen 40269992077 (#3434) and 40275443393 (#3453)`).
|
||||
|
||||
+5
-4
@@ -21,6 +21,7 @@
|
||||
*/
|
||||
import { createHash } from "crypto";
|
||||
import { scan, type Finding, type RepoVisibility } from "./redact-engine";
|
||||
import { CI_RUN_WORD_BEFORE } from "./redact-patterns";
|
||||
import { RECEIPT_FENCE } from "./ship-receipt";
|
||||
import { canonicalRemote } from "./remote-identity";
|
||||
import { parseChangelog } from "./changelog-check";
|
||||
@@ -329,8 +330,6 @@ function repoReleaseVersions(env: PostEnv): string[] {
|
||||
return [...versions];
|
||||
}
|
||||
|
||||
/** A run word with an optional ':' '#' '=' right before an 11-digit token: `run 19368712345`, `gate=40323989742`, `rerun passed 40287239206`. */
|
||||
const RUN_WORD_BEFORE = /\b(?:runs?|screens?|gates?|backstop|bisect|rerun|dispatch(?:ed)?|failed|passed)\s*[:#=]?\s*$/i;
|
||||
/** Between two 11-digit tokens of one list: `, `, ` and `, ` & `, ` or `, ` / `, with an optional `(#1234)` after the first. */
|
||||
const RUN_ID_CHAIN = /^(?:\s*\(#\d+\))?\s*(?:,|and|&|or|\/)\s*$/i;
|
||||
|
||||
@@ -344,7 +343,9 @@ const RUN_ID_CHAIN = /^(?:\s*\(#\d+\))?\s*(?:,|and|&|or|\/)\s*$/i;
|
||||
* word on the same line (run, runs, screen, screens, gate, gates, backstop,
|
||||
* bisect, rerun, dispatched, dispatch, failed, passed, with an optional ':'
|
||||
* '#' '=' between: `failed 40269992077`, `gate 40323989742`,
|
||||
* `run #19368712345`; generic words such as id, job, attempt and workflow
|
||||
* `run #19368712345`; the engine's isCiRunIdentifier exempts that position on
|
||||
* its own since the run-id fix for approval records, and the same regex drives
|
||||
* the chain below; generic words such as id, job, attempt and workflow
|
||||
* exempt nothing, since `customer id 14155551234` is a number), plus every 11-digit id chained
|
||||
* after an allowlisted one through ", ", " and ", " & ", " or " or " / ",
|
||||
* optionally with a "(#1234)" between (`screen 40269992077 (#3434) and
|
||||
@@ -361,7 +362,7 @@ function receiptDigitTokens(texts: string[]): string[] {
|
||||
let chainFrom = -1;
|
||||
for (const m of line.matchAll(/(?<![\d+])\d{11}(?!\d)/g)) {
|
||||
const at = m.index!;
|
||||
const listed = RUN_WORD_BEFORE.test(line.slice(0, at)) || (chainFrom >= 0 && RUN_ID_CHAIN.test(line.slice(chainFrom, at)));
|
||||
const listed = CI_RUN_WORD_BEFORE.test(line.slice(0, at)) || (chainFrom >= 0 && RUN_ID_CHAIN.test(line.slice(chainFrom, at)));
|
||||
if (listed) tokens.add(m[0]);
|
||||
chainFrom = listed ? at + m[0].length : -1;
|
||||
}
|
||||
|
||||
@@ -49,7 +49,11 @@ const scanner = new Bun.Transpiler({ loader: 'tsx' });
|
||||
// `( ) [ ] +` appear in framework route paths: Next.js route groups `(app)`,
|
||||
// dynamic segments `[id]` / `[[...slug]]`, SvelteKit `+page.svelte`. A literal
|
||||
// that matches more text only gains an edge when it resolves to a repo file.
|
||||
const QUOTED = /['"`]([A-Za-z0-9_@][A-Za-z0-9_./@()[\]+-]*)['"`]/g;
|
||||
// A relative literal (`'../ship/sections/pr-body.md.tmpl'` read through
|
||||
// `new URL(…, import.meta.url)` or `readFileSync`) resolves from the file that
|
||||
// names it, like a relative import; without it a test reading a template by
|
||||
// relative path had no edge to the template (GSTA-121's test/ship-publication-gates.test.ts).
|
||||
const QUOTED = /['"`]((?:\.\.?\/)*[A-Za-z0-9_@][A-Za-z0-9_./@()[\]+-]*)['"`]/g;
|
||||
const JOIN = /\bjoin\(([^()]*)\)/g;
|
||||
const RESOLVE_EXT = ['', '.ts', '.tsx', '.js', '.jsx', '.mjs', '.cjs', '.mts', '.cts', '/index.ts', '/index.tsx', '/index.js', '/index.jsx', '/index.mjs'];
|
||||
// A statement starts a line or follows `;` or a compound statement's `:`. The module spec is
|
||||
|
||||
+14
-2
@@ -328,16 +328,28 @@ export function isNumericMetadataValue(span: string, match: RegExpExecArray): bo
|
||||
* command. Only the position decides: a run/job flag (`--run 37235771700`,
|
||||
* `--job=111534615007`), a `gh run view|watch|rerun|download|cancel`
|
||||
* argument, an `actions/runs/<id>` or `/job/<id>` URL segment, or a
|
||||
* `run_id` / `job_id` key. The same digits anywhere else still report.
|
||||
* `run_id` / `job_id` key. An 11-digit id (every GitHub Actions run id since
|
||||
* 2025) also counts right after a run word on the same line, the way a ship
|
||||
* receipt, a READY block or an approval record names one (`screen 40477638211
|
||||
* GREEN`, `gate=40323989742`, `rerun passed 40287239206`, `run #19368712345`);
|
||||
* generic words (id, job, attempt, workflow) exempt nothing, since `customer id
|
||||
* 14155551234` is a number. A national number written without its `+` right
|
||||
* after a run word (`failed 14155551234`) is the accepted residual of the
|
||||
* owner's ruling that a run id is not a phone number; gstack-post's chained-id
|
||||
* allowlist reads the same position the same way. The same digits anywhere
|
||||
* else still report.
|
||||
*/
|
||||
const CI_ID_POSITION_BEFORE =
|
||||
/(?:--(?:run|job)(?:-id)?(?:[ \t]+|[ \t]*=[ \t]*)|\bgh[ \t]+run[ \t]+(?:view|watch|rerun|download|cancel)[ \t]+|\/actions\/runs\/|\/jobs?\/|(?:run|job)_id["']?[ \t]*[:=][ \t]*["']?)$/i;
|
||||
/** A run word with an optional ':' '#' '=' right before an 11-digit token; shared with gstack-post's chained-id allowlist. */
|
||||
export const CI_RUN_WORD_BEFORE = /\b(?:runs?|screens?|gates?|backstop|bisect|rerun|dispatch(?:ed)?|failed|passed)\s*[:#=]?\s*$/i;
|
||||
export function isCiRunIdentifier(span: string, match: RegExpExecArray): boolean {
|
||||
if (!/^\d+$/.test(span)) return false;
|
||||
const input = match.input ?? "";
|
||||
const { start } = spanBounds(match);
|
||||
const lineStart = input.lastIndexOf("\n", start - 1) + 1;
|
||||
return CI_ID_POSITION_BEFORE.test(input.slice(Math.max(lineStart, start - 80), start));
|
||||
const before = input.slice(Math.max(lineStart, start - 80), start);
|
||||
return CI_ID_POSITION_BEFORE.test(before) || (span.length === 11 && CI_RUN_WORD_BEFORE.test(before));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "gstack",
|
||||
"version": "1.91.278",
|
||||
"version": "1.91.279",
|
||||
"description": "Garry's Stack — Claude Code skills + fast headless browser. One repo, one install, entire AI engineering workflow.",
|
||||
"license": "MIT",
|
||||
"type": "module",
|
||||
|
||||
@@ -173,20 +173,22 @@ Core findings never earn a specialist confidence boost. Preserve `advisory`,
|
||||
|
||||
#### 4. Lead judgment
|
||||
|
||||
Reviewers fill the space they are given. Before any merged finding counts, the
|
||||
parent reads each one and drops or downgrades it on these grounds, saying so:
|
||||
- **Nit gravity.** When a reviewer's whole return is style, naming and preference,
|
||||
the code is probably fine; write that down and count nothing from it as a defect.
|
||||
Reviewers fill the space they are given. Before a merged finding counts, the
|
||||
parent reads it and drops or downgrades it on these grounds, saying so:
|
||||
- **Nit gravity.** A reviewer whose whole return is style, naming and preference
|
||||
found nothing; say so and count no defect.
|
||||
- **Hypothetical, not actual.** A defect names the call site or input that reaches
|
||||
it. One that does not is a note until someone traces it.
|
||||
it; one that does not is a note until someone traces it.
|
||||
- **Structure for a case that does not exist.** Advice to abstract, generalize or
|
||||
guard a path nothing takes is advisory, never a defect.
|
||||
guard a path nothing takes is advisory.
|
||||
- **A different design is not a defect.** "I would have done it differently" with
|
||||
no failing case behind it is dropped.
|
||||
no failing case is dropped.
|
||||
- **Missing context.** A finding that assumes a rule the repository documents
|
||||
otherwise is dropped with the pointer.
|
||||
Agreement between reviewers that did not see each other's output is the strongest
|
||||
signal; the `MULTI-SPECIALIST CONFIRMED` boost above records it. A reviewer that
|
||||
- **On record.** A class `docs/review-policy.md` rules below the floor is a note,
|
||||
named by its ruling.
|
||||
Independent agreement between reviewers is the strongest signal (the
|
||||
`MULTI-SPECIALIST CONFIRMED` boost records it). A reviewer that
|
||||
returned `NO FINDINGS` was right until a traced defect says otherwise.
|
||||
|
||||
#### 5. Apply specialist confidence gates
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
"scripts/lib/published-text.ts": {"blob":"3f2e4d616b5dd0204035710c66b64d49055c5714","since":6},
|
||||
"scripts/lib/shard-engine.ts": {"blob":"dc714d7d897aed185c0ec3d2c92ad22d954039e5","since":6,"nonBehavioral":"createShardSandbox drops the Capy launch markers (GSTACK_SESSION_KIND, GSTACK_CAPY, GSTACK_EPHEMERAL, GSTACK_INSTALL_VERDICT) from shard environments; CI never sets them, so no case's input or verdict changes there, and on a Capy machine fixtures now see the session kind they set themselves instead of the installer's"},
|
||||
"scripts/test-paid-shards.ts": {"blob":"5439d0a8f6e1569fa7c924af20d67c30c81d5d30","since":6,"nonBehavioral":"credential rejections are classed infra instead of assertion: no case's pass or fail changes; only failed trials whose provider rejected the run's key move class, so no rerun spends on a rejected key"},
|
||||
"scripts/test-pr-profile.ts": {"blob":"a80de69f1923afed67127640d68c99e1a8528c5b","since":6,"nonBehavioral":"classify the new weekly-release.yml workflow and scripts/release-cut.ts as free-lane only PR files: the release cut runs on a cron and manual dispatch after merge and inside no case, so no case's selection on a PR, input or verdict changes"},
|
||||
"scripts/test-pr-profile.ts": {"blob":"188710401a08156f585aebcb699151c60e3d6273","since":6,"nonBehavioral":"#3499: RELEASE_METADATA_FILES exempts NOTICE.md, LICENSE and licenses/** and captain/templates/** joins FREE_ONLY_PR_FILES; selection only, no case runs or is judged differently"},
|
||||
"scripts/test-strict-output.ts": {"blob":"01185d20dc9761b04b9b2f751ec98664fe0eaf93","since":6},
|
||||
"test/helpers/agent-sdk-runner.ts": {"blob":"210bba92d241a90df578b2c7fc943ac5755d0025","since":6},
|
||||
"test/helpers/auq-substance-panel.ts": {"blob":"4881260ef793668e4963a062db6d06a5e1f7a40f","since":6},
|
||||
|
||||
@@ -75,6 +75,7 @@ export const KNOWN_WINDOWS_INCOMPATIBLE: Array<{ file: string; reason: string }>
|
||||
{ file: 'test/pregate-dart.test.ts', reason: 'its dart, flutter and fvm stand-ins are bash scripts on a colon-joined PATH, which Bun.which and CreateProcess cannot use on Windows, and its containment case needs a POSIX symlink; the Dart lane code it drives is POSIX-run like the other pregate fixtures' },
|
||||
{ file: 'test/pregate-non-js.test.ts', reason: 'spawns bin/gstack-pregate through test/helpers/pregate-fixture.ts runBin and expects the Linux-host lanes verdict, like test/pregate-docs-lane-review.test.ts and test/pregate-selection.test.ts' },
|
||||
{ file: 'test/pregate-remote-gbrain.test.ts', reason: 'spawns bin/gstack-pregate through test/helpers/pregate-fixture.ts runBin (shebang via CreateProcess) and runs the gbrain-shaped ci:ubicloud package script through the POSIX lane runner, like test/pregate-remote.test.ts' },
|
||||
{ file: 'test/pregate-remote-stamp-only.test.ts', reason: 'expects the Linux-host lanes verdict like test/pregate-remote.test.ts (on Windows the fixture\'s Linux lane is a requires-remote obligation) and skips itself on win32 (describe.skipIf), so the curated lane would run an empty file (#3485, #3486)' },
|
||||
// C-pregate: these drive bin/gstack-issue-links and bin/gstack-ship-receipt through
|
||||
// test/helpers/pregate-fixture.ts runBin.
|
||||
{ file: 'test/stamp-templates.test.ts', reason: 'spawns bin/gstack-tree-receipt through test/helpers/restamp-fixture.ts runBin (shebang via CreateProcess)' },
|
||||
|
||||
@@ -218,20 +218,22 @@ Core findings never earn a specialist confidence boost. Preserve \`advisory\`,
|
||||
|
||||
#### 4. Lead judgment
|
||||
|
||||
Reviewers fill the space they are given. Before any merged finding counts, the
|
||||
parent reads each one and drops or downgrades it on these grounds, saying so:
|
||||
- **Nit gravity.** When a reviewer's whole return is style, naming and preference,
|
||||
the code is probably fine; write that down and count nothing from it as a defect.
|
||||
Reviewers fill the space they are given. Before a merged finding counts, the
|
||||
parent reads it and drops or downgrades it on these grounds, saying so:
|
||||
- **Nit gravity.** A reviewer whose whole return is style, naming and preference
|
||||
found nothing; say so and count no defect.
|
||||
- **Hypothetical, not actual.** A defect names the call site or input that reaches
|
||||
it. One that does not is a note until someone traces it.
|
||||
it; one that does not is a note until someone traces it.
|
||||
- **Structure for a case that does not exist.** Advice to abstract, generalize or
|
||||
guard a path nothing takes is advisory, never a defect.
|
||||
guard a path nothing takes is advisory.
|
||||
- **A different design is not a defect.** "I would have done it differently" with
|
||||
no failing case behind it is dropped.
|
||||
no failing case is dropped.
|
||||
- **Missing context.** A finding that assumes a rule the repository documents
|
||||
otherwise is dropped with the pointer.
|
||||
Agreement between reviewers that did not see each other's output is the strongest
|
||||
signal; the \`MULTI-SPECIALIST CONFIRMED\` boost above records it. A reviewer that
|
||||
- **On record.** A class \`docs/review-policy.md\` rules below the floor is a note,
|
||||
named by its ruling.
|
||||
Independent agreement between reviewers is the strongest signal (the
|
||||
\`MULTI-SPECIALIST CONFIRMED\` boost records it). A reviewer that
|
||||
returned \`NO FINDINGS\` was right until a traced defect says otherwise.
|
||||
|
||||
#### 5. Apply specialist confidence gates
|
||||
|
||||
@@ -235,6 +235,7 @@ export const FREE_ONLY_PR_FILES = [
|
||||
'scripts/paid-test-durations.json',
|
||||
'scripts/ubicloud/**',
|
||||
'capy/skill/**', // The vendored Capy skill (Drive bootstrapper): run by Capy agents, read by no paid case; test/capy-skill-wrapper.test.ts covers it.
|
||||
'captain/templates/**', // The /captain crew templates (CREW.md, merge-ask.md, slot-call.md, …): copied into a Captain's drive by people and Captains, read by no paid case; free test/captain-skill.test.ts covers them (#3499).
|
||||
'tsconfig.test.json', // Read only by `tsc -p tsconfig.test.json` (typecheck:test); Bun's runtime reads tsconfig.json.
|
||||
'.gitignore', // Changes which untracked files git reports, never tracked content a case reads.
|
||||
'.gstack/generated.json', // gstack's own regen registry: gstack-regen/gstack-pregate read it from origin/<base> of the repo they run in; paid cases run in fixture repos.
|
||||
@@ -285,6 +286,14 @@ export const FREE_ONLY_PR_FILES = [
|
||||
'.github/workflows/windows-setup-e2e.yml',
|
||||
] as const;
|
||||
|
||||
/**
|
||||
* Release metadata no paid case reads: the third-party notices and licence
|
||||
* texts, and the root LICENSE. A courtesy entry or a licence line never
|
||||
* widens the PR gate (#3499); CHANGELOG.md and VERSION, which the train stamp
|
||||
* rewrites on every slot, are already exempt in knownNonBehaviorFile.
|
||||
*/
|
||||
export const RELEASE_METADATA_FILES = ['NOTICE.md', 'LICENSE', 'licenses/**'] as const;
|
||||
|
||||
/**
|
||||
* Inputs every paid case consumes that must always restore the full gate
|
||||
* (fail closed, even when a touchfile or a derived reference also names
|
||||
@@ -317,6 +326,7 @@ export function knownNonBehaviorFile(file: string): boolean {
|
||||
// by free doc/generation checks and are not copied into paid fixtures.
|
||||
|| ['AGENTS.md', 'CLAUDE.md', 'agents-digest/gstack-AGENTS.md'].includes(file)
|
||||
|| FREE_ONLY_PR_FILES.some(pattern => matchGlob(file, pattern))
|
||||
|| RELEASE_METADATA_FILES.some(pattern => matchGlob(file, pattern))
|
||||
|| (PERIODIC_ONLY_PR_FILES as readonly string[]).includes(file)
|
||||
|| (file.startsWith('test/fixtures/') && Object.keys(FREE_FIXTURES).some(pattern => matchGlob(file, pattern)))
|
||||
// Free test files anywhere (test/, browse/test/, design/test/, make-pdf/test/): no paid case runs or reads a free test.
|
||||
|
||||
@@ -587,35 +587,69 @@ cmd_down() {
|
||||
}
|
||||
|
||||
# pack SRC: write a gzipped checkout tarball to stdout (tracked + untracked
|
||||
# files that are not ignored, plus .git; a non-git directory is taken whole).
|
||||
# A linked worktree's .git is a one-line pointer into the main repository,
|
||||
# useless on the VM, so it is replaced by a self-contained clone of the
|
||||
# checked-out branch carrying the worktree's own index.
|
||||
# files that are not ignored, plus a .git snapshot; a non-git directory is
|
||||
# taken whole). The live .git is never tarred: another process running git in
|
||||
# the checkout (a review subagent's diff, a trial merge's worktree add) writes
|
||||
# index.lock, packed-refs and objects while tar reads, and GNU tar exits 1
|
||||
# on a file that changed under it (GSTA-121's PREGATE_REMOTE_INFRA). Instead
|
||||
# the refs are fetched into a fresh repository through git's own transport,
|
||||
# which reads a consistent view, the remote.* config is replayed so
|
||||
# origin/<base> still resolves, and the index is copied (git writes it
|
||||
# atomically through a rename). A linked worktree's .git is a one-line
|
||||
# pointer into the main repository, useless on the VM; the same snapshot
|
||||
# replaces it, on the worktree's own branch and index. Not carried: submodule
|
||||
# repositories (.git/modules) and a partial clone's promisor (the fetch pulls
|
||||
# every missing blob first); gstack has neither.
|
||||
cmd_pack() {
|
||||
local src tmp="" gitdir branch
|
||||
local src tmp gitdir branch head entry
|
||||
src=$(cd "${1:-.}" && pwd)
|
||||
if git -C "$src" rev-parse --is-inside-work-tree >/dev/null 2>&1; then
|
||||
if [ -f "$src/.git" ]; then
|
||||
branch=$(git -C "$src" rev-parse --abbrev-ref HEAD)
|
||||
[ "$branch" != HEAD ] || die "pack: $src is a linked worktree on a detached HEAD; check out a branch so the VM gets a self-contained .git"
|
||||
tmp=$(mktemp -d)
|
||||
gitdir=$(cd "$src" && cd "$(git rev-parse --git-common-dir)" && pwd)
|
||||
git clone -q --bare --no-local --single-branch --branch "$branch" "$gitdir" "$tmp/.git"
|
||||
git -C "$tmp/.git" config core.bare false
|
||||
# The worktree's index rides along so the VM's status matches, except a
|
||||
# split index, whose shared half lives only in the original git dir.
|
||||
[ "$(git -C "$src" config --bool core.splitIndex 2>/dev/null)" = true ] \
|
||||
|| cp "$(cd "$src" && cd "$(dirname "$(git rev-parse --git-path index)")" && pwd)/index" "$tmp/.git/index"
|
||||
head=$(git -C "$src" rev-parse --verify -q HEAD) || die "pack: $src has no commits; the VM needs a HEAD to fingerprint"
|
||||
branch=$(git -C "$src" symbolic-ref -q --short HEAD || true)
|
||||
tmp=$(mktemp -d)
|
||||
# The path is baked in: a set -e failure unwinds the function before the trap runs, and a local $tmp is unset there.
|
||||
trap "rm -rf $(printf %q "$tmp")" EXIT
|
||||
# No template hooks from a global init.templateDir, and no auto-gc under the tar.
|
||||
git -c gc.auto=0 init -q --bare --template= "$tmp/.git"
|
||||
git -C "$tmp/.git" -c gc.auto=0 fetch -q --no-tags --update-shallow "$src" '+refs/*:refs/*' \
|
||||
|| die "pack: could not snapshot the refs of $src"
|
||||
if [ -z "$branch" ]; then
|
||||
branch="detached-${head:0:12}"
|
||||
git -C "$tmp/.git" fetch -q --no-tags --update-shallow "$src" "+HEAD:refs/heads/$branch"
|
||||
fi
|
||||
git -C "$tmp/.git" symbolic-ref HEAD "refs/heads/$branch"
|
||||
git -C "$tmp/.git" config core.bare false
|
||||
gitdir=$(cd "$src" && cd "$(git rev-parse --git-common-dir)" && pwd)
|
||||
while IFS= read -r -d '' entry; do
|
||||
# A key with no value (`[remote "x"] mirror`) is boolean true.
|
||||
case $entry in *$'\n'*) ;; *) entry="$entry"$'\n'true ;; esac
|
||||
git -C "$tmp/.git" config --add "${entry%%$'\n'*}" "${entry#*$'\n'}"
|
||||
done < <(git -C "$gitdir" config --local --null --get-regexp '^(remote|branch)\.' 2>/dev/null || true)
|
||||
# The checkout's index rides along so the VM's status matches, except a
|
||||
# split index, whose shared half lives only in the original git dir. The
|
||||
# index's blobs that HEAD's tree lacks (staged, intent-to-add, the stages
|
||||
# of an unmerged entry) are behind no ref, so they travel as a pack of
|
||||
# their own; without them the VM's write-tree (gstack-wtree) cannot read a
|
||||
# staged file.
|
||||
if [ "$(git -C "$src" config --bool core.splitIndex 2>/dev/null)" != true ]; then
|
||||
cp "$(cd "$src" && cd "$(dirname "$(git rev-parse --git-path index)")" && pwd)/index" "$tmp/.git/index"
|
||||
# Read the copy, not the live index, so the pack matches it; a gitlink's
|
||||
# sha (mode 160000) is a submodule commit this object store lacks.
|
||||
GIT_INDEX_FILE="$tmp/.git/index" git -C "$src" ls-files -s | awk '$1 != "160000" {print $2}' | sort -u \
|
||||
| comm -23 - <(git -C "$src" ls-tree -r HEAD | awk '{print $3}' | sort -u) \
|
||||
| git -C "$src" pack-objects -q --stdout \
|
||||
| git -C "$tmp/.git" unpack-objects -q \
|
||||
|| die "pack: could not snapshot the staged blobs of $src"
|
||||
fi
|
||||
(
|
||||
cd "$src"
|
||||
{ git ls-files -z -co --exclude-standard; [ -n "$tmp" ] || printf '.git\0'; } \
|
||||
git ls-files -z -co --exclude-standard \
|
||||
| while IFS= read -r -d '' f; do
|
||||
if [ -e "$f" ] || [ -L "$f" ]; then printf '%s\0' "$f"; fi
|
||||
done \
|
||||
| if [ -n "$tmp" ]; then tar --null -T - -c -C "$tmp" .git -zf -; else tar --null -T - -czf -; fi
|
||||
| tar --null -T - -c -C "$tmp" .git -zf -
|
||||
)
|
||||
[ -z "$tmp" ] || rm -rf "$tmp"
|
||||
rm -rf "$tmp"; trap - EXIT
|
||||
else
|
||||
tar -C "$src" -czf - .
|
||||
fi
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<!-- AUTO-GENERATED from capy-pr.md.tmpl — do not edit directly -->
|
||||
<!-- Regenerate: bun run gen:skill-docs -->
|
||||
## Step 19-C: Capy PR body (What changed, Why, Approvals)
|
||||
## Step 19-C: Capy PR body (What changed, Why, Blast radius, Approvals)
|
||||
|
||||
Read only when Step 19 printed `CAPY_PR: on`. Under Capy a person decides the merge from the PR and
|
||||
the READY report, through a Captain, so the body shows what changed in pictures, why, and who
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
## Step 19-C: Capy PR body (What changed, Why, Approvals)
|
||||
## Step 19-C: Capy PR body (What changed, Why, Blast radius, Approvals)
|
||||
|
||||
Read only when Step 19 printed `CAPY_PR: on`. Under Capy a person decides the merge from the PR and
|
||||
the READY report, through a Captain, so the body shows what changed in pictures, why, and who
|
||||
|
||||
@@ -456,20 +456,22 @@ Core findings never earn a specialist confidence boost. Preserve `advisory`,
|
||||
|
||||
#### 4. Lead judgment
|
||||
|
||||
Reviewers fill the space they are given. Before any merged finding counts, the
|
||||
parent reads each one and drops or downgrades it on these grounds, saying so:
|
||||
- **Nit gravity.** When a reviewer's whole return is style, naming and preference,
|
||||
the code is probably fine; write that down and count nothing from it as a defect.
|
||||
Reviewers fill the space they are given. Before a merged finding counts, the
|
||||
parent reads it and drops or downgrades it on these grounds, saying so:
|
||||
- **Nit gravity.** A reviewer whose whole return is style, naming and preference
|
||||
found nothing; say so and count no defect.
|
||||
- **Hypothetical, not actual.** A defect names the call site or input that reaches
|
||||
it. One that does not is a note until someone traces it.
|
||||
it; one that does not is a note until someone traces it.
|
||||
- **Structure for a case that does not exist.** Advice to abstract, generalize or
|
||||
guard a path nothing takes is advisory, never a defect.
|
||||
guard a path nothing takes is advisory.
|
||||
- **A different design is not a defect.** "I would have done it differently" with
|
||||
no failing case behind it is dropped.
|
||||
no failing case is dropped.
|
||||
- **Missing context.** A finding that assumes a rule the repository documents
|
||||
otherwise is dropped with the pointer.
|
||||
Agreement between reviewers that did not see each other's output is the strongest
|
||||
signal; the `MULTI-SPECIALIST CONFIRMED` boost above records it. A reviewer that
|
||||
- **On record.** A class `docs/review-policy.md` rules below the floor is a note,
|
||||
named by its ruling.
|
||||
Independent agreement between reviewers is the strongest signal (the
|
||||
`MULTI-SPECIALIST CONFIRMED` boost records it). A reviewer that
|
||||
returned `NO FINDINGS` was right until a traced defect says otherwise.
|
||||
|
||||
#### 5. Apply specialist confidence gates
|
||||
|
||||
@@ -76,6 +76,18 @@ describe('gstack-approvals add and render', () => {
|
||||
expect(fs.existsSync(path.join(state, 'projects'))).toBe(false);
|
||||
});
|
||||
|
||||
test('a GitHub Actions run id after a run word is not a phone number, and the record keeps it (GSTA-121)', () => {
|
||||
const cwd = repo(); const state = tmp('appr-state-');
|
||||
const words = 'Rule-19 grant: re-READY with --no-screen citing screen 40477638211 GREEN at 5619199800a2; gate=40477638210 passed';
|
||||
const r = bin(['add', '--ask', 'Third rule 19 grant on the GREEN screen', '--words', words, '--by', 'GStack Minder', '--scope', 'one merge'], { cwd, env: { GSTACK_STATE_ROOT: state } });
|
||||
expect(r.status, r.err).toBe(0);
|
||||
const file = /^APPROVAL_RECORDED: a-[0-9a-f]{12} (.*\.jsonl)$/m.exec(r.out)![1]!;
|
||||
expect(fs.readFileSync(file, 'utf8')).toContain('screen 40477638211 GREEN');
|
||||
const control = bin(['add', '--ask', 'ok', '--words', 'call me on 40477638211 or +14155550123', '--by', 'g', '--scope', 's'], { cwd, env: { GSTACK_STATE_ROOT: state } });
|
||||
expect(control.status).toBe(3);
|
||||
expect(control.err).toContain('pii.phone.e164');
|
||||
});
|
||||
|
||||
test('missing fields, bad outcomes and impossible dates are usage errors', () => {
|
||||
const cwd = repo(); const state = tmp('appr-state-');
|
||||
expect(bin(['add', '--ask', 'x'], { cwd, env: { GSTACK_STATE_ROOT: state } }).err).toContain('APPROVAL_RECORD_INVALID');
|
||||
|
||||
@@ -161,7 +161,7 @@ describe('the five recorded variant-c misses on this checkout', () => {
|
||||
|
||||
describe('shadow output never changes the real selection', () => {
|
||||
test('mode, cases, judges and reasons equal the selection without a shadow', () => {
|
||||
for (const files of [['bin/gstack-config'], ['design/src/evolve.ts'], ['review/SKILL.md.tmpl'], ['lib/fs-utils.ts', 'docs/x.md'], ['ETHOS.md']]) {
|
||||
for (const files of [['bin/gstack-config'], ['design/src/evolve.ts'], ['review/SKILL.md.tmpl'], ['lib/fs-utils.ts', 'docs/x.md'], ['SECURITY.md']]) {
|
||||
const withShadow = selectPrProfile({ selectedE2E: [], selectedJudges: [], changedFiles: files });
|
||||
const without = selectPrProfile({ selectedE2E: [], selectedJudges: [], changedFiles: files, exercised: null });
|
||||
expect(withShadow.shadow, files.join()).toBeDefined();
|
||||
|
||||
@@ -19,6 +19,10 @@
|
||||
/**
|
||||
* E2E test touchfiles — keyed by testName (the string passed to runSkillTest).
|
||||
* Each test lists the file patterns that, if changed, require the test to run.
|
||||
*
|
||||
* ETHOS.md is registered under the /office-hours and /plan-ceo-review cases,
|
||||
* whose prompts quote it; the tier>=3 preamble (Search Before Building) only
|
||||
* names it as a read-on-demand pointer that no paid case asserts on (#3499).
|
||||
*/
|
||||
export const E2E_TOUCHFILES: Record<string, string[]> = {
|
||||
'ship-skipped-queued-finding': [ 'lib/outside-review-result.ts', 'lib/gate-outcomes.ts', 'bin/gstack-codex-probe','bin/gstack-state-root.sh', 'lib/state-root.ts', 'package.json', 'bun.lock', '.github/docker/Dockerfile.ci',
|
||||
@@ -220,7 +224,7 @@ export const E2E_TOUCHFILES: Record<string, string[]> = {
|
||||
|
||||
"test/fixtures/plan-scope-target-aw.json",
|
||||
|
||||
'test/fixtures/auto-decide-saved-ai.json', 'test/fixtures/auto-decide-retry-ai.json','bin/gstack-skill-start', 'bin/gstack-skill-end', 'plan-ceo-review/**', 'scripts/resolvers/preamble/generate-completion-status.ts', 'scripts/resolvers/question-tuning.ts', 'scripts/resolvers/preamble/generate-ask-user-format.ts', 'scripts/resolvers/preamble.ts', 'scripts/resolvers/preamble-hoist.ts', 'scripts/resolvers/review-dashboard.ts', 'scripts/resolvers/plan-gates.ts', 'scripts/resolvers/spec-review.ts', 'scripts/resolvers/outside-voice-steps.ts', 'scripts/resolvers/review-scope.ts', 'scripts/resolvers/outside-voice.ts', 'test/helpers/claude-pty-runner.ts', 'test/helpers/pty/**', 'test/fixtures/design-ui-boxed-question.json', 'test/helpers/hermetic-skill-runtime.ts', 'test/helpers/pty-trust-dialog.ts', 'test/skill-e2e-plan-ceo-plan-mode.test.ts', 'test/fixtures/eng-d2-truncated-border-0bcd.json', 'test/fixtures/eng-d1-clipped-elision-1579.json', 'test/fixtures/eng-d2-planning-prelude-4d.json', 'test/fixtures/ceo-approach-z-call.json', 'test/fixtures/ceo-approach-z-screen.txt', 'test/helpers/plan-scope-selection.ts', 'test/helpers/native-auto-decide.ts', 'test/fixtures/auto-decide-current-declaration-6aef.json', 'test/fixtures/auto-decide-explanatory-mode-043a.json', 'test/fixtures/auto-decide-explanatory-mode-749df.json', 'test/fixtures/auto-decide-structured-77.json', 'test/helpers/auto-decision-state.ts', 'test/fixtures/auto-decide-state-cab3.json', 'bin/gstack-question-log', 'bin/gstack-question-preference', 'test/helpers/fake-plan-seed.ts', 'test/helpers/plan-seed-submission.ts', 'test/fixtures/plan-seed-cli.ts', 'test/fixtures/native-auto-decide-ag.json', 'test/fixtures/eng-seeded-completion-ai.json', 'test/helpers/plan-count-pending-exit.ts', 'test/helpers/pty-screen.ts', 'test/fixtures/pty-screen/**',
|
||||
'test/fixtures/auto-decide-saved-ai.json', 'test/fixtures/auto-decide-retry-ai.json','bin/gstack-skill-start', 'bin/gstack-skill-end', 'plan-ceo-review/**', 'ETHOS.md', 'scripts/resolvers/preamble/generate-completion-status.ts', 'scripts/resolvers/question-tuning.ts', 'scripts/resolvers/preamble/generate-ask-user-format.ts', 'scripts/resolvers/preamble.ts', 'scripts/resolvers/preamble-hoist.ts', 'scripts/resolvers/review-dashboard.ts', 'scripts/resolvers/plan-gates.ts', 'scripts/resolvers/spec-review.ts', 'scripts/resolvers/outside-voice-steps.ts', 'scripts/resolvers/review-scope.ts', 'scripts/resolvers/outside-voice.ts', 'test/helpers/claude-pty-runner.ts', 'test/helpers/pty/**', 'test/fixtures/design-ui-boxed-question.json', 'test/helpers/hermetic-skill-runtime.ts', 'test/helpers/pty-trust-dialog.ts', 'test/skill-e2e-plan-ceo-plan-mode.test.ts', 'test/fixtures/eng-d2-truncated-border-0bcd.json', 'test/fixtures/eng-d1-clipped-elision-1579.json', 'test/fixtures/eng-d2-planning-prelude-4d.json', 'test/fixtures/ceo-approach-z-call.json', 'test/fixtures/ceo-approach-z-screen.txt', 'test/helpers/plan-scope-selection.ts', 'test/helpers/native-auto-decide.ts', 'test/fixtures/auto-decide-current-declaration-6aef.json', 'test/fixtures/auto-decide-explanatory-mode-043a.json', 'test/fixtures/auto-decide-explanatory-mode-749df.json', 'test/fixtures/auto-decide-structured-77.json', 'test/helpers/auto-decision-state.ts', 'test/fixtures/auto-decide-state-cab3.json', 'bin/gstack-question-log', 'bin/gstack-question-preference', 'test/helpers/fake-plan-seed.ts', 'test/helpers/plan-seed-submission.ts', 'test/fixtures/plan-seed-cli.ts', 'test/fixtures/native-auto-decide-ag.json', 'test/fixtures/eng-seeded-completion-ai.json', 'test/helpers/plan-count-pending-exit.ts', 'test/helpers/pty-screen.ts', 'test/fixtures/pty-screen/**',
|
||||
|
||||
'test/fixtures/design-scope-announcement-ao.json',
|
||||
'test/fixtures/design-scope-declaration-ak.json',
|
||||
@@ -310,7 +314,7 @@ export const E2E_TOUCHFILES: Record<string, string[]> = {
|
||||
|
||||
"test/fixtures/plan-scope-recovery-av.json",
|
||||
"test/fixtures/design-scope-checkpoint-at.json",
|
||||
'test/fixtures/auto-decide-saved-ai.json', 'test/fixtures/auto-decide-retry-ai.json','bin/gstack-skill-start', 'bin/gstack-skill-end', 'plan-ceo-review/**', 'plan-eng-review/**', 'plan-design-review/**', 'scripts/resolvers/preamble/generate-completion-status.ts', 'scripts/resolvers/preamble.ts', 'scripts/resolvers/preamble-hoist.ts', 'test/helpers/claude-pty-runner.ts', 'test/helpers/pty/**', 'test/fixtures/design-ui-boxed-question.json', 'test/helpers/hermetic-skill-runtime.ts', 'test/helpers/pty-trust-dialog.ts', 'test/skill-e2e-plan-mode-no-op.test.ts', 'test/fixtures/eng-d2-truncated-border-0bcd.json', 'test/fixtures/eng-d1-clipped-elision-1579.json', 'test/fixtures/eng-d2-planning-prelude-4d.json', 'test/fixtures/ceo-approach-z-call.json', 'test/fixtures/ceo-approach-z-screen.txt', 'test/helpers/plan-scope-selection.ts', 'test/helpers/native-auto-decide.ts', 'test/fixtures/auto-decide-current-declaration-6aef.json', 'test/fixtures/auto-decide-explanatory-mode-043a.json', 'test/fixtures/auto-decide-explanatory-mode-749df.json', 'test/fixtures/auto-decide-structured-77.json', 'test/helpers/auto-decision-state.ts', 'test/fixtures/auto-decide-state-cab3.json', 'bin/gstack-question-log', 'bin/gstack-question-preference', 'test/helpers/fake-plan-seed.ts', 'test/fixtures/native-auto-decide-ag.json', 'test/fixtures/eng-seeded-completion-ai.json', 'test/helpers/plan-count-pending-exit.ts', 'test/helpers/pty-screen.ts', 'test/fixtures/pty-screen/**',
|
||||
'test/fixtures/auto-decide-saved-ai.json', 'test/fixtures/auto-decide-retry-ai.json','bin/gstack-skill-start', 'bin/gstack-skill-end', 'plan-ceo-review/**', 'ETHOS.md', 'plan-eng-review/**', 'plan-design-review/**', 'scripts/resolvers/preamble/generate-completion-status.ts', 'scripts/resolvers/preamble.ts', 'scripts/resolvers/preamble-hoist.ts', 'test/helpers/claude-pty-runner.ts', 'test/helpers/pty/**', 'test/fixtures/design-ui-boxed-question.json', 'test/helpers/hermetic-skill-runtime.ts', 'test/helpers/pty-trust-dialog.ts', 'test/skill-e2e-plan-mode-no-op.test.ts', 'test/fixtures/eng-d2-truncated-border-0bcd.json', 'test/fixtures/eng-d1-clipped-elision-1579.json', 'test/fixtures/eng-d2-planning-prelude-4d.json', 'test/fixtures/ceo-approach-z-call.json', 'test/fixtures/ceo-approach-z-screen.txt', 'test/helpers/plan-scope-selection.ts', 'test/helpers/native-auto-decide.ts', 'test/fixtures/auto-decide-current-declaration-6aef.json', 'test/fixtures/auto-decide-explanatory-mode-043a.json', 'test/fixtures/auto-decide-explanatory-mode-749df.json', 'test/fixtures/auto-decide-structured-77.json', 'test/helpers/auto-decision-state.ts', 'test/fixtures/auto-decide-state-cab3.json', 'bin/gstack-question-log', 'bin/gstack-question-preference', 'test/helpers/fake-plan-seed.ts', 'test/fixtures/native-auto-decide-ag.json', 'test/fixtures/eng-seeded-completion-ai.json', 'test/helpers/plan-count-pending-exit.ts', 'test/helpers/pty-screen.ts', 'test/fixtures/pty-screen/**',
|
||||
|
||||
'test/fixtures/design-scope-announcement-ao.json',
|
||||
'test/fixtures/design-scope-declaration-ak.json',
|
||||
@@ -332,7 +336,7 @@ export const E2E_TOUCHFILES: Record<string, string[]> = {
|
||||
'office-hours-auto-mode': [ 'ETHOS.md','bin/gstack-state-root.sh', 'lib/state-root.ts', 'test/fixtures/auto-decide-recommendation-361c.json',
|
||||
|
||||
'test/fixtures/auto-decide-target-361c.json',
|
||||
'test/fixtures/native-auto-decide-ag.json', 'test/helpers/native-auto-decide.ts', 'test/fixtures/auto-decide-current-declaration-6aef.json', 'test/fixtures/auto-decide-explanatory-mode-043a.json', 'test/fixtures/auto-decide-explanatory-mode-749df.json', 'bin/gstack-skill-start', 'bin/gstack-skill-end', 'office-hours/**', 'scripts/resolvers/preamble/generate-completion-status.ts', 'scripts/resolvers/question-tuning.ts', 'scripts/resolvers/preamble/generate-ask-user-format.ts', 'scripts/resolvers/preamble.ts', 'scripts/resolvers/preamble-hoist.ts', 'test/helpers/claude-pty-runner.ts', 'test/helpers/pty/**', 'test/fixtures/design-ui-boxed-question.json', 'test/helpers/hermetic-skill-runtime.ts', 'test/helpers/pty-trust-dialog.ts', 'test/skill-e2e-office-hours-auto-mode.test.ts', 'test/helpers/plan-mode-evidence.ts', 'test/fixtures/eng-d2-truncated-border-0bcd.json', 'test/fixtures/eng-d1-clipped-elision-1579.json', 'test/fixtures/eng-d2-planning-prelude-4d.json', 'test/fixtures/ceo-approach-z-call.json', 'test/fixtures/ceo-approach-z-screen.txt',
|
||||
'test/fixtures/native-auto-decide-ag.json', 'test/helpers/native-auto-decide.ts', 'test/fixtures/auto-decide-current-declaration-6aef.json', 'test/fixtures/auto-decide-explanatory-mode-043a.json', 'test/fixtures/auto-decide-explanatory-mode-749df.json', 'bin/gstack-skill-start', 'bin/gstack-skill-end', 'office-hours/**', 'ETHOS.md', 'scripts/resolvers/preamble/generate-completion-status.ts', 'scripts/resolvers/question-tuning.ts', 'scripts/resolvers/preamble/generate-ask-user-format.ts', 'scripts/resolvers/preamble.ts', 'scripts/resolvers/preamble-hoist.ts', 'test/helpers/claude-pty-runner.ts', 'test/helpers/pty/**', 'test/fixtures/design-ui-boxed-question.json', 'test/helpers/hermetic-skill-runtime.ts', 'test/helpers/pty-trust-dialog.ts', 'test/skill-e2e-office-hours-auto-mode.test.ts', 'test/helpers/plan-mode-evidence.ts', 'test/fixtures/eng-d2-truncated-border-0bcd.json', 'test/fixtures/eng-d1-clipped-elision-1579.json', 'test/fixtures/eng-d2-planning-prelude-4d.json', 'test/fixtures/ceo-approach-z-call.json', 'test/fixtures/ceo-approach-z-screen.txt',
|
||||
'test/fixtures/pty-companion-cli.ts', 'lib/fs-atomic.ts', 'test/helpers/plan-skill-questions.ts', 'test/fixtures/eng-auq-validation-error.json', 'test/fixtures/bash-directory-permission.json', 'test/fixtures/design-tasks-bash-permission.json', 'test/fixtures/read-permission.json', 'test/fixtures/ceo-split-e5-numbered-description-491.json', 'test/helpers/plan-skill-question-events.ts', 'test/helpers/plan-skill-question-hook-scope.ts', 'test/helpers/skill-census.ts', 'test/helpers/auto-decision-state.ts', 'test/helpers/autoplan-artifact-digest.ts', 'test/helpers/autoplan-artifact-permission.ts', 'test/helpers/autoplan-artifact-recorder.ts', 'test/helpers/dx-selected-navigation.ts', 'test/helpers/e2e-gate.ts', 'test/helpers/eng-cache-writer-decision.ts', 'test/helpers/owned-claude-transcript.ts', 'test/helpers/plan-count-artifacts.ts', 'test/helpers/plan-count-file-permission.ts', 'test/helpers/plan-count-fixture.ts', 'test/helpers/plan-count-pending-exit.ts', 'test/helpers/plan-count-pending-question.ts', 'test/helpers/plan-count-transcript.ts', 'test/helpers/plan-floor-review.ts', 'test/helpers/plan-floor-target.ts', 'test/helpers/plan-scope-selection.ts', 'test/helpers/plan-seed-submission.ts', 'test/helpers/pty-screen.ts', 'bin/gstack-context-recovery'],
|
||||
'office-hours-phase4-fork': [ 'ETHOS.md','bin/gstack-state-root.sh', 'lib/state-root.ts', 'bin/gstack-skill-start', 'bin/gstack-skill-end', 'office-hours/**', 'scripts/resolvers/preamble/generate-ask-user-format.ts', 'scripts/resolvers/preamble/generate-completion-status.ts', 'scripts/resolvers/preamble.ts', 'scripts/resolvers/preamble-hoist.ts', 'scripts/resolvers/question-tuning.ts', 'test/helpers/llm-judge.ts', 'test/skill-e2e-office-hours-phase4.test.ts', 'bin/gstack-context-recovery' ],
|
||||
'llm-judge-recommendation': ['codex/**', 'test/helpers/llm-judge.ts', 'test/llm-judge-recommendation.test.ts', 'scripts/resolvers/preamble/generate-ask-user-format.ts', 'codex/SKILL.md.tmpl', 'scripts/resolvers/review-dashboard.ts', 'scripts/resolvers/plan-gates.ts', 'scripts/resolvers/spec-review.ts', 'scripts/resolvers/outside-voice-steps.ts', 'scripts/resolvers/review-scope.ts', 'scripts/resolvers/outside-voice.ts'],
|
||||
@@ -350,7 +354,7 @@ export const E2E_TOUCHFILES: Record<string, string[]> = {
|
||||
'test/fixtures/autoplan-public-narration-ad.json',
|
||||
'test/fixtures/auto-decide-completed-mode-f359.json',
|
||||
'test/helpers/plan-count-transcript.ts', 'test/fixtures/plan-count-cross-cwd-ancestry-0bcd.json',
|
||||
'test/fixtures/auto-decide-saved-ai.json', 'test/fixtures/auto-decide-retry-ai.json','bin/gstack-skill-start', 'bin/gstack-skill-end', 'bin/gstack-session-kind', 'scripts/resolvers/question-tuning.ts', 'scripts/resolvers/preamble/generate-ask-user-format.ts', 'scripts/resolvers/preamble/generate-preamble-bash.ts', 'scripts/resolvers/preamble/generate-completion-status.ts', 'plan-ceo-review/**', 'bin/gstack-question-preference', 'bin/gstack-config', 'bin/gstack-slug', 'bin/gstack-remote-identity.sh', 'hosts/claude/hooks/question-preference-hook.ts', 'lib/bin-context.ts', 'lib/remote-identity.ts', 'hosts/claude/hooks/spawned-directive.ts', 'lib/is-conductor.ts', 'test/helpers/claude-pty-runner.ts', 'test/helpers/pty/**', 'test/fixtures/design-ui-boxed-question.json', 'test/helpers/hermetic-skill-runtime.ts', 'test/helpers/pty-trust-dialog.ts', 'test/skill-e2e-auto-decide-preserved.test.ts', 'test/fixtures/eng-d2-truncated-border-0bcd.json', 'test/fixtures/eng-d1-clipped-elision-1579.json', 'test/fixtures/eng-d2-planning-prelude-4d.json', 'test/fixtures/ceo-approach-z-call.json', 'test/fixtures/ceo-approach-z-screen.txt', 'test/helpers/native-auto-decide.ts', 'test/fixtures/auto-decide-current-declaration-6aef.json', 'test/fixtures/auto-decide-explanatory-mode-043a.json', 'test/fixtures/auto-decide-explanatory-mode-749df.json', 'test/fixtures/auto-decide-structured-77.json', 'test/helpers/auto-decision-state.ts', 'test/fixtures/auto-decide-state-cab3.json', 'bin/gstack-question-log', 'test/helpers/fake-plan-seed.ts', 'test/helpers/plan-seed-submission.ts', 'test/fixtures/plan-seed-cli.ts', 'test/fixtures/native-auto-decide-ag.json', 'test/fixtures/eng-seeded-completion-ai.json', 'test/helpers/plan-count-pending-exit.ts', 'test/helpers/pty-screen.ts', 'test/fixtures/pty-screen/**',
|
||||
'test/fixtures/auto-decide-saved-ai.json', 'test/fixtures/auto-decide-retry-ai.json','bin/gstack-skill-start', 'bin/gstack-skill-end', 'bin/gstack-session-kind', 'scripts/resolvers/question-tuning.ts', 'scripts/resolvers/preamble/generate-ask-user-format.ts', 'scripts/resolvers/preamble/generate-preamble-bash.ts', 'scripts/resolvers/preamble/generate-completion-status.ts', 'plan-ceo-review/**', 'ETHOS.md', 'bin/gstack-question-preference', 'bin/gstack-config', 'bin/gstack-slug', 'bin/gstack-remote-identity.sh', 'hosts/claude/hooks/question-preference-hook.ts', 'lib/bin-context.ts', 'lib/remote-identity.ts', 'hosts/claude/hooks/spawned-directive.ts', 'lib/is-conductor.ts', 'test/helpers/claude-pty-runner.ts', 'test/helpers/pty/**', 'test/fixtures/design-ui-boxed-question.json', 'test/helpers/hermetic-skill-runtime.ts', 'test/helpers/pty-trust-dialog.ts', 'test/skill-e2e-auto-decide-preserved.test.ts', 'test/fixtures/eng-d2-truncated-border-0bcd.json', 'test/fixtures/eng-d1-clipped-elision-1579.json', 'test/fixtures/eng-d2-planning-prelude-4d.json', 'test/fixtures/ceo-approach-z-call.json', 'test/fixtures/ceo-approach-z-screen.txt', 'test/helpers/native-auto-decide.ts', 'test/fixtures/auto-decide-current-declaration-6aef.json', 'test/fixtures/auto-decide-explanatory-mode-043a.json', 'test/fixtures/auto-decide-explanatory-mode-749df.json', 'test/fixtures/auto-decide-structured-77.json', 'test/helpers/auto-decision-state.ts', 'test/fixtures/auto-decide-state-cab3.json', 'bin/gstack-question-log', 'test/helpers/fake-plan-seed.ts', 'test/helpers/plan-seed-submission.ts', 'test/fixtures/plan-seed-cli.ts', 'test/fixtures/native-auto-decide-ag.json', 'test/fixtures/eng-seeded-completion-ai.json', 'test/helpers/plan-count-pending-exit.ts', 'test/helpers/pty-screen.ts', 'test/fixtures/pty-screen/**',
|
||||
|
||||
'test/helpers/plan-count-fixture.ts',
|
||||
'test/fixtures/pty-companion-cli.ts', 'lib/fs-atomic.ts', 'test/fixtures/auto-decide-mode-selector-749df.json', 'lib/redact-engine.ts', 'lib/redact-patterns.ts', 'test/helpers/ceo-finding-fixture.ts', 'test/helpers/owned-claude-transcript.ts', 'test/helpers/plan-skill-questions.ts', 'test/fixtures/eng-auq-validation-error.json', 'test/fixtures/bash-directory-permission.json', 'test/fixtures/design-tasks-bash-permission.json', 'test/fixtures/read-permission.json', 'test/fixtures/ceo-split-e5-numbered-description-491.json', 'test/helpers/plan-skill-question-events.ts', 'test/helpers/plan-skill-question-hook-scope.ts', 'test/helpers/skill-census.ts', 'scripts/resolvers/tasks-section.ts', 'test/helpers/autoplan-artifact-digest.ts', 'test/helpers/autoplan-artifact-permission.ts', 'test/helpers/autoplan-artifact-recorder.ts', 'test/helpers/dx-selected-navigation.ts', 'test/helpers/e2e-gate.ts', 'test/helpers/eng-cache-writer-decision.ts', 'test/helpers/plan-count-artifacts.ts', 'test/helpers/plan-count-file-permission.ts', 'test/helpers/plan-count-pending-question.ts', 'test/helpers/plan-floor-review.ts', 'test/helpers/plan-floor-target.ts', 'test/helpers/plan-scope-selection.ts', 'hosts/claude/hooks/hook-log.ts', 'bin/gstack-context-recovery'],
|
||||
@@ -380,7 +384,7 @@ export const E2E_TOUCHFILES: Record<string, string[]> = {
|
||||
'test/fixtures/ceo-mode-pending-submit.json',
|
||||
"test/fixtures/plan-count-cross-cwd-ancestry-0bcd.json",
|
||||
|
||||
"test/fixtures/ceo-mode-colon-at.json", 'plan-ceo-review/**', 'scripts/resolvers/preamble/generate-ask-user-format.ts', 'scripts/resolvers/preamble.ts', 'scripts/resolvers/preamble-hoist.ts', 'test/helpers/claude-pty-runner.ts', 'test/helpers/pty/**', 'test/fixtures/design-ui-boxed-question.json', 'test/helpers/pty-screen.ts', 'test/fixtures/pty-screen/**', 'test/helpers/hermetic-skill-runtime.ts', 'test/helpers/pty-trust-dialog.ts', 'test/helpers/ceo-mode-option.ts', 'test/fixtures/ceo-expansion-disposition-77.json', 'test/helpers/plan-count-transcript.ts', 'test/fixtures/autoplan-public-narration-ad.json', 'test/helpers/plan-count-fixture.ts', 'test/fixtures/ceo-prerequisite-n-call.json', 'test/fixtures/eng-prerequisite-77.json', 'test/skill-e2e-plan-ceo-mode-routing.test.ts', 'test/fixtures/ceo-hold-posture-l.json', 'test/fixtures/ceo-mode-labels-l.json', 'test/fixtures/ceo-checkbox-l.screen.txt', 'test/fixtures/ceo-mode-prerequisite-o-calls.json', 'test/fixtures/ceo-mode-prerequisite-q-call.json', 'test/fixtures/ceo-preview-u-call.json', 'test/fixtures/ceo-preview-u-screen.txt', 'test/fixtures/design-preview-v-screen.txt', 'test/fixtures/eng-d2-truncated-border-0bcd.json', 'test/fixtures/eng-d1-clipped-elision-1579.json', 'test/fixtures/eng-d2-planning-prelude-4d.json', 'test/fixtures/ceo-approach-z-call.json', 'test/fixtures/ceo-approach-z-screen.txt', 'test/fixtures/ceo-mode-preview-aa-screen.txt', 'test/fixtures/ceo-count-mode-preview-aa-screen.txt', 'test/fixtures/ceo-expansion-auq-ac.json', 'test/helpers/plan-count-pending-question.ts', 'test/fixtures/ceo-barless-submit-ac.json', 'test/fixtures/pending-question-completion-ad.json', 'test/fixtures/ceo-mode-posture-ad.json', 'test/fixtures/ceo-mode-full-ad.json', 'test/fixtures/ceo-prerequisite-ad-v2.json', 'test/fixtures/ceo-hold-posture-ag.json',
|
||||
"test/fixtures/ceo-mode-colon-at.json", 'plan-ceo-review/**', 'ETHOS.md', 'scripts/resolvers/preamble/generate-ask-user-format.ts', 'scripts/resolvers/preamble.ts', 'scripts/resolvers/preamble-hoist.ts', 'test/helpers/claude-pty-runner.ts', 'test/helpers/pty/**', 'test/fixtures/design-ui-boxed-question.json', 'test/helpers/pty-screen.ts', 'test/fixtures/pty-screen/**', 'test/helpers/hermetic-skill-runtime.ts', 'test/helpers/pty-trust-dialog.ts', 'test/helpers/ceo-mode-option.ts', 'test/fixtures/ceo-expansion-disposition-77.json', 'test/helpers/plan-count-transcript.ts', 'test/fixtures/autoplan-public-narration-ad.json', 'test/helpers/plan-count-fixture.ts', 'test/fixtures/ceo-prerequisite-n-call.json', 'test/fixtures/eng-prerequisite-77.json', 'test/skill-e2e-plan-ceo-mode-routing.test.ts', 'test/fixtures/ceo-hold-posture-l.json', 'test/fixtures/ceo-mode-labels-l.json', 'test/fixtures/ceo-checkbox-l.screen.txt', 'test/fixtures/ceo-mode-prerequisite-o-calls.json', 'test/fixtures/ceo-mode-prerequisite-q-call.json', 'test/fixtures/ceo-preview-u-call.json', 'test/fixtures/ceo-preview-u-screen.txt', 'test/fixtures/design-preview-v-screen.txt', 'test/fixtures/eng-d2-truncated-border-0bcd.json', 'test/fixtures/eng-d1-clipped-elision-1579.json', 'test/fixtures/eng-d2-planning-prelude-4d.json', 'test/fixtures/ceo-approach-z-call.json', 'test/fixtures/ceo-approach-z-screen.txt', 'test/fixtures/ceo-mode-preview-aa-screen.txt', 'test/fixtures/ceo-count-mode-preview-aa-screen.txt', 'test/fixtures/ceo-expansion-auq-ac.json', 'test/helpers/plan-count-pending-question.ts', 'test/fixtures/ceo-barless-submit-ac.json', 'test/fixtures/pending-question-completion-ad.json', 'test/fixtures/ceo-mode-posture-ad.json', 'test/fixtures/ceo-mode-full-ad.json', 'test/fixtures/ceo-prerequisite-ad-v2.json', 'test/fixtures/ceo-hold-posture-ag.json',
|
||||
"test/fixtures/ceo-hold-commitment-ar.json",
|
||||
'test/fixtures/pty-companion-cli.ts', 'lib/fs-atomic.ts', 'test/helpers/ceo-finding-fixture.ts', 'test/helpers/plan-skill-questions.ts', 'test/fixtures/eng-auq-validation-error.json', 'test/fixtures/bash-directory-permission.json', 'test/fixtures/design-tasks-bash-permission.json', 'test/fixtures/read-permission.json', 'test/fixtures/ceo-split-e5-numbered-description-491.json', 'test/helpers/plan-skill-question-events.ts', 'test/helpers/plan-skill-question-hook-scope.ts', 'test/helpers/skill-census.ts', 'test/helpers/owned-claude-transcript.ts', 'scripts/resolvers/tasks-section.ts',
|
||||
'test/fixtures/ceo-expansion-pacing-77.json', 'test/helpers/auto-decision-state.ts', 'test/helpers/autoplan-artifact-digest.ts', 'test/helpers/autoplan-artifact-permission.ts', 'test/helpers/autoplan-artifact-recorder.ts', 'test/helpers/dx-selected-navigation.ts', 'test/helpers/e2e-gate.ts', 'test/helpers/eng-cache-writer-decision.ts', 'test/helpers/native-auto-decide.ts', 'test/helpers/plan-count-artifacts.ts', 'test/helpers/plan-count-file-permission.ts', 'test/helpers/plan-count-pending-exit.ts', 'test/helpers/plan-floor-review.ts', 'test/helpers/plan-floor-target.ts', 'test/helpers/plan-scope-selection.ts', 'test/helpers/plan-seed-submission.ts'],
|
||||
@@ -417,7 +421,7 @@ export const E2E_TOUCHFILES: Record<string, string[]> = {
|
||||
'ship-section-loading': [ 'ship/**', 'scripts/resolvers/sections.ts', 'scripts/gen-skill-docs.ts', 'test/helpers/auq-sdk-capture.ts', 'test/helpers/session-runner.ts', 'test/skill-e2e-ship-section-loading.test.ts',
|
||||
'scripts/resolvers/testing.ts', 'test/helpers/agent-sdk-runner.ts', 'test/helpers/auq-native-capture.ts', 'test/helpers/auto-decision-state.ts', 'test/helpers/autoplan-artifact-digest.ts', 'test/helpers/autoplan-artifact-permission.ts', 'test/helpers/autoplan-artifact-recorder.ts', 'test/helpers/claude-pty-runner.ts', 'test/helpers/pty/**', 'test/helpers/dx-selected-navigation.ts', 'test/helpers/e2e-gate.ts', 'test/helpers/eng-cache-writer-decision.ts', 'test/helpers/hermetic-skill-runtime.ts', 'test/helpers/llm-judge.ts', 'test/helpers/native-auto-decide.ts', 'test/helpers/owned-claude-transcript.ts', 'test/helpers/plan-count-artifacts.ts', 'test/helpers/plan-count-file-permission.ts', 'test/helpers/plan-count-fixture.ts', 'test/helpers/plan-count-pending-exit.ts', 'test/helpers/plan-count-pending-question.ts', 'test/helpers/plan-count-transcript.ts', 'test/helpers/plan-floor-review.ts', 'test/helpers/plan-floor-target.ts', 'test/helpers/plan-scope-selection.ts', 'test/helpers/plan-seed-submission.ts', 'test/helpers/plan-skill-question-events.ts', 'test/helpers/plan-skill-question-hook-scope.ts', 'test/helpers/plan-skill-questions.ts', 'test/helpers/pty-screen.ts', 'test/helpers/pty-trust-dialog.ts', 'test/helpers/skill-census.ts'],
|
||||
'plan-ceo-section-loading': [ 'ETHOS.md',
|
||||
'test/fixtures/ceo-fill-lifetime.json', 'plan-ceo-review/**', 'scripts/resolvers/sections.ts', 'scripts/gen-skill-docs.ts', 'test/helpers/auq-sdk-capture.ts', 'test/helpers/session-runner.ts', 'test/helpers/ceo-section-loading-fixture.ts', 'test/helpers/ceo-stale-fill-decision.ts', 'test/skill-e2e-plan-ceo-review-section-loading.test.ts', 'test/fixtures/ceo-section-loading-l-report.md', 'test/fixtures/ceo-section-loading-q-report.md', 'test/fixtures/ceo-section-r-rejected-report.md', 'test/fixtures/ceo-section-s-trace-report.md', 'test/fixtures/ceo-section-u-report.md', 'test/fixtures/ceo-section-y-report.md', 'test/fixtures/ceo-section-aa-report.md', 'test/fixtures/sdk-stale-table-ad-v3.json', 'test/fixtures/sdk-ordering-ae.json', 'test/fixtures/sdk-columnar-af.json', 'test/fixtures/sdk-order-b-ag.json', 'test/fixtures/sdk-schedule-continuation-ah.json', 'test/fixtures/sdk-original-order-ai.json', 'test/fixtures/sdk-compact-sequence-aj.json',
|
||||
'test/fixtures/ceo-fill-lifetime.json', 'plan-ceo-review/**', 'ETHOS.md', 'scripts/resolvers/sections.ts', 'scripts/gen-skill-docs.ts', 'test/helpers/auq-sdk-capture.ts', 'test/helpers/session-runner.ts', 'test/helpers/ceo-section-loading-fixture.ts', 'test/helpers/ceo-stale-fill-decision.ts', 'test/skill-e2e-plan-ceo-review-section-loading.test.ts', 'test/fixtures/ceo-section-loading-l-report.md', 'test/fixtures/ceo-section-loading-q-report.md', 'test/fixtures/ceo-section-r-rejected-report.md', 'test/fixtures/ceo-section-s-trace-report.md', 'test/fixtures/ceo-section-u-report.md', 'test/fixtures/ceo-section-y-report.md', 'test/fixtures/ceo-section-aa-report.md', 'test/fixtures/sdk-stale-table-ad-v3.json', 'test/fixtures/sdk-ordering-ae.json', 'test/fixtures/sdk-columnar-af.json', 'test/fixtures/sdk-order-b-ag.json', 'test/fixtures/sdk-schedule-continuation-ah.json', 'test/fixtures/sdk-original-order-ai.json', 'test/fixtures/sdk-compact-sequence-aj.json',
|
||||
"test/fixtures/sdk-reported-coordination-ar.md", "test/fixtures/sdk-ordered-schedule-ar.md",
|
||||
'scripts/resolvers/review-dashboard.ts', 'scripts/resolvers/plan-gates.ts', 'scripts/resolvers/spec-review.ts', 'scripts/resolvers/outside-voice-steps.ts', 'scripts/resolvers/review-scope.ts', 'scripts/resolvers/outside-voice.ts', 'scripts/resolvers/preamble/generate-ask-user-format.ts', 'scripts/resolvers/tasks-section.ts', 'test/helpers/agent-sdk-runner.ts', 'test/helpers/auq-native-capture.ts', 'test/helpers/auto-decision-state.ts', 'test/helpers/autoplan-artifact-digest.ts', 'test/helpers/autoplan-artifact-permission.ts', 'test/helpers/autoplan-artifact-recorder.ts', 'test/helpers/claude-pty-runner.ts', 'test/helpers/pty/**', 'test/helpers/dx-selected-navigation.ts', 'test/helpers/e2e-gate.ts', 'test/helpers/eng-cache-writer-decision.ts', 'test/helpers/hermetic-skill-runtime.ts', 'test/helpers/llm-judge.ts', 'test/helpers/native-auto-decide.ts', 'test/helpers/owned-claude-transcript.ts', 'test/helpers/plan-count-artifacts.ts', 'test/helpers/plan-count-file-permission.ts', 'test/helpers/plan-count-fixture.ts', 'test/helpers/plan-count-pending-exit.ts', 'test/helpers/plan-count-pending-question.ts', 'test/helpers/plan-count-transcript.ts', 'test/helpers/plan-floor-review.ts', 'test/helpers/plan-floor-target.ts', 'test/helpers/plan-scope-selection.ts', 'test/helpers/plan-seed-submission.ts', 'test/helpers/plan-skill-question-events.ts', 'test/helpers/plan-skill-question-hook-scope.ts', 'test/helpers/plan-skill-questions.ts', 'test/helpers/pty-screen.ts', 'test/helpers/pty-trust-dialog.ts', 'test/helpers/skill-census.ts'],
|
||||
// Data-driven behavioral guard for the 'plan'/'prompt' carves: one case per
|
||||
@@ -477,7 +481,7 @@ export const E2E_TOUCHFILES: Record<string, string[]> = {
|
||||
|
||||
'test/fixtures/plan-floor-routing-361c.json',
|
||||
|
||||
'test/fixtures/ceo-report-permission-fb10.json', 'test/fixtures/plan-floor-permission-fb10.json', 'test/helpers/plan-count-file-permission.ts', 'test/fixtures/plan-edit-cropped-permission-1579.json', 'test/fixtures/plan-count-permission-target-ad-v2.json', 'test/fixtures/design-crop-gutter-ap.json', 'test/fixtures/plan-count-crop-ak.json', 'test/fixtures/plan-count-long-edit-0bcd.json', 'test/fixtures/plan-count-cropped-wrap-6714.json', 'bin/gstack-skill-start', 'bin/gstack-skill-end', 'plan-ceo-review/**', 'scripts/resolvers/preamble.ts', 'scripts/resolvers/preamble-hoist.ts', 'scripts/resolvers/preamble/generate-ask-user-format.ts', 'scripts/resolvers/preamble/generate-completion-status.ts', 'scripts/resolvers/review-dashboard.ts', 'scripts/resolvers/plan-gates.ts', 'scripts/resolvers/spec-review.ts', 'scripts/resolvers/outside-voice-steps.ts', 'scripts/resolvers/review-scope.ts', 'scripts/resolvers/outside-voice.ts', 'test/helpers/claude-pty-runner.ts', 'test/helpers/pty/**', 'test/fixtures/design-ui-boxed-question.json', 'test/helpers/hermetic-skill-runtime.ts', 'test/helpers/pty-trust-dialog.ts', 'test/fixtures/forcing-finding-seeds.ts', 'test/skill-e2e-plan-ceo-finding-floor.test.ts', 'test/fixtures/eng-d2-truncated-border-0bcd.json', 'test/fixtures/eng-d1-clipped-elision-1579.json', 'test/fixtures/eng-d2-planning-prelude-4d.json', 'test/fixtures/ceo-approach-z-call.json', 'test/fixtures/ceo-approach-z-screen.txt', 'test/helpers/plan-floor-target.ts', 'test/helpers/plan-count-fixture.ts', 'test/helpers/plan-count-artifacts.ts',
|
||||
'test/fixtures/ceo-report-permission-fb10.json', 'test/fixtures/plan-floor-permission-fb10.json', 'test/helpers/plan-count-file-permission.ts', 'test/fixtures/plan-edit-cropped-permission-1579.json', 'test/fixtures/plan-count-permission-target-ad-v2.json', 'test/fixtures/design-crop-gutter-ap.json', 'test/fixtures/plan-count-crop-ak.json', 'test/fixtures/plan-count-long-edit-0bcd.json', 'test/fixtures/plan-count-cropped-wrap-6714.json', 'bin/gstack-skill-start', 'bin/gstack-skill-end', 'plan-ceo-review/**', 'ETHOS.md', 'scripts/resolvers/preamble.ts', 'scripts/resolvers/preamble-hoist.ts', 'scripts/resolvers/preamble/generate-ask-user-format.ts', 'scripts/resolvers/preamble/generate-completion-status.ts', 'scripts/resolvers/review-dashboard.ts', 'scripts/resolvers/plan-gates.ts', 'scripts/resolvers/spec-review.ts', 'scripts/resolvers/outside-voice-steps.ts', 'scripts/resolvers/review-scope.ts', 'scripts/resolvers/outside-voice.ts', 'test/helpers/claude-pty-runner.ts', 'test/helpers/pty/**', 'test/fixtures/design-ui-boxed-question.json', 'test/helpers/hermetic-skill-runtime.ts', 'test/helpers/pty-trust-dialog.ts', 'test/fixtures/forcing-finding-seeds.ts', 'test/skill-e2e-plan-ceo-finding-floor.test.ts', 'test/fixtures/eng-d2-truncated-border-0bcd.json', 'test/fixtures/eng-d1-clipped-elision-1579.json', 'test/fixtures/eng-d2-planning-prelude-4d.json', 'test/fixtures/ceo-approach-z-call.json', 'test/fixtures/ceo-approach-z-screen.txt', 'test/helpers/plan-floor-target.ts', 'test/helpers/plan-count-fixture.ts', 'test/helpers/plan-count-artifacts.ts',
|
||||
|
||||
|
||||
'test/fixtures/pty-companion-cli.ts', 'lib/fs-atomic.ts', 'test/helpers/plan-skill-questions.ts', 'test/fixtures/eng-auq-validation-error.json', 'test/fixtures/bash-directory-permission.json', 'test/fixtures/design-tasks-bash-permission.json', 'test/fixtures/read-permission.json', 'test/fixtures/ceo-split-e5-numbered-description-491.json', 'test/helpers/plan-skill-question-events.ts', 'test/helpers/plan-skill-question-hook-scope.ts', 'test/helpers/skill-census.ts', 'scripts/resolvers/tasks-section.ts', 'test/helpers/auto-decision-state.ts', 'test/helpers/autoplan-artifact-digest.ts', 'test/helpers/autoplan-artifact-permission.ts', 'test/helpers/autoplan-artifact-recorder.ts', 'test/helpers/dx-selected-navigation.ts', 'test/helpers/e2e-gate.ts', 'test/helpers/eng-cache-writer-decision.ts', 'test/helpers/native-auto-decide.ts', 'test/helpers/owned-claude-transcript.ts', 'test/helpers/plan-count-pending-exit.ts', 'test/helpers/plan-count-pending-question.ts', 'test/helpers/plan-count-transcript.ts', 'test/helpers/plan-scope-selection.ts', 'test/helpers/plan-seed-submission.ts', 'bin/gstack-context-recovery'],
|
||||
@@ -567,7 +571,7 @@ export const E2E_TOUCHFILES: Record<string, string[]> = {
|
||||
'test/fixtures/plan-count-cropped-wrap-6714.json',
|
||||
|
||||
"test/fixtures/plan-count-cross-cwd-ancestry-0bcd.json",
|
||||
'test/fixtures/design-crop-gutter-ap.json', 'bin/gstack-config', 'plan-ceo-review/**', 'scripts/resolvers/preamble.ts', 'scripts/resolvers/preamble-hoist.ts', 'scripts/resolvers/preamble/generate-ask-user-format.ts', 'bin/gstack-question-preference', 'test/helpers/claude-pty-runner.ts', 'test/helpers/pty/**', 'test/fixtures/design-ui-boxed-question.json', 'test/helpers/pty-screen.ts', 'test/fixtures/pty-screen/**', 'test/helpers/hermetic-skill-runtime.ts', 'test/helpers/pty-trust-dialog.ts', 'test/helpers/plan-count-transcript.ts', 'test/fixtures/autoplan-public-narration-ad.json', 'test/helpers/plan-count-pending-exit.ts', 'test/helpers/plan-count-artifacts.ts', 'test/helpers/eval-store.ts', 'test/helpers/plan-count-fixture.ts', 'test/fixtures/ceo-prerequisite-n-call.json', 'test/fixtures/eng-prerequisite-77.json', 'test/fixtures/forcing-finding-seeds.ts', 'test/skill-e2e-plan-ceo-split-overflow.test.ts', 'test/fixtures/ceo-checkbox-l.screen.txt', 'test/helpers/plan-count-file-permission.ts', 'test/fixtures/plan-edit-cropped-permission-1579.json', 'test/fixtures/plan-count-edit-permission-t.json', 'test/fixtures/plan-count-owned-permission-v.json', 'test/fixtures/ceo-questionless-w-native.json', 'test/fixtures/eng-d2-truncated-border-0bcd.json', 'test/fixtures/eng-d1-clipped-elision-1579.json', 'test/fixtures/eng-d2-planning-prelude-4d.json', 'test/fixtures/ceo-approach-z-call.json', 'test/fixtures/ceo-approach-z-screen.txt', 'test/fixtures/plan-count-permission-ac.json', 'test/fixtures/plan-count-permission-ad.json', 'test/fixtures/plan-count-permission-ae.json', 'test/fixtures/plan-count-permission-target-ad-v2.json', 'test/fixtures/plan-count-permission-ah.json',
|
||||
'test/fixtures/design-crop-gutter-ap.json', 'bin/gstack-config', 'plan-ceo-review/**', 'ETHOS.md', 'scripts/resolvers/preamble.ts', 'scripts/resolvers/preamble-hoist.ts', 'scripts/resolvers/preamble/generate-ask-user-format.ts', 'bin/gstack-question-preference', 'test/helpers/claude-pty-runner.ts', 'test/helpers/pty/**', 'test/fixtures/design-ui-boxed-question.json', 'test/helpers/pty-screen.ts', 'test/fixtures/pty-screen/**', 'test/helpers/hermetic-skill-runtime.ts', 'test/helpers/pty-trust-dialog.ts', 'test/helpers/plan-count-transcript.ts', 'test/fixtures/autoplan-public-narration-ad.json', 'test/helpers/plan-count-pending-exit.ts', 'test/helpers/plan-count-artifacts.ts', 'test/helpers/eval-store.ts', 'test/helpers/plan-count-fixture.ts', 'test/fixtures/ceo-prerequisite-n-call.json', 'test/fixtures/eng-prerequisite-77.json', 'test/fixtures/forcing-finding-seeds.ts', 'test/skill-e2e-plan-ceo-split-overflow.test.ts', 'test/fixtures/ceo-checkbox-l.screen.txt', 'test/helpers/plan-count-file-permission.ts', 'test/fixtures/plan-edit-cropped-permission-1579.json', 'test/fixtures/plan-count-edit-permission-t.json', 'test/fixtures/plan-count-owned-permission-v.json', 'test/fixtures/ceo-questionless-w-native.json', 'test/fixtures/eng-d2-truncated-border-0bcd.json', 'test/fixtures/eng-d1-clipped-elision-1579.json', 'test/fixtures/eng-d2-planning-prelude-4d.json', 'test/fixtures/ceo-approach-z-call.json', 'test/fixtures/ceo-approach-z-screen.txt', 'test/fixtures/plan-count-permission-ac.json', 'test/fixtures/plan-count-permission-ad.json', 'test/fixtures/plan-count-permission-ae.json', 'test/fixtures/plan-count-permission-target-ad-v2.json', 'test/fixtures/plan-count-permission-ah.json',
|
||||
|
||||
'test/fixtures/plan-count-crop-ak.json',
|
||||
|
||||
@@ -618,7 +622,7 @@ export const E2E_TOUCHFILES: Record<string, string[]> = {
|
||||
'plan-ceo-review-prosons-cadence': [ 'ETHOS.md', 'test/helpers/prosons-posture.ts',
|
||||
|
||||
"test/fixtures/plan-scope-recovery-av.json",
|
||||
"test/fixtures/design-scope-checkpoint-at.json", 'plan-ceo-review/**', 'plan-eng-review/**', 'plan-design-review/**', 'plan-devex-review/**', 'scripts/resolvers/preamble/generate-ask-user-format.ts', 'scripts/resolvers/preamble.ts', 'scripts/resolvers/preamble-hoist.ts', 'model-overlays/opus-4-7.md', 'test/skill-e2e-plan-prosons.test.ts',
|
||||
"test/fixtures/design-scope-checkpoint-at.json", 'plan-ceo-review/**', 'ETHOS.md', 'plan-eng-review/**', 'plan-design-review/**', 'plan-devex-review/**', 'scripts/resolvers/preamble/generate-ask-user-format.ts', 'scripts/resolvers/preamble.ts', 'scripts/resolvers/preamble-hoist.ts', 'model-overlays/opus-4-7.md', 'test/skill-e2e-plan-prosons.test.ts',
|
||||
|
||||
|
||||
"scripts/resolvers/preamble/generate-preamble-bash.ts", "scripts/resolvers/preamble/generate-completion-status.ts",
|
||||
@@ -627,7 +631,7 @@ export const E2E_TOUCHFILES: Record<string, string[]> = {
|
||||
'plan-review-prosons-format': [ 'ETHOS.md', 'test/helpers/prosons-posture.ts',
|
||||
|
||||
"test/fixtures/plan-scope-recovery-av.json",
|
||||
"test/fixtures/design-scope-checkpoint-at.json", 'plan-ceo-review/**', 'plan-eng-review/**', 'plan-design-review/**', 'plan-devex-review/**', 'scripts/resolvers/preamble/generate-ask-user-format.ts', 'scripts/resolvers/preamble.ts', 'scripts/resolvers/preamble-hoist.ts', 'model-overlays/opus-4-7.md', 'test/skill-e2e-plan-prosons.test.ts',
|
||||
"test/fixtures/design-scope-checkpoint-at.json", 'plan-ceo-review/**', 'ETHOS.md', 'plan-eng-review/**', 'plan-design-review/**', 'plan-devex-review/**', 'scripts/resolvers/preamble/generate-ask-user-format.ts', 'scripts/resolvers/preamble.ts', 'scripts/resolvers/preamble-hoist.ts', 'model-overlays/opus-4-7.md', 'test/skill-e2e-plan-prosons.test.ts',
|
||||
|
||||
|
||||
"scripts/resolvers/preamble/generate-preamble-bash.ts", "scripts/resolvers/preamble/generate-completion-status.ts",
|
||||
@@ -764,7 +768,7 @@ export const E2E_TOUCHFILES: Record<string, string[]> = {
|
||||
|
||||
"test/fixtures/plan-scope-recovery-av.json",
|
||||
"test/fixtures/disabled-dated-record-at.json",
|
||||
'plan-eng-review/**', 'plan-ceo-review/**', 'hosts/claude.ts', 'hosts/define-host.ts',
|
||||
'plan-eng-review/**', 'plan-ceo-review/**', 'ETHOS.md', 'hosts/claude.ts', 'hosts/define-host.ts',
|
||||
'scripts/gen-skill-docs.ts', 'scripts/resolvers/index.ts', 'scripts/resolvers/sections.ts',
|
||||
'scripts/resolvers/review-dashboard.ts', 'scripts/resolvers/plan-gates.ts', 'scripts/resolvers/spec-review.ts', 'scripts/resolvers/outside-voice-steps.ts', 'scripts/resolvers/review-scope.ts', 'scripts/resolvers/outside-voice.ts', 'scripts/resolvers/constants.ts',
|
||||
'bin/gstack-config', 'bin/gstack-codex-probe', 'bin/gstack-review-log', 'bin/gstack-review-read', 'lib/review-evidence.ts', 'bin/gstack-slug', 'bin/gstack-remote-identity.sh', 'bin/gstack-wtree', 'bin/gstack-brain-enqueue', 'test/helpers/session-runner.ts',
|
||||
@@ -906,7 +910,7 @@ export const E2E_TOUCHFILES: Record<string, string[]> = {
|
||||
'test/fixtures/outside-async-task-m-events.json',
|
||||
'test/fixtures/autoplan-amend-input-77.json',
|
||||
'test/fixtures/autoplan-phase-handoff-6714.json','scripts/resolvers/learnings.ts', 'scripts/resolvers/preamble/generate-completion-status.ts', 'scripts/resolvers/preamble/generate-preamble-bash.ts', 'test/fixtures/plan-scope-recovery-av.json', 'test/fixtures/design-scope-checkpoint-at.json', 'scripts/resolvers/composition.ts', 'autoplan/**', 'lib/claude-journal-records.ts', 'lib/claude-owned-journal.ts', 'codex/**', 'bin/gstack-codex-probe', 'scripts/resolvers/review-dashboard.ts', 'scripts/resolvers/plan-gates.ts', 'scripts/resolvers/spec-review.ts', 'scripts/resolvers/outside-voice-steps.ts', 'scripts/resolvers/review-scope.ts', 'scripts/resolvers/outside-voice.ts', 'scripts/resolvers/design.ts', 'test/skill-e2e-autoplan-dual-voice.test.ts', 'bin/gstack-autoplan-snapshot.ts', 'test/fixtures/autoplan/t-ceo-omitted-obligations.json', 'test/fixtures/autoplan/u-ceo-original-loss.json', 'test/fixtures/autoplan/v-ceo-dangling-references.json',
|
||||
'scripts/resolvers/design-doc-discovery.ts', 'bin/gstack-design-doc-find', 'bin/gstack-ceo-plan-find', 'plan-ceo-review/**', 'plan-eng-review/**', 'plan-design-review/**', 'plan-devex-review/**', 'scripts/resolvers/testing.ts', 'scripts/resolvers/preamble/generate-ask-user-format.ts', 'scripts/resolvers/tasks-section.ts', 'test/helpers/autoplan-phase-observer.ts', 'scripts/resolve-codex-generation-model.ts', 'scripts/resolvers/constants.ts'],
|
||||
'scripts/resolvers/design-doc-discovery.ts', 'bin/gstack-design-doc-find', 'bin/gstack-ceo-plan-find', 'plan-ceo-review/**', 'ETHOS.md', 'plan-eng-review/**', 'plan-design-review/**', 'plan-devex-review/**', 'scripts/resolvers/testing.ts', 'scripts/resolvers/preamble/generate-ask-user-format.ts', 'scripts/resolvers/tasks-section.ts', 'test/helpers/autoplan-phase-observer.ts', 'scripts/resolve-codex-generation-model.ts', 'scripts/resolvers/constants.ts'],
|
||||
|
||||
// Multi-provider benchmark adapters — live API smoke against real claude/codex/gemini CLIs
|
||||
'benchmark-providers-live': ['bin/gstack-model-benchmark', 'test/helpers/providers/**', 'test/helpers/benchmark-runner.ts', 'test/helpers/pricing.ts', 'test/skill-e2e-benchmark-providers.test.ts'],
|
||||
|
||||
@@ -106,6 +106,25 @@ describe('paid/free dependency boundary', () => {
|
||||
} finally { fs.rmSync(root, { recursive: true, force: true }); }
|
||||
});
|
||||
|
||||
test('release metadata and crew templates never widen the PR gate; ETHOS.md selects only the prompts that quote it (#3499)', () => {
|
||||
// Release metadata no paid case reads keeps the PR profile, alone and beside a mapped prompt (#3499).
|
||||
for (const file of ['NOTICE.md', 'LICENSE', 'licenses/MIT-pstack.txt', 'captain/templates/merge-ask.md']) {
|
||||
expect(fs.existsSync(path.join(ROOT, file)), file).toBe(true);
|
||||
const result = computePaidCaseSelection({ profile: 'pr', env: {}, changedFiles: [file, 'CHANGELOG.md'] });
|
||||
expect(result.coverage?.mode, file).toBe('pr');
|
||||
expect(result.coverage?.unknownFiles, file).toEqual([]);
|
||||
expect(result.selection, file).toEqual({ e2e: [], judges: [] });
|
||||
}
|
||||
// ETHOS.md is read by the /office-hours and /plan-ceo-review prompts, so it selects their cases and judge, not the whole gate.
|
||||
const ethos = computePaidCaseSelection({ profile: 'pr', env: {}, changedFiles: ['ETHOS.md', 'NOTICE.md'] });
|
||||
expect(ethos.coverage?.mode).toBe('pr');
|
||||
expect(ethos.coverage?.unknownFiles).toEqual([]);
|
||||
expect(ethos.selection.e2e).toContain('plan-ceo-review-plan-mode');
|
||||
expect(ethos.selection.e2e).toContain('office-hours-auto-mode');
|
||||
expect(ethos.selection.judges).toEqual(['plan-ceo-review/SKILL.md modes']);
|
||||
expect(ethos.selection.e2e!.length).toBeLessThan(10);
|
||||
});
|
||||
|
||||
test('only audited free-only dependencies avoid unknown-dependency fallback', () => {
|
||||
for (const file of [...FREE_ONLY_PR_FILES, 'scripts\\test-free-shards.ts']) {
|
||||
const result = computePaidCaseSelection({ profile: 'pr', env: {}, changedFiles: [file] });
|
||||
@@ -114,7 +133,7 @@ describe('paid/free dependency boundary', () => {
|
||||
expect(result.selection).toEqual({ e2e: [], judges: [] });
|
||||
}
|
||||
// Tracked files outside every mapping and outside the derivable directories restore the full gate.
|
||||
for (const file of ['LICENSE', 'SECURITY.md', 'conductor.json', 'NOTICE.md']) {
|
||||
for (const file of ['SECURITY.md', 'conductor.json']) {
|
||||
const result = computePaidCaseSelection({ profile: 'pr', env: {}, changedFiles: [FREE_ONLY_PR_FILES[0], file] });
|
||||
expect(result.coverage?.mode, file).toBe('full-fallback');
|
||||
expect(result.coverage?.unknownFiles).toContain(file);
|
||||
|
||||
@@ -84,17 +84,17 @@ describe('allowlist: the secrets check', () => {
|
||||
expect(secrets(shape).status).toBe(EXIT.fail);
|
||||
});
|
||||
|
||||
// Value: protects=a category entry covers an advisory pii hit (a run id read as a phone number) and the WARN count drops;
|
||||
// Value: protects=a category entry covers an advisory pii hit (a run id after a generic word, which the engine's run-word exemption does not cover, read as a phone number) and the WARN count drops;
|
||||
// fails_when=category matching is id-only; why_new=new; seam=none; no_claim=none
|
||||
test('a category entry silences the advisory pii finding for a run id', () => {
|
||||
const repo = fixture({ ...BASE_PREGATE, allowlist: [{ finding: 'pii', pattern: '\\d{11}', reason: 'GitHub Actions run ids in reports' }] });
|
||||
commit(repo, { 'docs/runs.md': 'run 40302077688 passed\n' });
|
||||
commit(repo, { 'docs/runs.md': 'job 40302077688 passed\n' });
|
||||
expect(secrets(repo).status).toBe(EXIT.ok);
|
||||
const r = row(repo);
|
||||
expect([r.status, r.code]).toEqual(['pass', undefined]);
|
||||
expect(r.detail).toContain('1 allowlisted (pii.phone.e164)');
|
||||
const control = fixture(BASE_PREGATE);
|
||||
commit(control, { 'docs/runs.md': 'run 40302077688 passed\n' });
|
||||
commit(control, { 'docs/runs.md': 'job 40302077688 passed\n' });
|
||||
secrets(control);
|
||||
expect([row(control).status, row(control).code]).toEqual(['warn', 'PREGATE_SECRETS']);
|
||||
expect(row(control).detail).toContain('pii.phone.e164');
|
||||
|
||||
@@ -30,6 +30,24 @@ describe('import graph: route-group / dynamic-segment paths and tsconfig path al
|
||||
expect(testsFor(g, 'app/[[...slug]]/page.tsx').tests).toEqual(['test/pages.test.ts']);
|
||||
});
|
||||
|
||||
test('a relative literal a test reads through new URL or readFileSync maps from the file that names it', () => {
|
||||
const repo = fixture();
|
||||
commit(repo, {
|
||||
'ship/sections/pr-body.md.tmpl': '## Summary\n',
|
||||
'ship/sections/other.md.tmpl': '## Other\n',
|
||||
'docs/guide.md': '# guide\n',
|
||||
'test/publication.test.ts': "import { readFileSync } from 'node:fs';\nreadFileSync(new URL('../ship/sections/pr-body.md.tmpl', import.meta.url), 'utf8');\nreadFileSync('./../docs/guide.md', 'utf8');\n",
|
||||
'test/nested/deep.test.ts': "import { readFileSync } from 'node:fs';\nreadFileSync('../../ship/sections/other.md.tmpl', 'utf8');\n",
|
||||
});
|
||||
const g = buildGraph(repo.work);
|
||||
expect(testsFor(g, 'ship/sections/pr-body.md.tmpl').tests).toEqual(['test/publication.test.ts']);
|
||||
expect(testsFor(g, 'docs/guide.md').tests).toEqual(['test/publication.test.ts']);
|
||||
expect(testsFor(g, 'ship/sections/other.md.tmpl').tests).toEqual(['test/nested/deep.test.ts']);
|
||||
// A relative literal that climbs out of the repository names nothing.
|
||||
commit(repo, { 'test/escape.test.ts': "import { readFileSync } from 'node:fs';\nreadFileSync('../../../ship/sections/pr-body.md.tmpl', 'utf8');\n" });
|
||||
expect(testsFor(buildGraph(repo.work), 'ship/sections/pr-body.md.tmpl').tests).toEqual(['test/publication.test.ts']);
|
||||
});
|
||||
|
||||
test('a relative import of a bracketed path already maps (control: the import scanner is not the bug)', () => {
|
||||
const repo = fixture();
|
||||
commit(repo, {
|
||||
|
||||
@@ -225,6 +225,14 @@ describe("pii.phone.e164: GitHub Actions run and job ids in their id positions",
|
||||
"https://github.com/garrytan/gstack/actions/runs/37235771700/job/111534615007",
|
||||
"GITHUB_RUN_ID=37235771700",
|
||||
'{"run_id": 37235771700}',
|
||||
// An 11-digit id after a run word, as a READY block, a ship receipt or an approval record names one (GSTA-121's `screen <id> GREEN`).
|
||||
"citing screen 40477638211 GREEN at 5619199800a2",
|
||||
"gate=40477638211",
|
||||
"rerun passed 40477638211",
|
||||
"run #40477638211",
|
||||
"Screens: 40477638211",
|
||||
"dispatched 40477638211 at the pin",
|
||||
"cleared by run 19368712345",
|
||||
],
|
||||
[
|
||||
"call 37235771700 tomorrow",
|
||||
@@ -232,6 +240,12 @@ describe("pii.phone.e164: GitHub Actions run and job ids in their id positions",
|
||||
"https://example.com/contact/37235771700",
|
||||
'{"phone_id": 37235771700}',
|
||||
"run 4155550123 laps",
|
||||
// A run word before a 10-digit national number, a bare 11-digit number and a generic word exempt nothing.
|
||||
"gate 4155550123",
|
||||
"the id is 40477638211",
|
||||
"job 40477638211",
|
||||
"workflow 40477638211",
|
||||
"40477638211 failed",
|
||||
],
|
||||
);
|
||||
});
|
||||
@@ -311,6 +311,43 @@ test('pack ships a linked worktree with a self-contained .git on the checked-out
|
||||
} finally { rmSync(d, { recursive: true, force: true }); }
|
||||
}, 60_000);
|
||||
|
||||
test('pack snapshots a plain checkout without reading its live .git: remotes resolve, a detached HEAD gets a branch, and git running beside it cannot fail the tar', () => {
|
||||
const d = mkdtempSync(join(tmpdir(), 'ubi-pack-live-'));
|
||||
try {
|
||||
const sh = (cmd: string, cwd: string) => { const r = Bun.spawnSync(['bash', '-c', cmd], { cwd, timeout: 60_000, env: { ...process.env, GIT_AUTHOR_NAME: 't', GIT_AUTHOR_EMAIL: 't@t', GIT_COMMITTER_NAME: 't', GIT_COMMITTER_EMAIL: 't@t' } }); expect(r.exitCode, r.stderr.toString()).toBe(0); return r.stdout.toString(); };
|
||||
sh('git init -q --bare origin.git && git clone -q origin.git src 2>/dev/null && cd src && echo a > a && git add a && git commit -qm one && git push -q origin HEAD:main && echo b > b && git add b && git commit -qm two && git push -q origin HEAD:main && git checkout -q --detach && echo c > c && echo s > staged && git add staged && echo i > inta && git add -N inta', d);
|
||||
const src = join(d, 'src');
|
||||
const pack = `bash ${JSON.stringify(join(DIR, 'ubi-runner.sh'))} pack .`;
|
||||
// Twenty seconds of index.lock, worktree and ref churn in the checkout while pack runs three times.
|
||||
const churn = Bun.spawn(['bash', '-c', 'end=$((SECONDS+20)); while [ $SECONDS -lt $end ]; do git update-index -q --refresh; git worktree add -q ../wt HEAD >/dev/null 2>&1; git worktree remove --force ../wt >/dev/null 2>&1; git update-ref refs/heads/churn HEAD; git update-ref -d refs/heads/churn; done'], { cwd: src, stdout: 'ignore', stderr: 'ignore' });
|
||||
try {
|
||||
for (const i of [1, 2, 3]) sh(`${pack} > ../pack-${i}.tgz`, src);
|
||||
} finally { churn.kill(); }
|
||||
sh('mkdir vm && tar -xzf pack-3.tgz -C vm', d);
|
||||
const vm = join(d, 'vm');
|
||||
expect(lstatSync(join(vm, '.git')).isDirectory()).toBe(true);
|
||||
const head = sh('git rev-parse HEAD', src).trim();
|
||||
expect(sh('git rev-parse HEAD && git rev-parse --abbrev-ref HEAD && git rev-parse origin/main && git config --get remote.origin.url && git status --short', vm).split('\n').filter(Boolean))
|
||||
.toEqual([head, `detached-${head.slice(0, 12)}`, head, join(d, 'origin.git'), ' A inta', 'A staged', '?? c']);
|
||||
// The staged blob and the intent-to-add entry's empty blob are behind no ref; the snapshot carries them, so the VM can read and write-tree them.
|
||||
expect(sh('git fsck --no-progress 2>&1; git diff --cached --name-only', vm).trim()).toBe('staged');
|
||||
const wtree = (cwd: string) => sh(`bash ${JSON.stringify(join(ROOT, 'bin/gstack-wtree'))}`, cwd).trim();
|
||||
expect(wtree(vm)).toBe(wtree(src));
|
||||
// The snapshot carries no hook path or other local config from the source checkout.
|
||||
expect(sh('git config --local --list', vm)).not.toContain('hooksPath');
|
||||
// A failed pack leaves no object copy behind in TMPDIR, and a branch's upstream rides along with its remote.
|
||||
sh('mkdir ../tmpd && git checkout -q -b topic && git branch -q --set-upstream-to=origin/main topic', src);
|
||||
const broken = Bun.spawnSync(['bash', '-c', `rm -f .git/index && TMPDIR=../tmpd ${pack} > ../pack-broken.tgz; echo rc=$?; ls ../tmpd`], { cwd: src, timeout: 60_000 });
|
||||
expect(broken.stderr.toString()).toContain('.git/index');
|
||||
expect(broken.stdout.toString().trim()).toBe('rc=1');
|
||||
// A gitlink in the index (a submodule commit this object store lacks) is not packed, and a valueless
|
||||
// remote key (`[remote "origin"] mirror`) replays as true.
|
||||
sh('git reset -q && git update-index --add --cacheinfo 160000,1111111111111111111111111111111111111111,sub && printf \'[remote "origin"]\\n\\tmirror\\n\' >> .git/config', src);
|
||||
sh('mkdir vm2 && ' + pack.replace('pack .', 'pack src') + ' | tar -xzf - -C vm2', d);
|
||||
expect(sh('git rev-parse --abbrev-ref @{u} && git config --bool --get remote.origin.mirror && git ls-files -s sub | cut -c1-6', join(d, 'vm2')).split('\n').filter(Boolean)).toEqual(['origin/main', 'true', '160000']);
|
||||
} finally { rmSync(d, { recursive: true, force: true }); }
|
||||
}, 90_000);
|
||||
|
||||
test('up records the VM in the scratch file before the create request is sent', () => {
|
||||
const runner = read('scripts/ubicloud/ubi-runner.sh');
|
||||
const create = runner.indexOf('cli "${args[@]}" >/dev/null 2>"$dir/create.err"');
|
||||
|
||||
Reference in new issue
Block a user