mirror of
https://github.com/garrytan/gstack.git
synced 2026-10-31 14:06:50 +01:00
* v1.91.70.0 feat(unattended): artifact contract, unattended session kind, findings-derived review log, gate list Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * v1.91.72.0 feat(autoplan): unattended runner next|submit, pluggable outside voice, review checklists, spec-loop cap Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix(b-runner): atomic lock creation in spend-ledger, judge bundle skips runner-only sections, fixture env spelling - lib/spend-ledger.ts: create the lock by linking a complete temp file so a contender never reads an empty lock and breaks a live owner (CI admitted 20 of a cap of 8 under contention); unreadable locks break only by mtime age. - test/helpers/workflow-judge-input.ts: exclude manifest runner_only sections (the B4 checklists) from the judge bundle; the judge rates the interactive workflow. test/workflow-judge-input.test.ts expects the same set. - test/helpers/autoplan-run-fixture.ts: spell the runner's session kind without the literal the free-suite environment scan rejects. Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * v1.91.74.0 feat(review): plan reality rows, sideways review and cso sweep, investigate recipes Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * test: register the investigate carve case and map Reality rows to real headings in the review checklists Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * test: pin the corrected Reality rows obligation rows Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix(investigate): use allowlisted placeholders in the flake and polluter fences; select the investigate carve case on PTY changes 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>
68 lines
3.9 KiB
TypeScript
Executable File
68 lines
3.9 KiB
TypeScript
Executable File
#!/usr/bin/env bun
|
|
/**
|
|
* gstack-plan-reality — the reality rows of a plan review (plan D1).
|
|
*
|
|
* gstack-plan-reality rows --phase <ceo|eng|dx|design> [--scope <csv>] [--deep] [--json]
|
|
* gstack-plan-reality check --phase <p> <review-file|-> [--scope <csv>] [--session-kind <kind>] [--json]
|
|
*
|
|
* Logic: lib/plan-reality.ts. Codes: lib/result-codes.ts (REALITY_ROW_MISSING, REALITY_RECEIPT_MISSING).
|
|
*/
|
|
import * as fs from 'node:fs';
|
|
import { EXIT, exitTableHelp } from '../lib/headless-artifacts';
|
|
import { checkRealityRows, parseScopes, REALITY_LINE_SHAPE, REALITY_SCOPES, renderRows, requiredRows } from '../lib/plan-reality';
|
|
import { resultLine } from '../lib/result-codes';
|
|
|
|
const HELP = `gstack-plan-reality — reality rows for plan reviews
|
|
|
|
rows --phase <p> [--scope <csv>] [--deep] [--json] the rows this review must carry (tier 1 always, tier 2 by scope)
|
|
check --phase <p> <file|-> [--scope <csv>] [--session-kind <kind>] [--json]
|
|
parse the review's REALITY: lines; exit 1 with the missing rows
|
|
|
|
Row line: \`${REALITY_LINE_SHAPE}\` (receipt: path:line, path:line-line or a commit sha).
|
|
Scopes: ${REALITY_SCOPES.filter(s => s !== 'always').join(', ')}. Session kind unattended → verdict=incomplete; else verdict=repair.
|
|
Prints \`PLAN_REALITY: phase=<p> rows=<n>/<m> missing=<ids|none> [unverified=<n>] verdict=complete|incomplete|repair\`.
|
|
${exitTableHelp()}`;
|
|
|
|
function flag(args: string[], name: string): string | undefined {
|
|
const i = args.indexOf(name);
|
|
return i >= 0 ? args[i + 1] : undefined;
|
|
}
|
|
function usage(message: string): number {
|
|
process.stderr.write(`gstack-plan-reality: ${message}\n${HELP}\n`);
|
|
return EXIT.usage;
|
|
}
|
|
|
|
function main(argv: string[]): number {
|
|
const [cmd, ...rest] = argv;
|
|
if (!cmd || cmd === '--help' || cmd === '-h' || cmd === 'help') { process.stdout.write(HELP + '\n'); return cmd ? EXIT.ok : EXIT.usage; }
|
|
const phase = flag(rest, '--phase');
|
|
if (!phase || !/^[a-z-]+$/.test(phase)) return usage('--phase <ceo|eng|dx|design> is required');
|
|
const { scopes, unknown } = parseScopes(flag(rest, '--scope'));
|
|
if (unknown.length) return usage(`unknown scope ${unknown.join(', ')} (known: ${REALITY_SCOPES.join(', ')})`);
|
|
const json = rest.includes('--json');
|
|
|
|
if (cmd === 'rows') {
|
|
const rows = requiredRows(scopes);
|
|
process.stdout.write(json ? JSON.stringify({ phase, scopes, rows }) + '\n' : renderRows(rows, { deep: rest.includes('--deep') }) + '\n');
|
|
return EXIT.ok;
|
|
}
|
|
if (cmd === 'check') {
|
|
const valueFlags = new Set(['--phase', '--scope', '--session-kind']);
|
|
const file = rest.find((a, i) => !a.startsWith('--') && !(i > 0 && valueFlags.has(rest[i - 1]!)));
|
|
if (!file) return usage('a review file (or - for stdin) is required');
|
|
let text: string;
|
|
try { text = file === '-' ? fs.readFileSync(0, 'utf8') : fs.readFileSync(file, 'utf8'); } catch (e: any) { process.stderr.write(`gstack-plan-reality: ${file}: ${e.message}\n`); return EXIT.fail; }
|
|
const sessionKind = flag(rest, '--session-kind') ?? process.env.GSTACK_SESSION_KIND;
|
|
const result = checkRealityRows(text, { phase, scopes, sessionKind });
|
|
if (json) process.stdout.write(JSON.stringify(result) + '\n');
|
|
else process.stdout.write(result.line + '\n');
|
|
for (const id of result.unknownRows) process.stderr.write(`gstack-plan-reality: ${file}: REALITY row ${JSON.stringify(id)} is not a known row; it was ignored\n`);
|
|
if (result.missing.length) process.stderr.write(resultLine('gstack-plan-reality check', 'REALITY_ROW_MISSING', `phase ${phase}: ${result.missing.join(', ')}`) + '\n');
|
|
if (result.unreceipted.length) process.stderr.write(resultLine('gstack-plan-reality check', 'REALITY_RECEIPT_MISSING', `phase ${phase}: ${result.unreceipted.join(', ')}`) + '\n');
|
|
return result.verdict === 'complete' ? EXIT.ok : EXIT.fail;
|
|
}
|
|
return usage(`unknown command ${cmd}`);
|
|
}
|
|
|
|
process.exitCode = main(process.argv.slice(2));
|