@vostride/agent-qa-eval-local 0.0.0-stage → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/corpus-reader.ts","../src/committed-corpus-source.ts","../src/cassette-loader.ts","../src/artifact-source.ts","../src/cassette-store.ts","../src/committed-root-store.ts","../src/replay-read.ts","../src/replay-record-store.ts","../src/score-store.ts","../src/mark-ledger.ts","../src/mark-lock.ts","../src/calibration-source.ts","../src/report-metadata-guard.ts","../src/report-store.ts"],"sourcesContent":["/**\n * The proposer's ONLY read over the committed corpus (414 D-18 / D-19 / D-20, ROOT-10).\n *\n * The corpus is a train / validation / test partition. `natural` is the optimise set the\n * proposer may read and learn from; held-out `shared` is a VISIBLE validation set — readable,\n * still gated on; held-out `private` is the unseen test set. A measurement the thing being\n * measured can read is not a measurement, so the private arm is unreachable from here: not\n * listable, not loadable, not countable.\n *\n * Existence non-leak (D-19, the anti-pattern this module exists to make impossible): a private\n * cassetteId answers with the SAME `{ kind: 'not-found' }` an id that was never committed gets.\n * There is deliberately no `not-visible` arm — an arm that distinguishes the two tells the\n * proposer the id exists, which is the whole of what the private set withholds. For the same\n * reason there is no count method, no `listProducts`, and no visibility parameter: each of those\n * is a channel through which the size or the shape of the unseen set could be inferred.\n *\n * Refusals and notices from the underlying read are NOT exposed. A refused committed file is an\n * operator's problem, not the proposer's, and the paths it names are root-shaped information.\n *\n * Composition law (eval-local's ownership contract): this module reads and FILTERS. The\n * visibility predicate itself (`isProposerVisibleEntry`) lives in eval core beside the type it\n * narrows, so the filter here and the never-bar in `__tests__/reader.test-d.ts` are talking\n * about one definition rather than two that could drift.\n *\n * D-18 declined a physically separate private root, so the pin is at the PORT and at the TYPE:\n * `packages/eval-local/src/reader.ts` publishes exactly this function, `validate:arch` holds\n * every non-cli importer to that subpath, and three cells RED if a private-returning export or\n * method is added.\n */\n\nimport { isProposerVisibleEntry } from '@vostride/agent-qa-eval'\nimport type {\n LeafCassette,\n ProposerVisibleCorpusEntry,\n StandaloneLabelRecord,\n} from '@vostride/agent-qa-eval'\n\nimport { readCommittedCorpus } from './committed-corpus-source.js'\n\nexport type { ProposerVisibleCorpusEntry } from '@vostride/agent-qa-eval'\n\n/**\n * The two-arm answer every load gives. `not-found` is TOTAL: it covers an id that does not\n * exist, an id whose bytes are unreadable, and an id the proposer may not see — deliberately\n * indistinguishable (D-19).\n */\nexport type CorpusReaderLoad<TLoaded> = ({ kind: 'loaded' } & TLoaded) | { kind: 'not-found' }\n\n/** A visible entry's cassette bundle, or nothing. */\nexport type CorpusCassetteLoad = CorpusReaderLoad<{ cassette: LeafCassette }>\n\n/** A visible entry's standalone label record, or nothing. */\nexport type CorpusLabelLoad = CorpusReaderLoad<{ record: StandaloneLabelRecord }>\n\n/**\n * The port. EXACTLY three methods — every addition is a widening of what a proposer may learn\n * about the measurement it is being scored against, so the set is pinned by name in three places.\n */\nexport interface CorpusReader {\n /** Every proposer-visible entry, cassetteId ascending; optionally one product's slice. */\n listEntries(product?: string): readonly ProposerVisibleCorpusEntry[]\n /** The cassette bundle for a VISIBLE entry; `not-found` for anything else. */\n loadCassette(cassetteId: string): CorpusCassetteLoad\n /** The label record for a VISIBLE entry; `not-found` for anything else. */\n loadLabel(cassetteId: string): CorpusLabelLoad\n}\n\ninterface VisibleSlice {\n visible: readonly ProposerVisibleCorpusEntry[]\n visibleIds: ReadonlySet<string>\n cassettesById: ReadonlyMap<string, LeafCassette>\n labelsById: ReadonlyMap<string, StandaloneLabelRecord>\n}\n\n/**\n * Open a read port over the committed root at `committedRoot`.\n *\n * The root is read ONCE, lazily on first use, and the visible slice is kept: a port whose answers\n * changed mid-session would let a proposer watch the corpus move. An absent or unreadable root is\n * an empty port — `listEntries()` is `[]` and every load is `not-found` — never a throw, matching\n * the read leg's own posture for a hand-editable, git-tracked document.\n */\nexport function openCorpusReader(committedRoot: string): CorpusReader {\n let slice: VisibleSlice | undefined\n\n function load(): VisibleSlice {\n if (slice === undefined) {\n const read = readCommittedCorpus(committedRoot)\n const visible = read.snapshot.entries.filter((entry): entry is ProposerVisibleCorpusEntry =>\n isProposerVisibleEntry(entry),\n )\n slice = {\n visible,\n visibleIds: new Set(visible.map((entry) => entry.cassetteId)),\n cassettesById: read.snapshot.cassettesById,\n labelsById: read.snapshot.labelsById,\n }\n }\n return slice\n }\n\n return {\n listEntries(product?: string): readonly ProposerVisibleCorpusEntry[] {\n const { visible } = load()\n if (product === undefined) return visible\n return visible.filter((entry) => entry.product === product)\n },\n\n loadCassette(cassetteId: string): CorpusCassetteLoad {\n const { visibleIds, cassettesById } = load()\n if (!visibleIds.has(cassetteId)) return { kind: 'not-found' }\n const cassette = cassettesById.get(cassetteId)\n if (cassette === undefined) return { kind: 'not-found' }\n return { kind: 'loaded', cassette }\n },\n\n loadLabel(cassetteId: string): CorpusLabelLoad {\n const { visibleIds, labelsById } = load()\n if (!visibleIds.has(cassetteId)) return { kind: 'not-found' }\n const record = labelsById.get(cassetteId)\n if (record === undefined) return { kind: 'not-found' }\n return { kind: 'loaded', record }\n },\n }\n}\n","// The committed-root READ leg (413 D-14/D-08, RESEARCH F6) — the inverse of seedCommittedCorpus.\n//\n// Composition law (the eval-local ownership rule): every eval filesystem read lives here and the\n// derivation lives in eval core. This module loads corpus entries, label records, cassette\n// bundles and local blob presence off disk and hands eval core the CommittedCorpusSnapshot its\n// pure census folds. It computes nothing.\n//\n// Refusal posture (mirrors cassette-loader.ts): a committed root is a cross-machine,\n// hand-editable, git-tracked document, so NO content failure throws. An unreadable file,\n// unparseable JSON, or a guard rejection becomes a typed refusal row and the surrounding read\n// survives; an absent root is an empty snapshot with a notice, never an error. Blob presence is\n// reported rather than required — `**/blobs/` is gitignored, so a freshly cloned root legitimately\n// carries zero blobs (F6) and the census prints `blobs present locally: n/N`.\n\nimport { existsSync, readdirSync, readFileSync, statSync } from 'node:fs'\nimport type { Dirent } from 'node:fs'\nimport { join } from 'node:path'\n\nimport {\n activeMarkedCorpusEntries,\n BASELINE_POINTER_SCHEMA_VERSION,\n guardDecisionVectorsDocument,\n guardNoiseFloorArtefact,\n parseCorpusEntry,\n parseStandaloneLabelRecord,\n selectEffectiveLabels,\n partitionManifestSchema,\n runExclusionRecordSchema,\n} from '@vostride/agent-qa-eval'\nimport { slugBranch } from '@vostride/agent-qa-schema'\nimport type {\n CommittedCorpusSnapshot,\n CorpusEntry,\n DecisionVectorsDocument,\n EvalBatchReport,\n LeafCassette,\n NoiseFloorArtefact,\n PartitionManifest,\n RunExclusionRecord,\n StandaloneLabelRecord,\n} from '@vostride/agent-qa-eval'\nimport { z } from 'zod'\n\nimport { loadCassetteBundle } from './cassette-loader.js'\nimport { BLOBS_SUBDIR } from './cassette-store.js'\nimport {\n COMMITTED_CASSETTES_DIR,\n COMMITTED_CONTRACTS_DIR,\n COMMITTED_CORPUS_DIR,\n COMMITTED_EXCLUSIONS_DIR,\n COMMITTED_LABELS_DIR,\n COMMITTED_NOISE_FLOOR_SUBDIR,\n COMMITTED_REPORTS_DIR,\n PARTITION_MANIFEST_FILE_PREFIX,\n committedPartitionManifestPath,\n committedLabelPath,\n committedReportPath,\n} from './committed-root-store.js'\nimport { listCassetteBundleDirs } from './replay-read.js'\nimport { FsMarkLedger } from './mark-ledger.js'\nimport { readLabelCalibration, type LabelCalibrationRead } from './calibration-source.js'\nimport { reportMetadataKind } from './report-metadata-guard.js'\nimport { reportJudgeSchema } from './report-store.js'\n\n/** Gate-side effective view; raw committed history and the proposer reader remain unchanged. */\nexport function readActiveCommittedCorpus(committedRoot: string, agentQaRoot: string, calibrationRead?: LabelCalibrationRead): CommittedCorpusRead {\n const read = readCommittedCorpus(committedRoot, calibrationRead)\n const runs = new Set([...read.snapshot.labelsById.values()].filter(label =>\n label.provenance.sourceCassetteId !== undefined).map(label => label.provenance.runId))\n const marks = []\n const refusals = [...read.refusals]\n for (const runId of runs) {\n try { marks.push(...new FsMarkLedger({ root: agentQaRoot, runId }).latestPerAddress()) }\n catch { refusals.push({ path: agentQaRoot, reason: 'mark ledger unreadable; active gold unavailable' }) }\n }\n const entries = activeMarkedCorpusEntries(read.snapshot, marks)\n const activeIds = new Set(entries.map(entry => entry.cassetteId))\n return { ...read, refusals, snapshot: { ...read.snapshot, entries },\n retiredMarkedCassetteIds: new Set(read.snapshot.entries.filter(entry => !activeIds.has(entry.cassetteId)).map(entry => entry.cassetteId)),\n notices: [...read.notices, ...(entries.length === read.snapshot.entries.length ? []\n : [`${read.snapshot.entries.length - entries.length} historical or incomplete marked cases excluded`])] }\n}\n\n/** Census, baseline pointers and per-profile floors are not batch reports (415 D-04). */\nconst RESERVED_REPORT_DIRS = new Set(['census', 'baselines', 'noise-floor', 'judge-calibration'])\n\nconst reportSchema = z\n .object({\n batchKey: z.string(),\n matcherVersion: z.number(),\n judgeIdentity: z.literal('mixed').optional(),\n mixedJudgeModelIds: z.array(z.string()).optional(),\n headline: z\n .object({ perDecisionCost: z.object({ decisionCount: z.number() }).passthrough() })\n .passthrough(),\n honesty: z\n .object({\n unscorableIdentityCounts: z.record(z.string(), z.number()),\n refusalCounts: z.record(z.string(), z.number()),\n })\n .passthrough(),\n judge: reportJudgeSchema.optional(),\n })\n .passthrough().refine(report => report.judgeIdentity !== 'mixed' || report.judge === undefined)\n\nconst baselinePointerSchema = z\n .object({\n schemaVersion: z.literal(BASELINE_POINTER_SCHEMA_VERSION),\n profileKey: z.string(),\n batchKey: z.string(),\n fingerprint: z.string(),\n corpusDigest: z.string(),\n })\n .passthrough()\n\n/** One unreadable or unadmittable committed file, attributable by path (D-08). */\nexport interface CommittedReadRefusal {\n path: string\n reason: string\n}\n\n/** The loaded committed root: eval core's snapshot plus everything the read learned about it. */\nexport interface CommittedCorpusRead {\n /** Guarded ownership including scoring-excluded history, for incurred-cost reports. */\n corpusEntriesById?: ReadonlyMap<string, CorpusEntry>\n calibration?: EvalBatchReport['calibration']\n retiredMarkedCassetteIds?: ReadonlySet<string>\n snapshot: CommittedCorpusSnapshot\n productsSeen: readonly string[]\n notices: string[]\n refusals: readonly CommittedReadRefusal[]\n /**\n * How many committed files declared each schemaVersion, keyed by the declared version as a\n * string. An absent version key means zero files declared it.\n */\n schemaVersionsSeen: Record<string, number>\n}\n\n/** Every committed partition manifest under contracts/, and what the read refused (D-05). */\nexport interface PartitionManifestRead {\n manifests: readonly PartitionManifest[]\n notices: string[]\n refusals: readonly CommittedReadRefusal[]\n}\n\n/** Every committed run-exclusion record under exclusions/, and what the read refused (D-02). */\nexport interface RunExclusionRead {\n exclusions: readonly RunExclusionRecord[]\n refusals: readonly CommittedReadRefusal[]\n}\n\n/** One committed batch report keyed by its own dir name. */\nexport interface CommittedReportEntry {\n batchKey: string\n report: EvalBatchReport\n path: string\n}\n\n/** One committed per-profile baseline designation (D-19). */\nexport interface BaselinePointerEntry {\n schemaVersion: typeof BASELINE_POINTER_SCHEMA_VERSION\n profileKey: string\n batchKey: string\n fingerprint: string\n corpusDigest: string\n path: string\n}\n\nexport interface NoiseFloorArtefactEntry {\n profileKey: string\n artefact: NoiseFloorArtefact\n path: string\n}\n\nexport type CommittedDecisionVectorsLoad =\n | { kind: 'loaded'; document: DecisionVectorsDocument; path: string }\n | { kind: 'absent'; path: string }\n | { kind: 'refused'; refusal: CommittedReadRefusal }\n\nexport type ResolveMinimumGateSetOutcome =\n | { kind: 'resolved'; minimumGateSet: number; path: string }\n | { kind: 'no-floor' }\n | { kind: 'no-value'; path: string }\n\n/** Read only the per-profile files; the former literal reports/noise-floor.json is invalid. */\nexport function listNoiseFloorArtefacts(committedRoot: string): {\n artefacts: NoiseFloorArtefactEntry[]\n refusals: CommittedReadRefusal[]\n} {\n const reportsRoot = join(committedRoot, COMMITTED_REPORTS_DIR)\n const floorRoot = join(reportsRoot, COMMITTED_NOISE_FLOOR_SUBDIR)\n const artefacts: NoiseFloorArtefactEntry[] = []\n const refusals: CommittedReadRefusal[] = []\n const strayPath = join(reportsRoot, 'noise-floor.json')\n if (existsSync(strayPath)) {\n refusals.push({ path: strayPath, reason: 'noise floor must live under reports/noise-floor/<profile-key>.json' })\n }\n let entries: Dirent[]\n try {\n entries = readdirSync(floorRoot, { withFileTypes: true })\n } catch {\n return { artefacts, refusals }\n }\n for (const entry of entries.sort((a, b) => a.name < b.name ? -1 : a.name > b.name ? 1 : 0)) {\n if (!entry.isFile() || !entry.name.endsWith('.json')) continue\n const path = join(floorRoot, entry.name)\n const read = readJson(path)\n if (!read.ok) {\n refusals.push({ path, reason: read.reason })\n continue\n }\n const guarded = guardNoiseFloorArtefact(read.value)\n if (guarded.kind === 'rejected') {\n refusals.push({ path, reason: `noise floor rejected: ${guarded.reason}` })\n continue\n }\n artefacts.push({ profileKey: guarded.artefact.profileKey, artefact: guarded.artefact, path })\n }\n artefacts.sort((a, b) => a.profileKey < b.profileKey ? -1 : a.profileKey > b.profileKey ? 1 : 0)\n return { artefacts, refusals }\n}\n\nexport function readCommittedDecisionVectors(\n committedRoot: string,\n batchKey: string,\n): CommittedDecisionVectorsLoad {\n const path = join(committedRoot, committedReportPath(batchKey, 'decisions.json'))\n if (!existsSync(path)) return { kind: 'absent', path }\n const read = readJson(path)\n if (!read.ok) return { kind: 'refused', refusal: { path, reason: read.reason } }\n const guarded = guardDecisionVectorsDocument(read.value)\n if (guarded.kind === 'rejected') {\n return { kind: 'refused', refusal: { path, reason: `decisions rejected: ${guarded.reason}` } }\n }\n if (guarded.document.batchKey !== batchKey) {\n return { kind: 'refused', refusal: { path, reason: 'decisions batchKey does not match the requested batchKey' } }\n }\n return { kind: 'loaded', document: guarded.document, path }\n}\n\n/** 414 D-15 → 417 handoff: resolve the measured integer without changing the pure posture port. */\nexport function resolveMinimumGateSet(input: {\n committedRoot: string\n profileKey: string\n}): ResolveMinimumGateSetOutcome {\n const entry = listNoiseFloorArtefacts(input.committedRoot).artefacts.find(\n candidate => candidate.profileKey === input.profileKey,\n )\n if (entry === undefined) return { kind: 'no-floor' }\n const minimumGateSet = entry.artefact.minimumGateSet.value\n if (minimumGateSet === undefined) return { kind: 'no-value', path: entry.path }\n return { kind: 'resolved', minimumGateSet, path: entry.path }\n}\n\nfunction listDirNames(dir: string): string[] {\n try {\n return readdirSync(dir, { withFileTypes: true })\n .filter((entry) => entry.isDirectory())\n .map((entry) => entry.name)\n .sort()\n } catch {\n return []\n }\n}\n\n/** Every `*.json` file under dir at any depth, sorted; ENOENT is an empty list. */\nfunction listJsonFiles(dir: string): string[] {\n const found: string[] = []\n\n function walk(current: string): void {\n let entries: Dirent[]\n try {\n entries = readdirSync(current, { withFileTypes: true })\n } catch {\n return\n }\n for (const entry of entries) {\n const path = join(current, entry.name)\n if (entry.isDirectory()) walk(path)\n else if (entry.isFile() && entry.name.endsWith('.json')) found.push(path)\n }\n }\n\n walk(dir)\n return found.sort()\n}\n\nfunction readJson(path: string): { ok: true; value: unknown } | { ok: false; reason: string } {\n let raw: string\n try {\n raw = readFileSync(path, 'utf8')\n } catch (error) {\n return { ok: false, reason: `unreadable: ${(error as NodeJS.ErrnoException).message}` }\n }\n try {\n return { ok: true, value: JSON.parse(raw) }\n } catch (error) {\n return { ok: false, reason: `invalid JSON: ${(error as Error).message}` }\n }\n}\n\nfunction issueText(error: z.ZodError): string {\n return error.issues\n .map(\n (issue) => `${issue.path.length === 0 ? '<root>' : issue.path.join('.')}: ${issue.message}`,\n )\n .join('; ')\n}\n\n/** Every `*.json` file directly under dir (no recursion), sorted; ENOENT is an empty list. */\nfunction listJsonFilesShallow(dir: string): string[] {\n try {\n return readdirSync(dir, { withFileTypes: true })\n .filter((entry) => entry.isFile() && entry.name.endsWith('.json'))\n .map((entry) => entry.name)\n .sort()\n } catch {\n return []\n }\n}\n\nfunction bumpCounter(counter: Record<string, number>, key: string): void {\n counter[key] = (counter[key] ?? 0) + 1\n}\n\n/** A blobs/ dir counts as present only when it exists AND holds at least one file (F6). */\nfunction blobsPresent(bundleDir: string): boolean {\n const blobsDir = join(bundleDir, BLOBS_SUBDIR)\n if (!existsSync(blobsDir)) return false\n try {\n return readdirSync(blobsDir).length > 0\n } catch {\n return false\n }\n}\n\n/**\n * Load the committed root at `committedRoot` into the snapshot eval core's `deriveCensus` folds.\n * Never throws for content: every parse or guard failure lands in `refusals`, and a missing root\n * yields an empty snapshot with a notice naming it.\n */\nexport function readCommittedCorpus(committedRoot: string, calibrationRead?: LabelCalibrationRead): CommittedCorpusRead {\n const notices: string[] = []\n const refusals: CommittedReadRefusal[] = []\n const entries: CorpusEntry[] = []\n const schemaVersionsSeen: Record<string, number> = {}\n const labelsById = new Map<string, StandaloneLabelRecord>()\n const labelHistory: unknown[] = []\n const excludedLabelIds = new Set<string>()\n const cassettesById = new Map<string, LeafCassette>()\n const blobPresentById = new Map<string, boolean>()\n\n if (!existsSync(committedRoot)) {\n notices.push(`committed root absent: ${committedRoot}`)\n return {\n snapshot: { entries, labelsById, cassettesById, blobPresentById, labelHistory, excludedLabelIds },\n productsSeen: [],\n notices,\n refusals,\n schemaVersionsSeen,\n }\n }\n\n const corpusRoot = join(committedRoot, COMMITTED_CORPUS_DIR)\n const productsSeen = listDirNames(corpusRoot)\n\n for (const product of productsSeen) {\n for (const path of listJsonFiles(join(corpusRoot, product))) {\n const read = readJson(path)\n if (!read.ok) {\n refusals.push({ path, reason: read.reason })\n continue\n }\n // The one guard in eval core, not a second zod shape here.\n const parsed = parseCorpusEntry(read.value)\n if (!parsed.ok) {\n refusals.push({ path, reason: `corpus entry rejected: ${parsed.issues}` })\n continue\n }\n if (parsed.entry.product !== product) {\n notices.push(\n `corpus dir '${product}' holds an entry declaring product '${parsed.entry.product}' (${parsed.entry.cassetteId}); the entry field is authoritative`,\n )\n }\n entries.push(parsed.entry)\n bumpCounter(schemaVersionsSeen, String(parsed.schemaVersion))\n }\n }\n\n const labelsRoot = join(committedRoot, COMMITTED_LABELS_DIR)\n const labelOwners = new Map<string, Set<string>>()\n const idsByBasePath = new Map<string, Set<string>>()\n for (const entry of entries) {\n const owners = labelOwners.get(entry.cassetteId) ?? new Set<string>()\n owners.add(slugBranch(entry.product))\n labelOwners.set(entry.cassetteId, owners)\n const path = join(committedRoot, committedLabelPath(entry.product, entry.cassetteId))\n const ids = idsByBasePath.get(path) ?? new Set<string>()\n ids.add(entry.cassetteId)\n idsByBasePath.set(path, ids)\n }\n const readInvalidIds = new Set<string>()\n const validHistory: StandaloneLabelRecord[] = []\n for (const product of listDirNames(labelsRoot)) {\n for (const path of listJsonFiles(join(labelsRoot, product))) {\n const basePath = path.replace(/--revision-[^/]*\\.json$/, '.json')\n const pathIds = idsByBasePath.get(basePath) ?? new Set<string>()\n const refuse = (reason: string, cassetteId?: string) => {\n refusals.push({ path, reason })\n for (const id of pathIds) readInvalidIds.add(id)\n if (cassetteId !== undefined) readInvalidIds.add(cassetteId)\n }\n const read = readJson(path)\n if (!read.ok) {\n refuse('label record unreadable or invalid JSON')\n continue\n }\n labelHistory.push(read.value)\n const parsed = parseStandaloneLabelRecord(read.value)\n if (!parsed.ok) {\n const claimedId = typeof read.value === 'object' && read.value !== null && 'cassetteId' in read.value && typeof read.value.cassetteId === 'string' ? read.value.cassetteId : undefined\n refuse(`label record rejected: ${parsed.issues}`, claimedId)\n continue\n }\n const record = parsed.record\n const expectedBase = join(committedRoot, committedLabelPath(product, record.cassetteId))\n const expectedPath = record.revision === undefined ? expectedBase\n : expectedBase.replace(/\\.json$/, `--revision-${record.revision.id}.json`)\n const owners = labelOwners.get(record.cassetteId)\n if (path !== expectedPath || (owners !== undefined && (owners.size !== 1 || !owners.has(product)))) {\n refuse('label path or product ownership mismatch', record.cassetteId)\n continue\n }\n validHistory.push(record)\n }\n }\n // Generation-bearing refreshes stay invisible until the guarded publication loader\n // explicitly authorizes their marker. Seed/base revisions require no marker.\n const calibration = calibrationRead ?? readLabelCalibration(committedRoot)\n const authorized = calibration.kind === 'loaded' ? calibration.authorizedRevisionIds : new Set<string>()\n const eligibleHistory = validHistory.filter(record => record.revision?.generationId === undefined || authorized.has(record.revision.id))\n const generations = new Set(eligibleHistory.flatMap(record => record.revision?.generationId ? [record.revision.generationId] : []))\n if (calibration.kind === 'refused') {\n refusals.push({ path: join(labelsRoot, 'calibration.json'), reason: calibration.reason })\n for (const id of calibration.targetCassetteIds ?? []) readInvalidIds.add(id)\n for (const record of validHistory) if (record.revision?.generationId) readInvalidIds.add(record.cassetteId)\n }\n const selected = selectEffectiveLabels(eligibleHistory, { committedGenerationIds: generations })\n for (const [id, label] of selected.effective) {\n if (!readInvalidIds.has(id)) labelsById.set(id, label)\n }\n for (const id of [...readInvalidIds, ...selected.excludedLabelIds]) excludedLabelIds.add(id)\n for (const refusal of selected.refusals) {\n refusals.push({ path: labelsRoot, reason: `label history rejected: ${refusal.reason}` })\n }\n\n for (const bundleDir of listCassetteBundleDirs(join(committedRoot, COMMITTED_CASSETTES_DIR))) {\n const load = loadCassetteBundle(bundleDir)\n if (load.kind === 'refused') {\n refusals.push({\n path: join(bundleDir, 'cassette.json'),\n reason: `${load.refusal.reason}: ${load.refusal.detail}`,\n })\n continue\n }\n if (load.cassette.granularity !== 'leaf') {\n notices.push(`skipped non-leaf cassette bundle: ${bundleDir}`)\n continue\n }\n cassettesById.set(load.cassette.id, load.cassette)\n blobPresentById.set(load.cassette.id, blobsPresent(bundleDir))\n }\n\n entries.sort((first, second) =>\n first.cassetteId < second.cassetteId ? -1 : first.cassetteId > second.cassetteId ? 1 : 0,\n )\n\n return {\n corpusEntriesById: new Map(entries.map(entry => [entry.cassetteId, entry])),\n calibration: calibration.kind === 'loaded' ? {\n status: calibration.artifact.channels['recorded-verdict'].status,\n n_gold: calibration.artifact.channels['recorded-verdict'].n_gold,\n verifierFalsePassRate: calibration.artifact.verifierFalsePassRate,\n } : { status: 'uncalibrated' },\n snapshot: { entries: entries.filter(entry => !excludedLabelIds.has(entry.cassetteId)),\n labelsById, cassettesById, blobPresentById, labelHistory, excludedLabelIds },\n productsSeen,\n notices,\n refusals,\n schemaVersionsSeen,\n }\n}\n\n/**\n * Load every committed partition manifest under `contracts/` (414 D-05).\n *\n * Manifests are found by file-name prefix, not by parsing every contract: the dir also holds the\n * D-04 matcher and judge constitution projections, and asking the manifest guard about those would\n * manufacture refusals for documents that are not manifests. An absent `contracts/` is an empty\n * list with no refusal — a root that has never been partitioned is a legitimate root.\n */\nexport function readPartitionManifests(committedRoot: string): PartitionManifestRead {\n const contractsRoot = join(committedRoot, COMMITTED_CONTRACTS_DIR)\n const manifests: PartitionManifest[] = []\n const notices: string[] = []\n const refusals: CommittedReadRefusal[] = []\n\n for (const name of listJsonFilesShallow(contractsRoot)) {\n if (!name.startsWith(PARTITION_MANIFEST_FILE_PREFIX)) continue\n const path = join(contractsRoot, name)\n const read = readJson(path)\n if (!read.ok) {\n refusals.push({ path, reason: read.reason })\n continue\n }\n const parsed = partitionManifestSchema.safeParse(read.value)\n if (!parsed.success) {\n refusals.push({ path, reason: `partition manifest rejected: ${issueText(parsed.error)}` })\n continue\n }\n const expected = committedPartitionManifestPath(parsed.data.product)\n if (expected !== `${COMMITTED_CONTRACTS_DIR}/${name}`) {\n notices.push(\n `partition manifest '${name}' declares product '${parsed.data.product}', whose committed path is '${expected}'; the manifest field is authoritative`,\n )\n }\n manifests.push(parsed.data)\n }\n\n manifests.sort((first, second) =>\n first.product < second.product ? -1 : first.product > second.product ? 1 : 0,\n )\n return { manifests, notices, refusals }\n}\n\n/**\n * Load every committed run-exclusion record under `exclusions/<product>/` (414 D-02).\n *\n * Same posture as the corpus walk: a committed root is hand-editable, so an unparseable or\n * unadmittable record is a refusal row naming its path, never a throw. An absent `exclusions/`\n * dir is an empty list with no refusal — a root with nothing excluded is the common case.\n */\nexport function readRunExclusions(committedRoot: string): RunExclusionRead {\n const exclusionsRoot = join(committedRoot, COMMITTED_EXCLUSIONS_DIR)\n const exclusions: RunExclusionRecord[] = []\n const refusals: CommittedReadRefusal[] = []\n\n for (const product of listDirNames(exclusionsRoot)) {\n for (const path of listJsonFiles(join(exclusionsRoot, product))) {\n const read = readJson(path)\n if (!read.ok) {\n refusals.push({ path, reason: read.reason })\n continue\n }\n const parsed = runExclusionRecordSchema.safeParse(read.value)\n if (!parsed.success) {\n refusals.push({ path, reason: `run exclusion rejected: ${issueText(parsed.error)}` })\n continue\n }\n exclusions.push(parsed.data)\n }\n }\n\n return { exclusions, refusals }\n}\n\n/**\n * List every committed batch report under `reports/`, one per batchKey dir. The reserved `census/`\n * and `baselines/`, `noise-floor/`, `judge-calibration/` dirs are not batch reports.\n */\nexport function listCommittedReports(committedRoot: string): {\n reports: CommittedReportEntry[]\n refusals: CommittedReadRefusal[]\n} {\n const reportsRoot = join(committedRoot, COMMITTED_REPORTS_DIR)\n const reports: CommittedReportEntry[] = []\n const refusals: CommittedReadRefusal[] = []\n\n for (const name of listDirNames(reportsRoot)) {\n if (RESERVED_REPORT_DIRS.has(name)) continue\n const path = join(reportsRoot, name, 'report.json')\n if (!existsSync(path)) {\n refusals.push({ path, reason: 'report.json absent' })\n continue\n }\n const read = readJson(path)\n if (!read.ok) {\n refusals.push({ path, reason: read.reason })\n continue\n }\n const parsed = reportSchema.safeParse(read.value)\n if (!parsed.success) {\n refusals.push({ path, reason: `report rejected: ${issueText(parsed.error)}` })\n continue\n }\n if (reportMetadataKind(read.value) === 'invalid') {\n refusals.push({ path, reason: 'report label metadata incomplete or malformed' })\n continue\n }\n reports.push({ batchKey: name, report: parsed.data as unknown as EvalBatchReport, path })\n }\n\n reports.sort((first, second) =>\n first.batchKey < second.batchKey ? -1 : first.batchKey > second.batchKey ? 1 : 0,\n )\n return { reports, refusals }\n}\n\n/** List every committed per-profile baseline pointer under `reports/baselines/`, sorted (D-19). */\nexport function listBaselinePointers(committedRoot: string): {\n pointers: BaselinePointerEntry[]\n refusals: CommittedReadRefusal[]\n} {\n const baselinesRoot = join(committedRoot, COMMITTED_REPORTS_DIR, 'baselines')\n const pointers: BaselinePointerEntry[] = []\n const refusals: CommittedReadRefusal[] = []\n\n let names: string[]\n try {\n names = readdirSync(baselinesRoot).sort()\n } catch {\n return { pointers, refusals }\n }\n\n for (const name of names) {\n if (!name.endsWith('.json')) continue\n const path = join(baselinesRoot, name)\n if (!statSync(path).isFile()) continue\n const read = readJson(path)\n if (!read.ok) {\n refusals.push({ path, reason: read.reason })\n continue\n }\n const parsed = baselinePointerSchema.safeParse(read.value)\n if (!parsed.success) {\n refusals.push({ path, reason: `baseline pointer rejected: ${issueText(parsed.error)}` })\n continue\n }\n pointers.push({\n schemaVersion: parsed.data.schemaVersion,\n profileKey: parsed.data.profileKey,\n batchKey: parsed.data.batchKey,\n fingerprint: parsed.data.fingerprint,\n corpusDigest: parsed.data.corpusDigest,\n path,\n })\n }\n\n pointers.sort((first, second) =>\n first.profileKey < second.profileKey ? -1 : first.profileKey > second.profileKey ? 1 : 0,\n )\n return { pointers, refusals }\n}\n","// Filesystem cassette bundle loader (Phase 368, D-08 / RPLY-02).\n//\n// Inverts FsCassetteStore's write leg: reads `cassette.json`, version-gates + structurally validates\n// it through guardCassette (the V5 input-validation control), and binds a DirArtifactSource over the\n// bundle's `blobs/` directory. A cassette bundle is an untrusted, hand-editable, cross-machine\n// document, so every failure — an unreadable/missing file, unparseable JSON, or a guard rejection —\n// becomes a typed ReplayRefusal instead of a throw. The cassette is never partially loaded and never\n// replayed on refusal; the refusal surfaces in reports and the surrounding batch always survives.\n\nimport { readFileSync } from 'node:fs'\nimport { basename, join } from 'node:path'\n\nimport {\n guardCassette,\n type ArtifactSource,\n type Cassette,\n type ReplayRefusal,\n} from '@vostride/agent-qa-eval'\n\nimport { DirArtifactSource } from './artifact-source.js'\nimport { BLOBS_SUBDIR, CASSETTE_FILE } from './cassette-store.js'\n\n/** The discriminated bundle-load outcome: a guarded cassette + its blob source, or a typed refusal. */\nexport type CassetteBundleLoad =\n | { kind: 'loaded'; cassette: Cassette; artifacts: ArtifactSource }\n | { kind: 'refused'; refusal: ReplayRefusal }\n\nfunction refused(\n cassetteId: string,\n reason: ReplayRefusal['reason'],\n detail: string,\n): CassetteBundleLoad {\n return { kind: 'refused', refusal: { kind: 'integrity-refusal', cassetteId, reason, detail } }\n}\n\n// Recover the recorded id from a parsed-but-rejected document so the refusal is attributable; falls\n// back to the bundle directory basename when the id is unrecoverable (unparseable / absent / non-string).\nfunction recoverCassetteId(document: unknown, fallback: string): string {\n if (typeof document === 'object' && document !== null) {\n const id = (document as Record<string, unknown>).id\n if (typeof id === 'string' && id.length > 0) return id\n }\n return fallback\n}\n\n/**\n * Load a cassette bundle directory into a guarded Cassette + DirArtifactSource over its blobs.\n * Never throws for a malformed bundle — read/parse failures map to a 'parse-failed' refusal and\n * guard rejections (version or structural) map to 'guard-failed', each carrying the underlying\n * message in `detail` (D-08).\n */\nexport function loadCassetteBundle(bundleDir: string): CassetteBundleLoad {\n const fallbackId = basename(bundleDir)\n const cassettePath = join(bundleDir, CASSETTE_FILE)\n\n let raw: string\n try {\n raw = readFileSync(cassettePath, 'utf8')\n } catch (error) {\n return refused(\n fallbackId,\n 'parse-failed',\n `Failed to read ${cassettePath}: ${(error as NodeJS.ErrnoException).message}`,\n )\n }\n\n let document: unknown\n try {\n document = JSON.parse(raw)\n } catch (error) {\n return refused(\n fallbackId,\n 'parse-failed',\n `Invalid cassette JSON at ${cassettePath}: ${(error as Error).message}`,\n )\n }\n\n let cassette: Cassette\n try {\n cassette = guardCassette(document)\n } catch (error) {\n return refused(recoverCassetteId(document, fallbackId), 'guard-failed', (error as Error).message)\n }\n\n const artifacts = new DirArtifactSource({ dir: join(bundleDir, BLOBS_SUBDIR) })\n return { kind: 'loaded', cassette, artifacts }\n}\n","// Filesystem artifact-source implementations for eval extraction (Phase 367, D-02 / Open Q1a).\n//\n// No run directory currently owns a sha-keyed blob store. `absentArtifactSource` states that fact\n// honestly so a present-but-unservable reference becomes evidence-incomplete downstream. The\n// directory-backed implementation is the real copy-in seam for tests and future callers:\n//\n// <dir>/<lowercase 64-hex sha>\n//\n// The sha is validated before any path or filesystem use. A future runner-local blob write leg can\n// bind the same eval port without changing pure extraction logic.\n\nimport { readFileSync } from 'node:fs'\nimport { join } from 'node:path'\n\nimport type { ArtifactSource } from '@vostride/agent-qa-eval'\nimport { FsEvidenceArtifactStore } from '@vostride/agent-qa-runner-local'\n\nconst BLOB_SHA_RE = /^[0-9a-f]{64}$/\n\nexport const absentArtifactSource: ArtifactSource = {\n getBlob: () => undefined,\n}\n\nexport interface DirArtifactSourceConfig {\n /** Directory containing one file per lowercase 64-hex content sha. */\n dir: string\n}\n\nexport class DirArtifactSource implements ArtifactSource {\n private readonly dir: string\n\n constructor(config: DirArtifactSourceConfig) {\n this.dir = config.dir\n }\n\n getBlob(sha: string): Uint8Array | undefined {\n if (!BLOB_SHA_RE.test(sha)) return undefined\n\n try {\n return new Uint8Array(readFileSync(join(this.dir, sha)))\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') return undefined\n throw error\n }\n }\n}\n\nexport interface FsRunArtifactSourceConfig {\n /** Runtime root the production run store persisted under (e.g. `.agent-qa`). */\n root: string\n /** The run whose sha-keyed screenshot blobs this source reads. */\n runId: string\n}\n\n// Production-shaped read leg over the exact runner-local store that persisted planner screenshots.\n// Run layout, slugging, and sha validation live in FsEvidenceArtifactStore; this adapter only binds\n// eval's neutral ArtifactSource port to that store's read method.\nexport class FsRunArtifactSource implements ArtifactSource {\n private readonly store: FsEvidenceArtifactStore\n\n constructor(config: FsRunArtifactSourceConfig) {\n this.store = new FsEvidenceArtifactStore({ root: config.root, runId: config.runId })\n }\n\n getBlob(sha: string): Uint8Array | undefined {\n return this.store.getBlob(sha)\n }\n}\n","// Filesystem cassette bundle store (Phase 367, D-05/D-06/D-07).\n//\n// Owns the storageImplementation write leg behind eval's in-memory ExtractedCassette contract.\n// Each deterministic bundle is recreated in full before publication:\n//\n// <output-root>/<slugged cassette id segments>/\n// ├── cassette.json\n// └── blobs/<verified lowercase 64-hex sha>\n//\n// Every id segment delegates to schema's security-reviewed slugBranch sanitizer. Blob names are\n// validated and their bytes are hashed with node:crypto before path use; corrupt copy-in degrades\n// completeness loudly instead of publishing bytes under an untrue content address.\n\nimport { createHash } from 'node:crypto'\nimport { mkdirSync, rmSync, writeFileSync } from 'node:fs'\nimport { join } from 'node:path'\n\nimport {\n guardCassette,\n type Cassette,\n type ExtractedCassette,\n type ExtractedBlob,\n} from '@vostride/agent-qa-eval'\nimport { slugBranch } from '@vostride/agent-qa-schema'\n\nexport const CASSETTE_FILE = 'cassette.json'\nexport const BLOBS_SUBDIR = 'blobs'\nexport const DEFAULT_EVAL_CASSETTES_SUBDIR = 'eval/cassettes'\n\nconst BLOB_SHA_RE = /^[0-9a-f]{64}$/\n\nexport interface FsCassetteStoreConfig {\n /** Runtime root (normally the workdir's `.agent-qa` directory). */\n root: string\n /** Optional retargeted root used by later committed-corpus composition. */\n outputDir?: string\n}\n\nexport interface WrittenCassette {\n id: string\n dir: string\n writtenBlobShas: string[]\n corruptBlobShas: string[]\n}\n\nfunction byteSha(bytes: Uint8Array): string {\n return createHash('sha256').update(bytes).digest('hex')\n}\n\nfunction addUnique(values: string[], value: string): void {\n if (!values.includes(value)) values.push(value)\n}\n\nfunction withMissingBlobs(cassette: Cassette, missingShas: readonly string[]): Cassette {\n if (missingShas.length === 0) return cassette\n const combined = [...(cassette.completeness.missingBlobShas ?? [])]\n for (const sha of missingShas) addUnique(combined, sha)\n return {\n ...cassette,\n completeness: {\n ...cassette.completeness,\n status: 'evidence-incomplete',\n missingBlobShas: combined,\n },\n }\n}\n\nexport class FsCassetteStore {\n private readonly outputRoot: string\n\n constructor(config: FsCassetteStoreConfig) {\n this.outputRoot = config.outputDir ?? join(config.root, DEFAULT_EVAL_CASSETTES_SUBDIR)\n }\n\n write(extracted: ExtractedCassette): WrittenCassette {\n const sourceCassette = guardCassette(extracted.cassette)\n const dir = join(\n this.outputRoot,\n ...sourceCassette.id.split('/').map(segment => slugBranch(segment)),\n )\n rmSync(dir, { recursive: true, force: true })\n mkdirSync(dir, { recursive: true })\n\n const verifiedBlobs: ExtractedBlob[] = []\n const writtenBlobShas: string[] = []\n const corruptBlobShas: string[] = []\n const copyInMissingShas: string[] = []\n\n for (const blob of extracted.blobs) {\n if (!BLOB_SHA_RE.test(blob.sha)) {\n addUnique(corruptBlobShas, blob.sha)\n continue\n }\n if (byteSha(blob.bytes) !== blob.sha) {\n addUnique(corruptBlobShas, blob.sha)\n addUnique(copyInMissingShas, blob.sha)\n continue\n }\n if (!writtenBlobShas.includes(blob.sha)) {\n verifiedBlobs.push(blob)\n writtenBlobShas.push(blob.sha)\n }\n }\n\n if (verifiedBlobs.length > 0) {\n const blobsDir = join(dir, BLOBS_SUBDIR)\n mkdirSync(blobsDir, { recursive: true })\n for (const blob of verifiedBlobs) writeFileSync(join(blobsDir, blob.sha), blob.bytes)\n }\n\n const cassette = guardCassette(withMissingBlobs(sourceCassette, copyInMissingShas))\n writeFileSync(join(dir, CASSETTE_FILE), `${JSON.stringify(cassette, null, 2)}\\n`, 'utf8')\n\n return {\n id: cassette.id,\n dir,\n writtenBlobShas,\n corruptBlobShas,\n }\n }\n}\n","// Write-once committed eval-root store (Phase 371, ROOT-01/D-15/D-17).\n//\n// Owns the physical committed root: cassettes/ · corpus/ · labels/ · contracts/ · reports/,\n// product-partitioned, scaffolded on first seed with a .gitignore whose one rule covers\n// every blobs/ dir at any depth plus a schemaVersion root marker (D-15). Every text write\n// is write-once: a byte-identical re-write is a silent 'unchanged' no-op, the same path\n// with differing bytes is a typed 'write-conflict' refusal — never an overwrite, never a\n// delete (D-17). The ONLY mutations in this file are recursive mkdir and create-new-file\n// writes; the staging store's dir-recreate posture is deliberately inverted here.\n//\n// Boundary: text reaching this store has passed the committed-egress checkpoint upstream\n// (the seeding composition helper checks BEFORE handing text over) — the store enforces\n// write-once and layout, never content policy. Blob bytes bypass the text checkpoint and\n// land only under ignore-covered blobs/ dirs after sha-format + byte-hash verification.\n//\n// Every id-derived path segment passes schema's security-reviewed slugBranch sanitizer\n// (T-368-10 posture): a crafted cassette id cannot escape the committed root.\n\nimport { createHash } from 'node:crypto'\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs'\nimport { basename, dirname, join } from 'node:path'\n\nimport type { ExtractedBlob } from '@vostride/agent-qa-eval'\nimport { slugBranch } from '@vostride/agent-qa-schema'\n\nimport { BLOBS_SUBDIR, CASSETTE_FILE } from './cassette-store.js'\n\nexport const COMMITTED_CASSETTES_DIR = 'cassettes'\nexport const COMMITTED_CORPUS_DIR = 'corpus'\nexport const COMMITTED_LABELS_DIR = 'labels'\nexport const COMMITTED_CONTRACTS_DIR = 'contracts'\nexport const COMMITTED_REPORTS_DIR = 'reports'\n/** 414 D-02: one committed file per excluded run, product-partitioned. */\nexport const COMMITTED_EXCLUSIONS_DIR = 'exclusions'\n/** 414 D-05: the file-name prefix that makes a partition manifest findable under contracts/. */\nexport const PARTITION_MANIFEST_FILE_PREFIX = 'partition-'\n/** 413 D-19: baseline pointers live one level under reports/, beside the batchKey dirs. */\nexport const COMMITTED_BASELINES_SUBDIR = 'baselines'\n/** 413 D-15: the corpus census lives one level under reports/, beside the batchKey dirs. */\nexport const COMMITTED_CENSUS_SUBDIR = 'census'\n/** 415 D-04: one floor artefact per profile, beside the baseline-pointer set. */\nexport const COMMITTED_NOISE_FLOOR_SUBDIR = 'noise-floor'\n\nexport const COMMITTED_ROOT_MARKER_FILE = 'eval-root.json'\nexport const COMMITTED_ROOT_SCHEMA_VERSION = 2\n\n/** Scaffolded ignore content: one rule covering every blobs/ dir at any depth (D-15). */\nexport const COMMITTED_GITIGNORE_TEXT =\n '# agent-qa committed eval root: binary blob bytes stay untracked (D-15/L12).\\n**/blobs/\\n'\n\nconst BLOB_SHA_RE = /^[0-9a-f]{64}$/\n\nexport interface CommittedRootStoreConfig {\n /**\n * Explicit absolute committed-root path — `<configDir>/agent-qa-eval` for the Phase 372\n * CLI. Deliberately NO default: nothing may silently resolve into a workdir (OQ-2).\n */\n committedRoot: string\n}\n\nexport type CommittedWriteOutcome =\n | { kind: 'written' }\n | { kind: 'unchanged' }\n | { kind: 'write-conflict'; path: string }\n\nexport function buildRootMarkerValue(): { schemaVersion: number } {\n return { schemaVersion: COMMITTED_ROOT_SCHEMA_VERSION }\n}\n\nfunction sluggedIdSegments(cassetteId: string): string[] {\n return cassetteId.split('/').map(segment => slugBranch(segment))\n}\n\nfunction sluggedJsonLeaf(cassetteId: string): string[] {\n const segments = sluggedIdSegments(cassetteId)\n const last = segments.pop() ?? 'default'\n return [...segments, `${last}.json`]\n}\n\nexport function committedCassettePath(product: string, cassetteId: string): string {\n return [\n COMMITTED_CASSETTES_DIR,\n slugBranch(product),\n ...sluggedIdSegments(cassetteId),\n CASSETTE_FILE,\n ].join('/')\n}\n\nexport function committedBlobPath(product: string, cassetteId: string, sha: string): string {\n return [\n COMMITTED_CASSETTES_DIR,\n slugBranch(product),\n ...sluggedIdSegments(cassetteId),\n BLOBS_SUBDIR,\n slugBranch(sha),\n ].join('/')\n}\n\nexport function committedLabelPath(product: string, cassetteId: string): string {\n return [COMMITTED_LABELS_DIR, slugBranch(product), ...sluggedJsonLeaf(cassetteId)].join('/')\n}\n\n/** Preserve validated cassette path segments; suffix only the JSON leaf, never slug the whole ID. */\nexport function committedLabelRevisionPath(product: string, cassetteId: string, revisionId: string): string {\n if (!BLOB_SHA_RE.test(revisionId) || !cassetteId.split('/').every(segment =>\n segment.length > 0 && segment !== '.' && segment !== '..' && slugBranch(segment) === segment)) {\n throw new TypeError('invalid label revision path')\n }\n return committedLabelPath(product, cassetteId).replace(/\\.json$/, `--revision-${revisionId}.json`)\n}\n\nexport function committedCorpusPath(product: string, cassetteId: string): string {\n return [COMMITTED_CORPUS_DIR, slugBranch(product), ...sluggedJsonLeaf(cassetteId)].join('/')\n}\n\nexport function committedContractPath(fileName: string): string {\n return [COMMITTED_CONTRACTS_DIR, slugBranch(fileName)].join('/')\n}\n\n/**\n * Phase 372 D-13: the baseline report commits beside the corpus/contracts that produced it —\n * one dir per batchKey, so a changed corpus (⇒ changed key) gets a fresh dir under write-once\n * while an identical regen byte-compares 'unchanged'. Every segment slugged (T-368-10).\n */\nexport function committedReportPath(batchKey: string, fileName: string): string {\n return [COMMITTED_REPORTS_DIR, slugBranch(batchKey), slugBranch(fileName)].join('/')\n}\n\n/**\n * Phase 413 D-15: the census commits one dir per CORPUS DIGEST, the reports/<batchKey>/ shape\n * applied to the population rather than to a replay batch — a grown corpus hashes to a fresh\n * digest and therefore gets a fresh dir under write-once, while re-running the census over an\n * unchanged corpus byte-compares 'unchanged' and is an idempotent no-op. Every segment slugged,\n * so a crafted digest cannot escape the committed root (T-368-10 / T-413-18).\n */\nexport function committedCensusPath(corpusDigest: string, fileName: string): string {\n return [\n COMMITTED_REPORTS_DIR,\n COMMITTED_CENSUS_SUBDIR,\n slugBranch(corpusDigest),\n slugBranch(fileName),\n ].join('/')\n}\n\n/**\n * Phase 413 D-19: baseline designation is a committed pointer, one file per profile key, so\n * moving a baseline is a reviewed git edit of that file rather than a silent overwrite. The\n * key is a single path segment and every segment is slugged (T-368-10).\n */\nexport function committedBaselinePointerPath(profileKey: string): string {\n return [COMMITTED_REPORTS_DIR, COMMITTED_BASELINES_SUBDIR, `${slugBranch(profileKey)}.json`].join(\n '/',\n )\n}\n\n/** 415 D-04: the entire profile key is one slugged segment, never a batch report dir. */\nexport function committedNoiseFloorPath(profileKey: string): string {\n return [COMMITTED_REPORTS_DIR, COMMITTED_NOISE_FLOOR_SUBDIR, `${slugBranch(profileKey)}.json`].join('/')\n}\n\n/**\n * Phase 414 D-05: one partition manifest per product, under contracts/ rather than beside the\n * entries it governs. Two reasons. (1) The recursive corpus/ walk parses every *.json it finds,\n * so a manifest under corpus/ would be handed to the corpus-entry guard and refused as a corpus\n * entry — putting it under contracts/ means the walk never sees it (T-414-20). (2) contracts/ is\n * already where this root keeps its reviewed constitution documents, and a manifest IS one: a\n * change to its salt or fractions re-partitions a committed corpus, so it lands as a reviewed git\n * edit and a named migration, never a silent rewrite. Every segment slugged (T-368-10).\n */\nexport function committedPartitionManifestPath(product: string): string {\n return committedContractPath(`${PARTITION_MANIFEST_FILE_PREFIX}${slugBranch(product)}.json`)\n}\n\n/**\n * Phase 414 D-02: one committed file per excluded run, so corpus membership becomes a function of\n * a committed list rather than a fact about whoever ran the growth campaign. 413 dropped excluded\n * runs before anything was committed and they left no trace; a record per run lets the census\n * count what never arrived and 422's curator ask why a run is missing. Every segment slugged, so a\n * crafted product or runSlug cannot escape the committed root (T-368-10 / T-414-16).\n */\nexport function committedExclusionPath(product: string, runSlug: string): string {\n return [COMMITTED_EXCLUSIONS_DIR, slugBranch(product), `${slugBranch(runSlug)}.json`].join('/')\n}\n\nfunction byteSha(bytes: Uint8Array): string {\n return createHash('sha256').update(bytes).digest('hex')\n}\n\nexport class FsCommittedRootStore {\n private readonly committedRoot: string\n\n constructor(config: CommittedRootStoreConfig) {\n this.committedRoot = config.committedRoot\n }\n\n /**\n * Scaffold the committed root deterministically: the blobs-covering .gitignore plus the\n * schemaVersion root marker (D-15). Both texts arrive as ALREADY-CHECKED strings from\n * the seeding composition helper — the store never imports the guardrail. Re-scaffolding\n * an intact root yields two 'unchanged' outcomes and mutates nothing.\n */\n scaffold(\n checkedGitignoreText: string,\n checkedRootMarkerText: string,\n ): { gitignore: CommittedWriteOutcome; rootMarker: CommittedWriteOutcome } {\n return {\n gitignore: this.writeText('.gitignore', checkedGitignoreText),\n rootMarker: this.writeText(COMMITTED_ROOT_MARKER_FILE, checkedRootMarkerText),\n }\n }\n\n /** The write-once comparison WITHOUT any mkdir or write — pure read + compare. */\n previewText(relPath: string, text: string): 'absent' | 'identical' | 'conflict' {\n const file = join(this.committedRoot, relPath)\n if (!existsSync(file)) return 'absent'\n return readFileSync(file, 'utf8') === text ? 'identical' : 'conflict'\n }\n\n /** Write-once text: read-compare-refuse, never overwrite (D-17). */\n writeText(relPath: string, text: string): CommittedWriteOutcome {\n const file = join(this.committedRoot, relPath)\n if (existsSync(file)) {\n return readFileSync(file, 'utf8') === text\n ? { kind: 'unchanged' }\n : { kind: 'write-conflict', path: relPath }\n }\n mkdirSync(dirname(file), { recursive: true })\n writeFileSync(file, text, 'utf8')\n return { kind: 'written' }\n }\n\n /**\n * Write-once blob bytes, confined to ignore-covered blobs/ dirs. The sha must match the\n * lowercase 64-hex content-address format, the byte hash must equal it, and the target\n * path must be `.../blobs/<sha>` — any verification failure is a typed refusal, so\n * corrupt bytes never land under an untrue content address (T-371-11).\n */\n writeBlob(relBlobPath: string, blob: ExtractedBlob): CommittedWriteOutcome {\n if (!BLOB_SHA_RE.test(blob.sha)) return { kind: 'write-conflict', path: relBlobPath }\n if (byteSha(blob.bytes) !== blob.sha) return { kind: 'write-conflict', path: relBlobPath }\n if (basename(relBlobPath) !== blob.sha || basename(dirname(relBlobPath)) !== BLOBS_SUBDIR) {\n return { kind: 'write-conflict', path: relBlobPath }\n }\n const file = join(this.committedRoot, relBlobPath)\n if (existsSync(file)) {\n return readFileSync(file).equals(Buffer.from(blob.bytes))\n ? { kind: 'unchanged' }\n : { kind: 'write-conflict', path: relBlobPath }\n }\n mkdirSync(dirname(file), { recursive: true })\n writeFileSync(file, blob.bytes)\n return { kind: 'written' }\n }\n}\n","// Phase 372 G2 read legs — the report verb's input integrity layer.\n//\n// Inverts FsReplayRecordStore's and FsScoreStore's write legs: reads `replay.json` / `score.json`\n// back from a staged cassette dir with the exact typed-refusal posture of loadCassetteBundle —\n// staged files are hand-editable, cross-machine documents, so every read/parse failure becomes a\n// typed ReplayRefusal instead of a throw (D-08: refusals are data; the surrounding batch always\n// survives). A missing score.json is typed ABSENCE, not an error: refusal outcomes are never\n// scored, the report verb counts them instead. eval-local serializes/deserializes only (F9) —\n// no scoring or aggregation logic lives here; deep validation stays with eval core's guards.\n\nimport { existsSync, readFileSync, readdirSync, type Dirent } from 'node:fs'\nimport { basename, join } from 'node:path'\n\nimport type { ReplayCassetteOutcome, ReplayRefusal } from '@vostride/agent-qa-eval'\nimport { scoringContextSchema, hasValidScoreJudgeMetadata } from '@vostride/agent-qa-eval'\n\nimport { CASSETTE_FILE } from './cassette-store.js'\nimport { REPLAY_RECORD_FILE } from './replay-record-store.js'\nimport { SCORE_FILE, type ScoreRecord } from './score-store.js'\n\n/** The discriminated replay-record read outcome: a persisted outcome, or a typed refusal. */\nexport type ReplayRecordLoad =\n | { kind: 'loaded'; outcome: ReplayCassetteOutcome }\n | { kind: 'refused'; refusal: ReplayRefusal }\n\n/** The score read outcome: a persisted record, typed absence (never scored), or a typed refusal. */\nexport type ScoreRecordLoad =\n | { kind: 'loaded'; record: ScoreRecord }\n | { kind: 'absent' }\n | { kind: 'refused'; refusal: ReplayRefusal }\n\n// Every refusal is attributed to a cassette id recovered from the dir basename: unlike\n// loadCassetteBundle there is no guard step here (deep validation stays with eval core), so a\n// document is never parsed-but-rejected — read/parse failures are the only refusal paths and the\n// basename is the only recoverable id on them.\nfunction refusal(cassetteId: string, detail: string): ReplayRefusal {\n return { kind: 'integrity-refusal', cassetteId, reason: 'parse-failed', detail }\n}\n\n// The shared read→parse ladder (loadCassetteBundle's posture): every failure is a returned value.\nfunction readDocument(\n path: string,\n fallbackId: string,\n): { kind: 'parsed'; document: unknown } | { kind: 'refused'; refusal: ReplayRefusal } {\n let raw: string\n try {\n raw = readFileSync(path, 'utf8')\n } catch (error) {\n return {\n kind: 'refused',\n refusal: refusal(\n fallbackId,\n `Failed to read ${path}: ${(error as NodeJS.ErrnoException).message}`,\n ),\n }\n }\n\n let document: unknown\n try {\n document = JSON.parse(raw)\n } catch (error) {\n return {\n kind: 'refused',\n refusal: refusal(fallbackId, `Invalid JSON at ${path}: ${(error as Error).message}`),\n }\n }\n\n return { kind: 'parsed', document }\n}\n\n/**\n * Read a persisted ReplayCassetteOutcome back from `<dir>/replay.json`. A replay dir without its\n * marker file is corrupt, not empty — a missing or unreadable file refuses like a parse failure.\n * Never throws; the store serialized the outcome verbatim so the parsed document is it.\n */\nexport function loadReplayRecord(dir: string): ReplayRecordLoad {\n const read = readDocument(join(dir, REPLAY_RECORD_FILE), basename(dir))\n if (read.kind === 'refused') return read\n return { kind: 'loaded', outcome: read.document as ReplayCassetteOutcome }\n}\n\n/**\n * Read a persisted ScoreRecord back from `<dir>/score.json`. Absence is typed and legitimate\n * (refusal outcomes are never scored); an existing-but-unreadable/unparseable file refuses.\n * Never throws.\n */\nexport function loadScoreRecord(dir: string): ScoreRecordLoad {\n const path = join(dir, SCORE_FILE)\n if (!existsSync(path)) return { kind: 'absent' }\n\n const read = readDocument(path, basename(dir))\n if (read.kind === 'refused') return read\n if (!hasValidScoreJudgeMetadata(read.document)) {\n return { kind: 'refused', refusal: refusal(basename(dir), `Invalid score judge metadata at ${path}`) }\n }\n if (typeof read.document === 'object' && read.document !== null && 'scoringContext' in read.document &&\n !scoringContextSchema.safeParse(read.document.scoringContext).success) {\n return { kind: 'refused', refusal: refusal(basename(dir), `Invalid scoring context at ${path}`) }\n }\n return { kind: 'loaded', record: read.document as ScoreRecord }\n}\n\n// Recursive walk collecting every directory that directly contains the marker file. Cassette ids\n// are multi-segment slugged paths, so staged dirs nest at arbitrary depth — the walk must recurse,\n// not glob one level. The root read is ENOENT-safe (a missing root is an empty sweep, D-02) and\n// the result is sorted lexicographically before returning: deterministic sweep order, no\n// readdir-ordering leaks (the eval determinism law).\nfunction listMarkerDirs(root: string, markerFile: string): string[] {\n const found: string[] = []\n\n function walk(dir: string): void {\n let entries: Dirent[]\n try {\n entries = readdirSync(dir, { withFileTypes: true })\n } catch {\n return\n }\n if (entries.some(entry => entry.isFile() && entry.name === markerFile)) found.push(dir)\n for (const entry of entries) {\n if (entry.isDirectory()) walk(join(dir, entry.name))\n }\n }\n\n walk(root)\n return found.sort()\n}\n\n/** Enumerate every staged replay-record dir (contains replay.json) under the replays root, sorted. */\nexport function listReplayRecordDirs(replaysRoot: string): string[] {\n return listMarkerDirs(replaysRoot, REPLAY_RECORD_FILE)\n}\n\n/** Enumerate every staged cassette-bundle dir (contains cassette.json) under the cassettes root, sorted. */\nexport function listCassetteBundleDirs(cassettesRoot: string): string[] {\n return listMarkerDirs(cassettesRoot, CASSETTE_FILE)\n}\n","// Filesystem replay-record store (Phase 368, D-12).\n//\n// Persists each per-cassette replay outcome — a ReplayResult or a typed ReplayRefusal (refusals are\n// records too, never silent; Phase 372's baseline reader consumes them) — under an uncommitted\n// runtime staging root beside `eval/cassettes`:\n//\n// <output-root>/<slugged cassette id segments>/replay.json\n//\n// The store mirrors FsCassetteStore's posture exactly: every id segment delegates to schema's\n// security-reviewed slugBranch sanitizer (path-traversal control, T-368-10), and each record is\n// recreated in full (rm + mkdir) before writing so a re-run leaves no stale files behind (idempotent\n// overwrite). The write is deterministic: the same outcome yields the same path and the same bytes —\n// no timestamps or random ids are minted here; timing data already lives inside the outcome from the\n// engine clock. Records land ONLY under this output root — never knowledge dirs or committed trees;\n// the committed corpus root is Phase 371's leg. Records are written as handed over (441 D-15).\n\nimport { mkdirSync, rmSync, writeFileSync } from 'node:fs'\nimport { join } from 'node:path'\n\nimport type { ReplayCassetteOutcome } from '@vostride/agent-qa-eval'\nimport { slugBranch } from '@vostride/agent-qa-schema'\n\nexport const DEFAULT_EVAL_REPLAYS_SUBDIR = 'eval/replays'\nexport const REPLAY_RECORD_FILE = 'replay.json'\n\nexport interface FsReplayRecordStoreConfig {\n /** Runtime root (normally the workdir's `.agent-qa` directory). */\n root: string\n /** Optional retargeted root used by later committed-corpus composition. */\n outputDir?: string\n /** Actual producer seed; embedded in the bytes bound by the run publication. */\n executionSeed?: number\n}\n\nexport interface WrittenReplayRecord {\n cassetteId: string\n dir: string\n file: string\n}\n\nexport class FsReplayRecordStore {\n private readonly outputRoot: string\n private readonly executionSeed?: number\n\n constructor(config: FsReplayRecordStoreConfig) {\n this.outputRoot = config.outputDir ?? join(config.root, DEFAULT_EVAL_REPLAYS_SUBDIR)\n this.executionSeed = config.executionSeed\n }\n\n save(outcome: ReplayCassetteOutcome): WrittenReplayRecord {\n const dir = join(\n this.outputRoot,\n ...outcome.cassetteId.split('/').map((segment) => slugBranch(segment)),\n )\n rmSync(dir, { recursive: true, force: true })\n mkdirSync(dir, { recursive: true })\n\n const file = join(dir, REPLAY_RECORD_FILE)\n const record =\n this.executionSeed === undefined ? outcome : { ...outcome, executionSeed: this.executionSeed }\n writeFileSync(file, `${JSON.stringify(record, null, 2)}\\n`, 'utf8')\n\n return { cassetteId: outcome.cassetteId, dir, file }\n }\n}\n","// Filesystem score store (Phase 369, D-13).\n//\n// Persists one cassette's matcher verdict — the CassetteScore plus its per-decision D-16 diffs —\n// as `score.json` BESIDE the replay record, in the exact same slugged cassette dir\n// FsReplayRecordStore derives (same output root, same slugBranch-per-segment derivation,\n// T-368-10 path-traversal control):\n//\n// <output-root>/<slugged cassette id segments>/score.json\n//\n// Pitfall-5 deviation from the record store's posture: FsReplayRecordStore recreates its dir in\n// full (rm + mkdir) so a re-run leaves no stale files — copying that here would wipe the sibling\n// replay.json. Score writes are therefore FILE-scoped (mkdir-if-missing + overwrite the one file,\n// never rmSync the dir), and the sanctioned composition orders record-save BEFORE score-save so\n// the record store's dir-recreate can never destroy a fresher score (OQ-2). Bytes stay\n// deterministic: same record ⇒ same path, same bytes — no timestamps or random ids minted here.\n//\n// eval-local serializes only (F9): every score and diff arrives fully computed from eval core\n// (scoreCassetteReplay / buildDecisionDiff); no scoring logic of any kind lives in this file.\n\nimport { mkdirSync, writeFileSync } from 'node:fs'\nimport { join } from 'node:path'\n\nimport type { CassetteScore, DecisionDiff, JudgeIdentityBlock, ScoringContext } from '@vostride/agent-qa-eval'\nimport { slugBranch } from '@vostride/agent-qa-schema'\n\nimport { DEFAULT_EVAL_REPLAYS_SUBDIR } from './replay-record-store.js'\n\nexport const SCORE_FILE = 'score.json'\n\n/** The persisted per-cassette score document — computed entirely in eval core (F9). */\nexport interface ScoreRecord {\n cassetteId: string\n /** The matcher constitution that minted the score (attribution stamp). */\n matcherVersion: number\n score: CassetteScore\n diffs: readonly DecisionDiff[]\n /**\n * D-16 score-side reproducibility receipt: the full identity of the judge that produced any\n * stamp on this score — judge model id, resolved family, judgeVersion, threshold. Computed\n * entirely by eval-core adjudication and serialized verbatim here (F9: eval-local never\n * computes, only writes). Typed-absent when no judge walk ran, keeping 369 records\n * byte-compatible. The cassette's reserved tier4JudgeId is Phase 371 label territory —\n * the scorer never writes back into the measurement.\n */\n judge?: JudgeIdentityBlock\n scoringContext?: ScoringContext\n}\n\nexport interface FsScoreStoreConfig {\n /** Runtime root (normally the workdir's `.agent-qa` directory). */\n root: string\n /** Optional retargeted root used by later committed-corpus composition. */\n outputDir?: string\n}\n\nexport interface WrittenScoreRecord {\n cassetteId: string\n dir: string\n file: string\n}\n\nexport class FsScoreStore {\n private readonly outputRoot: string\n\n constructor(config: FsScoreStoreConfig) {\n // SAME subdir as the record store — the score sits beside its replay.json.\n this.outputRoot = config.outputDir ?? join(config.root, DEFAULT_EVAL_REPLAYS_SUBDIR)\n }\n\n save(record: ScoreRecord): WrittenScoreRecord {\n const dir = join(\n this.outputRoot,\n ...record.cassetteId.split('/').map(segment => slugBranch(segment)),\n )\n // File-scoped write (Pitfall 5): no rmSync dir-recreate — that would wipe the sibling\n // replay.json. Idempotent overwrite of the one file only.\n mkdirSync(dir, { recursive: true })\n\n const file = join(dir, SCORE_FILE)\n writeFileSync(file, `${JSON.stringify(record, null, 2)}\\n`, 'utf8')\n\n return { cassetteId: record.cassetteId, dir, file }\n }\n}\n","import { closeSync, existsSync, fsyncSync, openSync, readFileSync, renameSync, unlinkSync, writeFileSync } from 'node:fs'\nimport { join } from 'node:path'\n\nimport { canonicalJson } from '@vostride/agent-qa-eval'\nimport { HANDOFF_FILE, RUNS_SUBDIR, slugBranch } from '@vostride/agent-qa-runner-local'\nimport { ImprovementPlanSchema, MarkReceiptSchema, markAddressKey, type MarkAddress, type MarkReceipt } from '@vostride/agent-qa-schema'\nimport { z } from 'zod'\nimport { acquireMarkLock } from './mark-lock.js'\n\nexport const MARKS_LEDGER_FILE = 'marks.jsonl'\nexport const MARKS_LEDGER_LOCK_FILE = 'marks.jsonl.lock'\n/** A run's one unfinished mark, beside its ledger. */\nexport const MARK_INTENT_FILE = 'mark-intent.json'\n/**\n * 2026-09-30, 432-09 (D-04/D-11): the resumable mark. `operationId` is the stable identity every\n * effect of this mark carries (the knowledge change id derives from it), so a retry at any stage\n * lands the same fix once. `applied` is the last stage: the fix is published and the receipt is\n * next, and only the receipt remains to be written. The applied change itself rides the mark's\n * own disposition, so there is one record of what changed.\n */\nexport const MARK_INTENT_STAGES = ['prepared', 'evidence', 'applied'] as const\nconst intentSchema = z.object({\n schemaVersion: z.literal(2), mark: MarkReceiptSchema,\n stage: z.enum(MARK_INTENT_STAGES),\n operationId: z.uuid(),\n plan: ImprovementPlanSchema.optional(),\n}).strict().superRefine((intent, ctx) => {\n if (intent.stage === 'applied' && intent.mark.disposition.kind === 'drafted') {\n ctx.addIssue({ code: 'custom', path: ['mark', 'disposition'], message: 'a mark applies its fix; it never files a draft' })\n }\n})\nexport type MarkIntent = z.infer<typeof intentSchema>\n\nfunction atomicPublish(path: string, text: string): void {\n const staged = `${path}.pending`\n const fd = openSync(staged, 'w', 0o600)\n try { writeFileSync(fd, text); fsyncSync(fd) } finally { closeSync(fd) }\n renameSync(staged, path)\n}\n\nexport class MarkLedgerBusyError extends Error {\n constructor() {\n super('Mark ledger is busy; retry the mark after the current transaction finishes')\n this.name = 'MarkLedgerBusyError'\n }\n}\n\nexport class MarkLedgerCorruptError extends Error {\n constructor(readonly line: number) {\n // Never echo malformed user text or parser diagnostics into a host surface.\n super(`Mark ledger is corrupt at line ${line}`)\n this.name = 'MarkLedgerCorruptError'\n }\n}\n\n/**\n * 2026-10-03 (quick 261003-lgd, Phase 432 review IN-23): the run's unfinished-mark record cannot be\n * resumed, refused by the record's own name. It used to surface as `MarkLedgerCorruptError` at the\n * NEXT ordinal, a line `marks.jsonl` does not have. D-02: nothing is migrated or read further; the\n * message names the file to remove, after which the run can be marked again.\n */\nexport class MarkIntentUnusableError extends Error {\n constructor(\n readonly intentPath: string,\n readonly reason: 'earlier-version' | 'unreadable' | 'mismatched',\n ) {\n const what = reason === 'earlier-version'\n ? 'was written by an earlier agent-qa version, which this version does not read'\n : reason === 'unreadable' ? 'is unreadable' : 'does not match this run\\'s mark ledger'\n // Never echo the record's bytes or parser diagnostics into a host surface.\n super(`The unfinished-mark record ${intentPath} ${what}; nothing was marked. Remove that file, `\n + 'then mark again.')\n this.name = 'MarkIntentUnusableError'\n }\n}\n\n/** Keys only a pre-432 intent carried: a filed proposal, the drafted stage, a stored test draft. */\nfunction isEarlierVersionIntent(value: unknown): boolean {\n if (value === null || typeof value !== 'object') return false\n const intent = value as Record<string, unknown>\n return 'proposalUuid' in intent || 'testDraft' in intent || intent['stage'] === 'drafted'\n}\n\n/** The one latest-per-address rule, over receipts a caller has already read once. */\nexport function latestMarkReceiptsPerAddress(receipts: readonly MarkReceipt[]): MarkReceipt[] {\n const latest = new Map<string, MarkReceipt>()\n for (const receipt of receipts) {\n const key = markAddressKey(receipt)\n if ((latest.get(key)?.ordinal ?? 0) < receipt.ordinal) latest.set(key, receipt)\n }\n return [...latest.values()].sort((a, b) =>\n a.testIndex - b.testIndex || (a.stepIndex ?? -1) - (b.stepIndex ?? -1),\n )\n}\n\nexport function latestMarkReceiptFor(\n receipts: readonly MarkReceipt[],\n runId: string,\n address: MarkAddress,\n): MarkReceipt | undefined {\n if (address.runId !== runId) return undefined\n return latestMarkReceiptsPerAddress(receipts).find(receipt => markAddressKey(receipt) === markAddressKey(address)\n && (address.testId === undefined || receipt.testId === address.testId))\n}\n\nexport class MarkLedgerNoRunError extends Error {\n constructor() {\n super('run-not-found: mark ledger requires a completed run handoff')\n this.name = 'MarkLedgerNoRunError'\n }\n}\n\n/** Gate-side runtime ledger. The proposer reader entry never exports this store. */\nexport class FsMarkLedger {\n private readonly dir: string\n private readonly file: string\n private readonly lock: string\n private readonly runId: string\n private readonly runSlug: string\n private inTransaction = false\n private intentValue: MarkIntent | undefined\n\n constructor(config: { root: string; runId: string }) {\n this.runId = config.runId\n this.runSlug = slugBranch(config.runId)\n this.dir = join(config.root, RUNS_SUBDIR, this.runSlug)\n this.file = join(this.dir, MARKS_LEDGER_FILE)\n this.lock = join(this.dir, MARKS_LEDGER_LOCK_FILE)\n }\n\n read(): MarkReceipt[] {\n let text: string\n try {\n text = readFileSync(this.file, 'utf8')\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') return []\n throw error\n }\n if (text === '') return []\n const lines = text.split('\\n')\n // A missing terminator may be a torn append; concatenating another receipt would corrupt it.\n if (lines.pop() !== '') throw new MarkLedgerCorruptError(lines.length + 1)\n return lines.map((line, index) => {\n try {\n const receipt = MarkReceiptSchema.parse(JSON.parse(line))\n if (receipt.ordinal !== index + 1 || receipt.runId !== this.runId || receipt.runSlug !== this.runSlug) {\n throw new Error('ledger identity or ordinal mismatch')\n }\n return receipt\n } catch {\n throw new MarkLedgerCorruptError(index + 1)\n }\n })\n }\n\n latestPerAddress(): MarkReceipt[] {\n return latestMarkReceiptsPerAddress(this.read())\n }\n\n latestFor(address: MarkAddress): MarkReceipt | undefined {\n // A foreign address never reads this run's ledger.\n if (address.runId !== this.runId) return undefined\n return latestMarkReceiptFor(this.read(), this.runId, address)\n }\n\n /** Private resumable state is admitted only while this run's existing lock is held. */\n prepareIntent(mark: MarkReceipt, operationId: string): MarkIntent {\n if (!this.inTransaction) throw new Error('mark intent requires the run transaction')\n const prior = this.intentValue\n if (prior !== undefined) {\n const request = (value: MarkReceipt) => ({ runId: value.runId, testIndex: value.testIndex,\n testId: value.testId, stepIndex: value.stepIndex, kind: value.kind, note: value.note,\n correctedExpectation: value.correctedExpectation, correctedAction: value.correctedAction })\n if (canonicalJson(request(prior.mark)) !== canonicalJson(request(mark))) {\n throw new Error('An unfinished mark must be retried with its original address and correction')\n }\n return prior\n }\n return this.checkpointIntent({ schemaVersion: 2, mark, operationId, stage: 'prepared' })\n }\n\n checkpointIntent(value: MarkIntent): MarkIntent {\n if (!this.inTransaction) throw new Error('mark intent requires the run transaction')\n const intent = intentSchema.parse(value)\n if (intent.mark.runId !== this.runId || intent.mark.runSlug !== this.runSlug\n || intent.mark.ordinal !== this.read().length + 1) throw new MarkLedgerCorruptError(intent.mark.ordinal)\n atomicPublish(join(this.dir, MARK_INTENT_FILE), `${canonicalJson(intent)}\\n`)\n this.intentValue = intent\n return intent\n }\n\n async transact(\n fn: (nextOrdinal: number) => Promise<Omit<MarkReceipt, 'ordinal'> | undefined>,\n ): Promise<MarkReceipt | undefined> {\n if (!existsSync(join(this.dir, HANDOFF_FILE))) throw new MarkLedgerNoRunError()\n const release = await acquireMarkLock(this.lock, this.runId, () => new MarkLedgerBusyError())\n try {\n // Freeze and draft inside this callback: the next host sees the entire prior receipt.\n const receipts = this.read()\n const nextOrdinal = receipts.length + 1\n const intentPath = join(this.dir, MARK_INTENT_FILE)\n this.intentValue = undefined\n if (existsSync(intentPath)) {\n let raw: unknown\n try {\n raw = JSON.parse(readFileSync(intentPath, 'utf8'))\n } catch { throw new MarkIntentUnusableError(intentPath, 'unreadable') }\n const parsed = intentSchema.safeParse(raw)\n if (!parsed.success) {\n throw new MarkIntentUnusableError(intentPath,\n isEarlierVersionIntent(raw) ? 'earlier-version' : 'unreadable')\n }\n const intent = parsed.data\n if (intent.mark.runId !== this.runId || intent.mark.runSlug !== this.runSlug) {\n throw new MarkIntentUnusableError(intentPath, 'mismatched')\n }\n if (intent.mark.ordinal === nextOrdinal) this.intentValue = intent\n else if (canonicalJson(receipts[intent.mark.ordinal - 1]) === canonicalJson(intent.mark)) unlinkSync(intentPath)\n else throw new MarkIntentUnusableError(intentPath, 'mismatched')\n }\n this.inTransaction = true\n const record = await fn(nextOrdinal)\n if (record === undefined) return undefined\n const receipt = MarkReceiptSchema.parse({ ...record, ordinal: nextOrdinal })\n if (receipt.runId !== this.runId || receipt.runSlug !== this.runSlug) {\n throw new Error('mark receipt does not belong to this run ledger')\n }\n if (this.intentValue !== undefined && (this.intentValue.stage !== 'applied'\n || canonicalJson(this.intentValue.mark) !== canonicalJson(receipt))) {\n throw new MarkLedgerCorruptError(nextOrdinal)\n }\n // Preserve the exact historical prefix while atomically publishing the next complete line.\n const before = existsSync(this.file) ? readFileSync(this.file, 'utf8') : ''\n atomicPublish(this.file, `${before}${canonicalJson(receipt)}\\n`)\n if (this.intentValue !== undefined) unlinkSync(intentPath)\n return receipt\n } finally {\n this.inTransaction = false\n this.intentValue = undefined\n release()\n }\n }\n\n append(record: Omit<MarkReceipt, 'ordinal'>): Promise<MarkReceipt | undefined> {\n return this.transact(async () => record)\n }\n}\n","import { randomUUID } from 'node:crypto'\nimport { closeSync, fsyncSync, lstatSync, mkdirSync, openSync, readFileSync, renameSync, rmSync, writeFileSync } from 'node:fs'\nimport { dirname, join } from 'node:path'\nimport { z } from 'zod'\n\nconst ownerSchema = z.object({ schemaVersion: z.literal(2), kind: z.literal('mark-ledger-lock'),\n pid: z.number().int().positive(), token: z.uuid(), runId: z.string().min(1) }).strict()\ntype Owner = z.infer<typeof ownerSchema>\n\nfunction syncDirectory(path: string): void {\n const fd = openSync(path, 'r')\n try { fsyncSync(fd) } finally { closeSync(fd) }\n}\n\nfunction readOwner(path: string, runId: string): Owner | undefined {\n try {\n const directory = lstatSync(path)\n if (!directory.isDirectory() || directory.isSymbolicLink()) return undefined\n const ownerFile = join(path, 'owner.json')\n const file = lstatSync(ownerFile)\n if (!file.isFile() || file.isSymbolicLink()) return undefined\n const parsed = ownerSchema.safeParse(JSON.parse(readFileSync(ownerFile, 'utf8')))\n return parsed.success && parsed.data.runId === runId ? parsed.data : undefined\n } catch { return undefined }\n}\n\nfunction positivelyDead(pid: number): boolean {\n try { process.kill(pid, 0); return false }\n catch (error) { return (error as NodeJS.ErrnoException).code === 'ESRCH' }\n}\n\n/** Complete owner publication; malformed, legacy and uncertain owners are never reaped. */\nexport async function acquireMarkLock(lock: string, runId: string, busy: () => Error): Promise<() => void> {\n const owner: Owner = { schemaVersion: 2, kind: 'mark-ledger-lock', pid: process.pid, token: randomUUID(), runId }\n const candidate = `${lock}.candidate-${owner.token}`\n mkdirSync(candidate)\n const fd = openSync(join(candidate, 'owner.json'), 'wx', 0o600)\n try { writeFileSync(fd, JSON.stringify(owner)); fsyncSync(fd) } finally { closeSync(fd) }\n syncDirectory(candidate)\n const started = Date.now()\n let acquired = false\n try {\n while (!acquired) {\n try {\n // mkdir is create-only even against an empty legacy/partial directory. Publish the\n // complete owner immediately afterward; death in this tiny gap remains busy by law.\n mkdirSync(lock)\n renameSync(join(candidate, 'owner.json'), join(lock, 'owner.json'))\n syncDirectory(lock)\n syncDirectory(dirname(lock))\n rmSync(candidate, { recursive: true })\n acquired = true\n } catch (error) {\n if (!['EEXIST', 'ENOTEMPTY', 'ENOTDIR', 'EISDIR', 'EPERM', 'EACCES'].includes((error as NodeJS.ErrnoException).code ?? '')) throw error\n const current = readOwner(lock, runId)\n if (current !== undefined && positivelyDead(current.pid)) {\n try {\n // Retain this NONEMPTY generation tombstone. A delayed reclaimer of G cannot\n // rename a newer live H over dead-G after another contender already moved G.\n renameSync(lock, `${lock}.dead-${current.token}`)\n syncDirectory(dirname(lock))\n continue\n } catch (recoveryError) {\n if ((recoveryError as NodeJS.ErrnoException).code === 'ENOENT') continue\n if (!['EEXIST', 'ENOTEMPTY', 'ENOTDIR', 'EISDIR', 'EPERM', 'EACCES'].includes((recoveryError as NodeJS.ErrnoException).code ?? '')) throw recoveryError\n }\n }\n const remaining = 10_000 - (Date.now() - started)\n if (remaining <= 0) throw busy()\n await new Promise(resolve => setTimeout(resolve, Math.min(50, remaining)))\n }\n }\n } finally {\n if (!acquired) rmSync(candidate, { recursive: true, force: true })\n }\n return () => {\n if (readOwner(lock, runId)?.token !== owner.token) return\n const released = `${lock}.released-${owner.token}`\n renameSync(lock, released)\n syncDirectory(dirname(lock))\n rmSync(released, { recursive: true })\n syncDirectory(dirname(lock))\n }\n}\n","import { lstatSync, readdirSync, readFileSync } from 'node:fs'\nimport { dirname, join, relative, resolve } from 'node:path'\nimport { z } from 'zod'\nimport { canonicalJson, deriveLabelRevisionId, parseStandaloneLabelRecord, selectEffectiveLabels, type LabelCalibrationReport, type StandaloneLabelRecord } from '@vostride/agent-qa-eval'\nimport { ORIGIN_PRIORS, sha256hex } from '@vostride/agent-qa-schema'\nimport { committedLabelPath } from './committed-root-store.js'\n\nexport const LABEL_CALIBRATION_PATH = 'labels/calibration.json'\nexport const LABEL_CALIBRATION_SCHEMA_VERSION = 2\nexport interface LabelCalibrationArtifact extends Omit<LabelCalibrationReport, 'minGold'> {\n schemaVersion: typeof LABEL_CALIBRATION_SCHEMA_VERSION\n minGold: { value: number; source: 'default' | 'cli' }\n evidenceDigest: string\n generationId: string\n targetRevisions: readonly { cassetteId: string; revisionId: string }[]\n}\nexport type LabelCalibrationRead =\n | { kind: 'absent' }\n | { kind: 'refused'; reason: string; targetCassetteIds?: readonly string[] }\n | { kind: 'loaded'; artifact: LabelCalibrationArtifact; authorizedRevisionIds: ReadonlySet<string> }\n\nconst count = z.number().int().min(0).max(Number.MAX_SAFE_INTEGER)\nconst unit = z.number().finite().min(0).max(1)\nconst digest = z.string().regex(/^[0-9a-f]{64}$/)\nconst noisy = z.union([\n z.object({ status: z.literal('uncalibrated'), n: count, n_gold: count }).strict(),\n z.object({ status: z.literal('calibrated'), n: count, n_gold: count, precision: unit,\n wilson95: z.object({ lower: unit, upper: unit, halfWidth: unit }).strict()\n .refine(v => v.lower <= v.upper),\n }).strict(),\n])\nconst gold = z.object({ n_gold: count, precision: z.literal('by-construction') }).strict()\nconst prior = z.object({ alpha: z.number().finite().min(0), beta: z.number().finite().min(0) }).strict()\n .refine(v => Number.isFinite(v.alpha + v.beta) && v.alpha + v.beta > 0)\nconst rate = z.object({ numerator: count, denominator: count, rate: unit.optional() }).strict()\n .refine(v => v.numerator <= v.denominator && (v.denominator === 0 ? v.rate === undefined : v.rate === v.numerator / v.denominator))\nconst accuracy = z.object({ n: count, correct: count, accuracy: unit.optional() }).strict()\n .refine(v => v.correct <= v.n && (v.n === 0 ? v.accuracy === undefined : v.accuracy === v.correct / v.n))\nconst artifactSchema = z.object({\n schemaVersion: z.literal(LABEL_CALIBRATION_SCHEMA_VERSION), generationId: digest, evidenceDigest: digest,\n corpusDigest: digest, goldSetDigest: digest,\n minGold: z.object({ value: count.min(2), source: z.enum(['default', 'cli']) }).strict(),\n channels: z.object({ 'recorded-verdict': noisy, 'self-heal-pair': noisy, 'user-mark': gold, 'overturned-verdict': gold }).strict(),\n priors: z.object({ 'agent-verified': prior.optional(), 'agent-observed': prior.optional() }).strict(),\n unchangedOrigins: z.tuple([z.literal('user'), z.literal('import')]),\n verifierFalsePassRate: rate, verifierFalseFailRate: rate,\n rootCauseClassifierAccuracy: z.object({ 'analyze-run': accuracy, 'deterministic-classifier': accuracy }).strict(),\n rootCauseClassifierGrain: z.literal('member/producer:app_bug-binary'),\n skippedCounts: z.object({ 'missing-source': count.optional(), 'invalid-gold': count.optional(), 'conflicting-truth': count.optional(), 'missing-verdict': count.optional(), 'missing-pairing': count.optional(), 'conflicting-observation': count.optional() }).strict(),\n targetRevisions: z.array(z.object({ cassetteId: z.string().min(1), revisionId: digest }).strict()),\n}).strict()\n\n/** Refuse symlinks at every existing segment, including a dangling leaf; never follow one. */\nexport function assertCalibrationPath(root: string, path: string): void {\n const base = resolve(root), full = resolve(path), rel = relative(base, full)\n if (rel === '..' || rel.startsWith('../') || rel.startsWith('/')) throw new Error('invalid calibration path')\n let cursor = full\n while (true) {\n try { if (lstatSync(cursor).isSymbolicLink()) throw new Error('invalid calibration path') }\n catch (error) { if ((error as NodeJS.ErrnoException).code !== 'ENOENT') throw error }\n if (cursor === base) break\n const parent = dirname(cursor)\n if (parent === cursor) break\n cursor = parent\n }\n}\n\nexport function calibrationGenerationId(body: Omit<LabelCalibrationArtifact, 'generationId'>): string {\n return sha256hex(canonicalJson(body))\n}\n\n/** Structural checks plus consistency between minimum, measured channels and emitted priors. */\nexport function guardLabelCalibration(value: unknown): LabelCalibrationArtifact | undefined {\n const parsed = artifactSchema.safeParse(value)\n if (!parsed.success) return undefined\n const artifact = parsed.data\n const { generationId, ...body } = artifact\n if (calibrationGenerationId(body) !== generationId) return undefined\n let previous: string | undefined\n for (const target of artifact.targetRevisions) {\n if (previous !== undefined && previous >= target.cassetteId) return undefined\n previous = target.cassetteId\n }\n for (const [channel, origin] of [['recorded-verdict', 'agent-verified'], ['self-heal-pair', 'agent-observed']] as const) {\n const row = artifact.channels[channel], emitted = artifact.priors[origin]\n if (row.n !== row.n_gold || (row.status === 'calibrated') !== (row.n >= artifact.minGold.value)) return undefined\n if (row.status === 'uncalibrated') { if (emitted !== undefined) return undefined }\n else {\n const mass = ORIGIN_PRIORS[origin].alpha + ORIGIN_PRIORS[origin].beta\n if (!emitted || emitted.alpha !== row.precision * mass || emitted.beta !== (1 - row.precision) * mass) return undefined\n }\n }\n return artifact\n}\n\n/** Independent marker loading: never calls the corpus reader and never grants a whole generation. */\nexport function readLabelCalibration(committedRoot: string): LabelCalibrationRead {\n const marker = join(committedRoot, LABEL_CALIBRATION_PATH)\n let targetCassetteIds: string[] | undefined\n try {\n assertCalibrationPath(committedRoot, marker)\n try { lstatSync(marker) } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') return { kind: 'absent' }\n throw error\n }\n const artifact = guardLabelCalibration(JSON.parse(readFileSync(marker, 'utf8')))\n if (!artifact) return { kind: 'refused', reason: 'invalid label calibration artifact' }\n targetCassetteIds = artifact.targetRevisions.map(target => target.cassetteId)\n const records = new Map<string, StandaloneLabelRecord>()\n function walk(directory: string): void {\n assertCalibrationPath(committedRoot, directory)\n for (const entry of readdirSync(directory, { withFileTypes: true })) {\n const path = join(directory, entry.name)\n if (entry.isDirectory()) { walk(path); continue }\n if (!entry.isFile() || !entry.name.endsWith('.json') || path === marker) continue\n let parsed\n try { parsed = parseStandaloneLabelRecord(JSON.parse(readFileSync(path, 'utf8'))) } catch { continue }\n if (!parsed.ok) continue\n const record = parsed.record, product = relative(join(committedRoot, 'labels'), path).split('/')[0]!\n const expected = join(committedRoot, committedLabelPath(product, record.cassetteId))\n .replace(/\\.json$/, record.revision ? `--revision-${record.revision.id}.json` : '.json')\n if (expected !== path) continue\n const id = record.revision?.id ?? deriveLabelRevisionId(record)\n if (records.has(id)) throw new Error('duplicate label identity')\n records.set(id, record)\n }\n }\n walk(join(committedRoot, 'labels'))\n const authorizedRevisionIds = new Set<string>()\n for (const target of artifact.targetRevisions) {\n const chain: StandaloneLabelRecord[] = [], visited = new Set<string>(), generations = new Set<string>()\n let id: string | undefined = target.revisionId\n while (id !== undefined) {\n const record = records.get(id)\n if (!record || record.cassetteId !== target.cassetteId || visited.has(id)) throw new Error('incomplete label chain')\n visited.add(id); chain.push(record)\n if (record.revision?.generationId) generations.add(record.revision.generationId)\n id = record.revision?.predecessorId\n }\n const selected = selectEffectiveLabels(chain, { committedGenerationIds: generations })\n const terminal = selected.effective.get(target.cassetteId)\n if (selected.refusals.length || !terminal || (terminal.revision?.id ?? deriveLabelRevisionId(terminal)) !== target.revisionId) throw new Error('invalid label chain')\n for (const id of visited) authorizedRevisionIds.add(id)\n }\n return { kind: 'loaded', artifact, authorizedRevisionIds }\n } catch { return { kind: 'refused', reason: 'label calibration or target history unavailable', targetCassetteIds } }\n}\n","import { z } from 'zod'\nimport {\n ADVERSARIAL_MUTATORS,\n GENERATOR_KINDS,\n GENERATOR_PROVENANCES,\n} from '@vostride/agent-qa-eval'\nimport type { EvalBatchReport } from '@vostride/agent-qa-eval'\n\nconst count = z.number().int().nonnegative().safe()\nconst amount = z.number().finite().nonnegative()\nconst unit = z.number().finite().min(0).max(1)\nconst rate = z.object({ numerator: count, denominator: count, rate: unit.optional() })\n .passthrough().refine(row => row.numerator <= row.denominator &&\n (row.denominator === 0 ? row.rate === undefined : row.rate === row.numerator / row.denominator))\nconst recovery = z.object({ weightSum: z.number().finite().nonnegative(),\n pairsWithoutWeight: count, weightedAccuracy: unit.optional(),\n selfHealPairCount: count, recoveredPositiveCount: count, eligiblePositiveCount: count,\n recoveryAccuracy: unit.optional(), negativeDecisionCount: count, negativeFullMatchCount: count,\n}).passthrough()\nconst cell = z.object({ firstAttemptDecisionAccuracy: rate, verifierFalsePassRate: rate,\n recovery, perDecisionCost: z.object({ decisionCount: count, promptTokens: amount,\n completionTokens: amount, totalTokens: amount, durationMs: amount }).passthrough(),\n}).passthrough()\n/**\n * 2026-09-12, 422 D-05/D-08: the stored generation mix, validated STRICTLY and additively.\n *\n * A closed-set group is `z.record` keyed on the shipped vocabulary rather than a free string map,\n * so a mutator or provenance arm read off disk that this build does not know is a REFUSAL and not\n * a value that passes through into a disclosure a reader trusts. The open-set groups (table\n * generation, prompt version, authoring model and family) cannot be enumerated — nobody can list\n * the generations that do not exist — so those keep a string key and validate only their counts.\n */\nconst closedCounts = <T extends string>(values: readonly [T, ...T[]]) =>\n z.record(z.enum(values), count)\nconst openCounts = z.record(z.string(), count)\nconst generationMix = z.object({\n total: count,\n metadataUnavailable: count,\n byProvenance: closedCounts(GENERATOR_PROVENANCES),\n byKind: closedCounts(GENERATOR_KINDS),\n byMutator: closedCounts(ADVERSARIAL_MUTATORS),\n byTableVersion: openCounts,\n tableHashesByVersion: z.record(z.string(), z.array(z.string())),\n byPromptVersion: openCounts,\n byAuthoringModel: openCounts,\n byAuthoringFamily: openCounts,\n}).strict()\n\nconst current = z.object({\n // Additive-optional: absent is the pre-422 shape and stays admissible forever.\n adversarialGenerationMix: generationMix.optional(),\n headline: z.object({ verifierFalsePassRate: rate }).passthrough(),\n recovery,\n calibration: z.discriminatedUnion('status', [\n z.object({ status: z.literal('uncalibrated'), n_gold: count.optional(), verifierFalsePassRate: rate.optional() }).passthrough(),\n z.object({ status: z.literal('calibrated'), n_gold: count, verifierFalsePassRate: rate }).passthrough(),\n ]),\n perSplit: z.object({ natural: cell, 'held-out': z.object({ shared: cell, private: cell }).passthrough(),\n adversarial: cell, unassignedCount: count }).passthrough(),\n}).passthrough()\n\n/** Read boundary only: a partially present addition is never evidence of a legacy format. */\nexport function reportMetadataKind(value: unknown): 'legacy' | 'current' | 'invalid' {\n if (value === null || typeof value !== 'object') return 'invalid'\n const report = value as Record<string, unknown>\n const has = (object: unknown, key: string) => object !== null && typeof object === 'object'\n && Object.hasOwn(object, key)\n // 422: a report carrying the generation mix is certainly not a legacy report, so the mix joins\n // the addition set rather than being ignored — bytes naming it but missing `perSplit` are\n // `invalid`, which is the same \"partially present is never evidence of legacy\" rule as above.\n const additions = has(report, 'calibration') || has(report, 'perSplit')\n || has(report, 'adversarialGenerationMix')\n || has(report.headline, 'verifierFalsePassRate') || has(report.recovery, 'weightSum')\n || has(report.recovery, 'pairsWithoutWeight') || has(report.recovery, 'weightedAccuracy')\n return !additions ? 'legacy' : current.safeParse(value).success ? 'current' : 'invalid'\n}\n\n/**\n * Strip the six named Phase420 additions and 422's generation mix from a fresh comparison view;\n * never saved bytes. The mix joins for exactly the reason the six did: a legacy report predates\n * the field, so leaving it on the candidate would make every legacy report stop reproducing from\n * its own retained records the moment the field shipped.\n */\nexport function legacyReportComparison(report: EvalBatchReport): Record<string, unknown> {\n const {\n calibration: _calibration,\n perSplit: _perSplit,\n adversarialGenerationMix: _generationMix,\n ...historical\n } = report\n const { verifierFalsePassRate: _falsePass, ...headline } = report.headline\n const { weightSum: _weightSum, pairsWithoutWeight: _missing, weightedAccuracy: _weighted, ...recovery } = report.recovery\n return { ...historical, headline, recovery }\n}\n","// Filesystem batch-report store (Phase 369, D-13/D-14).\n//\n// Persists one aggregated EvalBatchReport as its own artifact under the uncommitted runtime\n// staging root:\n//\n// <output-root>/eval/reports/<slugged batchKey>.json\n//\n// The filename derives ONLY from report.batchKey — eval core's deterministic replay-batch key\n// (sha256 over the canonical sorted cassette-id list, D-13) — through schema's slugBranch\n// sanitizer (T-368-10 path-traversal control). No timestamps or random components are minted\n// in the published identity, so a rerun of the same batch replaces the same file and a\n// different batch lands beside it. Bytes are deterministic: same report ⇒ same path, same bytes.\n// 415 D-16 adds an optional .decisions.json sidecar. A staging-only publication manifest\n// binds both file digests before either replacement; interrupted or interleaved generations\n// refuse at the reader. Legacy evidence without a manifest remains readable unchanged.\n//\n// `.agent-qa/eval/reports/` joins `eval/replays/` as UNCOMMITTED runtime staging: committed\n// placement (the corpus root with fail-closed egress) is Phase 371/372's concern; Phase 372's\n// baseline reads reports from exactly this path.\n//\n// eval-local serializes only (F9): the report arrives fully aggregated from eval core's\n// buildEvalReport — no aggregation or scoring logic lives in this file.\n\nimport { mkdirSync, mkdtempSync, readFileSync, renameSync, rmdirSync, unlinkSync, writeFileSync } from 'node:fs'\nimport { basename, dirname, join } from 'node:path'\n\nimport { guardDecisionVectorsDocument, judgeIdentitySchema } from '@vostride/agent-qa-eval'\nimport type { DecisionVectorsDocument, EvalBatchReport } from '@vostride/agent-qa-eval'\nimport { sha256hex, slugBranch } from '@vostride/agent-qa-schema'\nimport { z } from 'zod'\nimport { reportMetadataKind } from './report-metadata-guard.js'\n\nexport const DEFAULT_EVAL_REPORTS_SUBDIR = 'eval/reports'\n\nexport interface FsReportStoreConfig {\n /** Runtime root (normally the workdir's `.agent-qa` directory). */\n root: string\n /** Optional retargeted root used by later committed-corpus composition. */\n outputDir?: string\n}\n\nexport interface WrittenReport {\n batchKey: string\n file: string\n decisionsFile?: string\n}\n\nexport type StagedReportLoad =\n | {\n kind: 'loaded'\n report: EvalBatchReport\n decisions?: DecisionVectorsDocument\n file: string\n replayGeneration?: ReplayGeneration\n }\n | { kind: 'absent'; file: string }\n | { kind: 'refused'; file: string; reason: string }\n\nconst calibrationCount = z.number().int().nonnegative().safe()\nconst calibrationUnit = z.number().finite().min(0).max(1)\nconst copiedInterval = z.object({ lower: calibrationUnit, upper: calibrationUnit,\n halfWidth: calibrationUnit.max(0.5) }).strict().refine(value => value.lower <= value.upper\n && value.upper - value.lower <= 2 * value.halfWidth + Number.EPSILON * 4)\n// Check possible integer agreement counts only; never replace the copied estimate or interval.\nconst consistentAgreement = (n: number, agreement: number) => agreement === Math.round(n * agreement) / n\nconst copiedMember = z.object({ judgeModelId: z.string(), n: calibrationCount,\n agreement: calibrationUnit.optional(), wilson95: copiedInterval.optional() }).strict()\n .refine(value => value.n === 0 ? value.agreement === undefined && value.wilson95 === undefined\n : value.agreement !== undefined && value.wilson95 !== undefined && consistentAgreement(value.n, value.agreement))\nconst copiedCalibration = z.discriminatedUnion('status', [\n z.object({ status: z.literal('calibrated'), n: calibrationCount.min(2),\n pooledAgreement: calibrationUnit, wilson95: copiedInterval,\n perMember: z.array(copiedMember).min(2).max(3) }).strict()\n .refine(value => consistentAgreement(value.n, value.pooledAgreement)),\n z.object({ status: z.literal('uncalibrated'), n: calibrationCount,\n provenance: z.literal('unavailable').optional() }).strict()\n .refine(value => value.provenance === undefined || value.n === 0),\n z.object({ status: z.literal('capability-gap'),\n reason: z.enum(['panel-not-configured', 'no-credential', 'single-family-host']) }).strict(),\n])\n\n/** Independent report admission preserves copied precision and never reads calibration storage. */\nexport const reportJudgeSchema = judgeIdentitySchema.safeExtend({ calibration: copiedCalibration.optional() })\n .refine(judge => {\n const calibration = judge.calibration\n if (calibration === undefined) return true\n if (judge.panelSize === undefined || judge.members === undefined) return false\n if (calibration.status !== 'calibrated') return true\n const memberIds = calibration.perMember.map(member => member.judgeModelId).sort()\n return calibration.perMember.length === judge.members.length\n && memberIds.every((id, index) => id === judge.members![index]!.judgeModelId)\n }, 'calibration requires the complete recorded composite identity')\n\n// A light read boundary, not a format migration. Passthrough at every checked object\n// retains the full report: seed evidence, honesty, judge provenance, costs and future fields.\nconst stagedReportSchema = z.object({\n batchKey: z.string().min(1),\n matcherVersion: z.number().int().nonnegative(),\n headline: z.object({\n perDecisionCost: z.object({ decisionCount: z.number() }).passthrough(),\n }).passthrough(),\n // Optional historical provenance remains optional; malformed mixed identities must never\n // reach the comparator's renderer as though its judge-id list were a string array.\n judgeIdentity: z.literal('mixed').optional(),\n mixedJudgeModelIds: z.array(z.string()).optional(),\n judge: reportJudgeSchema.optional(),\n}).passthrough().refine(report => report.judgeIdentity !== 'mixed' || report.judge === undefined)\n\nconst replayGenerationSchema = z\n .object({\n seed: z.number().int().safe(),\n records: z.array(\n z\n .object({\n cassetteId: z.string().min(1),\n replayDigest: z.string().regex(/^[a-f0-9]{64}$/),\n scoreDigest: z\n .string()\n .regex(/^[a-f0-9]{64}$/)\n .nullable(),\n })\n .strict(),\n ),\n })\n .strict()\nexport type ReplayGeneration = z.infer<typeof replayGenerationSchema>\n\nconst publicationSchema = z.object({\n schemaVersion: z.literal(2),\n reportDigest: z.string().regex(/^[a-f0-9]{64}$/),\n decisionsDigest: z.string().regex(/^[a-f0-9]{64}$/).nullable(),\n replayGeneration: replayGenerationSchema.optional(),\n}).strict()\n\nfunction readOptional(file: string): string | undefined {\n try {\n return readFileSync(file, 'utf8')\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') return undefined\n throw error\n }\n}\n\nfunction removeOptional(file: string): void {\n try {\n unlinkSync(file)\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== 'ENOENT') throw error\n }\n}\n\n/** Probe exactly one directory; callers own the gate-root-first search order. */\nexport function loadStagedReport(\n root: string,\n batchKey: string,\n outputDir?: string,\n): StagedReportLoad {\n const outputRoot = outputDir ?? join(root, DEFAULT_EVAL_REPORTS_SUBDIR)\n const stem = slugBranch(batchKey)\n const file = join(outputRoot, `${stem}.json`)\n const publicationFile = join(outputRoot, `${stem}.publication.json`)\n let publicationText: string | undefined\n try {\n publicationText = readOptional(publicationFile)\n } catch (error) {\n return { kind: 'refused', file: publicationFile, reason: `publication unreadable: ${(error as Error).message}` }\n }\n let reportText: string\n let value: unknown\n try {\n reportText = readFileSync(file, 'utf8')\n value = JSON.parse(reportText)\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') return { kind: 'absent', file }\n return { kind: 'refused', file, reason: `report unreadable: ${(error as Error).message}` }\n }\n const parsed = stagedReportSchema.safeParse(value)\n if (!parsed.success) {\n return { kind: 'refused', file, reason: `report rejected: ${parsed.error.message}` }\n }\n if (reportMetadataKind(value) === 'invalid') {\n return { kind: 'refused', file, reason: 'report label metadata incomplete or malformed' }\n }\n if (parsed.data.batchKey !== batchKey) {\n return { kind: 'refused', file, reason: 'report batchKey does not match the requested batchKey' }\n }\n const report = parsed.data as unknown as EvalBatchReport\n const decisionsFile = join(outputRoot, `${stem}.decisions.json`)\n let decisionsText: string | undefined\n let decisionsValue: unknown\n try {\n decisionsText = readOptional(decisionsFile)\n decisionsValue = decisionsText === undefined ? undefined : JSON.parse(decisionsText)\n } catch (error) {\n return {\n kind: 'refused', file: decisionsFile,\n reason: `decisions unreadable: ${(error as Error).message}`,\n }\n }\n let decisions: DecisionVectorsDocument | undefined\n if (decisionsValue !== undefined) {\n const guarded = guardDecisionVectorsDocument(decisionsValue)\n if (guarded.kind === 'rejected') {\n return { kind: 'refused', file: decisionsFile, reason: `decisions rejected: ${guarded.reason}` }\n }\n // Plan 09 passes only vectors into the gate; check document identity at this boundary.\n for (const field of ['batchKey', 'matcherVersion'] as const) {\n if (guarded.document[field] !== report[field]) {\n return { kind: 'refused', file: decisionsFile,\n reason: `decisions ${field} does not match the report ${field}` }\n }\n }\n decisions = guarded.document\n }\n // Read the manifest on both sides of the data reads, including the legacy absent case.\n // No newly published generation can be mistaken for an unbound historical pair.\n let replayGeneration: ReplayGeneration | undefined\n try {\n if (readOptional(publicationFile) !== publicationText) {\n return { kind: 'refused', file: publicationFile, reason: 'report publication changed during read' }\n }\n if (publicationText !== undefined) {\n const publication = publicationSchema.parse(JSON.parse(publicationText))\n replayGeneration = publication.replayGeneration\n if (publication.reportDigest !== sha256hex(reportText)) {\n return { kind: 'refused', file, reason: 'report publication digest mismatch' }\n }\n if (publication.decisionsDigest !== (decisionsText === undefined ? null : sha256hex(decisionsText))) {\n return { kind: 'refused', file: decisionsFile, reason: 'decisions publication digest mismatch or incomplete sidecar' }\n }\n }\n } catch (error) {\n return { kind: 'refused', file: publicationFile, reason: `publication rejected: ${(error as Error).message}` }\n }\n return { kind: 'loaded', report, ...(decisions === undefined ? {} : { decisions }), file,\n ...(replayGeneration === undefined ? {} : { replayGeneration }) }\n}\n\nexport class FsReportStore {\n private readonly outputRoot: string\n\n constructor(config: FsReportStoreConfig) {\n this.outputRoot = config.outputDir ?? join(config.root, DEFAULT_EVAL_REPORTS_SUBDIR)\n }\n\n save(report: EvalBatchReport, decisions?: DecisionVectorsDocument, replayGeneration?: ReplayGeneration): WrittenReport {\n if (replayGeneration !== undefined) replayGenerationSchema.parse(replayGeneration)\n if (decisions !== undefined) {\n for (const field of ['batchKey', 'matcherVersion'] as const) {\n if (decisions[field] !== report[field]) {\n throw new TypeError(`decisions ${field} does not match the report ${field}`)\n }\n }\n }\n const file = join(this.outputRoot, `${slugBranch(report.batchKey)}.json`)\n const decisionsFile = join(this.outputRoot, `${slugBranch(report.batchKey)}.decisions.json`)\n const publicationFile = join(this.outputRoot, `${slugBranch(report.batchKey)}.publication.json`)\n const reportText = `${JSON.stringify(report, null, 2)}\\n`\n const decisionsText = decisions === undefined ? undefined : `${JSON.stringify(decisions, null, 2)}\\n`\n const publicationText = `${JSON.stringify({ schemaVersion: 2,\n reportDigest: sha256hex(reportText),\n decisionsDigest: decisionsText === undefined ? null : sha256hex(decisionsText),\n ...(replayGeneration === undefined ? {} : { replayGeneration }),\n }, null, 2)}\\n`\n mkdirSync(dirname(file), { recursive: true })\n // Unique temporary storage keeps concurrent writers from sharing a staging file.\n const temporary = mkdtempSync(join(this.outputRoot, '.publish-'))\n const replace = (target: string, text: string): void => {\n const staged = join(temporary, basename(target))\n try {\n writeFileSync(staged, text, 'utf8')\n renameSync(staged, target)\n } catch (error) {\n const io = error as NodeJS.ErrnoException\n if (typeof io.code !== 'string') throw error\n // The public failed file is the intended publication; retain OS detail and cause.\n throw Object.assign(new Error(io.message, { cause: error }), { code: io.code, path: target })\n }\n }\n try {\n replace(publicationFile, publicationText)\n // Invalidate even when this save supplies new vectors, before replacing the report.\n removeOptional(decisionsFile)\n replace(file, reportText)\n if (decisionsText !== undefined) replace(decisionsFile, decisionsText)\n } finally {\n for (const target of [publicationFile, file, decisionsFile]) {\n removeOptional(join(temporary, basename(target)))\n }\n rmdirSync(temporary)\n }\n return { batchKey: report.batchKey, file, ...(decisions === undefined ? {} : { decisionsFile }) }\n }\n}\n"],"mappings":"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","names":["existsSync","readdirSync","readFileSync","join","guardDecisionVectorsDocument","parseStandaloneLabelRecord","selectEffectiveLabels","slugBranch","z","readFileSync","join","guardCassette","join","join","readFileSync","guardCassette","createHash","mkdirSync","readFileSync","writeFileSync","basename","join","slugBranch","slugBranch","slugBranch","existsSync","readFileSync","basename","join","mkdirSync","rmSync","writeFileSync","join","slugBranch","mkdirSync","writeFileSync","join","slugBranch","join","closeSync","existsSync","fsyncSync","openSync","readFileSync","renameSync","writeFileSync","join","slugBranch","z","mkdirSync","readFileSync","rmSync","writeFileSync","dirname","join","z","lstatSync","readdirSync","readFileSync","dirname","join","z","canonicalJson","z","lstatSync","dirname","canonicalJson","join","walk","readdirSync","readFileSync","id","z","count","unit","rate","mkdirSync","readFileSync","renameSync","unlinkSync","writeFileSync","basename","dirname","join","sha256hex","slugBranch","z","z","z","readdirSync","current","join","readFileSync","join","existsSync","readdirSync","slugBranch","parseStandaloneLabelRecord","selectEffectiveLabels"]}
package/package.json CHANGED
@@ -1,6 +1,71 @@
1
1
  {
2
2
  "name": "@vostride/agent-qa-eval-local",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.2.1",
4
+ "private": false,
5
+ "description": "StorageImplementation shell for local agent-qa cassette IO behind eval ports.",
6
+ "keywords": [
7
+ "agent-qa",
8
+ "testing",
9
+ "agent",
10
+ "ports"
11
+ ],
12
+ "license": "SEE LICENSE IN LICENSE.md",
13
+ "homepage": "https://github.com/vostride/agent-qa#readme",
14
+ "bugs": {
15
+ "url": "https://github.com/vostride/agent-qa/issues"
16
+ },
17
+ "repository": {
18
+ "type": "git",
19
+ "url": "git+https://github.com/vostride/agent-qa.git",
20
+ "directory": "packages/eval-local"
21
+ },
22
+ "engines": {
23
+ "node": ">=24"
24
+ },
25
+ "publishConfig": {
26
+ "access": "public",
27
+ "registry": "https://registry.npmjs.org/"
28
+ },
29
+ "type": "module",
30
+ "exports": {
31
+ ".": {
32
+ "types": "./dist/index.d.ts",
33
+ "import": "./dist/index.js",
34
+ "require": "./dist/index.cjs"
35
+ },
36
+ "./reader": {
37
+ "types": "./dist/reader.d.ts",
38
+ "import": "./dist/reader.js",
39
+ "require": "./dist/reader.cjs"
40
+ }
41
+ },
42
+ "files": [
43
+ "dist",
44
+ "LICENSE.md",
45
+ "NOTICE.md"
46
+ ],
47
+ "scripts": {
48
+ "build": "tsup",
49
+ "dev": "tsup --watch",
50
+ "test": "vitest run",
51
+ "typecheck": "tsc --noEmit",
52
+ "clean": "rm -rf dist .turbo"
53
+ },
54
+ "dependencies": {
55
+ "@vostride/agent-qa-eval": "0.2.1",
56
+ "@vostride/agent-qa-model": "0.2.1",
57
+ "@vostride/agent-qa-runner": "0.2.1",
58
+ "@vostride/agent-qa-runner-local": "0.2.1",
59
+ "@vostride/agent-qa-safety": "0.2.1",
60
+ "@vostride/agent-qa-schema": "0.2.1",
61
+ "ai": "6.0.174",
62
+ "zod": "4.6.7"
63
+ },
64
+ "devDependencies": {
65
+ "@types/node": "~24.12.2",
66
+ "@vostride/agent-qa-arch": "0.2.1",
67
+ "tsup": "~8.5.1",
68
+ "typescript": "~6.0.3",
69
+ "vitest": "~4.1.5"
70
+ }
71
+ }
package/README.md DELETED
@@ -1,3 +0,0 @@
1
- # Temporary Holding Version
2
-
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.