@mmnto/totem 1.105.0 → 1.107.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 (49) hide show
  1. package/dist/ast-classifier.d.ts +3 -3
  2. package/dist/ast-classifier.js +12 -3
  3. package/dist/ast-classifier.js.map +1 -1
  4. package/dist/ast-classifier.test.js +33 -2
  5. package/dist/ast-classifier.test.js.map +1 -1
  6. package/dist/capability/schema.d.ts +2 -2
  7. package/dist/errors.d.ts +3 -1
  8. package/dist/errors.d.ts.map +1 -1
  9. package/dist/errors.js.map +1 -1
  10. package/dist/index.d.ts +8 -2
  11. package/dist/index.d.ts.map +1 -1
  12. package/dist/index.js +11 -2
  13. package/dist/index.js.map +1 -1
  14. package/dist/ledger.d.ts +92 -4
  15. package/dist/ledger.d.ts.map +1 -1
  16. package/dist/ledger.js +91 -13
  17. package/dist/ledger.js.map +1 -1
  18. package/dist/qbd/compliance.d.ts +216 -0
  19. package/dist/qbd/compliance.d.ts.map +1 -0
  20. package/dist/qbd/compliance.js +490 -0
  21. package/dist/qbd/compliance.js.map +1 -0
  22. package/dist/qbd/compliance.test.d.ts +2 -0
  23. package/dist/qbd/compliance.test.d.ts.map +1 -0
  24. package/dist/qbd/compliance.test.js +539 -0
  25. package/dist/qbd/compliance.test.js.map +1 -0
  26. package/dist/qbd/correlation-id.d.ts +130 -0
  27. package/dist/qbd/correlation-id.d.ts.map +1 -0
  28. package/dist/qbd/correlation-id.js +224 -0
  29. package/dist/qbd/correlation-id.js.map +1 -0
  30. package/dist/qbd/correlation-id.test.d.ts +2 -0
  31. package/dist/qbd/correlation-id.test.d.ts.map +1 -0
  32. package/dist/qbd/correlation-id.test.js +136 -0
  33. package/dist/qbd/correlation-id.test.js.map +1 -0
  34. package/dist/qbd/record.d.ts +140 -0
  35. package/dist/qbd/record.d.ts.map +1 -0
  36. package/dist/qbd/record.js +419 -0
  37. package/dist/qbd/record.js.map +1 -0
  38. package/dist/qbd/record.test.d.ts +27 -0
  39. package/dist/qbd/record.test.d.ts.map +1 -0
  40. package/dist/qbd/record.test.js +607 -0
  41. package/dist/qbd/record.test.js.map +1 -0
  42. package/dist/rule-engine.test.js +85 -0
  43. package/dist/rule-engine.test.js.map +1 -1
  44. package/dist/session-id.d.ts.map +1 -1
  45. package/dist/session-id.js +19 -3
  46. package/dist/session-id.js.map +1 -1
  47. package/dist/session-id.test.js +35 -0
  48. package/dist/session-id.test.js.map +1 -1
  49. package/package.json +1 -1
@@ -0,0 +1,490 @@
1
+ /**
2
+ * Query-before-derive compliance — the metric (mmnto-ai/totem#2510).
3
+ *
4
+ * ## The number
5
+ *
6
+ * Of the derive-class actions in an agent session (spec synthesis, orientation
7
+ * derivation, review grounding), what fraction was preceded by a corpus query
8
+ * correlated to that action?
9
+ *
10
+ * compliance = correlated derive events / ALL derive events
11
+ *
12
+ * ## Denominator discipline (#2510 falsifier 1)
13
+ *
14
+ * The denominator is **every** derive-class event in the evaluated sessions,
15
+ * including sessions that fired zero queries. A session that derived without
16
+ * ever querying is the exact behaviour this metric exists to catch, so it must
17
+ * pull the number DOWN, not vanish from it. Two places this could have been
18
+ * gamed, and how each is closed:
19
+ *
20
+ * - *at the ratio level* — counting only derives that carry an ID. Closed: the
21
+ * denominator counts derive rows, correlated or not.
22
+ * - *at the window level* — enumerating only sessions that contain queries.
23
+ * Closed: session selection keys on "carries ≥1 derive-class event,
24
+ * regardless of query count", which is the operator-pinned window phrasing.
25
+ *
26
+ * ## Numerator discipline (#2510 falsifier 2)
27
+ *
28
+ * A derive counts as correlated only when its `qbd_correlation_id` resolves to
29
+ * a `corpus_query` row that actually appears earlier in the ledger, in the same
30
+ * session and from the same seat. Ways that check fails, all counted as
31
+ * anomalies and none as compliant: a schema-rejected ID (see
32
+ * `correlation-id.ts`), an ID with no matching query row (orphan — a truncated
33
+ * or tampered ledger), an ID whose query row belongs to a different session or
34
+ * a different seat, and a row whose timestamp regresses in an append-only file
35
+ * (a backdated append trying to seize the evaluation window).
36
+ *
37
+ * One query grounds exactly ONE derive — the pointer is consumed on use (see
38
+ * `record.ts`). Without that, a single query would credit every derive for the
39
+ * rest of the correlation window.
40
+ *
41
+ * ## What this metric CANNOT see (#2510 falsifier 4, ritual query)
42
+ *
43
+ * A query fired purely to satisfy the metric, whose results the following
44
+ * derive never reads, is indistinguishable here from a query that genuinely
45
+ * grounded the derive. Both produce a query row and a correlated derive row.
46
+ * v1 senses adjacency, not influence. This is named, not solved — the honest
47
+ * statement is that the number measures whether querying happened before
48
+ * deriving, NOT whether the derive used what the query returned. Any reading
49
+ * that treats it as the latter is over-claiming.
50
+ *
51
+ * ## Degraded reads (ADR-115 § 2)
52
+ *
53
+ * `readLedgerEvents` skips schema-invalid lines silently, which would let a torn
54
+ * or tampered ledger render as a confident 100% or a confident 0%. This scanner
55
+ * therefore parses the NDJSON itself and counts every rejected line by class. A
56
+ * scan with any integrity anomaly is `degraded`, and the render must announce
57
+ * that rather than print a bare number.
58
+ */
59
+ import { LEDGER_EVENT_TYPES, LedgerEventSchema, QBD_EVENT_TYPES } from '../ledger.js';
60
+ import { QBD_CORRELATION_WINDOW_MS } from './correlation-id.js';
61
+ // ─── Pre-registration (fixed before the sensor first emitted) ───
62
+ /** Pre-registered floor. Below this at the checkpoint, the claim is falsified. */
63
+ export const QBD_PRE_REGISTERED_THRESHOLD = 0.5;
64
+ /** Pre-registered evaluation window, in sessions. */
65
+ export const QBD_PRE_REGISTERED_WINDOW_SESSIONS = 20;
66
+ /**
67
+ * The pre-registration, verbatim as pinned by the operator ruling 2026-07-28 on
68
+ * mmnto-ai/totem#2510. Rendered verbatim by the doctor section — the exact
69
+ * phrasing is load-bearing: an earlier "first 20 correlated sessions" wording
70
+ * was post-hoc-interpretable as excluding zero-query sessions, which would have
71
+ * enacted falsifier 1 at the window level. Do not paraphrase this string.
72
+ */
73
+ export const QBD_PRE_REGISTRATION_STATEMENT = 'compliance ≥ 0.50, evaluated over the first 20 instrumented sessions carrying ≥1 derive-class event, regardless of query count';
74
+ /**
75
+ * Window for the id-less rolling-window session fallback. Same span as the
76
+ * correlation window so the slice carries one time constant rather than two;
77
+ * see `correlation-id.ts` for why that value is this slice's design call and
78
+ * not something ADR-029 § 2 authorises.
79
+ */
80
+ const SESSION_ROLLUP_WINDOW_MS = QBD_CORRELATION_WINDOW_MS;
81
+ /** Minimum instrumented sessions per side before a trend is worth printing. */
82
+ const MIN_SESSIONS_PER_TREND_SIDE = 2;
83
+ // ─── Scan ───────────────────────────────────────────────
84
+ /**
85
+ * Event types that exist in the ledger and are not part of this metric.
86
+ *
87
+ * DERIVED from the ledger schema's own type list, never hand-mirrored: a copied
88
+ * list rots the moment a type is added to `ledger.ts`, and here that rot is not
89
+ * cosmetic — an unrecognised type used to flip the whole scan to DEGRADED.
90
+ * `ledger-type-parity` in the tests pins this to the schema.
91
+ */
92
+ const NON_QBD_TYPES = new Set(LEDGER_EVENT_TYPES.filter((t) => !QBD_EVENT_TYPES.includes(t)));
93
+ /**
94
+ * How far a row's timestamp may regress behind the newest timestamp seen so far
95
+ * before the scan calls it backdated.
96
+ *
97
+ * The ledger is append-only, so timestamps should march forward. Small
98
+ * regressions are legitimate — concurrent writers in one repo, sub-second clock
99
+ * jitter, a cohort seat whose clock drifts slightly — so the tolerance is
100
+ * generous. What it catches is the real attack: APPENDING rows stamped far in
101
+ * the past to seize the pre-registered first-20-session window.
102
+ */
103
+ const BACKDATE_TOLERANCE_MS = 5 * 60 * 1000;
104
+ /** Cap on stored detail strings so a pathological ledger cannot balloon memory. */
105
+ const MAX_ANOMALY_DETAILS = 20;
106
+ function emptyAnomalies() {
107
+ return {
108
+ malformedJson: 0,
109
+ correlationContractViolations: 0,
110
+ unclassifiedInvalid: 0,
111
+ orphanCorrelations: 0,
112
+ crossSessionCorrelations: 0,
113
+ duplicateCorrelations: 0,
114
+ seatMismatchHints: 0,
115
+ backdatedRows: 0,
116
+ unknownTypeRows: 0,
117
+ details: [],
118
+ };
119
+ }
120
+ function pushDetail(anomalies, detail) {
121
+ if (anomalies.details.length < MAX_ANOMALY_DETAILS)
122
+ anomalies.details.push(detail);
123
+ }
124
+ /**
125
+ * Parse the raw `events.ndjson` contents, lifting out QBD rows and counting
126
+ * every line this metric could not trust.
127
+ *
128
+ * Deliberately does NOT use `readLedgerEvents`: that helper drops schema-invalid
129
+ * lines without telling anyone, which is precisely how a tampered ledger would
130
+ * render as a clean number.
131
+ */
132
+ export function scanQbdLedger(content) {
133
+ const rows = [];
134
+ const anomalies = emptyAnomalies();
135
+ let linesScanned = 0;
136
+ /** Newest QBD timestamp seen so far, in FILE order — the monotonicity baseline. */
137
+ let maxSeenMs;
138
+ const lines = content.split('\n');
139
+ for (let i = 0; i < lines.length; i++) {
140
+ const line = lines[i].trim();
141
+ if (line.length === 0)
142
+ continue;
143
+ linesScanned++;
144
+ let parsed;
145
+ try {
146
+ parsed = JSON.parse(line);
147
+ // totem-context: a torn or hand-edited NDJSON line is counted (malformedJson) and surfaced in the DEGRADED envelope per ADR-115 § 2 — never silently dropped, and never a crash of the read-only sensor.
148
+ }
149
+ catch {
150
+ anomalies.malformedJson++;
151
+ pushDetail(anomalies, `line ${i + 1}: not valid JSON`);
152
+ continue;
153
+ }
154
+ const rawObject = typeof parsed === 'object' && parsed !== null
155
+ ? parsed
156
+ : undefined;
157
+ const rawType = rawObject?.type;
158
+ const result = LedgerEventSchema.safeParse(parsed);
159
+ if (!result.success) {
160
+ // ORDER IS LOAD-BEARING. The QBD-marker test must run BEFORE the
161
+ // "belongs to another subsystem" fast path. A row can carry a forged
162
+ // `qbd_correlation_id` while claiming a non-QBD `type` — and if the
163
+ // fast path ran first, that row would be dropped with zero anomalies
164
+ // and the ledger would render clean. That is exactly the tampering
165
+ // ADR-115 § 2 exists to make impossible to miss, so a qbd-field-bearing
166
+ // schema failure is a contract violation on ANY type.
167
+ const looksQbd = rawType === 'corpus_query' ||
168
+ rawType === 'derive_action' ||
169
+ (rawObject !== undefined && 'qbd_correlation_id' in rawObject);
170
+ const reason = result.error.issues.map((issue) => issue.message).join('; ');
171
+ if (looksQbd) {
172
+ anomalies.correlationContractViolations++;
173
+ pushDetail(anomalies, `line ${i + 1}: correlation contract violation — ${reason}`);
174
+ continue;
175
+ }
176
+ if (typeof rawType === 'string' && NON_QBD_TYPES.has(rawType)) {
177
+ // Another subsystem's row failed its OWN schema and carries no QBD
178
+ // markers. Not this metric's integrity problem, not counted against it.
179
+ continue;
180
+ }
181
+ if (typeof rawType === 'string' && rawType.length > 0) {
182
+ // A type this build does not know. The benign cause is version skew —
183
+ // a newer writer in the same repo emitting an event type this binary
184
+ // predates — which is emphatically NOT evidence that the ledger was
185
+ // tampered with. Report it as a named advisory; do NOT flip DEGRADED,
186
+ // or routine skew would drown the signal that real tampering produces.
187
+ anomalies.unknownTypeRows++;
188
+ pushDetail(anomalies, `line ${i + 1}: unknown event type '${rawType}' (this build predates it — version skew, not tampering)`);
189
+ continue;
190
+ }
191
+ // No usable type at all — this is structural corruption, not skew.
192
+ anomalies.unclassifiedInvalid++;
193
+ pushDetail(anomalies, `line ${i + 1}: unclassifiable invalid row — ${reason}`);
194
+ continue;
195
+ }
196
+ const event = result.data;
197
+ const ms = Date.parse(event.timestamp);
198
+ if (event.type !== 'corpus_query' && event.type !== 'derive_action') {
199
+ // Not our row — but its timestamp still advances the monotonicity
200
+ // reference. This is load-bearing: on a ledger adopted mid-life, the
201
+ // EXISTING non-QBD history is the only baseline a backdated QBD append
202
+ // can be measured against. Referencing QBD rows alone left the check
203
+ // blind in exactly the fresh-adopter state the pre-registered first-20
204
+ // window is filled from — 20 backdated pairs read PASS, degraded=false.
205
+ if (Number.isFinite(ms) && (maxSeenMs === undefined || ms > maxSeenMs))
206
+ maxSeenMs = ms;
207
+ continue;
208
+ }
209
+ if (!Number.isFinite(ms)) {
210
+ anomalies.unclassifiedInvalid++;
211
+ pushDetail(anomalies, `line ${i + 1}: QBD row with an unparseable timestamp`);
212
+ continue;
213
+ }
214
+ // Append-only monotonicity. The ledger only ever grows at the end, so a row
215
+ // stamped far behind the newest one already seen was APPENDED with a
216
+ // backdated timestamp. That is the cheap attack on a "first N sessions"
217
+ // window — no whole-file rewrite required, just `>> events.ndjson` with old
218
+ // stamps — so it must degrade the read. The row is still counted (dropping
219
+ // it would shrink the denominator, which is the other failure mode); the
220
+ // scan simply refuses to call the resulting number verified.
221
+ if (maxSeenMs !== undefined && ms < maxSeenMs - BACKDATE_TOLERANCE_MS) {
222
+ anomalies.backdatedRows++;
223
+ pushDetail(anomalies, `line ${i + 1}: timestamp ${new Date(ms).toISOString()} regresses more than BACKDATE_TOLERANCE_MS (${BACKDATE_TOLERANCE_MS}ms) behind ${new Date(maxSeenMs).toISOString()} in an append-only ledger`);
224
+ }
225
+ if (maxSeenMs === undefined || ms > maxSeenMs)
226
+ maxSeenMs = ms;
227
+ rows.push({
228
+ ms,
229
+ type: event.type,
230
+ ...(event.activity_name !== undefined && { activityName: event.activity_name }),
231
+ ...(event.session_id !== undefined && { sessionId: event.session_id }),
232
+ ...(event.qbd_correlation_id !== undefined && { correlationId: event.qbd_correlation_id }),
233
+ ...(event.agent_source !== undefined && { agentSource: event.agent_source }),
234
+ });
235
+ }
236
+ rows.sort((a, b) => a.ms - b.ms);
237
+ // `unknownTypeRows` is deliberately absent: version skew is not tampering.
238
+ const degraded = anomalies.malformedJson > 0 ||
239
+ anomalies.correlationContractViolations > 0 ||
240
+ anomalies.unclassifiedInvalid > 0 ||
241
+ anomalies.backdatedRows > 0;
242
+ return { rows, linesScanned, anomalies, degraded };
243
+ }
244
+ /**
245
+ * Group rows into sessions.
246
+ *
247
+ * Rows carrying a `session_id` group by it. Rows without one (hookless agents,
248
+ * pre-hook runs) fall back to a rolling-window roll-up, applied among
249
+ * themselves and PARTITIONED BY `agent_source`. Both are "instrumented
250
+ * sessions"; neither is privileged.
251
+ *
252
+ * The agent partition matters concretely: cohort seats share one working tree
253
+ * per repo, so two seats writing to the same ledger inside the same two-hour
254
+ * span would otherwise be rolled into a single "session", letting one seat's
255
+ * query sit in the same session as another seat's derive. Sessions are
256
+ * per-agent, so the fallback is too.
257
+ *
258
+ * Note on provenance: preferring an explicit session id over a time heuristic
259
+ * follows the `.session-id` primitive's own documented contract
260
+ * (`session-id.ts`), not ADR-029 § 2 — that section defines a session-GROUPING
261
+ * heuristic for the recall metric, which is a different question.
262
+ */
263
+ export function groupQbdSessions(rows) {
264
+ const byId = new Map();
265
+ const idless = [];
266
+ for (const row of rows) {
267
+ if (row.sessionId === undefined) {
268
+ idless.push(row);
269
+ continue;
270
+ }
271
+ // Key on the agent too: a session id is only unique within the agent that
272
+ // minted it, and two seats must never share a session bucket.
273
+ const key = `sid:${row.agentSource ?? ''}:${row.sessionId}`;
274
+ const existing = byId.get(key);
275
+ if (existing === undefined) {
276
+ byId.set(key, { key, firstMs: row.ms, rows: [row] });
277
+ }
278
+ else {
279
+ existing.rows.push(row);
280
+ if (row.ms < existing.firstMs)
281
+ existing.firstMs = row.ms;
282
+ }
283
+ }
284
+ const sessions = [...byId.values()];
285
+ // Rolling-window fallback for id-less rows, PER AGENT (rows are time-sorted).
286
+ const openByAgent = new Map();
287
+ let windowIndex = 0;
288
+ for (const row of idless) {
289
+ const agent = row.agentSource ?? '';
290
+ const open = openByAgent.get(agent);
291
+ if (open === undefined || row.ms - open.lastMs > SESSION_ROLLUP_WINDOW_MS) {
292
+ const session = {
293
+ key: `win:${agent}:${windowIndex++}`,
294
+ firstMs: row.ms,
295
+ rows: [row],
296
+ };
297
+ sessions.push(session);
298
+ openByAgent.set(agent, { session, lastMs: row.ms });
299
+ }
300
+ else {
301
+ open.session.rows.push(row);
302
+ open.lastMs = row.ms;
303
+ }
304
+ }
305
+ sessions.sort((a, b) => a.firstMs - b.firstMs);
306
+ return sessions;
307
+ }
308
+ // ─── Compute ────────────────────────────────────────────
309
+ /**
310
+ * Decide which derive rows earn credit, across the WHOLE scan at once.
311
+ *
312
+ * Runs globally rather than per-session for two reasons: a correlation ID's
313
+ * query row may live outside the session being scored, and — the important one
314
+ * — **the 1:1 rule has to be enforced against every derive in the file**, not
315
+ * within one session's slice.
316
+ *
317
+ * ## Why the read side re-enforces a write-side rule
318
+ *
319
+ * `record.ts` consumes the pointer so one query grounds one derive. That is a
320
+ * write-time rule with no read-time check, which is the same shape as the
321
+ * tampering the scanner already exists to catch — and it is reachable without
322
+ * any adversary: the documented `clearPointer` failure ("may over-credit until
323
+ * removed"), the non-atomic read-then-clear between concurrent derives, or a
324
+ * duplicated line all produce several derives citing one ID. Before this, three
325
+ * derives on one query read as `compliance 1.00, degraded=false`.
326
+ *
327
+ * So: the FIRST derive (in time order) citing an ID takes the credit, and every
328
+ * later citation of that same ID is a named anomaly that degrades the read. The
329
+ * non-atomic read-then-clear race in `record.ts` is accepted *because* this
330
+ * check catches its result rather than silently inflating the number.
331
+ *
332
+ * Mutates `anomalies` with per-item counts. Returns the set of credited rows.
333
+ */
334
+ function creditDerives(rows, queryIndex, anomalies) {
335
+ const credited = new Set();
336
+ /** Correlation IDs already spent. One query grounds one derive. */
337
+ const consumed = new Set();
338
+ // Query rows in time order, for the seat-mismatch diagnostic below.
339
+ const queries = rows.filter((r) => r.type === 'corpus_query');
340
+ // `rows` is time-sorted by the scanner, so "first" is unambiguous.
341
+ for (const row of rows) {
342
+ if (row.type !== 'derive_action')
343
+ continue;
344
+ if (row.correlationId === undefined) {
345
+ // Seat-plumbing diagnostic (NOT an integrity anomaly). An uncorrelated
346
+ // derive whose in-window candidate query carries a DIFFERENT seat is the
347
+ // signature of one-sided seating — e.g. a seated CLI and an unseated MCP
348
+ // server — which silently drives the number to 0.00 with no other tell.
349
+ // A config smell, not tampering, so it never degrades the read.
350
+ // The hint means "this derive had NO same-seat query to correlate to, but
351
+ // a different seat was querying in the same window" — the one-sided-seat
352
+ // signature. Seeing a different-seat query is therefore not sufficient:
353
+ // if a same-seat query is also in-window, the uncorrelated derive has an
354
+ // ordinary explanation (spent pointer, out-of-session) and seat plumbing
355
+ // is not implicated. Flagging on the first different-seat hit reported a
356
+ // config problem that was not there — and the `.some()` this walk replaced
357
+ // had the same false-positive semantics, so this corrects the semantic,
358
+ // not just the rewrite.
359
+ //
360
+ // Reverse walk: `queries` is time-sorted, so the walk stops at the window
361
+ // edge once it is below the derive's timestamp. Bounded for interleaved
362
+ // ledgers; when a run of derives precedes a run of queries, the `continue`
363
+ // over newer queries makes it O(derives × queries) worst-case again —
364
+ // measured 83ms at 5k×5k, accepted. Both arrays are time-sorted, so a
365
+ // two-pointer sweep can tighten this if ledgers outgrow that.
366
+ let sawSameSeat = false;
367
+ let sawDifferentSeat = false;
368
+ for (let q = queries.length - 1; q >= 0; q--) {
369
+ const query = queries[q];
370
+ if (query.ms > row.ms)
371
+ continue;
372
+ if (row.ms - query.ms > QBD_CORRELATION_WINDOW_MS)
373
+ break;
374
+ if (query.agentSource === row.agentSource) {
375
+ sawSameSeat = true;
376
+ break;
377
+ }
378
+ sawDifferentSeat = true;
379
+ }
380
+ if (!sawSameSeat && sawDifferentSeat)
381
+ anomalies.seatMismatchHints++;
382
+ continue;
383
+ }
384
+ const candidates = queryIndex.get(row.correlationId);
385
+ const grounding = candidates?.find((q) => q.ms <= row.ms);
386
+ if (grounding === undefined) {
387
+ anomalies.orphanCorrelations++;
388
+ pushDetail(anomalies, `derive at ${new Date(row.ms).toISOString()} cites correlation ID ${row.correlationId} with no preceding query row`);
389
+ continue;
390
+ }
391
+ if (grounding.sessionId !== row.sessionId || grounding.agentSource !== row.agentSource) {
392
+ // Different session OR different seat. Both are the same defect from the
393
+ // metric's side: a query that cannot have grounded this derive.
394
+ anomalies.crossSessionCorrelations++;
395
+ pushDetail(anomalies, `derive at ${new Date(row.ms).toISOString()} correlates to a query from a different session or seat`);
396
+ continue;
397
+ }
398
+ if (consumed.has(row.correlationId)) {
399
+ anomalies.duplicateCorrelations++;
400
+ pushDetail(anomalies, `derive at ${new Date(row.ms).toISOString()} re-cites correlation ID ${row.correlationId}, already spent by an earlier derive (one query grounds one derive)`);
401
+ continue;
402
+ }
403
+ consumed.add(row.correlationId);
404
+ credited.add(row);
405
+ }
406
+ return credited;
407
+ }
408
+ /** Tally one session against the globally-decided credit set. */
409
+ function scoreSession(session, credited) {
410
+ const stat = { derives: 0, correlated: 0 };
411
+ for (const row of session.rows) {
412
+ if (row.type !== 'derive_action')
413
+ continue;
414
+ // Every derive counts, correlated or not (falsifier 1).
415
+ stat.derives++;
416
+ if (credited.has(row))
417
+ stat.correlated++;
418
+ }
419
+ return stat;
420
+ }
421
+ function sumStats(stats) {
422
+ return stats.reduce((acc, s) => ({ derives: acc.derives + s.derives, correlated: acc.correlated + s.correlated }), { derives: 0, correlated: 0 });
423
+ }
424
+ /**
425
+ * Compute the compliance report from a scan.
426
+ *
427
+ * The verdict stays `PENDING` until the pre-registered window fills. That is
428
+ * deliberate: declaring PASS at n=3 would be as much a post-hoc reinterpretation
429
+ * of the registration as moving the threshold would be.
430
+ */
431
+ export function computeQbdCompliance(scan) {
432
+ const anomalies = {
433
+ ...scan.anomalies,
434
+ details: [...scan.anomalies.details],
435
+ };
436
+ const queryIndex = new Map();
437
+ for (const row of scan.rows) {
438
+ if (row.type !== 'corpus_query' || row.correlationId === undefined)
439
+ continue;
440
+ const bucket = queryIndex.get(row.correlationId);
441
+ if (bucket === undefined)
442
+ queryIndex.set(row.correlationId, [row]);
443
+ else
444
+ bucket.push(row);
445
+ }
446
+ const sessions = groupQbdSessions(scan.rows);
447
+ const instrumented = sessions.filter((s) => s.rows.some((r) => r.type === 'derive_action'));
448
+ // Credit is decided globally FIRST — the 1:1 rule spans sessions.
449
+ const credited = creditDerives(scan.rows, queryIndex, anomalies);
450
+ const perSession = instrumented.map((s) => scoreSession(s, credited));
451
+ const evaluated = perSession.slice(0, QBD_PRE_REGISTERED_WINDOW_SESSIONS);
452
+ const window = sumStats(evaluated);
453
+ const compliance = window.derives === 0 ? null : window.correlated / window.derives;
454
+ let verdict = 'PENDING';
455
+ if (instrumented.length >= QBD_PRE_REGISTERED_WINDOW_SESSIONS && compliance !== null) {
456
+ verdict = compliance >= QBD_PRE_REGISTERED_THRESHOLD ? 'PASS' : 'FAIL';
457
+ }
458
+ // Trend across ALL instrumented sessions, earlier half vs recent half.
459
+ let trend = null;
460
+ if (perSession.length >= MIN_SESSIONS_PER_TREND_SIDE * 2) {
461
+ const mid = Math.floor(perSession.length / 2);
462
+ trend = {
463
+ earlier: sumStats(perSession.slice(0, mid)),
464
+ recent: sumStats(perSession.slice(mid)),
465
+ };
466
+ }
467
+ // `seatMismatchHints` is deliberately absent: one-sided seat plumbing is a
468
+ // config smell, not evidence the ledger cannot be trusted.
469
+ const degraded = scan.degraded ||
470
+ anomalies.orphanCorrelations > 0 ||
471
+ anomalies.crossSessionCorrelations > 0 ||
472
+ anomalies.duplicateCorrelations > 0;
473
+ return {
474
+ instrumentedSessions: instrumented.length,
475
+ evaluatedSessions: evaluated.length,
476
+ window,
477
+ compliance,
478
+ verdict,
479
+ trend,
480
+ anomalies,
481
+ degraded,
482
+ };
483
+ }
484
+ /** Format a rate as a fixed-2 fraction, or `n/a` when there is nothing to divide. */
485
+ export function formatQbdRate(stat) {
486
+ if (stat.derives === 0)
487
+ return 'n/a (0 derive-class events)';
488
+ return `${(stat.correlated / stat.derives).toFixed(2)} (${stat.correlated}/${stat.derives})`;
489
+ }
490
+ //# sourceMappingURL=compliance.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compliance.js","sourceRoot":"","sources":["../../src/qbd/compliance.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=compliance.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compliance.test.d.ts","sourceRoot":"","sources":["../../src/qbd/compliance.test.ts"],"names":[],"mappings":""}