@mmnto/totem 1.64.0 → 1.64.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,"file":"panel.d.ts","sourceRoot":"","sources":["../../src/artifacts/panel.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,503 @@
1
+ /**
2
+ * Panel synthesis — independent lanes, deterministic script aggregation
3
+ * (mmnto-ai/totem#2104, strategy#474 slice 5).
4
+ *
5
+ * A "panel" is N independent runs (lanes) of the same task over one immutable
6
+ * grounding bundle, each emitting a #2100 {@link RunArtifact} plus its #2103
7
+ * {@link PostCheckReport}. This module is the ENGINE: a pure, zero-LLM script
8
+ * (Tenet 9) that aggregates the lanes — group findings by `ruleName`, tally
9
+ * lane verdicts, surface divergence, and label vendor diversity HONESTLY — plus
10
+ * content-addressed storage for the resulting {@link PanelArtifact}. It does NOT
11
+ * run backends or dispatch lanes (the CLI runner is a deferred fast-follow), and
12
+ * it emits NO panel-level gate: the panel is a SENSOR (a verdict *distribution*,
13
+ * never a single accept/reject), leaving any gating policy to a later consumer.
14
+ *
15
+ * Diversity-labeling honesty is the load-bearing decision (strategy#474 / Prop
16
+ * 291 / Tenet 19): cross-VENDOR convergence is the strong signal, NOT vote count.
17
+ * The raw `providers[]` is always emitted lossless so a label can never overclaim
18
+ * rigor, and an unrecognized provider string trips a fail-loud `coarse` marker
19
+ * (see {@link classifyDiversity}).
20
+ *
21
+ * Schema-evolution policy mirrors {@link RunArtifactSchema} (F1): the reader is
22
+ * version-tolerant within major 1; a major bump needs a migration entry before
23
+ * the writer ships. Zod is the persisted-JSON boundary (read back from disk).
24
+ */
25
+ import * as fs from 'node:fs';
26
+ import * as path from 'node:path';
27
+ import { z } from 'zod';
28
+ import { rethrowAsParseError, TotemParseError } from '../errors.js';
29
+ import { readJsonSafe } from '../sys/fs.js';
30
+ import { calculateDeterministicHash } from './hash.js';
31
+ import { RunArtifactSchema } from './schema.js';
32
+ // ─── Schema version (mirrors RunArtifact F1) ────────────────────────────────
33
+ /** The panel schemaVersion WRITTEN by this code. Readers accept any 1.x. */
34
+ export const PANEL_ARTIFACT_SCHEMA_VERSION = '1.0.0';
35
+ /** The major this reader understands; other majors need a migration entry. */
36
+ export const PANEL_ARTIFACT_KNOWN_MAJOR = 1;
37
+ /** Major-1 semver literal — keep in sync with {@link PANEL_ARTIFACT_KNOWN_MAJOR} (a literal beats runtime RegExp construction; the major only changes alongside a migration entry). */
38
+ const PANEL_SCHEMA_VERSION_RE = /^1\.\d+\.\d+$/;
39
+ /** Accept any 1.x version; reject other majors loud (F1). Zod `.regex()` is the
40
+ * validation boundary (mirrors schema.ts's `z.string().regex(...)`) — not a bare
41
+ * RegExp.test; the ZodError carries the offending value. */
42
+ const panelSchemaVersionField = z.string().regex(PANEL_SCHEMA_VERSION_RE, {
43
+ message: `unsupported panel-artifact schemaVersion — this reader understands major ${PANEL_ARTIFACT_KNOWN_MAJOR}.x; a new major requires a migration entry in readPanelArtifact`,
44
+ });
45
+ /** sha256 hex content hash (full digest — identity, not display). */
46
+ const SHA256_HEX = /^[0-9a-f]{64}$/;
47
+ /** Zod guard for the content-address id — the validation boundary (mirrors schema.ts; no bare RegExp.test). */
48
+ const Sha256HexSchema = z.string().regex(SHA256_HEX);
49
+ // ─── Diversity ──────────────────────────────────────────────────────────────
50
+ /**
51
+ * The two honest diversity labels.
52
+ *
53
+ * `same-vendor-isolated` means **context-isolation, NOT rater-independence**: the
54
+ * lanes ran in isolated contexts but on one vendor family, so the panel's
55
+ * `verdictDistribution` is **N correlated samples, not N independent votes**
56
+ * (Prop 277 correlated-raters; strategy-claude PP2). A consumer must never read a
57
+ * same-vendor split as independent agreement. `cross-vendor` means ≥2 distinct
58
+ * provider families ran — the Tenet-19 strong-signal case — and is trustworthy
59
+ * only when `diversityConfidence === 'verified'` (see {@link classifyDiversity}).
60
+ */
61
+ export const PanelDiversityClassSchema = z.enum(['cross-vendor', 'same-vendor-isolated']);
62
+ /**
63
+ * Provider strings whose vendor FAMILY is known and currently 1:1 with the
64
+ * string. INVARIANT: `distinctProviders` is a valid independent-cluster count
65
+ * only while provider-string ≡ provider-family (1:1). The day a string that
66
+ * ALIASES one of these families appears (`vertex`→Gemini, `bedrock`→Anthropic,
67
+ * `azure`→OpenAI), counting it as a separate cluster would silently overclaim
68
+ * `cross-vendor`. So an unrecognized string trips `diversityConfidence='coarse'`
69
+ * rather than confidently asserting diversity — the sensor speaking at its own
70
+ * failure point. The actual provider→family MAP is deliberately deferred until a
71
+ * real aliasing string exists (Tenet 21); this allowlist is the tripwire, not
72
+ * the map.
73
+ */
74
+ const KNOWN_PROVIDER_FAMILIES = new Set(['gemini', 'anthropic', 'openai']);
75
+ /**
76
+ * Honest vendor-diversity label for a panel. `providers[]` is ALWAYS present and
77
+ * lossless (per-lane, canonical laneId order) so no derived field can overclaim.
78
+ */
79
+ export const PanelDiversitySchema = z.object({
80
+ /** Per-lane provider strings, lossless, in canonical (laneId-sorted) order. */
81
+ providers: z.array(z.string()),
82
+ /** `new Set(providers).size` — a true cluster count ONLY while confidence is `verified`. */
83
+ distinctProviders: z.number().int().nonnegative(),
84
+ class: PanelDiversityClassSchema,
85
+ /** Sorted unique providers outside {@link KNOWN_PROVIDER_FAMILIES} — the overclaim risk. */
86
+ unrecognizedProviders: z.array(z.string()),
87
+ /** `verified` when every provider's family is known; `coarse` otherwise (don't trust `cross-vendor`). */
88
+ diversityConfidence: z.enum(['verified', 'coarse']),
89
+ });
90
+ // ─── Synthesis ──────────────────────────────────────────────────────────────
91
+ /**
92
+ * One rule's outcome aggregated across all lanes. The dedup anchor is `ruleName`
93
+ * (PostCheckFinding has no path:line anchor); `messages` are preserved VERBATIM
94
+ * (Tenet 9 — no LLM rewrite) and sorted for determinism.
95
+ */
96
+ export const SynthesisFindingSchema = z.object({
97
+ ruleName: z.string(),
98
+ tier: z.enum(['decidable', 'sensor']),
99
+ /** Lane verdict tally; the three keys always sum to `lanes.length` (absent lanes count as `abstain`). */
100
+ verdicts: z.object({
101
+ pass: z.number().int().nonnegative(),
102
+ fail: z.number().int().nonnegative(),
103
+ abstain: z.number().int().nonnegative(),
104
+ }),
105
+ /** True IFF both `pass` and `fail` appear across lanes (`abstain` is neutral). */
106
+ divergent: z.boolean(),
107
+ /** Verbatim lane messages (present lanes only), sorted. */
108
+ messages: z.array(z.string()),
109
+ });
110
+ /**
111
+ * The deterministic aggregation. SENSOR ONLY: a `verdictDistribution` tally (of
112
+ * each lane's own `PostCheckReport.isRejected`) plus per-rule findings and a
113
+ * divergence count — and deliberately NO panel-level `isRejected`/gate boolean.
114
+ * A bare tally invites the vote-counting Tenet 19 forbids, so consumers must
115
+ * lead with divergence + diversity; the tally is one subordinate raw signal.
116
+ */
117
+ export const PanelSynthesisSchema = z.object({
118
+ /** Tally of lane outcomes: `isRejected===false ⟹ accepted`, `true ⟹ rejected`. Sums to lane count. */
119
+ verdictDistribution: z.object({
120
+ accepted: z.number().int().nonnegative(),
121
+ rejected: z.number().int().nonnegative(),
122
+ }),
123
+ findings: z.array(SynthesisFindingSchema),
124
+ /** `=== findings.filter(f => f.divergent).length`. */
125
+ divergences: z.number().int().nonnegative(),
126
+ });
127
+ // ─── Persisted post-check report (a JSON-safe Zod copy of the slice-4 shape) ──
128
+ /**
129
+ * A persisted copy of a {@link PostCheckFinding}. slice-4's report is plain TS
130
+ * (in-memory only); persisting the panel's audit inputs across the disk boundary
131
+ * needs a Zod boundary (codex #1). `context` is OMITTED from the persisted copy:
132
+ * it is rule-specific, unbounded, not JSON-safe by contract, and never load-bearing
133
+ * for synthesis (which keys on `ruleName`) — "constrain JSON-safe or omit", omitted.
134
+ */
135
+ export const PersistedPostCheckFindingSchema = z.object({
136
+ ruleName: z.string(),
137
+ tier: z.enum(['decidable', 'sensor']),
138
+ verdict: z.enum(['pass', 'fail', 'abstain']),
139
+ message: z.string(),
140
+ });
141
+ /**
142
+ * A persisted copy of a {@link PostCheckReport}. The `isRejected` ADR-109
143
+ * invariant is re-validated here at BOTH write and read (codex #1): a stored
144
+ * report whose `isRejected` disagrees with its findings is a corrupt audit
145
+ * record and must be rejected loud, never trusted.
146
+ */
147
+ export const PersistedPostCheckReportSchema = z
148
+ .object({
149
+ findings: z.array(PersistedPostCheckFindingSchema),
150
+ isRejected: z.boolean(),
151
+ })
152
+ .refine((r) => r.isRejected === r.findings.some((f) => f.tier === 'decidable' && f.verdict === 'fail'), {
153
+ message: 'persisted report isRejected must equal "some decidable finding failed" (ADR-109) — record is corrupt',
154
+ });
155
+ // ─── Panel artifact ───────────────────────────────────────────────────────────
156
+ /**
157
+ * One persisted lane: its stable id, the #2100 run artifact, and the persisted
158
+ * #2103 report. The full inputs are stored (not just the aggregate) so a panel
159
+ * is re-auditable offline without re-running rules that may have since changed
160
+ * (codex #1 — the same immutability principle behind RunArtifact).
161
+ */
162
+ export const PanelLaneSchema = z.object({
163
+ laneId: z.string().min(1),
164
+ artifact: RunArtifactSchema,
165
+ report: PersistedPostCheckReportSchema,
166
+ });
167
+ export const PanelArtifactSchema = z
168
+ .object({
169
+ schemaVersion: panelSchemaVersionField,
170
+ /** Persisted lanes, canonical laneId order. At least one — a zero-lane panel is structurally meaningless. */
171
+ lanes: z.array(PanelLaneSchema).min(1),
172
+ diversity: PanelDiversitySchema,
173
+ synthesis: PanelSynthesisSchema,
174
+ /**
175
+ * ISO-8601 emission time. EXCLUDED from the content address (identical panels
176
+ * dedup regardless of when they ran) — observability only.
177
+ */
178
+ createdAt: z.string(),
179
+ })
180
+ // Cross-field invariants the sensor DOCUMENTS are now ENFORCED at the persisted
181
+ // boundary (greptile P2 on mmnto-ai/totem#2179): a hand-edited / corrupt artifact
182
+ // with inconsistent tallies must FAIL parse, not silently violate the stated
183
+ // guarantees — the same .refine() discipline as the ADR-109 isRejected check.
184
+ .superRefine((a, ctx) => {
185
+ const n = a.lanes.length;
186
+ if (a.diversity.providers.length !== n) {
187
+ ctx.addIssue({
188
+ code: z.ZodIssueCode.custom,
189
+ message: `diversity.providers length (${a.diversity.providers.length}) must equal lane count (${n})`,
190
+ });
191
+ }
192
+ else {
193
+ // providers[] must be GROUNDED in the lane records, not merely correct-length
194
+ // (CodeRabbit on mmnto-ai/totem#2179): both are in canonical laneId order, so
195
+ // providers[i] must equal lanes[i]'s backend.provider. Only run when lengths
196
+ // align — otherwise the length issue above already names the root cause, so
197
+ // skipping avoids duplicate issues for one fault (CR noise note on #2179).
198
+ a.lanes.forEach((lane, i) => {
199
+ if (a.diversity.providers[i] !== lane.artifact.backend.provider) {
200
+ ctx.addIssue({
201
+ code: z.ZodIssueCode.custom,
202
+ message: `diversity.providers[${i}] must equal lanes[${i}].artifact.backend.provider ("${lane.artifact.backend.provider}")`,
203
+ });
204
+ }
205
+ });
206
+ }
207
+ // The diversity LABEL fields (distinctProviders / class / unrecognizedProviders /
208
+ // diversityConfidence) are PURE FUNCTIONS of providers[] — re-derive and require a
209
+ // match, so a tampered artifact can't carry diversityConfidence:'verified' or
210
+ // class:'cross-vendor' over an unrecognized alias and bypass the PP1 tripwire at
211
+ // READ (greptile on mmnto-ai/totem#2179). classifyDiversity is the single source of truth.
212
+ const derived = classifyDiversity(a.diversity.providers);
213
+ if (a.diversity.distinctProviders !== derived.distinctProviders) {
214
+ ctx.addIssue({
215
+ code: z.ZodIssueCode.custom,
216
+ message: `diversity.distinctProviders (${a.diversity.distinctProviders}) must equal the value derived from providers (${derived.distinctProviders})`,
217
+ });
218
+ }
219
+ if (a.diversity.class !== derived.class) {
220
+ ctx.addIssue({
221
+ code: z.ZodIssueCode.custom,
222
+ message: `diversity.class "${a.diversity.class}" must equal the value derived from providers ("${derived.class}")`,
223
+ });
224
+ }
225
+ if (a.diversity.diversityConfidence !== derived.diversityConfidence) {
226
+ ctx.addIssue({
227
+ code: z.ZodIssueCode.custom,
228
+ message: `diversity.diversityConfidence "${a.diversity.diversityConfidence}" must equal the value derived from providers ("${derived.diversityConfidence}")`,
229
+ });
230
+ }
231
+ if (a.diversity.unrecognizedProviders.length !== derived.unrecognizedProviders.length ||
232
+ !a.diversity.unrecognizedProviders.every((p, i) => p === derived.unrecognizedProviders[i])) {
233
+ ctx.addIssue({
234
+ code: z.ZodIssueCode.custom,
235
+ message: `diversity.unrecognizedProviders must equal the set derived from providers ([${derived.unrecognizedProviders.join(', ')}])`,
236
+ });
237
+ }
238
+ const vd = a.synthesis.verdictDistribution;
239
+ if (vd.accepted + vd.rejected !== n) {
240
+ ctx.addIssue({
241
+ code: z.ZodIssueCode.custom,
242
+ message: `verdictDistribution (accepted ${vd.accepted} + rejected ${vd.rejected}) must sum to lane count (${n})`,
243
+ });
244
+ }
245
+ for (const f of a.synthesis.findings) {
246
+ const sum = f.verdicts.pass + f.verdicts.fail + f.verdicts.abstain;
247
+ if (sum !== n) {
248
+ ctx.addIssue({
249
+ code: z.ZodIssueCode.custom,
250
+ message: `finding "${f.ruleName}" verdicts sum (${sum}) must equal lane count (${n}) — present verdicts plus implicit abstain`,
251
+ });
252
+ }
253
+ if (f.divergent !== (f.verdicts.pass > 0 && f.verdicts.fail > 0)) {
254
+ ctx.addIssue({
255
+ code: z.ZodIssueCode.custom,
256
+ message: `finding "${f.ruleName}" divergent flag must equal (pass > 0 && fail > 0)`,
257
+ });
258
+ }
259
+ }
260
+ const divergent = a.synthesis.findings.filter((f) => f.divergent).length;
261
+ if (a.synthesis.divergences !== divergent) {
262
+ ctx.addIssue({
263
+ code: z.ZodIssueCode.custom,
264
+ message: `synthesis.divergences (${a.synthesis.divergences}) must equal the count of divergent findings (${divergent})`,
265
+ });
266
+ }
267
+ });
268
+ // ─── Pure aggregators ─────────────────────────────────────────────────────────
269
+ /**
270
+ * Label the vendor diversity of a panel from its per-lane provider strings.
271
+ * Pure. The `class` is the mechanical reading of `distinctProviders`; the
272
+ * `diversityConfidence` marker is whether that reading is trustworthy — `coarse`
273
+ * means an unrecognized provider string is present, so a `cross-vendor` class
274
+ * MUST NOT be taken as a confident independent-cluster claim (strategy-claude
275
+ * PP1/OQ2 tripwire). See {@link KNOWN_PROVIDER_FAMILIES} for the invariant.
276
+ */
277
+ export function classifyDiversity(providers) {
278
+ const distinctProviders = new Set(providers).size;
279
+ const unrecognizedProviders = [
280
+ ...new Set(providers.filter((p) => !KNOWN_PROVIDER_FAMILIES.has(p))),
281
+ ].sort();
282
+ return {
283
+ providers: [...providers],
284
+ distinctProviders,
285
+ class: distinctProviders >= 2 ? 'cross-vendor' : 'same-vendor-isolated',
286
+ unrecognizedProviders,
287
+ diversityConfidence: unrecognizedProviders.length === 0 ? 'verified' : 'coarse',
288
+ };
289
+ }
290
+ /**
291
+ * Aggregate N lanes into a {@link PanelSynthesis}. Pure, deterministic, zero-LLM.
292
+ * Output is identical under any permutation of `lanes` (findings sorted by
293
+ * `ruleName`; per-rule lanes walked in laneId order; messages sorted).
294
+ *
295
+ * Throws (fail-loud, never silently degrade — Tenet 4) on a corrupt input
296
+ * contract: a lane with two findings sharing one `ruleName` (would masquerade as
297
+ * cross-lane agreement — codex #2), or one `ruleName` carrying conflicting
298
+ * `tier`s across lanes (a rule's tier is static — codex #9). A `ruleName` simply
299
+ * ABSENT from some lanes is NOT an error: those lanes count as implicit
300
+ * `abstain` (agy #4), so each finding's verdicts sum to `lanes.length`.
301
+ */
302
+ export function synthesizePanel(lanes) {
303
+ if (lanes.length === 0) {
304
+ throw new Error('synthesizePanel requires at least one lane (got 0).');
305
+ }
306
+ const ordered = [...lanes].sort((a, b) => a.laneId.localeCompare(b.laneId));
307
+ // Index each lane's findings by ruleName, hard-erroring on within-lane dupes.
308
+ const perLane = ordered.map((lane) => {
309
+ const byRule = new Map();
310
+ for (const f of lane.report.findings) {
311
+ if (byRule.has(f.ruleName)) {
312
+ throw new Error(`lane "${lane.laneId}" has duplicate finding for ruleName "${f.ruleName}" — within-lane duplicates would masquerade as cross-lane agreement.`);
313
+ }
314
+ byRule.set(f.ruleName, { tier: f.tier, verdict: f.verdict, message: f.message });
315
+ }
316
+ return byRule;
317
+ });
318
+ const ruleNames = [...new Set(perLane.flatMap((m) => [...m.keys()]))].sort();
319
+ const findings = ruleNames.map((ruleName) => {
320
+ const verdicts = { pass: 0, fail: 0, abstain: 0 };
321
+ const messages = [];
322
+ let tier;
323
+ for (const byRule of perLane) {
324
+ const hit = byRule.get(ruleName);
325
+ if (hit === undefined) {
326
+ // Absent here = the rule did not apply to this lane (heterogeneous
327
+ // appliesTo) → implicit abstain, keeping Σ verdicts === lanes.length.
328
+ verdicts.abstain += 1;
329
+ continue;
330
+ }
331
+ if (tier !== undefined && tier !== hit.tier) {
332
+ throw new Error(`ruleName "${ruleName}" has conflicting tiers across lanes ("${tier}" vs "${hit.tier}") — a rule's tier is static.`);
333
+ }
334
+ tier = hit.tier;
335
+ verdicts[hit.verdict] += 1;
336
+ messages.push(hit.message);
337
+ }
338
+ return {
339
+ ruleName,
340
+ // `tier` is defined: a ruleName is in the set only if ≥1 lane carried it.
341
+ tier: tier,
342
+ verdicts,
343
+ divergent: verdicts.pass > 0 && verdicts.fail > 0,
344
+ messages: messages.sort(),
345
+ };
346
+ });
347
+ const verdictDistribution = { accepted: 0, rejected: 0 };
348
+ for (const lane of ordered) {
349
+ if (lane.report.isRejected)
350
+ verdictDistribution.rejected += 1;
351
+ else
352
+ verdictDistribution.accepted += 1;
353
+ }
354
+ return {
355
+ verdictDistribution,
356
+ findings,
357
+ divergences: findings.filter((f) => f.divergent).length,
358
+ };
359
+ }
360
+ /**
361
+ * Assemble a full {@link PanelArtifact} from lane inputs. Pure. Canonicalizes
362
+ * lane order by `laneId` so the content address is stable regardless of caller /
363
+ * completion order, derives diversity from the per-lane providers (same canonical
364
+ * order), synthesizes, and validates the whole shape (which re-checks each
365
+ * persisted report's ADR-109 invariant). `createdAt` is supplied by the caller
366
+ * (this module reads no clock — determinism).
367
+ */
368
+ export function assemblePanelArtifact(lanes, createdAt) {
369
+ if (lanes.length === 0) {
370
+ throw new Error('assemblePanelArtifact requires at least one lane (got 0).');
371
+ }
372
+ // synthesizePanel owns its own canonical sort (it is independently callable), so
373
+ // hand it the raw lanes rather than a pre-sorted copy it would only re-sort (CR
374
+ // nit on mmnto-ai/totem#2179). `ordered` is assemble's OWN canonical order, needed
375
+ // for the persisted lanes[] and the per-lane diversity providers[] (both go to disk).
376
+ const synthesis = synthesizePanel(lanes);
377
+ const ordered = [...lanes].sort((a, b) => a.laneId.localeCompare(b.laneId));
378
+ const diversity = classifyDiversity(ordered.map((l) => l.artifact.backend.provider));
379
+ const persistedLanes = ordered.map((l) => ({
380
+ laneId: l.laneId,
381
+ artifact: l.artifact,
382
+ report: {
383
+ isRejected: l.report.isRejected,
384
+ findings: l.report.findings.map((f) => ({
385
+ ruleName: f.ruleName,
386
+ tier: f.tier,
387
+ verdict: f.verdict,
388
+ message: f.message,
389
+ })),
390
+ },
391
+ }));
392
+ // parse() validates the assembled shape AND each report's isRejected invariant
393
+ // on the way out — a builder bug must never poison the ledger (Tenet 4).
394
+ return PanelArtifactSchema.parse({
395
+ schemaVersion: PANEL_ARTIFACT_SCHEMA_VERSION,
396
+ lanes: persistedLanes,
397
+ diversity,
398
+ synthesis,
399
+ createdAt,
400
+ });
401
+ }
402
+ // ─── Content-addressed storage (mirrors storage.ts exactly) ───────────────────
403
+ /** Storage layout segments under the totem dir. */
404
+ const PANELS_DIR_SEGMENTS = ['artifacts', 'panels'];
405
+ /**
406
+ * Migration-on-read registry (F1). Keyed by MAJOR. EMPTY at 1.0.0 by design —
407
+ * the policy requires a major bump to land its migration entry here before the
408
+ * writer ships, so empty is the honest statement that no other major exists. Each
409
+ * entry MUST return current-schema output; readPanelArtifact re-validates it via
410
+ * parse() before returning (CR note on mmnto-ai/totem#2179).
411
+ */
412
+ const MIGRATIONS = new Map();
413
+ /** Absolute panels directory for a given absolute totem dir. */
414
+ export function panelsDir(totemDirAbs) {
415
+ return path.join(totemDirAbs, ...PANELS_DIR_SEGMENTS);
416
+ }
417
+ /**
418
+ * Content address of a panel: deterministic hash over everything EXCEPT
419
+ * `createdAt` (observability, not identity). The artifact is already canonical
420
+ * (assemblePanelArtifact sorts lanes/findings/messages), so the address is a
421
+ * pure function of the logical panel.
422
+ */
423
+ export function computePanelArtifactContentHash(artifact) {
424
+ const { createdAt: _excluded, ...identity } = artifact;
425
+ return calculateDeterministicHash(identity);
426
+ }
427
+ /**
428
+ * Persist a panel at its content address, write-if-absent. An existing file is
429
+ * NEVER rewritten (append-only: first write wins). Validates on the way out so a
430
+ * writer bug cannot poison the ledger with a record the reader would reject.
431
+ */
432
+ export function writePanelArtifact(totemDirAbs, artifact) {
433
+ const validated = PanelArtifactSchema.parse(artifact);
434
+ const hash = computePanelArtifactContentHash(validated);
435
+ const dir = panelsDir(totemDirAbs);
436
+ const filePath = path.join(dir, `${hash}.json`);
437
+ if (fs.existsSync(filePath)) {
438
+ return { hash, path: filePath, existed: true };
439
+ }
440
+ fs.mkdirSync(dir, { recursive: true });
441
+ try {
442
+ // `wx` = atomic create-exclusive: closes the TOCTOU window between the
443
+ // existsSync fast-path and the write so concurrent saves of the same hash
444
+ // can never overwrite the first record — first-write-wins is enforced by
445
+ // the filesystem. mode 0o600 matches run storage: panels embed run records
446
+ // that carry masked prompt content.
447
+ fs.writeFileSync(filePath, JSON.stringify(validated, null, 2), {
448
+ encoding: 'utf-8',
449
+ mode: 0o600,
450
+ flag: 'wx',
451
+ });
452
+ }
453
+ catch (err) {
454
+ if (err !== null && typeof err === 'object' && 'code' in err && err.code === 'EEXIST') {
455
+ return { hash, path: filePath, existed: true };
456
+ }
457
+ throw err;
458
+ }
459
+ return { hash, path: filePath, existed: false };
460
+ }
461
+ /**
462
+ * Load + validate a panel by content address. Throws {@link TotemParseError} on
463
+ * a missing file, corrupt JSON, schema violation, an unknown major with no
464
+ * migration entry, or a persisted report whose `isRejected` invariant is broken
465
+ * — loud, never a silent partial (Tenet 4).
466
+ */
467
+ export function readPanelArtifact(totemDirAbs, hash) {
468
+ if (!Sha256HexSchema.safeParse(hash).success) {
469
+ throw new TotemParseError(`Invalid panel-artifact id "${hash}" — expected a 64-char sha256 hex content address.`, 'Pass the hash exactly as reported at emission (or from the artifacts/panels/ filename).');
470
+ }
471
+ const filePath = path.join(panelsDir(totemDirAbs), `${hash}.json`);
472
+ // Migration seam (F1): peek the major BEFORE strict validation so a known
473
+ // older major routes through its migration. Empty registry ⇒ straight
474
+ // fall-through today.
475
+ const raw = readJsonSafe(filePath);
476
+ const major = readMajor(raw);
477
+ if (major !== undefined) {
478
+ const migrate = MIGRATIONS.get(major);
479
+ // Re-validate migrated output against the CURRENT schema before returning (CR
480
+ // note on mmnto-ai/totem#2179): a migration's contract is to PRODUCE the current
481
+ // shape, so a migration bug must fail loud here — never return it unvalidated.
482
+ if (migrate !== undefined)
483
+ return PanelArtifactSchema.parse(migrate(raw));
484
+ }
485
+ try {
486
+ return PanelArtifactSchema.parse(raw);
487
+ // totem-context: rethrowAsParseError always throws (returns `never`) — this catch RE-throws via the shared helper, normalizing ZodError to the module's stated TotemParseError load contract; nothing is swallowed.
488
+ }
489
+ catch (err) {
490
+ rethrowAsParseError(`Panel artifact ${hash} failed schema validation`, err, 'The artifact may be corrupted or written by an incompatible totem version; re-emit it (or add the migration entry for its major).');
491
+ }
492
+ }
493
+ /** Best-effort major extraction from a raw parsed payload; undefined when absent/garbled. */
494
+ function readMajor(raw) {
495
+ if (typeof raw !== 'object' || raw === null || !('schemaVersion' in raw))
496
+ return undefined;
497
+ const version = raw.schemaVersion;
498
+ if (typeof version !== 'string')
499
+ return undefined;
500
+ const major = Number.parseInt(version.split('.')[0] ?? '', 10);
501
+ return Number.isNaN(major) ? undefined : major;
502
+ }
503
+ //# sourceMappingURL=panel.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"panel.js","sourceRoot":"","sources":["../../src/artifacts/panel.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Panel-synthesis engine tests (mmnto-ai/totem#2104, strategy#474 slice 5).
3
+ *
4
+ * The invariants the cohort + strategy pre-build rounds locked in:
5
+ * - Tenet 9: aggregation is a pure deterministic script (order-independent).
6
+ * - dedup anchor = `ruleName`; divergence = pass∧fail present, abstain neutral.
7
+ * - honest diversity (Prop 291): distinctProviders is a cluster count only while
8
+ * provider-string ≡ family; an unrecognized string trips `coarse` (PP1 tripwire).
9
+ * - sensor-only: no panel-level gate field (PP3).
10
+ * - codex folds: persist reports w/ ADR-109 invariant at write+read; within-lane
11
+ * duplicate ruleName throws; conflicting tier throws; canonical-order hash.
12
+ * - agy folds: missing ruleName across lanes ⇒ implicit abstain (Σ === N); N=1.
13
+ */
14
+ export {};
15
+ //# sourceMappingURL=panel.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"panel.test.d.ts","sourceRoot":"","sources":["../../src/artifacts/panel.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG"}