mirror of
https://github.com/garrytan/gstack.git
synced 2026-11-10 10:40:32 +01:00
* v1.91.77.0 feat(ship): two-stage gstack-pregate, gstack-regen registry, intent-based issue links (multi-agent wave C-pregate) Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * test(pregate): keep the bin-driving pregate tests off the Windows subset; pin files are free-only for eval selection The three C-pregate tests that spawn bin/gstack-regen, bin/gstack-pregate, bin/gstack-issue-links and bin/gstack-ship-receipt do so through test/helpers/pregate-fixture.ts runBin, so the Windows curation's source scan of the test file never saw a bin/ spawn and shipped them to windows-latest, where CreateProcess cannot exec a shebang (14 failures in windows-free-shard 2 and 5). Each is now a KNOWN_WINDOWS_INCOMPATIBLE entry with the precise reason. .gstack/generated.json and .gstack/pregate.json join FREE_ONLY_PR_FILES: no paid case reads gstack's own pin files (the tools read origin/<base> of the repo they run in, and paid cases run in fixture repos), so touching them no longer restores every gate case. Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * test(harness-version): record the FREE_ONLY_PR_FILES pin-file entries as non-behavioral scripts/test-pr-profile.ts is a pinned shared-harness file; the two .gstack pin-file entries change selection only, never a case verdict. 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>
37 lines
3.1 KiB
TypeScript
37 lines
3.1 KiB
TypeScript
/**
|
||
* pregate check `regen` (tier 1, preflight): every `.gstack/generated.json`
|
||
* entry whose inputs or outputs the branch touched is regenerated in a
|
||
* scratch git worktree (lib/regen.ts) and compared with the working tree.
|
||
* Incident p0-4-stale-goldens: a template changed, the committed render and
|
||
* the golden did not, and the remote gate failed an hour later.
|
||
*/
|
||
import { renderEntry, runRegen, selectEntries } from '../../regen';
|
||
import { baseInputs } from '../context';
|
||
import type { CheckDef, CheckResult, PregateContext } from '../types';
|
||
|
||
function run(ctx: PregateContext): CheckResult {
|
||
const inputs = baseInputs(ctx, { registry: ctx.registryLabel });
|
||
const touched = ctx.touched.map(t => t.path).concat(ctx.touched.flatMap(t => (t.renamedFrom ? [t.renamedFrom] : [])));
|
||
const selected = selectEntries(ctx.registry, touched);
|
||
if (ctx.registry.entries.length === 0) return { id: 'regen', stage: 'preflight', status: 'pass', detail: `no ${ctx.registryLabel.includes('(no ') ? 'registry' : 'entries'} (${ctx.registryLabel})`, inputs };
|
||
if (selected.length === 0) return { id: 'regen', stage: 'preflight', status: 'pass', detail: `no registered input or output touched (${ctx.registry.entries.length} entries)`, inputs };
|
||
const commands = selected.filter(e => e.command);
|
||
if (commands.length && !ctx.allowRepoCommands) {
|
||
return {
|
||
id: 'regen', stage: 'preflight', status: 'incomplete', inputs, code: 'REPO_COMMANDS_NOT_ALLOWED',
|
||
detail: `repo commands not executed (pass --allow-repo-commands): ${commands.map(c => `${c.id}: ${c.command}`).join('; ')}`,
|
||
fix: 'rerun with --allow-repo-commands (what /ship passes)', lines: commands.map(c => `COMMAND: ${c.id}=${JSON.stringify(c.command)} outputs=${c.outputs.join(',')}`),
|
||
};
|
||
}
|
||
const { results } = runRegen({ repoRoot: ctx.repoRoot, registry: ctx.registry, mode: 'check', only: selected.map(e => e.id), env: ctx.env, timeoutMs: ctx.timeoutMs });
|
||
const lines = results.map(renderEntry);
|
||
const stale = results.filter(r => r.status === 'stale');
|
||
const failed = results.filter(r => r.status === 'failed');
|
||
ctx.explain.push(...results.map(r => `regen ${r.id}: selected because touched ∩ (${selected.find(e => e.id === r.id)?.inputs.join(',') || 'always'} ∪ outputs) ≠ ∅`));
|
||
if (failed.length) return { id: 'regen', stage: 'preflight', status: 'incomplete', inputs, lines, code: 'REGEN_COMMAND_FAILED', detail: `${failed.length} regen command(s) failed: ${failed.map(f => f.id).join(', ')}`, fix: failed.map(f => f.fix).join('; ') };
|
||
if (stale.length) return { id: 'regen', stage: 'preflight', status: 'fail', inputs, lines, code: 'REGEN_STALE', detail: `${stale.length} stale: ${stale.flatMap(s => s.changed.concat(s.outside)).join(' ')}`, fix: stale.map(s => s.fix).join('; ') };
|
||
return { id: 'regen', stage: 'preflight', status: 'pass', inputs, lines, detail: `${results.length} entr${results.length === 1 ? 'y' : 'ies'} fresh${results.some(r => r.status === 'listed') ? ' (hand-regenerated outputs listed)' : ''}` };
|
||
}
|
||
|
||
export const regenCheck: CheckDef = { id: 'regen', stage: 'preflight', tier: 1, downgradable: true, needsRepoCommands: true, run };
|