Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
80 commits
Select commit Hold shift + click to select a range
d7a650f
fix(capy): the install trailer says behind=<n> and its fix, so a stal…
garrytan Oct 16, 2026
7fa9a1b
fix(capy): a lease records its holder's start identity, so a pid reus…
garrytan Oct 16, 2026
78fbd0b
Merge remote-tracking branch 'origin/main' into capy/fl2-F1
garrytan Oct 16, 2026
c982493
fix(lease): stale needs proof (unreadable or malformed identities kee…
garrytan Oct 16, 2026
e1a3e81
feat(autoplan): enforced round cap and spend budget, deferred close, …
garrytan Oct 16, 2026
ad3f839
fix(lease): one directory lock for publish, prune and reclaim; reclai…
garrytan Oct 16, 2026
4168d4f
fix(lease): the directory lock names its holder; broken only when the…
garrytan Oct 17, 2026
47d1ddf
Merge remote-tracking branch 'origin/main' into capy/fl2-F1
garrytan Oct 17, 2026
1529da1
fix(lease): breakers serialize on .lock.break and reread the lock bef…
garrytan Oct 17, 2026
3900759
docs(lease): name the double-crash limit of breaking a dead lock
garrytan Oct 17, 2026
e83c1ef
Merge remote-tracking branch 'origin/capy/fl2-F1' into capy/fl2-wave-A
garrytan Oct 17, 2026
2314b6f
feat(capy): --for review-voice install profile, no-Drive bootstrap co…
garrytan Oct 16, 2026
60da139
test: pin review-voice row, helpers row and the no-Drive fallback in …
garrytan Oct 16, 2026
b536316
test: the DRIVER template names the reviewer trailer relay (pregate l…
garrytan Oct 16, 2026
30aabd2
test(context-budget): capy skill eager ceiling for the review-voice s…
garrytan Oct 17, 2026
3f7a78c
Merge remote-tracking branch 'origin/main' into capy/fl2-F1
garrytan Oct 17, 2026
c390f28
feat(eval-plan): prereg diagnostics dry run (W8) and .gstack/eval-mod…
garrytan Oct 16, 2026
0fd0977
feat(eval-plan): W8/W9 tests, docs/eval-models.md, troubleshooting an…
garrytan Oct 16, 2026
9ef3fe9
test(eval-plan): eval-plan/SKILL.md names only defined codes and acce…
garrytan Oct 16, 2026
e0e9fa0
Merge remote-tracking branch 'origin/capy/fl2-F1' into capy/fl2-wave-A
garrytan Oct 17, 2026
68e15d9
test(review-voice): the launch line points at the verdict's bun-only …
garrytan Oct 17, 2026
641916c
Merge remote-tracking branch 'origin/main' into capy/fl2-F1
garrytan Oct 17, 2026
32e1067
test(third-party-actions): the Aside drive-offer detector matches the…
garrytan Oct 17, 2026
a1f0d08
fix(autoplan): export refuses artifact paths outside the run before w…
garrytan Oct 17, 2026
afb7556
Merge remote-tracking branch 'origin/capy/fl2-F1' into capy/fl2-wave-A
garrytan Oct 17, 2026
c7a2983
Merge remote-tracking branch 'origin/main' into capy/fl2-F1
garrytan Oct 17, 2026
b736a1f
Merge remote-tracking branch 'origin/capy/fl2-F1' into capy/fl2-wave-A
garrytan Oct 17, 2026
3411c5e
fix(restamp): gate-ahead keeps the lane CHANGELOG entry when both PRs…
garrytan Oct 16, 2026
03810c9
test(tree-receipt): a real gbrain stamp pair is same-modulo-stamps un…
garrytan Oct 16, 2026
90e5f82
feat(ship-policy): gate_command, full_e2e and min_gstack_version; unk…
garrytan Oct 16, 2026
00e6dc4
feat(ship-queue): gstack-ship-queue ready, stamp, verify and gate-ahe…
garrytan Oct 16, 2026
d22290a
test(ship-queue): stamp journal and resume, verify from a separate cl…
garrytan Oct 16, 2026
8171f52
docs(ship-policy): Run a shared merge queue; new keys and compatibili…
garrytan Oct 16, 2026
da67a3c
fix(ship): shared-queue pointer rides the show-effective ship: line (…
garrytan Oct 16, 2026
063292e
test(windows): curate the shared-queue tests out of the Windows subse…
garrytan Oct 16, 2026
71dbe62
test(fixtures): register the gbrain stamp-pair fixture as free-only
garrytan Oct 16, 2026
67f636a
test(ship-queue): receipt digest contract names lib/ship-queue.ts for…
garrytan Oct 16, 2026
ed97384
feat(pregate): timing check flags elapsed-time bounds and sleep-then-…
garrytan Oct 16, 2026
83d9c97
feat(update-check): an equal VERSION with a moved main is an upgrade,…
garrytan Oct 16, 2026
801195f
fix(pregate): treat planning and evidence markdown as an empty lane
cursoragent Oct 16, 2026
08164b5
fix(pregate): keep test-read markdown on its test lane
cursoragent Oct 16, 2026
534b5fa
test(pregate): reviewer edges for the docs/no-tests empty lane (#3215)
garrytan Oct 16, 2026
4218823
test(pregate): requires-remote on a gbrain-shaped repo (ci:ubicloud, …
garrytan Oct 16, 2026
9ff541a
test(pregate): the ship receipt counts the timing check (7 checks)
garrytan Oct 16, 2026
b36c48a
test(context-budget): gstack-upgrade eager ceiling for the W12 equal-…
garrytan Oct 17, 2026
f6835fc
feat(approval-classes): gstack-approval-classes and ship-receipt cita…
garrytan Oct 16, 2026
2129eac
feat(approval-classes): tests, docs, /review and /ship wiring (W2)
garrytan Oct 16, 2026
37abc15
feat(review): state-machines specialist and gstack-state-matrix round…
garrytan Oct 16, 2026
aead737
fix(review-army): keep the --simplification force flag pinned; approv…
garrytan Oct 16, 2026
e8ffadf
test(free-fixtures): register the state-matrix-6792 fixture
garrytan Oct 16, 2026
037ca1d
test(state-matrix): cover state-machines NEVER_GATE in gstack-special…
garrytan Oct 16, 2026
9671522
test(fixtures): register the pregate-timing fixtures as free-only (W4)
garrytan Oct 17, 2026
aa8a119
fix(ship-queue): full_e2e needs bound gate evidence in ready and veri…
garrytan Oct 17, 2026
66aad71
fix(pregate): timing reads a native diff and never passes an unread one
garrytan Oct 17, 2026
ea49e49
fix(approval-classes): only affirmative APPROVED evidence counts, exp…
garrytan Oct 17, 2026
efccf00
fix(state-matrix): detect reads the native diff, ignoring diff.extern…
garrytan Oct 17, 2026
1c4ff4a
test(e2e): review-army-state-matrix behavior case on the #6792 fixtur…
garrytan Oct 17, 2026
0202c29
fix(review-army): tighten the State Machines dispatch line so /ship s…
garrytan Oct 17, 2026
bf05ecf
fix(approval-classes): only top-level approval lines count; quoted, f…
garrytan Oct 17, 2026
6d83402
fix(approval-classes): approval evidence is the first non-blank line …
garrytan Oct 17, 2026
205d9c8
Merge origin/main (v1.91.113.0, #3232 squash) into capy/fl2-wave-A
garrytan Oct 17, 2026
c7917e3
Merge remote-tracking branch 'origin/capy/fl2-wave-A' into capy/fl2-w…
garrytan Oct 17, 2026
b40d841
test(autoplan-budget): bind the reservation expectation to lib/autopl…
garrytan Oct 17, 2026
867defc
Merge remote-tracking branch 'origin/capy/fl2-wave-A' into capy/fl2-w…
garrytan Oct 17, 2026
46547e8
fix: keep gstack-upgrade under its size budget, exempt state-matrix's…
garrytan Oct 17, 2026
32c3ddc
fix(evals): the docs observer accepts the spawned-prefixed gstack-ski…
garrytan Oct 17, 2026
81c1503
Merge remote-tracking branch 'origin/main' into capy/fl2-wave-A
garrytan Oct 17, 2026
388aab4
fix(windows): curate capy-review-voice out of the Windows subset (POS…
garrytan Oct 17, 2026
7290ec4
docs: changelog entry for field list 2 wave A
garrytan Oct 17, 2026
d896c92
chore: stamp v1.91.115.0
garrytan Oct 17, 2026
e95e083
test: the scope-signal clarity check follows helper-printed signals; …
garrytan Oct 17, 2026
f28a472
Merge remote-tracking branch 'origin/capy/fl2-wave-A' into capy/fl2-w…
garrytan Oct 17, 2026
8f8d951
refactor: move the release-path result codes to lib/result-codes-rele…
garrytan Oct 17, 2026
7138583
Merge remote-tracking branch 'origin/main' into capy/fl2-wave-B
garrytan Oct 17, 2026
6cdcdd6
docs: changelog entry for field list 2 wave B
garrytan Oct 17, 2026
7864483
chore: stamp v1.91.116.0
garrytan Oct 17, 2026
8792179
test: copy lib/result-codes-release.ts wherever a fixture copies lib/…
garrytan Oct 17, 2026
48f0cef
test: state-matrix stats test resolves its slug under its own state r…
garrytan Oct 17, 2026
63dd960
fix(windows): curate pregate-docs-lane-review out of the Windows subs…
garrytan Oct 17, 2026
6f8ef0b
fix(windows): curate pregate-remote-gbrain out of the Windows subset …
garrytan Oct 17, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .github/workflows/evals-periodic.yml
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ jobs:
- name: Emit run manifest (ALL periodic tests minus reasoned excludes)
env:
EVALS_ALL: "1"
run: EVALS_TIER=periodic bun --no-install run scripts/test-paid-shards.ts --tier periodic --emit-plan /tmp/paid-plan/manifest.json --slice-budget 420 --jobs 2 --max-parallel 36
run: EVALS_TIER=periodic bun --no-install run scripts/test-paid-shards.ts --tier periodic --emit-plan /tmp/paid-plan/manifest.json --slice-budget 420 --jobs 2 --max-parallel 38

# Codex-only slices run on the host-run eval-codex-slices job; the
# container executor runs every other slice.
Expand Down Expand Up @@ -258,7 +258,7 @@ jobs:
fail-fast: false
# Every planned slice starts at once; test/evals-workflow-wiring.test.ts
# fails when the live plan outgrows this cap.
max-parallel: 36
max-parallel: 38
matrix:
slice: ${{ fromJSON(needs.plan-slices.outputs.periodic_slices) }}
steps:
Expand Down Expand Up @@ -397,7 +397,7 @@ jobs:
packages: read
strategy:
fail-fast: false
max-parallel: 36
max-parallel: 38
matrix:
slice: ${{ fromJSON(needs.plan-slices.outputs.periodic_codex_slices) }}
steps:
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/windows-pregate-e2e.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ on:
- 'lib/gate-diff/**'
- 'lib/pregate/**'
- 'lib/result-codes.ts'
- 'lib/result-codes-release.ts'
- 'bin/gstack-wtree'
- 'bin/gstack-slug'
- 'bin/gstack-path-spelling.sh'
Expand Down
19 changes: 19 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,24 @@
# Changelog

## [1.91.116.0] - 2026-10-17

**Lanes and the coordinator now share a merge queue whose stamp claims are checked instead of trusted, risky changes need a cited owner approval before they ship, /review checks state machines cell by cell, pregate warns about timing assertions, and the update check notices when main moves without a version bump.** These are the field list 2 wave B items from the gbrain crew (W1-W4, W10, W12).

### What this means for you

- **`gstack-ship-queue` runs a shared merge queue.** `ready` prints one READY block and refuses when the tree differs from the pushed head. `stamp` checks every proof input, restamps through `gstack-restamp`, and resumes on an identical retry. The coordinator's `verify` reruns the tree receipt and recomputes a gate-ahead merge, so a lane's stamp claim is never taken on trust. Ship policy gains `gate_command`, `full_e2e` and `min_gstack_version`; a gstack older than a key it reads refuses with `POLICY_GSTACK_TOO_OLD`. See `docs/ship-policy.md#run-a-shared-merge-queue`.
- **Approval classes.** `.gstack/approval-classes.json` (read from the base branch) declares what needs an owner's approval. `gstack-ship-receipt write` refuses a branch that touches a class until the PR body cites `Approved by <owner> <date> via <ref> for <class>@<digest12>`, bound to the effects being shipped.
- **/review's state-machines specialist** builds a kind × owner action matrix for diffs that change a state machine. `gstack-state-matrix check` reports every missing cell, and an untested cell is CRITICAL.
- **Pregate's `timing` check** warns on added test lines that bound wall or CPU time or sleep and then assert. Mark a deliberate bound with `gstack-timing-ok: <reason>`. /investigate gains a timing-flake playbook.
- **The update check sees a moved main.** At an equal VERSION it reports `UPGRADE_AVAILABLE <ver> <ver>+<n> <sha>` through the GitHub compare API (never `git fetch`). The cache is bound to HEAD and upstream, and snooze is keyed by SHA. /gstack-upgrade summarizes the commits instead of a CHANGELOG entry.
- **Pregate's docs lane** comes from #3215 by Cursor Agent: Markdown no test depends on is already a docs/no-tests empty lane.

### For contributors

- The release-path result codes live in `lib/result-codes-release.ts`, spread into `RESULT_CODES`, so `lib/result-codes.ts` stays inside the module-size ratchet.
- New paid case `review-army-state-matrix` (periodic, behavior). The periodic lane's `max-parallel` is 38 so every planned slice still starts at once.
- The scope-signal clarity test also accepts a signal printed by the helper its own selection line runs (SCOPE_STATE_MACHINE), and the hermetic update-check fixture writes the identity cache.

## [1.91.115.0] - 2026-10-17

**Autoplan now enforces its round cap and spend budget and can export a public copy of a run, /eval-plan tests its preregistered diagnostics and checks every arm's model against repo rules before money moves, and a reviewer subagent can install gstack's read-only helpers in seconds.** These are the field list 2 wave A items from the gbrain crew (autoplan W5-W7, eval-plan W8/W9, reviewer install W11).
Expand Down
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
1.91.115.0
1.91.116.0
2 changes: 1 addition & 1 deletion agents-digest/gstack-AGENTS.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# gstack digest v1.91.115.0 — regenerate/re-copy after upgrading gstack
# gstack digest v1.91.116.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,
Expand Down
84 changes: 84 additions & 0 deletions bin/gstack-approval-classes
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
#!/usr/bin/env bun
/**
* gstack-approval-classes — changes that need the owner's approval (field list 2, W2).
*
* gstack-approval-classes [check] [--base <ref>] [--head <ref>] [--diff-base <sha>] [--json]
* gstack-approval-classes init [--force]
* gstack-approval-classes validate
*
* `check` reads the classes from the committed `origin/<base>` copy of
* .gstack/approval-classes.json (never the working tree) and the migration
* rule from the base's ship policy, scans the branch's changes since the
* merge base, and prints `APPROVAL_CLASS: <class> <file>:<line>` per hit, one
* `APPROVAL_SCOPE: <class>@<digest12>` per declared class with hits, and
* `APPROVAL_CLASSES: ok | found=<n> | none` last. Built-in detectors only
* report. Exit 0 ok or none, 1 found, 3 when the base file is invalid or the
* base is unresolvable. Enforcement is gstack-ship-receipt write
* (APPROVAL_CITATION_MISSING). Logic: lib/approval-classes.ts.
*/
import { spawnSync } from 'node:child_process';
import * as fs from 'node:fs';
import * as path from 'node:path';
import { APPROVAL_CLASSES_PATH, exampleApprovalClassesJson, loadWorkingApprovalClasses, renderApprovalScan, runApprovalScan } from '../lib/approval-classes';
import { EXIT, exitTableHelp } from '../lib/headless-artifacts';
import { resultLine } from '../lib/result-codes';
import { resolveBaseBranch } from '../lib/ship-policy';

const HELP = `gstack-approval-classes — changes that need the owner's approval (docs/ship-policy.md#approval-classes)

check [--base <ref>] [--head <ref>] [--diff-base <sha>] [--json]
changes since the merge base with origin/<base> (default origin/HEAD's branch), at --head or the
working tree; classes come only from the committed ${APPROVAL_CLASSES_PATH} on origin/<base>
init [--force] write an example ${APPROVAL_CLASSES_PATH}
validate schema and containment check of the working-tree file (before you commit it)

A declared class with hits needs a line in the PR body that gstack-ship-receipt write can resolve:
Approved by <owner> <YYYY-MM-DD> via <plan sha|comment or review URL|ref> [for <class>@<digest12>]

${exitTableHelp()}`;

function flag(args: string[], name: string): string | undefined {
const i = args.indexOf(name);
return i >= 0 ? args[i + 1] : undefined;
}

function main(argv: string[]): number {
if (argv.includes('--help') || argv.includes('-h')) { process.stdout.write(HELP + '\n'); return EXIT.ok; }
const cmd = argv[0] && !argv[0].startsWith('--') ? argv[0] : 'check';
const rest = cmd === argv[0] ? argv.slice(1) : argv;
const root = spawnSync('git', ['rev-parse', '--show-toplevel'], { encoding: 'utf8', timeout: 15_000 });
if (root.status !== 0) { process.stderr.write('gstack-approval-classes: not inside a git repository\n'); return EXIT.usage; }
const repoRoot = root.stdout.trim();
const file = path.join(repoRoot, APPROVAL_CLASSES_PATH);

if (cmd === 'init') {
if (fs.existsSync(file) && !rest.includes('--force')) { process.stderr.write(`gstack-approval-classes: ${APPROVAL_CLASSES_PATH} exists (pass --force to overwrite)\n`); return EXIT.refused; }
fs.mkdirSync(path.dirname(file), { recursive: true });
fs.writeFileSync(file, exampleApprovalClassesJson());
process.stdout.write(`APPROVAL_CLASSES_INIT: ${APPROVAL_CLASSES_PATH} (edit, commit and merge it; check reads only the base copy)\n`);
return EXIT.ok;
}
if (cmd === 'validate') {
const load = loadWorkingApprovalClasses(repoRoot);
if ('absent' in load) { process.stdout.write(`APPROVAL_CLASSES: none (no ${APPROVAL_CLASSES_PATH})\n`); return EXIT.ok; }
if ('errors' in load) { for (const e of load.errors) process.stderr.write(resultLine('gstack-approval-classes validate', 'APPROVAL_CLASSES_INVALID', `${e.message} at ${e.path || '<root>'}`) + '\n'); return EXIT.refused; }
if ('unavailable' in load || !load.config) return EXIT.fail;
process.stdout.write(`APPROVAL_CLASSES_VALID: ${APPROVAL_CLASSES_PATH} classes=${load.config.classes.length}\n`);
return EXIT.ok;
}
if (cmd !== 'check') { process.stderr.write(`gstack-approval-classes: unknown command ${cmd}\n${HELP}\n`); return EXIT.usage; }

const json = rest.includes('--json');
const base = resolveBaseBranch(repoRoot, flag(rest, '--base'));
const outcome = runApprovalScan({ repoRoot, base, head: flag(rest, '--head'), diffBase: flag(rest, '--diff-base') });
if ('unavailable' in outcome) { process.stderr.write(resultLine('gstack-approval-classes', 'APPROVAL_SOURCE_UNAVAILABLE', `${outcome.unavailable}; classes are read only from the base, so nothing was checked`) + '\n'); return EXIT.refused; }
if ('errors' in outcome) { for (const e of outcome.errors) process.stderr.write(resultLine('gstack-approval-classes', 'APPROVAL_CLASSES_INVALID', `${e.message} at ${e.path || '<root>'} (${outcome.source})`) + '\n'); return EXIT.refused; }
if ('error' in outcome && outcome.unreadable) { process.stderr.write(resultLine('gstack-approval-classes', 'APPROVAL_SOURCE_UNAVAILABLE', `${outcome.error}, so nothing was checked`) + '\n'); return EXIT.refused; }
if ('error' in outcome) { process.stderr.write(`gstack-approval-classes: ${outcome.error}\n`); return EXIT.usage; }
const { scan, absent } = outcome;
process.stdout.write(json ? JSON.stringify({ ...scan, absent }) + '\n' : renderApprovalScan(scan, absent));
if (scan.hits.length) { process.stderr.write(resultLine('gstack-approval-classes', 'APPROVAL_CLASS_FOUND', `${scan.scopes.map(s => s.token).join(', ')} need an Approved by line`) + '\n'); return EXIT.fail; }
return EXIT.ok;
}

process.exitCode = main(process.argv.slice(2));
13 changes: 13 additions & 0 deletions bin/gstack-doctor-components.sh
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,19 @@ gstack_result_anchor() {
DX_CAPTURE_INTERRUPTED) printf '%s' 'dx-capture-interrupted' ;;
DX_CAPTURE_REDACTION_FAILED) printf '%s' 'dx-capture-redaction-failed' ;;
DX_CAPTURE_INTERNAL) printf '%s' 'dx-capture-internal' ;;
POLICY_GSTACK_TOO_OLD) printf '%s' 'policy-gstack-too-old' ;;
QUEUE_NOT_READY) printf '%s' 'queue-not-ready' ;;
QUEUE_PROOF_MISSING) printf '%s' 'queue-proof-missing' ;;
QUEUE_STAMP_NOT_STAMP_ONLY) printf '%s' 'queue-stamp-not-stamp-only' ;;
QUEUE_VERIFY_MISMATCH) printf '%s' 'queue-verify-mismatch' ;;
QUEUE_GATE_MISSING) printf '%s' 'queue-gate-missing' ;;
GATE_AHEAD_ATTEMPT_INVALID) printf '%s' 'gate-ahead-attempt-invalid' ;;
PREGATE_TIMING_ASSERTION) printf '%s' 'pregate-timing-assertion' ;;
APPROVAL_CLASSES_INVALID) printf '%s' 'approval-classes-invalid' ;;
APPROVAL_CLASS_FOUND) printf '%s' 'approval-class-found' ;;
APPROVAL_CITATION_MISSING) printf '%s' 'approval-citation-missing' ;;
APPROVAL_SOURCE_UNAVAILABLE) printf '%s' 'approval-source-unavailable' ;;
STATE_MATRIX_INCOMPLETE) printf '%s' 'state-matrix-incomplete' ;;
*) return 1 ;;
esac
}
11 changes: 8 additions & 3 deletions bin/gstack-ship-policy
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ import * as path from 'node:path';
import { EXIT, exitTableHelp } from '../lib/headless-artifacts';
import { resultLine } from '../lib/result-codes';
import {
SHIP_POLICY_PATH, examplePolicyJson, explainPolicy, isUnavailable, loadPolicyFromBase, loadPolicyFromHead,
policyUnavailableLine, renderEffective, resolveBaseBranch, type LoadedPolicy,
POLICY_KEYS, POLICY_KEY_INTRODUCED, SHIP_POLICY_PATH, examplePolicyJson, explainPolicy, isUnavailable, loadPolicyFromBase, loadPolicyFromHead,
minimumReader, policyUnavailableLine, renderEffective, resolveBaseBranch, type LoadedPolicy,
} from '../lib/ship-policy';

const HELP = `gstack-ship-policy — the repo ship policy (docs/ship-policy.md)
Expand Down Expand Up @@ -86,13 +86,18 @@ function main(argv: string[]): number {
if (cmd === 'validate' || cmd === 'show-effective') {
const loaded = load(root, rest, cmd === 'validate' ? 'head' : 'base');
if (typeof loaded === 'number') return loaded;
const set = POLICY_KEYS.filter(k => loaded.values[k] && loaded.values[k] !== 'default');
const introduced = Object.fromEntries(set.map(k => [k, POLICY_KEY_INTRODUCED[k]]));
const minReader = set.length ? minimumReader(introduced) : null;
if (json) {
process.stdout.write(JSON.stringify({ valid: loaded.errors.length === 0, present: loaded.present, source: loaded.source, policy: loaded.policy, values: loaded.values, errors: loaded.errors }) + '\n');
process.stdout.write(JSON.stringify({ valid: loaded.errors.length === 0, present: loaded.present, source: loaded.source, policy: loaded.policy, values: loaded.values, introduced, min_reader: minReader, errors: loaded.errors }) + '\n');
return loaded.errors.length ? EXIT.fail : EXIT.ok;
}
printErrors(loaded);
if (cmd === 'validate') {
process.stdout.write(loaded.errors.length ? `POLICY_INVALID: ${loaded.errors.length} error(s) in ${loaded.source.label}\n` : `POLICY_VALID: ${loaded.present ? loaded.source.label : `no ${SHIP_POLICY_PATH} (defaults apply)`}\n`);
for (const k of set) process.stdout.write(`KEY: ${k} introduced=${introduced[k]}\n`);
if (minReader) process.stdout.write(`MIN_READER: gstack ${minReader} (every lane that reads this policy needs at least this version)\n`);
return loaded.errors.length ? EXIT.fail : EXIT.ok;
}
process.stdout.write(renderEffective(loaded, { base: flag(rest, '--base') ?? resolveBaseBranch(root) }));
Expand Down
Loading
Loading