mirror of
https://github.com/garrytan/gstack.git
synced 2026-11-01 06:24:41 +01:00
C1 diagnosis (gbrain 92f0ea87, a core edit selecting 3,683 local files, 358 serial; Bun 1.4.2 and 1.4.3 identical): --timeout was applied per runner spawn (unit group plus one per serial file), so the check was unbounded, and spawnSync buffered all output until it returned: 360 s with 0 bytes on stdout and stderr. A backgrounded child holding stdout made spawnSync wait the full timeout and then report exit 0 (a silent wait, then green). Not the Bun 1.4.2 spawnSync hang. C2/C-b: test-tier commands (lanes runner, guards, the --remote gate) run via lib/pregate/supervise.ts: own process group (setsid), stdout/stderr appended to a log file (no pipes), async supervision; CheckDef.run may return a promise and runPregate/withFingerprint await it (a rejection is incomplete). Leftover group members are killed after exit; SIGINT/SIGTERM to pregate SIGKILLs running groups, writes no pregate.json, exits 130/143. Windows: taskkill /T /F (reparented or detached descendants can survive; documented). C3/C-a: --timeout bounds the whole check; groups past the deadline are not started. --stall-timeout <s> (default stall_timeout_s, else 300; 0 disables) kills a group whose log stops growing and records PREGATE_STALLED naming the last file started. Live stderr: PREGATE_TEST per file (bun test adapter; any runner for one-file runs), PREGATE_HEARTBEAT after 60 s without progress, PREGATE: tests <verdict>. --json stdout unchanged. C4/C-c: under 8 cores, a base-branch remote_gate or a non-local test_backend records the local tier as a requires-remote obligation with its command (pregate.json/remote_gate or test_backend/<backend>). --remote runs the declared remote_gate now (needs --allow-repo-commands, never --policy-from head) under --spend-cap/GSTACK_SPEND_CAP_USD; it clears only on PREGATE_REMOTE: tree=<tested tree> tests>0 failed=0 with exit 0, and reports spent/reserved/unknown. G1: new code PREGATE_STALLED has its result-codes row, troubleshooting anchor and the installer-form error line (CODE; url#anchor). Files (G2): lib/pregate/supervise.ts (new), lib/pregate/types.ts, lib/pregate/context.ts, lib/pregate/index.ts, lib/pregate/checks/lanes.ts, lib/pregate/checks/guards.ts, lib/pregate/config.ts, lib/headless-artifacts.ts, lib/result-codes.ts, bin/gstack-pregate, bin/gstack-doctor-components.sh (generated), docs/pregate.md, docs/troubleshooting.md, test/pregate-supervise.test.ts (new), test/pregate-remote.test.ts (new). Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
95 lines
3.3 KiB
TypeScript
95 lines
3.3 KiB
TypeScript
/**
|
|
* pregate/types — the shapes every pre-gate check shares (plan C8). One check
|
|
* per file under lib/pregate/checks/, registered in lib/pregate/index.ts.
|
|
*/
|
|
import type { PregateCheck } from '../headless-artifacts';
|
|
import type { Registry } from '../regen';
|
|
import type { PregateConfig } from './config';
|
|
import type { Lane } from './workflows';
|
|
|
|
export type CheckStatus = PregateCheck['status'];
|
|
export type Stage = 'preflight' | 'tests';
|
|
|
|
export interface TouchedFile {
|
|
path: string;
|
|
/** A = added, M = modified, D = deleted, R = renamed (from `renamedFrom`). */
|
|
status: 'A' | 'M' | 'D' | 'R';
|
|
renamedFrom?: string;
|
|
/** True when the change is only in the working tree (uncommitted or untracked). */
|
|
uncommitted: boolean;
|
|
}
|
|
|
|
export interface PregateContext {
|
|
repoRoot: string;
|
|
/** Base branch name without `origin/`. */
|
|
base: string;
|
|
baseRef: string;
|
|
baseSha: string;
|
|
/** merge-base of origin/<base> and HEAD: the diff anchor. */
|
|
mergeBase: string;
|
|
head: string;
|
|
/** Working-tree content fingerprint at the start of the run (bin/gstack-wtree). */
|
|
wtree: string;
|
|
touched: TouchedFile[];
|
|
config: PregateConfig;
|
|
configLabel: string;
|
|
registry: Registry;
|
|
registryLabel: string;
|
|
lanes: Lane[];
|
|
platform: NodeJS.Platform;
|
|
allowRepoCommands: boolean;
|
|
timeoutMs: number;
|
|
/** No output for this long kills a test-tier process group (`--stall-timeout`, `stall_timeout_s`); 0 disables. */
|
|
stallTimeoutMs: number;
|
|
/** Live stderr lines: per-file progress, heartbeats, the stage result. */
|
|
progress: (line: string) => void;
|
|
/** CPU count and `gstack-config test_backend`: under 8 cores a configured remote gate records an obligation instead of running locally. */
|
|
cores: number;
|
|
testBackend: string;
|
|
policyFrom: 'base' | 'head';
|
|
/** `--remote`: run the declared remote gate now, under this spend cap. */
|
|
remote: { spendCapUsd: number } | null;
|
|
env: NodeJS.ProcessEnv;
|
|
/** `--explain`: checks append provenance lines here. */
|
|
explain: string[];
|
|
}
|
|
|
|
export interface RemoteObligation {
|
|
/** `<workflow file>/<job id>`. */
|
|
lane: string;
|
|
platform: string;
|
|
workflow: string;
|
|
job: string;
|
|
/** Test files this machine cannot run that the touched files select. */
|
|
tests: string[];
|
|
/** What clears it: a run URL or an evidence-bundle lane label, recorded by `gstack-pregate clear`. */
|
|
receipt: string | null;
|
|
cleared_by?: string;
|
|
/** The exact command that discharges the obligation (a small machine's remote gate). */
|
|
command?: string;
|
|
}
|
|
|
|
export interface CheckResult extends PregateCheck {
|
|
stage: Stage;
|
|
/** The input identity the check ran against (tree, selection, registry source). */
|
|
inputs: Record<string, string>;
|
|
/** Exact command or edit an author runs; present on fail/warn/incomplete. */
|
|
fix?: string;
|
|
/** Result code the detail line ends with. */
|
|
code?: string;
|
|
/** Per-file or per-item lines for the table body. */
|
|
lines?: string[];
|
|
remote?: RemoteObligation[];
|
|
}
|
|
|
|
export interface CheckDef {
|
|
id: string;
|
|
stage: Stage;
|
|
tier: 1 | 2;
|
|
/** `secrets` and required lanes can never be downgraded to warn. */
|
|
downgradable: boolean;
|
|
/** True when the check executes commands the repo declares (pin files or its test runner). */
|
|
needsRepoCommands: boolean;
|
|
run(ctx: PregateContext): CheckResult | Promise<CheckResult>;
|
|
}
|