audit-tools 0.40.1 → 0.41.0

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.
Files changed (114) hide show
  1. package/dist/audit/cli/auditStep.d.ts +1 -1
  2. package/dist/audit/cli/conceptualDispatch.d.ts +3 -0
  3. package/dist/audit/cli/conceptualDispatch.d.ts.map +1 -1
  4. package/dist/audit/cli/conceptualDispatch.js +82 -24
  5. package/dist/audit/cli/conceptualDispatch.js.map +1 -1
  6. package/dist/audit/cli/dispatch/hostHandoff.d.ts +12 -0
  7. package/dist/audit/cli/dispatch/hostHandoff.d.ts.map +1 -1
  8. package/dist/audit/cli/dispatch/hostHandoff.js +84 -56
  9. package/dist/audit/cli/dispatch/hostHandoff.js.map +1 -1
  10. package/dist/audit/cli/fanoutLanes.d.ts +47 -11
  11. package/dist/audit/cli/fanoutLanes.d.ts.map +1 -1
  12. package/dist/audit/cli/fanoutLanes.js +15 -7
  13. package/dist/audit/cli/fanoutLanes.js.map +1 -1
  14. package/dist/audit/cli/laneSubmissions.d.ts +241 -0
  15. package/dist/audit/cli/laneSubmissions.d.ts.map +1 -0
  16. package/dist/audit/cli/laneSubmissions.js +438 -0
  17. package/dist/audit/cli/laneSubmissions.js.map +1 -0
  18. package/dist/audit/cli/laneValidators.d.ts +65 -0
  19. package/dist/audit/cli/laneValidators.d.ts.map +1 -0
  20. package/dist/audit/cli/laneValidators.js +145 -0
  21. package/dist/audit/cli/laneValidators.js.map +1 -0
  22. package/dist/audit/cli/nextStepCommand.d.ts +1 -1
  23. package/dist/audit/cli/nextStepCommand.d.ts.map +1 -1
  24. package/dist/audit/cli/nextStepCommand.js +118 -69
  25. package/dist/audit/cli/nextStepCommand.js.map +1 -1
  26. package/dist/audit/cli/nextStepHelpers.d.ts +73 -42
  27. package/dist/audit/cli/nextStepHelpers.d.ts.map +1 -1
  28. package/dist/audit/cli/nextStepHelpers.js +389 -253
  29. package/dist/audit/cli/nextStepHelpers.js.map +1 -1
  30. package/dist/audit/cli/recoverSubmissionCommand.d.ts +16 -0
  31. package/dist/audit/cli/recoverSubmissionCommand.d.ts.map +1 -0
  32. package/dist/audit/cli/recoverSubmissionCommand.js +73 -0
  33. package/dist/audit/cli/recoverSubmissionCommand.js.map +1 -0
  34. package/dist/audit/cli/semanticReviewStep.d.ts +3 -0
  35. package/dist/audit/cli/semanticReviewStep.d.ts.map +1 -1
  36. package/dist/audit/cli/semanticReviewStep.js +26 -1
  37. package/dist/audit/cli/semanticReviewStep.js.map +1 -1
  38. package/dist/audit/cli/steps.d.ts +75 -2
  39. package/dist/audit/cli/steps.d.ts.map +1 -1
  40. package/dist/audit/cli/steps.js +14 -0
  41. package/dist/audit/cli/steps.js.map +1 -1
  42. package/dist/audit/cli.d.ts.map +1 -1
  43. package/dist/audit/cli.js +5 -1
  44. package/dist/audit/cli.js.map +1 -1
  45. package/dist/audit/contracts/wrapperResponse.d.ts +7 -7
  46. package/dist/audit/contracts/wrapperResponse.js +1 -1
  47. package/dist/audit/contracts/wrapperResponse.js.map +1 -1
  48. package/dist/audit/io/artifacts.d.ts +12 -1
  49. package/dist/audit/io/artifacts.d.ts.map +1 -1
  50. package/dist/audit/io/artifacts.js +28 -1
  51. package/dist/audit/io/artifacts.js.map +1 -1
  52. package/dist/audit/orchestrator/synthesisExecutors.d.ts.map +1 -1
  53. package/dist/audit/orchestrator/synthesisExecutors.js +2 -0
  54. package/dist/audit/orchestrator/synthesisExecutors.js.map +1 -1
  55. package/dist/audit/reporting/synthesis.d.ts +9 -1
  56. package/dist/audit/reporting/synthesis.d.ts.map +1 -1
  57. package/dist/audit/reporting/synthesis.js +69 -1
  58. package/dist/audit/reporting/synthesis.js.map +1 -1
  59. package/dist/audit/supervisor/operatorHandoff.d.ts +9 -2
  60. package/dist/audit/supervisor/operatorHandoff.d.ts.map +1 -1
  61. package/dist/audit/supervisor/operatorHandoff.js +17 -10
  62. package/dist/audit/supervisor/operatorHandoff.js.map +1 -1
  63. package/dist/audit/types/designAssessment.d.ts +9 -5
  64. package/dist/audit/types/designAssessment.d.ts.map +1 -1
  65. package/dist/remediate/index.d.ts.map +1 -1
  66. package/dist/remediate/index.js +48 -1
  67. package/dist/remediate/index.js.map +1 -1
  68. package/dist/remediate/steps/dispatch/hostHandoff.d.ts +38 -6
  69. package/dist/remediate/steps/dispatch/hostHandoff.d.ts.map +1 -1
  70. package/dist/remediate/steps/dispatch/hostHandoff.js +87 -75
  71. package/dist/remediate/steps/dispatch/hostHandoff.js.map +1 -1
  72. package/dist/shared/decompose/charterClarification.d.ts +1 -1
  73. package/dist/shared/decompose/charterClarification.js +1 -1
  74. package/dist/shared/decompose/charterExtraction.js +1 -1
  75. package/dist/shared/decompose/charterExtraction.js.map +1 -1
  76. package/dist/shared/decompose/systemicChallenge.js +1 -1
  77. package/dist/shared/decompose/systemicChallenge.js.map +1 -1
  78. package/dist/shared/index.d.ts +11 -1
  79. package/dist/shared/index.d.ts.map +1 -1
  80. package/dist/shared/index.js +6 -1
  81. package/dist/shared/index.js.map +1 -1
  82. package/dist/shared/io/auditToolsPaths.d.ts +24 -4
  83. package/dist/shared/io/auditToolsPaths.d.ts.map +1 -1
  84. package/dist/shared/io/auditToolsPaths.js +29 -5
  85. package/dist/shared/io/auditToolsPaths.js.map +1 -1
  86. package/dist/shared/io/schemaVersion.d.ts +5 -4
  87. package/dist/shared/io/schemaVersion.d.ts.map +1 -1
  88. package/dist/shared/io/schemaVersion.js +17 -5
  89. package/dist/shared/io/schemaVersion.js.map +1 -1
  90. package/dist/shared/loopCorePaths.d.ts.map +1 -1
  91. package/dist/shared/loopCorePaths.js +8 -2
  92. package/dist/shared/loopCorePaths.js.map +1 -1
  93. package/dist/shared/submission/expectedSubmissions.d.ts +88 -0
  94. package/dist/shared/submission/expectedSubmissions.d.ts.map +1 -0
  95. package/dist/shared/submission/expectedSubmissions.js +84 -0
  96. package/dist/shared/submission/expectedSubmissions.js.map +1 -0
  97. package/dist/shared/submission/handRecovery.d.ts +37 -0
  98. package/dist/shared/submission/handRecovery.d.ts.map +1 -0
  99. package/dist/shared/submission/handRecovery.js +123 -0
  100. package/dist/shared/submission/handRecovery.js.map +1 -0
  101. package/dist/shared/submission/submissionClassifier.d.ts +48 -0
  102. package/dist/shared/submission/submissionClassifier.d.ts.map +1 -0
  103. package/dist/shared/submission/submissionClassifier.js +83 -0
  104. package/dist/shared/submission/submissionClassifier.js.map +1 -0
  105. package/dist/shared/submission/submissionIdentity.d.ts +49 -0
  106. package/dist/shared/submission/submissionIdentity.d.ts.map +1 -0
  107. package/dist/shared/submission/submissionIdentity.js +90 -0
  108. package/dist/shared/submission/submissionIdentity.js.map +1 -0
  109. package/dist/shared/submission/submissionLedger.d.ts +37 -0
  110. package/dist/shared/submission/submissionLedger.d.ts.map +1 -0
  111. package/dist/shared/submission/submissionLedger.js +81 -0
  112. package/dist/shared/submission/submissionLedger.js.map +1 -0
  113. package/docs/audit-pkg/product.md +0 -2
  114. package/package.json +1 -1
@@ -0,0 +1,438 @@
1
+ /**
2
+ * The audit draw of the shared submission core: how a GATE lane's bound path is
3
+ * computed, and where the expected-set / ledger for those lanes are persisted.
4
+ *
5
+ * Two facts about the audit gates shape everything here:
6
+ *
7
+ * 1. **They carry no run id.** `NextStepParams` has none, and threading one
8
+ * into every gate would change ten signatures to carry a value the gates
9
+ * never use. Their submissions are therefore ARTIFACTS-DIR-scoped: the
10
+ * constant `AUDIT_GATE_SUBMISSION_SCOPE` stands in for run identity, so the
11
+ * same gate lane in the same artifacts dir denotes the same submission
12
+ * across `next-step` calls. That is not a shortcut — it is exactly the
13
+ * K-of-N resume property, which requires a re-emitted step to re-declare
14
+ * the identical bound path rather than mint a new one and re-ask for work
15
+ * the host already delivered.
16
+ *
17
+ * 2. **The lane id is the only join key.** The emitter and the gate reader
18
+ * never meet; they agree because both derive the id from the lane id
19
+ * through this module. A lane id is declared once in
20
+ * `HOST_GATE_DESCRIPTORS` and used verbatim on both sides.
21
+ *
22
+ * The bound path is DERIVED, not looked up. The persisted expected set states
23
+ * what is owed (and feeds the diff and the ledger), but a gate that could only
24
+ * read a submission when a bookkeeping file happened to exist would lose a
25
+ * valid submission the first time that file was absent — so derivation is the
26
+ * mechanism and the record is the record.
27
+ */
28
+ import { z } from "zod";
29
+ import { CharterKindSchema, absoluteSubmissionPath, buildExpectedSubmissionSet, diffExpectedSet, discardOnSchemaVersionMismatch, expectedSubmissionsPath, hashContent, mergeExpectedSets, mintSubmissionId, outputDirFor, readOptionalJsonFile, readSubmissionDocument, readSubmissionLedger, submissionsDir, withoutExpectedSubmissions, writeJsonFile, EXPECTED_SET_CONTRACT_VERSION, SUBMISSION_LEDGER_EVENT_CONTRACT_VERSION, appendSubmissionEvent, } from "audit-tools/shared";
30
+ /**
31
+ * Gate lane ids — the single join key between an emitter and its gate reader.
32
+ *
33
+ * The two never meet: an emitter writes a lane's prompt and declares where the
34
+ * answer goes, and (possibly several `next-step` calls later) the gate reader
35
+ * looks for it. They agree because both derive the bound path from the lane id
36
+ * through this module. Declared once, here, so no site can spell a lane one way
37
+ * on the emit side and another on the read side.
38
+ */
39
+ export const GATE_LANES = {
40
+ analyzer_consent: "analyzer_consent",
41
+ analyzer_decisions: "analyzer_decisions",
42
+ edge_reasoning: "edge_reasoning",
43
+ design_review_legacy: "design_review_legacy",
44
+ design_review_contract: "design_review_contract",
45
+ design_review_conceptual: "design_review_conceptual",
46
+ critical_flow_fallback: "critical_flow_fallback",
47
+ intent_equivalence: "intent_equivalence",
48
+ synthesis_narrative: "synthesis_narrative",
49
+ charter_delta: "charter_delta",
50
+ charter_clarification: "charter_clarification",
51
+ systemic_challenge: "systemic_challenge",
52
+ };
53
+ /** The blind per-kind charter lanes (one per estimator channel). */
54
+ export function charterExtractionLane(kind) {
55
+ return `charter_extraction_${kind}`;
56
+ }
57
+ /** Recover the charter kind a `charter_extraction_*` lane id denotes. */
58
+ export function charterKindForLane(lane) {
59
+ if (!lane.startsWith(CHARTER_EXTRACTION_LANE_PREFIX))
60
+ return undefined;
61
+ const parsed = CharterKindSchema.safeParse(lane.slice(CHARTER_EXTRACTION_LANE_PREFIX.length));
62
+ return parsed.success ? parsed.data : undefined;
63
+ }
64
+ const CHARTER_EXTRACTION_LANE_PREFIX = "charter_extraction_";
65
+ export const CONCEPTUAL_PERSPECTIVE_LANE_PREFIX = "design_review_conceptual_p";
66
+ /**
67
+ * The deep conceptual pass's independent perspective lanes. Their COUNT is
68
+ * resolved at emit from confirmed intent, so the ids are generated rather than
69
+ * enumerated.
70
+ *
71
+ * The id carries the ROUND — a digest of the upstream content the round asks
72
+ * about — because a perspective is the one lane class whose reuse is WRONG.
73
+ * Every other lane's identity is deliberately stable so a re-emitted step
74
+ * re-declares the same bound path (K-of-N resume); a perspective, though, is a
75
+ * fresh independent reading of the artifacts as they now stand. Keyed on the
76
+ * index alone, a re-review after staleness would find the PREVIOUS round's
77
+ * submission at the bound path, skip the lane, leave its prompt unrewritten,
78
+ * and hand the judge the old round's findings as if they were this round's.
79
+ * Same round, same digest, same id — so resume WITHIN a round still works.
80
+ */
81
+ export function conceptualPerspectiveLane(index, roundToken) {
82
+ return `${CONCEPTUAL_PERSPECTIVE_LANE_PREFIX}${index}_${roundToken}`;
83
+ }
84
+ /**
85
+ * The round token every perspective lane in one deep conceptual emission
86
+ * shares: a digest of exactly what this round asks. Deterministic in its inputs
87
+ * — never a timestamp or a counter, which would mint new ids (and re-ask for
88
+ * already-delivered work) on every re-emission of an unchanged round.
89
+ */
90
+ export function conceptualRoundToken(inputs) {
91
+ return hashContent(inputs.join("\n"), { length: 12 });
92
+ }
93
+ /**
94
+ * The per-kind evidence packet a charter lane reads. TOOL-written, so it lives
95
+ * under the lane-asset dir with the lane prompts — a host only ever reads it.
96
+ */
97
+ export function charterExtractionPacketFilename(kind) {
98
+ return `charter-extraction-${kind}-packet.md`;
99
+ }
100
+ /**
101
+ * The tool-side merge of the per-kind charter lanes, handed to the extraction
102
+ * executor by path and deleted once ingested. Tool-written, so it is a lane
103
+ * asset and not a submission.
104
+ */
105
+ export const CHARTER_EXTRACTION_MERGED_FILENAME = "charter-extraction-merged.json";
106
+ /**
107
+ * The submission FAMILY every audit gate lane belongs to. Distinguishes these
108
+ * from the host-handoff work-item submissions, which mint through the same rule
109
+ * under their own run id.
110
+ */
111
+ export const AUDIT_LANE_SUBMISSION_KIND = "audit_host_lane";
112
+ /** See the module comment: the audit gates' stand-in for run identity. */
113
+ export const AUDIT_GATE_SUBMISSION_SCOPE = "audit-host-gates";
114
+ /**
115
+ * Bound paths for gate lanes are expressed relative to the artifact tree root
116
+ * (`.audit-tools/`), not the repository root: the gates are handed an artifacts
117
+ * dir and nothing else, and a recorded path must be derivable from exactly what
118
+ * the reader has.
119
+ */
120
+ export function laneSubmissionRoots(artifactsDir) {
121
+ return {
122
+ root: outputDirFor(artifactsDir),
123
+ submissionDir: submissionsDir(artifactsDir),
124
+ };
125
+ }
126
+ /** The tool-minted id for one lane. Deterministic in the lane id alone. */
127
+ export function laneSubmissionId(lane, runId = AUDIT_GATE_SUBMISSION_SCOPE) {
128
+ return mintSubmissionId({ kind: AUDIT_LANE_SUBMISSION_KIND, lane, runId });
129
+ }
130
+ /**
131
+ * Absolute on-disk path a lane's submission must land at.
132
+ *
133
+ * `runId` is threaded rather than defaulted-and-forgotten: the bound path and
134
+ * the recorded expectation must come from ONE derivation. They were two — the
135
+ * path taking the module constant while the record took the caller's scope —
136
+ * which coincided only because every caller happened to pass the constant, i.e.
137
+ * the invariant held by the caller remembering.
138
+ */
139
+ export function laneSubmissionPath(artifactsDir, lane, runId = AUDIT_GATE_SUBMISSION_SCOPE) {
140
+ return absoluteSubmissionPath(laneSubmissionRoots(artifactsDir), laneSubmissionId(lane, runId));
141
+ }
142
+ /**
143
+ * The current statement of what is owed, or `undefined` when there isn't one.
144
+ *
145
+ * REGENERABLE state: the set is rewritten at every emit from the lanes the
146
+ * emission declares, so a set left by another release is DISCARDED rather than
147
+ * reinterpreted under this release's field semantics. The fail-shape is the one
148
+ * this reader already has for an absent file — the merge below starts from
149
+ * nothing, every declared lane counts as newly added, and the carried-lane diff
150
+ * therefore sees no prior entries and reports no shortfall. That is exactly
151
+ * right after an upgrade: nothing was carried into this emission that this
152
+ * release ever asked for, so the emission is first-emission-silent instead of
153
+ * accusing the host of dropping lanes it was never coherently asked for.
154
+ */
155
+ async function loadExpectedSet(artifactsDir) {
156
+ try {
157
+ return discardOnSchemaVersionMismatch(await readOptionalJsonFile(expectedSubmissionsPath(artifactsDir)), EXPECTED_SET_CONTRACT_VERSION);
158
+ }
159
+ catch {
160
+ // Corrupt bookkeeping must never fail the emission it is recording; the
161
+ // merge below simply starts from nothing.
162
+ return undefined;
163
+ }
164
+ }
165
+ /**
166
+ * What a re-emission is still owed, stated by name.
167
+ *
168
+ * Persisted on the step contract and rendered into the step prompt, so "the
169
+ * host dropped a lane" is a fact the run REPORTS rather than one an operator
170
+ * has to infer from an identical step arriving twice.
171
+ */
172
+ const OutstandingLaneSubmissionSchema = z
173
+ .object({
174
+ lane: z.string(),
175
+ submission_id: z.string(),
176
+ /**
177
+ * The tool-computed bound path, ABSOLUTE — the same form (and the same
178
+ * file) as the step's `access.write_paths` entry for this lane, so the two
179
+ * compare directly. Deliberately NOT the expected set's recorded form:
180
+ * that one is relative to the artifact tree root, a base the step contract
181
+ * declares nowhere, so a consumer joining it against `repo_root` or
182
+ * `artifacts_dir` would build a path that never existed. `extraFields` ride
183
+ * through `writeStepContract` un-normalized (only prompt_path / repo_root /
184
+ * artifacts_dir / artifact_paths are), exactly as `access` does.
185
+ */
186
+ submission_path: z.string(),
187
+ issue_code: z.string(),
188
+ message: z.string(),
189
+ })
190
+ .strict();
191
+ export const LaneSubmissionShortfallSchema = z
192
+ .object({
193
+ /** Lanes CARRIED into this emission (a prior emission already asked). */
194
+ expected: z.number().int(),
195
+ /** How many of those have a readable submission at their bound path. */
196
+ accepted: z.number().int(),
197
+ outstanding: z.array(OutstandingLaneSubmissionSchema),
198
+ })
199
+ .strict();
200
+ const NO_SHORTFALL = {
201
+ expected: 0,
202
+ accepted: 0,
203
+ outstanding: [],
204
+ };
205
+ /** Read every carried member's bound path once, for the diff below. */
206
+ async function observeLanes(artifactsDir, entries) {
207
+ const roots = laneSubmissionRoots(artifactsDir);
208
+ return new Map(await Promise.all(entries.map(async (entry) => [
209
+ entry.submission_id,
210
+ await readSubmissionDocument(absoluteSubmissionPath(roots, entry.submission_id)),
211
+ ])));
212
+ }
213
+ /**
214
+ * Record what this emission owes, and report what a PREVIOUS one is still owed.
215
+ *
216
+ * Merges into the current statement (a step that materializes several lane
217
+ * groups accumulates them) and appends one `expected` event per NEWLY declared
218
+ * lane, so a re-emission does not spam the arrival record.
219
+ *
220
+ * The returned shortfall diffs only the lanes CARRIED into this emission — the
221
+ * ones an earlier emission already asked for and that are still not on disk.
222
+ * Restricting it to carried lanes is what makes the report meaningful: on a
223
+ * first emission every lane is legitimately absent (it is being asked for right
224
+ * now), so counting those would make every fan-out look like a failure.
225
+ */
226
+ export async function recordExpectedLanes(artifactsDir, runId, lanes) {
227
+ if (lanes.length === 0)
228
+ return NO_SHORTFALL;
229
+ const declared = buildExpectedSubmissionSet({
230
+ runId,
231
+ paths: laneSubmissionRoots(artifactsDir),
232
+ lanes: lanes.map((lane) => ({
233
+ lane: lane.lane,
234
+ submissionId: laneSubmissionId(lane.lane, runId),
235
+ promptSha256: hashContent(lane.promptText),
236
+ })),
237
+ });
238
+ const { set, addedIds } = mergeExpectedSets(await loadExpectedSet(artifactsDir), declared);
239
+ await writeJsonFile(expectedSubmissionsPath(artifactsDir), set);
240
+ const added = new Set(addedIds);
241
+ for (const entry of set.entries) {
242
+ if (!added.has(entry.submission_id))
243
+ continue;
244
+ await appendSubmissionEvent(artifactsDir, {
245
+ contract_version: SUBMISSION_LEDGER_EVENT_CONTRACT_VERSION,
246
+ run_id: runId,
247
+ submission_id: entry.submission_id,
248
+ lane: entry.lane,
249
+ kind: "expected",
250
+ recorded_at: new Date().toISOString(),
251
+ });
252
+ }
253
+ const carried = {
254
+ ...declared,
255
+ entries: declared.entries.filter((entry) => !added.has(entry.submission_id)),
256
+ };
257
+ if (carried.entries.length === 0)
258
+ return NO_SHORTFALL;
259
+ const byId = new Map(carried.entries.map((entry) => [entry.submission_id, entry]));
260
+ const diff = diffExpectedSet(carried, await observeLanes(artifactsDir, carried.entries));
261
+ const outstanding = diff.members.flatMap((member) => member.status === "issue" ? [member] : []);
262
+ // A REFUSED lane also reads as ENOENT, because quarantining moves the file
263
+ // off the bound path and only an ACCEPTED lane is dropped from the set. The
264
+ // disk cannot tell the two apart, so the record does: without this, a host
265
+ // whose submission was received and rejected is told it "submitted nothing",
266
+ // which is false and points it at the wrong repair.
267
+ const refusals = outstanding.length > 0
268
+ ? await lastRefusals(artifactsDir, outstanding.map((member) => member.submission_id))
269
+ : new Map();
270
+ const roots = laneSubmissionRoots(artifactsDir);
271
+ return {
272
+ expected: diff.expected,
273
+ accepted: diff.accepted,
274
+ outstanding: outstanding.map((member) => {
275
+ const entry = byId.get(member.submission_id);
276
+ const lane = entry?.lane ?? member.submission_id;
277
+ const refusal = refusals.get(member.submission_id);
278
+ return {
279
+ lane,
280
+ submission_id: member.submission_id,
281
+ // Absolute, matching this step's `access.write_paths` entry — see the
282
+ // schema note; the expected set's own record stays artifact-relative.
283
+ submission_path: absoluteSubmissionPath(roots, member.submission_id),
284
+ issue_code: refusal ? "submission_rejected" : member.issue.code,
285
+ message: refusal
286
+ ? `lane '${lane}' submitted, and the submission was refused` +
287
+ `${refusal.message ? `: ${refusal.message}` : ""}` +
288
+ " — fix it and resubmit at the bound path"
289
+ : member.issue.message,
290
+ };
291
+ }),
292
+ };
293
+ }
294
+ /**
295
+ * The last ledger event per submission id, kept only where it is a REFUSAL.
296
+ * A later acceptance or hand recovery ends the refusal, so only the trailing
297
+ * state counts: this answers "is this lane outstanding BECAUSE it was refused",
298
+ * never "was it ever refused".
299
+ */
300
+ async function lastRefusals(artifactsDir, submissionIds) {
301
+ const wanted = new Set(submissionIds);
302
+ const last = new Map();
303
+ for (const event of await readSubmissionLedger(artifactsDir)) {
304
+ if (!wanted.has(event.submission_id))
305
+ continue;
306
+ if (event.kind === "expected")
307
+ continue;
308
+ last.set(event.submission_id, event);
309
+ }
310
+ for (const [id, event] of [...last.entries()]) {
311
+ if (event.kind !== "rejected")
312
+ last.delete(id);
313
+ }
314
+ return last;
315
+ }
316
+ /** Merge the shortfalls of a step that materializes several lane groups. */
317
+ export function mergeLaneShortfalls(shortfalls) {
318
+ return {
319
+ expected: shortfalls.reduce((total, one) => total + one.expected, 0),
320
+ accepted: shortfalls.reduce((total, one) => total + one.accepted, 0),
321
+ outstanding: shortfalls.flatMap((one) => one.outstanding),
322
+ };
323
+ }
324
+ /**
325
+ * The host-facing statement of an unmet expectation, by lane and issue code.
326
+ * Empty when nothing was carried or everything carried has arrived — a clean
327
+ * emission says nothing, so the notice's presence is itself the signal.
328
+ */
329
+ export function renderLaneShortfallLines(shortfall) {
330
+ if (shortfall.outstanding.length === 0)
331
+ return [];
332
+ return [
333
+ "## Submissions still outstanding",
334
+ "",
335
+ `A previous emission asked for ${shortfall.expected} lane submission(s); ${shortfall.accepted} arrived. ` +
336
+ "These are still unsatisfied at the bound paths the tool declared — nothing was lost, and the paths are unchanged:",
337
+ "",
338
+ ...shortfall.outstanding.map((entry) => `- lane \`${entry.lane}\` (${entry.issue_code}): ${entry.message}`),
339
+ "",
340
+ ];
341
+ }
342
+ /**
343
+ * Record the outcome of one lane. An accepted lane stops being owed; a rejected
344
+ * one stays owed (the host is asked again) but the refusal is on the record
345
+ * with its reason, which is what makes a repaired run distinguishable from a
346
+ * clean one.
347
+ */
348
+ export async function recordLaneOutcome(artifactsDir, lane, outcome) {
349
+ // ONE derivation of the scope: the id and the event's run field are the same
350
+ // value by construction, not two sites spelling the same constant.
351
+ const runId = AUDIT_GATE_SUBMISSION_SCOPE;
352
+ const submissionId = laneSubmissionId(lane, runId);
353
+ await appendSubmissionEvent(artifactsDir, {
354
+ contract_version: SUBMISSION_LEDGER_EVENT_CONTRACT_VERSION,
355
+ run_id: runId,
356
+ submission_id: submissionId,
357
+ lane,
358
+ kind: outcome.kind,
359
+ ...(outcome.kind === "rejected"
360
+ ? { issue_code: outcome.issueCode, message: outcome.message }
361
+ : {}),
362
+ ...(outcome.kind === "accepted" && outcome.message
363
+ ? { message: outcome.message }
364
+ : {}),
365
+ recorded_at: new Date().toISOString(),
366
+ });
367
+ if (outcome.kind !== "accepted")
368
+ return;
369
+ const set = await loadExpectedSet(artifactsDir);
370
+ if (!set)
371
+ return;
372
+ await writeJsonFile(expectedSubmissionsPath(artifactsDir), withoutExpectedSubmissions(set, [submissionId]));
373
+ }
374
+ /** How an event reads for dedupe: kind, code, and message, in one string. */
375
+ function eventSignature(kind, issueCode, message) {
376
+ return [kind, issueCode ?? "", message ?? ""].join("|");
377
+ }
378
+ /**
379
+ * Record what the host-handoff ingest just decided about each work item, on the
380
+ * same ledger the gate lanes use — so a submission that never arrived (or
381
+ * arrived unreadable) is a durable fact rather than a value that died inside
382
+ * the call that computed it.
383
+ *
384
+ * Two rules keep the record drift-focused and arithmetically honest:
385
+ *
386
+ * • Deduped against the LAST recorded event for each submission. Ingest runs
387
+ * on every `next-step`, so a host still working through its workload would
388
+ * otherwise append the same "missing" line every poll, burying the state
389
+ * CHANGES the ledger exists to preserve. A changed classification still
390
+ * appends, in arrival order.
391
+ * • An acceptance is recorded ONLY where a refusal precedes it. A work item
392
+ * the host got right first try says nothing (the ledger is not an inventory
393
+ * of work), but one that was refused and later accepted must close its own
394
+ * story — otherwise a run where every failure was repaired reports its
395
+ * refusals with no matching repairs, and the report reads as a run that
396
+ * never recovered.
397
+ */
398
+ export async function recordHostResultOutcomes(artifactsDir, runId, outcomes) {
399
+ if (outcomes.issues.length === 0 && outcomes.acceptedIds.length === 0)
400
+ return;
401
+ const last = new Map();
402
+ for (const event of await readSubmissionLedger(artifactsDir)) {
403
+ last.set(event.submission_id, eventSignature(event.kind, event.issue_code, event.message));
404
+ }
405
+ const append = async (submissionId, event) => {
406
+ const signature = eventSignature(event.kind, event.issue_code, event.message);
407
+ if (last.get(submissionId) === signature)
408
+ return;
409
+ last.set(submissionId, signature);
410
+ await appendSubmissionEvent(artifactsDir, {
411
+ contract_version: SUBMISSION_LEDGER_EVENT_CONTRACT_VERSION,
412
+ run_id: runId,
413
+ submission_id: submissionId,
414
+ // A host work item's id IS its submission identity: the bound path is
415
+ // derived from it through the same rule the gate lanes use.
416
+ lane: submissionId,
417
+ ...event,
418
+ recorded_at: new Date().toISOString(),
419
+ });
420
+ };
421
+ for (const submissionId of outcomes.acceptedIds) {
422
+ // Nothing on the record for this item means it was clean on the first try.
423
+ if (!last.has(submissionId))
424
+ continue;
425
+ await append(submissionId, { kind: "accepted" });
426
+ }
427
+ for (const issue of outcomes.issues) {
428
+ const submissionId = issue.work_item_id ?? issue.submission_id;
429
+ if (submissionId === undefined)
430
+ continue;
431
+ await append(submissionId, {
432
+ kind: "rejected",
433
+ issue_code: issue.code,
434
+ message: issue.message,
435
+ });
436
+ }
437
+ }
438
+ //# sourceMappingURL=laneSubmissions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"laneSubmissions.js","sourceRoot":"","sources":["../../../src/audit/cli/laneSubmissions.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,65 @@
1
+ /**
2
+ * The ONE registry of what a lane's submission must satisfy.
3
+ *
4
+ * Every gate validated its submission inline, which was fine while the only
5
+ * reader was the gate itself. The hand-recovery verb is a SECOND reader of the
6
+ * same contract, and a second reader is exactly where a weaker copy grows: an
7
+ * operator rescue that skipped the schema would be a door into the tool the
8
+ * normal lane does not have. So the rule lives here once, and both the gate and
9
+ * `recover-submission` resolve it from this table.
10
+ *
11
+ * Two shapes of rule, because two shapes of lane exist: those whose contract IS
12
+ * a zod schema, and those whose contract is a tolerated JSON shape (an array of
13
+ * findings, a decisions map). Both are expressed as the same
14
+ * `SubmissionIssue | null` validator so a caller never has to know which.
15
+ */
16
+ import type { ZodTypeAny } from "zod";
17
+ import { type CharterKind, type SubmissionIssue } from "audit-tools/shared";
18
+ /** Human-readable description of why a submission is neither an array nor a single-array-wrapped object. */
19
+ export declare function describeSubmissionShapeMismatch(value: unknown): string;
20
+ /**
21
+ * The single tolerant-unwrap rule: a bare array is accepted as-is; a top-level
22
+ * object wrapping exactly one array-valued property is unambiguous and is
23
+ * accepted as that array. Anything else fails with a shape description.
24
+ * Single-sourced so the design-review gate, the edge-reasoning gate, and the
25
+ * recovery verb cannot drift on what shapes are accepted.
26
+ */
27
+ export declare function unwrapSubmissionArray(value: unknown): {
28
+ ok: true;
29
+ array: unknown[];
30
+ } | {
31
+ ok: false;
32
+ reason: string;
33
+ };
34
+ /** True for a plain top-level key → value map (the decisions-file shape). */
35
+ export declare function isSubmissionObjectMap(value: unknown): value is Record<string, unknown>;
36
+ /**
37
+ * The charter lane schema: the submission shape PLUS the two refinements that
38
+ * make a blind lane trustworthy — kind purity (a lane may only carry its own
39
+ * kind; anything else is a mis-routed submission) and scope grounding (a
40
+ * teleology node citing files the repo does not contain is refused whole,
41
+ * naming them, never silently narrowed).
42
+ *
43
+ * `repoFiles` is the manifest's path set. It is a parameter rather than a
44
+ * capture so the gate and the recovery verb apply the identical refinement
45
+ * against the identical universe.
46
+ */
47
+ export declare function charterLaneSchema(kind: CharterKind, repoFiles: ReadonlySet<string>): ZodTypeAny;
48
+ /**
49
+ * Lanes whose contract is a zod schema, keyed by lane id. The gate descriptors
50
+ * read this table; so does the recovery verb.
51
+ */
52
+ export declare const LANE_SUBMISSION_SCHEMAS: Readonly<Record<string, ZodTypeAny>>;
53
+ /** What the recovery verb needs in order to apply the FULL lane contract. */
54
+ export interface LaneValidationContext {
55
+ /** The repo manifest's path set — the charter lanes' scope grounding. */
56
+ readonly repoFiles: ReadonlySet<string>;
57
+ }
58
+ /**
59
+ * The validator the normal lane applies to `lane`, or `null` when the lane id
60
+ * is not one this tool knows. Callers that cannot supply a validator must
61
+ * REFUSE rather than accept — an unknown lane has no contract to check against,
62
+ * and "no contract" must never read as "passes".
63
+ */
64
+ export declare function laneSubmissionValidator(lane: string, context: LaneValidationContext): ((value: unknown) => SubmissionIssue | null) | null;
65
+ //# sourceMappingURL=laneValidators.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"laneValidators.d.ts","sourceRoot":"","sources":["../../../src/audit/cli/laneValidators.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,KAAK,CAAC;AAEtC,OAAO,EAOL,KAAK,WAAW,EAChB,KAAK,eAAe,EACrB,MAAM,oBAAoB,CAAC;AAQ5B,4GAA4G;AAC5G,wBAAgB,+BAA+B,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAiBtE;AAED;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CACnC,KAAK,EAAE,OAAO,GACb;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,KAAK,EAAE,OAAO,EAAE,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAShE;AAED,6EAA6E;AAC7E,wBAAgB,qBAAqB,CACnC,KAAK,EAAE,OAAO,GACb,KAAK,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAElC;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,iBAAiB,CAC/B,IAAI,EAAE,WAAW,EACjB,SAAS,EAAE,WAAW,CAAC,MAAM,CAAC,GAC7B,UAAU,CAsBZ;AAED;;;GAGG;AACH,eAAO,MAAM,uBAAuB,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAOxE,CAAC;AA6BF,6EAA6E;AAC7E,MAAM,WAAW,qBAAqB;IACpC,yEAAyE;IACzE,QAAQ,CAAC,SAAS,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;CACzC;AAED;;;;;GAKG;AACH,wBAAgB,uBAAuB,CACrC,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,qBAAqB,GAC7B,CAAC,CAAC,KAAK,EAAE,OAAO,KAAK,eAAe,GAAG,IAAI,CAAC,GAAG,IAAI,CA4BrD"}