mirror of
https://github.com/garrytan/gstack.git
synced 2026-06-21 17:20:02 +02:00
924025a59c
Free deterministic guards for the carve: - required-reads.ts + unit test: assertRequiredReads(run, requiredFiles) — the mechanical layer-5 check that the agent Read the sections its situation needs (required set comes from the fixture, not the passive manifest) - section-manifest-consistency: 3-tier orphan classification (generated orphan + hand-edited generated file → FAIL; manifest orphan → WARN per v2_PLAN.md) and pins the PASSIVE-manifest contract (no applies_when/required_for) - template-context-parity: generated sections have zero unresolved placeholders and gated resolvers (ADVERSARIAL_STEP/CONFIDENCE_CALIBRATION/CHANGELOG_WORKFLOW) rendered — proving sections resolve with the parent skillName, not 'sections' 16 tests, all green. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
41 lines
1.6 KiB
TypeScript
41 lines
1.6 KiB
TypeScript
/**
|
|
* requiredReads enforcement (v2 plan T9, mitigation layer 5 — the only CI-failing
|
|
* layer against silent section-skip).
|
|
*
|
|
* Given a /ship run's tool calls and the set of section files the run's SITUATION
|
|
* required, assert the agent actually Read each one. The required set comes from
|
|
* the TEST FIXTURE (which situation it set up), NOT from the manifest — the
|
|
* manifest is passive (CM2). This keeps "when is a section required" in exactly
|
|
* one machine-checkable place: the eval fixtures.
|
|
*
|
|
* Builds on extractSectionReads from transcript-section-logger so section-path
|
|
* matching (the `/sections/<file>.md` segment, host-layout agnostic) lives in one
|
|
* place.
|
|
*/
|
|
|
|
import { extractSectionReads, type TranscriptResultLike } from './transcript-section-logger';
|
|
|
|
export interface RequiredReadsResult {
|
|
required: string[];
|
|
read: string[];
|
|
missing: string[];
|
|
ok: boolean;
|
|
}
|
|
|
|
/**
|
|
* @param result the skill run (anything with toolCalls)
|
|
* @param requiredFiles section basenames the situation required, e.g.
|
|
* ['version-bump.md','changelog.md'] (or with a sections/
|
|
* prefix — normalized to basename here)
|
|
*/
|
|
export function assertRequiredReads(
|
|
result: TranscriptResultLike,
|
|
requiredFiles: string[],
|
|
): RequiredReadsResult {
|
|
const read = extractSectionReads(result);
|
|
const readSet = new Set(read);
|
|
const required = requiredFiles.map(f => f.replace(/^.*\//, '')); // tolerate sections/<f>
|
|
const missing = required.filter(f => !readSet.has(f));
|
|
return { required, read, missing, ok: missing.length === 0 };
|
|
}
|