feat(run): resume-aware per-check runs, verify hooks, run-state hardening
/pygienium-<check> is now resume-aware (terminal checks skipped unless --fresh) and shares run-state with all/resume; every check gets a verify hook that fails loudly when a sub-agent returns ok with no artifact; run-state clears stale errors on retry success and reconciles a run as failed only when every check failed. Drops the superseded hygiene-state.ts model.
This commit is contained in:
@@ -270,6 +270,34 @@ async function complexityGate(cwd: string): Promise<string | undefined> {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify hook: confirms the check actually produced its artifacts. After the
|
||||
* scan phase `findings.md` must exist; after the fix phase `changes.md` must
|
||||
* exist too. Without this, a sub-agent that returns empty/ok without writing
|
||||
* its report would be stamped `complete` — a false positive. Mirrors
|
||||
* {@link commentsVerify} / {@link todosVerify}.
|
||||
*/
|
||||
async function complexityVerify(
|
||||
scope: CheckScope,
|
||||
): Promise<string | undefined> {
|
||||
const { stat } = await import("node:fs/promises");
|
||||
const f = findingsPath(scope);
|
||||
try {
|
||||
await stat(f);
|
||||
} catch {
|
||||
return `complexity verify: expected findings.md at ${f} after scan, none found.`;
|
||||
}
|
||||
if (scope.fix) {
|
||||
const c = changesPath(scope);
|
||||
try {
|
||||
await stat(c);
|
||||
} catch {
|
||||
return `complexity verify: expected changes.md at ${c} after --fix, none found.`;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/** The excessive complexity check definition. */
|
||||
export const complexityCheck = {
|
||||
name: "complexity",
|
||||
@@ -282,6 +310,7 @@ export const complexityCheck = {
|
||||
buildScanTask: buildComplexityScanTask,
|
||||
buildFixTask: buildComplexityFixTask,
|
||||
gate: complexityGate,
|
||||
verify: complexityVerify,
|
||||
} as const;
|
||||
|
||||
// Self-register on import so index.ts auto-discovery picks it up.
|
||||
|
||||
@@ -1104,6 +1104,31 @@ export async function deadCodeGate(cwd: string): Promise<string | undefined> {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify hook: confirms the check actually produced its artifacts (mirrors
|
||||
* {@link commentsVerify} / {@link todosVerify}). After scan `findings.md`
|
||||
* must exist; after `--fix` `changes.md` must exist too. Catches a sub-agent
|
||||
* that returns ok with no output — which would otherwise be a false
|
||||
* `complete`.
|
||||
*/
|
||||
async function deadCodeVerify(scope: CheckScope): Promise<string | undefined> {
|
||||
const f = findingsPath(scope.target);
|
||||
try {
|
||||
await stat(f);
|
||||
} catch {
|
||||
return `dead-code verify: expected findings.md at ${f} after scan, none found.`;
|
||||
}
|
||||
if (scope.fix) {
|
||||
const c = changesPath(scope.target);
|
||||
try {
|
||||
await stat(c);
|
||||
} catch {
|
||||
return `dead-code verify: expected changes.md at ${c} after --fix, none found.`;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/** The registered `CheckDefinition` (module self-registers on import). */
|
||||
export const deadCodeCheck: CheckDefinition = {
|
||||
name: "dead-code",
|
||||
@@ -1116,6 +1141,7 @@ export const deadCodeCheck: CheckDefinition = {
|
||||
buildScanTask: buildDeadCodeScanTask,
|
||||
buildFixTask: buildDeadCodeFixTask,
|
||||
gate: deadCodeGate,
|
||||
verify: deadCodeVerify,
|
||||
};
|
||||
|
||||
// Self-register so `index.ts` auto-discovers this check with zero wiring edits.
|
||||
|
||||
@@ -69,6 +69,33 @@ function deepModulesGate(cwd: string): string | undefined {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify hook: confirms the check actually produced its artifacts (mirrors
|
||||
* {@link commentsVerify} / {@link todosVerify}). After scan `findings.md` must
|
||||
* exist; after `--fix` `changes.md` must exist too. Catches a sub-agent that
|
||||
* returns ok with no output — which would otherwise be a false `complete`.
|
||||
*/
|
||||
async function deepModulesVerify(
|
||||
scope: CheckScope,
|
||||
): Promise<string | undefined> {
|
||||
const { stat } = await import("node:fs/promises");
|
||||
const f = findingsPath(scope.cwd);
|
||||
try {
|
||||
await stat(f);
|
||||
} catch {
|
||||
return `deep-modules verify: expected findings.md at ${f} after scan, none found.`;
|
||||
}
|
||||
if (scope.fix) {
|
||||
const c = changesPath(scope.cwd);
|
||||
try {
|
||||
await stat(c);
|
||||
} catch {
|
||||
return `deep-modules verify: expected changes.md at ${c} after --fix, none found.`;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the scan task. The deep-modules scanner agent inspects the target,
|
||||
* classifies modules by abstraction depth against the rubric, and writes a
|
||||
@@ -155,6 +182,7 @@ const deepModulesCheck: CheckDefinition = {
|
||||
buildScanTask: buildDeepScanTask,
|
||||
buildFixTask: buildDeepFixTask,
|
||||
gate: deepModulesGate,
|
||||
verify: deepModulesVerify,
|
||||
};
|
||||
|
||||
registerCheck(deepModulesCheck);
|
||||
|
||||
@@ -89,6 +89,33 @@ function defensiveGuardsGate(cwd: string): string | undefined {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify hook: confirms the check actually produced its artifacts (mirrors
|
||||
* {@link commentsVerify} / {@link todosVerify}). After scan `findings.md` must
|
||||
* exist; after `--fix` `changes.md` must exist too. Catches a sub-agent that
|
||||
* returns ok with no output — which would otherwise be a false `complete`.
|
||||
*/
|
||||
async function defensiveGuardsVerify(
|
||||
scope: CheckScope,
|
||||
): Promise<string | undefined> {
|
||||
const { stat } = await import("node:fs/promises");
|
||||
const f = findingsPath(scope.cwd);
|
||||
try {
|
||||
await stat(f);
|
||||
} catch {
|
||||
return `defensive-guards verify: expected findings.md at ${f} after scan, none found.`;
|
||||
}
|
||||
if (scope.fix) {
|
||||
const c = changesPath(scope.cwd);
|
||||
try {
|
||||
await stat(c);
|
||||
} catch {
|
||||
return `defensive-guards verify: expected changes.md at ${c} after --fix, none found.`;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the scan task. The defensive-guards scanner agent inspects the target,
|
||||
* classifies each guard as redundant or a legitimate boundary guard against the
|
||||
@@ -185,6 +212,7 @@ const defensiveGuardsCheck: CheckDefinition = {
|
||||
buildScanTask: buildDefensiveGuardsScanTask,
|
||||
buildFixTask: buildDefensiveGuardsFixTask,
|
||||
gate: defensiveGuardsGate,
|
||||
verify: defensiveGuardsVerify,
|
||||
};
|
||||
|
||||
registerCheck(defensiveGuardsCheck);
|
||||
|
||||
Reference in New Issue
Block a user