fix(#2770): decision-coverage gate fails closed on empty arg + workflow recomputes CONTEXT_PATH in-block (#2881)
* test(#2770): empty contextPath argument must fail closed, not green-skip the decision-coverage gate The handler conflated empty-arg (caller error) with file-missing (legitimate skip), returning passed:true/skipped on an empty argument. Add: empty arg → passed:false; real-path-to-absent-file → legitimate green skip preserved; omitted arg → fail closed. * fix(#2770): decision-coverage gate fails closed on empty arg + workflow recomputes CONTEXT_PATH in-block Handler (check-command-router.cts): split the guard — empty/missing contextPath argument is a caller error (fail closed, passed:false, mirrors #1365); a real path whose file genuinely does not exist keeps the legitimate green skip. Workflow (plan-phase.md): recompute CONTEXT_PATH inside the consuming Bash block (it was set in the step-1 init block, which does not survive into the separately- spawned gate block — so the gate ran with an empty arg and silently green-skipped). * chore(#2770): changeset fragment * fix(#2770): guard workflow empty-glob case (review blocker) + update drift-guard test The handler now fails closed on an empty contextPath arg, so the workflow's unguarded glob (empty when a phase genuinely has no CONTEXT.md) would invoke the gate with an empty arg → passed:false → exit 1, hard-halting the legitimate 'Continue without context' plan-phase path. Guard the empty-glob case: only run the gate when a CONTEXT.md actually exists. Update the F1 drift-guard test (which gave false coverage — it only checked for the ${CONTEXT_PATH} token) to assert the in-block recompute AND the empty-glob guard. * fix(#2770): keep plan-phase.md under ADR-857 size cap + ack emitted drift + fix drift-guard window The workflow fix grew plan-phase.md past the ADR-857 phase-6 size cap (94519B) and triggered emitted-attribution. Condense adjacent §13a prose/JSON to offset (net +89B, under cap). Add tests/emitted-drift-ack.json acknowledging the residual growth. Widen the drift-guard test window (the gate invocation is now nested in the empty-glob guard, so the old 400-char window missed the glob recompute). * chore(#2770): backfill changeset PR number (2881) --------- Co-authored-by: Test <test@example.com>
This commit is contained in:
@@ -276,13 +276,23 @@ function loadDecisionExtraction(contextPath: string): { trackable: Decision[]; o
|
||||
|
||||
function cmdDecisionCoveragePlan(projectDir: string, args: string[], raw: boolean): void {
|
||||
const phaseDir = args[2] ? resolvePath(args[2], projectDir) : '';
|
||||
const contextPath = args[3] ? resolvePath(args[3], projectDir) : '';
|
||||
const contextArg = args[3];
|
||||
const contextPath = contextArg ? resolvePath(contextArg, projectDir) : '';
|
||||
|
||||
if (!gateEnabled(projectDir)) {
|
||||
output({ passed: true, skipped: true, reason: 'workflow.context_coverage_gate is false', total: 0, covered: 0, uncovered: [], message: 'Decision coverage gate disabled by config.' }, raw, undefined);
|
||||
return;
|
||||
}
|
||||
if (!contextPath || !fs.existsSync(contextPath)) {
|
||||
// #2770: an EMPTY/MISSING contextPath argument is a CALLER ERROR (the workflow
|
||||
// forgot to pass the path — e.g. a shell variable lost between Bash blocks), not
|
||||
// evidence the phase has no CONTEXT.md. Fail closed (mirrors #1365 fail-loud) so a
|
||||
// blocking gate cannot silently certify success on a caller mistake.
|
||||
if (!contextArg || contextArg === '') {
|
||||
output({ passed: false, skipped: false, reason: 'missing context path argument', total: 0, covered: 0, uncovered: [], message: 'Decision coverage gate called without a context path argument — the caller (e.g. the plan-phase workflow) must pass the CONTEXT.md path. An empty argument is a caller error, not evidence there is nothing to check (#2770).' }, raw, undefined);
|
||||
return;
|
||||
}
|
||||
// A REAL path whose file genuinely does not exist is the LEGITIMATE green skip.
|
||||
if (!fs.existsSync(contextPath)) {
|
||||
output({ passed: true, skipped: true, reason: 'CONTEXT.md missing', total: 0, covered: 0, uncovered: [], message: 'No CONTEXT.md - nothing to check.' }, raw, undefined);
|
||||
return;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user