@mmnto/totem 1.115.0 → 1.117.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.
- package/dist/attribution-sense.d.ts +119 -0
- package/dist/attribution-sense.d.ts.map +1 -0
- package/dist/attribution-sense.js +167 -0
- package/dist/attribution-sense.js.map +1 -0
- package/dist/attribution-sense.test.d.ts +21 -0
- package/dist/attribution-sense.test.d.ts.map +1 -0
- package/dist/attribution-sense.test.js +277 -0
- package/dist/attribution-sense.test.js.map +1 -0
- package/dist/errors.d.ts +1 -1
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/recurrence-stats.d.ts +16 -14
- package/dist/recurrence-stats.d.ts.map +1 -1
- package/dist/recurrence-stats.js +1 -0
- package/dist/recurrence-stats.js.map +1 -1
- package/dist/recurrence-stats.test.js +6 -0
- package/dist/recurrence-stats.test.js.map +1 -1
- package/dist/retrospect.d.ts +54 -54
- package/dist/retrospect.d.ts.map +1 -1
- package/dist/retrospect.js +1 -0
- package/dist/retrospect.js.map +1 -1
- package/dist/selection-manifest.d.ts +51 -1
- package/dist/selection-manifest.d.ts.map +1 -1
- package/dist/selection-manifest.js +58 -4
- package/dist/selection-manifest.js.map +1 -1
- package/dist/selection-manifest.test.js +102 -0
- package/dist/selection-manifest.test.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent-attribution provenance sense (mmnto-ai/totem#2629).
|
|
3
|
+
*
|
|
4
|
+
* On a shared multi-seat machine, a seat session that mints no identity of its
|
|
5
|
+
* own can inherit ANOTHER seat's attribution through two ambient channels:
|
|
6
|
+
* a wider-than-launch-shell `TOTEM_SELF_AGENT` (user/machine scope) and the
|
|
7
|
+
* shared `.totem/ledger/.session-id` pointer. A wrong-seat row corrupts two
|
|
8
|
+
* denominators; an unattributed row only under-counts — so the ruled posture
|
|
9
|
+
* (operator, 2026-08-12, on the issue) is:
|
|
10
|
+
*
|
|
11
|
+
* - **Provenance on every row, not only on mismatch:** `agent_source_provenance`
|
|
12
|
+
* is the two-value `'env' | 'absent'` enum — `'env'` iff `agent_source` was
|
|
13
|
+
* stamped from `TOTEM_SELF_AGENT`, `'absent'` iff no attribution was stamped
|
|
14
|
+
* (the honest stamped-absence default, Tenet 4).
|
|
15
|
+
* - **Fail-closed at the stamp seam:** when the ambient env seat and the
|
|
16
|
+
* session pointer's minting seat disagree, stamp NEITHER — no `agent_source`,
|
|
17
|
+
* plus a conflict disclosure naming both candidates. Fail-closed scopes to
|
|
18
|
+
* the *attribution*, never the *operation*: the sense never throws into its
|
|
19
|
+
* caller, so the query/write it decorates is never refused (Tenet 13).
|
|
20
|
+
* - **Never remembered:** identity derives from process env + on-disk state at
|
|
21
|
+
* call time, per call. No cache at any scope — a session-lifetime cache
|
|
22
|
+
* converts a transient contamination into a whole-session one (the
|
|
23
|
+
* `b8d7aa9b` specimen shape).
|
|
24
|
+
*
|
|
25
|
+
* The minting seat is derived from the pointer session's `session_start` row
|
|
26
|
+
* in `events.ndjson` — written by the SessionStart hook's mint block at the
|
|
27
|
+
* #2625 template, so the lookup needs no new producer. Absence of that row
|
|
28
|
+
* (pre-hook ledger, rotated file, unseated mint) is NOT a disagreement: only
|
|
29
|
+
* positive counter-evidence strips attribution.
|
|
30
|
+
*
|
|
31
|
+
* NAMED LIMIT (stated so coverage is never overread): the conflict arm fires
|
|
32
|
+
* only when two PRESENT candidates disagree — a seated process joining a
|
|
33
|
+
* foreign seat's pointer session. The b8d7aa9b specimen itself had an
|
|
34
|
+
* UNSEATED mint row (its minting process's env carried no var), and a
|
|
35
|
+
* machine-wide ambient var makes every mint agree with every process env —
|
|
36
|
+
* both shapes yield an env stamp with no conflict here: the ruling's
|
|
37
|
+
* fail-closed clause covers disagreement between two PRESENT candidates, and
|
|
38
|
+
* the missing-counter-evidence rule (design invariant 3) covers the rest. The
|
|
39
|
+
* sensors for the ambient-scope class are the provenance field (post-hoc
|
|
40
|
+
* partitioning), doctor arm (d)'s registry scope-sense, and the launch-shell
|
|
41
|
+
* scope protocol — not this predicate.
|
|
42
|
+
*/
|
|
43
|
+
/** The ruled two-value provenance enum — `'env' | 'absent'`, nothing else. */
|
|
44
|
+
export declare const AGENT_SOURCE_PROVENANCE_VALUES: readonly ["env", "absent"];
|
|
45
|
+
export type AgentSourceProvenance = (typeof AGENT_SOURCE_PROVENANCE_VALUES)[number];
|
|
46
|
+
/**
|
|
47
|
+
* The fail-closed disclosure: both candidates named, so a post-hoc reader can
|
|
48
|
+
* partition contaminated windows without guessing which seat was real.
|
|
49
|
+
*/
|
|
50
|
+
export interface AttributionConflict {
|
|
51
|
+
/** What ambient `TOTEM_SELF_AGENT` claimed (first non-empty comma entry). */
|
|
52
|
+
env_seat: string;
|
|
53
|
+
/** What the pointer session's `session_start` row recorded at mint time. */
|
|
54
|
+
minting_seat: string;
|
|
55
|
+
/** The `.session-id` pointer value the two candidates disagree about. */
|
|
56
|
+
pointer_session_id: string;
|
|
57
|
+
}
|
|
58
|
+
/** The per-call sense result both stamp seams consume. */
|
|
59
|
+
export interface AgentAttributionSense {
|
|
60
|
+
/** Seat to stamp, or `null` — never a guessed value (Tenet 4). */
|
|
61
|
+
agent_source: string | null;
|
|
62
|
+
/** `'env'` iff `agent_source` is non-null — writer-guaranteed biconditional. */
|
|
63
|
+
agent_source_provenance: AgentSourceProvenance;
|
|
64
|
+
/** Present iff the fail-closed rule fired (env seat ≠ minting seat). */
|
|
65
|
+
attribution_conflict?: AttributionConflict;
|
|
66
|
+
/**
|
|
67
|
+
* Present iff the conflict probe failed for a non-benign reason (EACCES-class
|
|
68
|
+
* read failure, torn pointer read throw). The env stamp stands — a broken
|
|
69
|
+
* probe is not counter-evidence — but the degradation names itself on the
|
|
70
|
+
* row rather than vanishing (Tenet 4).
|
|
71
|
+
*/
|
|
72
|
+
attribution_probe_error?: string;
|
|
73
|
+
}
|
|
74
|
+
/** Discriminated lookup result — `found: false` is a normal state, not an error. */
|
|
75
|
+
export type SessionMintingSeatLookup = {
|
|
76
|
+
found: true;
|
|
77
|
+
seat: string | null;
|
|
78
|
+
} | {
|
|
79
|
+
found: false;
|
|
80
|
+
};
|
|
81
|
+
/**
|
|
82
|
+
* Derive the minting seat of a session from its `session_start` row in
|
|
83
|
+
* `<totemDir>/ledger/events.ndjson`.
|
|
84
|
+
*
|
|
85
|
+
* Scans BACKWARDS (newest row wins) with a cheap substring prefilter before
|
|
86
|
+
* any `JSON.parse`, so the common case — an active session whose mint row is
|
|
87
|
+
* near the tail — costs a file read plus a short scan. Malformed/torn lines
|
|
88
|
+
* are skipped per line (the `parseSearchLog` tolerance precedent): a torn
|
|
89
|
+
* `session_start` reads as not-found, never as a fabricated conflict.
|
|
90
|
+
*
|
|
91
|
+
* Absent events file (ENOENT class) returns `found: false` — the honest
|
|
92
|
+
* not-instrumented state. Every other read errno throws a `TotemError` so the
|
|
93
|
+
* caller can name the probe failure instead of silently degrading.
|
|
94
|
+
*/
|
|
95
|
+
export declare function readSessionMintingSeat(totemDir: string, sessionId: string): SessionMintingSeatLookup;
|
|
96
|
+
export interface AgentAttributionSenseInput {
|
|
97
|
+
/**
|
|
98
|
+
* Resolved `.totem` directory. Omit to run env-only (no conflict probe) —
|
|
99
|
+
* the honest shape for callers with no ledger in reach; provenance is still
|
|
100
|
+
* disclosed.
|
|
101
|
+
*/
|
|
102
|
+
totemDir?: string;
|
|
103
|
+
/** Test seam — production callers omit and the sense reads `process.env`. */
|
|
104
|
+
env?: NodeJS.ProcessEnv;
|
|
105
|
+
/**
|
|
106
|
+
* The pointer session id the caller already resolved for the row it is
|
|
107
|
+
* stamping (the manifest builder's shape — one pointer read per row, so the
|
|
108
|
+
* probe cannot race a concurrent rotation into checking a DIFFERENT session
|
|
109
|
+
* than the row joins). Omit and the probe reads the pointer itself.
|
|
110
|
+
*/
|
|
111
|
+
pointerSessionId?: string;
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Derive the agent-attribution stamp for one row. Per call, never cached —
|
|
115
|
+
* see the module contract. Never throws: every probe failure degrades to the
|
|
116
|
+
* env-only stamp with the failure named on `attribution_probe_error`.
|
|
117
|
+
*/
|
|
118
|
+
export declare function senseAgentAttribution(input?: AgentAttributionSenseInput): AgentAttributionSense;
|
|
119
|
+
//# sourceMappingURL=attribution-sense.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"attribution-sense.d.ts","sourceRoot":"","sources":["../src/attribution-sense.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyCG;AAoBH,8EAA8E;AAC9E,eAAO,MAAM,8BAA8B,4BAA6B,CAAC;AACzE,MAAM,MAAM,qBAAqB,GAAG,CAAC,OAAO,8BAA8B,CAAC,CAAC,MAAM,CAAC,CAAC;AAEpF;;;GAGG;AACH,MAAM,WAAW,mBAAmB;IAClC,6EAA6E;IAC7E,QAAQ,EAAE,MAAM,CAAC;IACjB,4EAA4E;IAC5E,YAAY,EAAE,MAAM,CAAC;IACrB,yEAAyE;IACzE,kBAAkB,EAAE,MAAM,CAAC;CAC5B;AAED,0DAA0D;AAC1D,MAAM,WAAW,qBAAqB;IACpC,kEAAkE;IAClE,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,gFAAgF;IAChF,uBAAuB,EAAE,qBAAqB,CAAC;IAC/C,wEAAwE;IACxE,oBAAoB,CAAC,EAAE,mBAAmB,CAAC;IAC3C;;;;;OAKG;IACH,uBAAuB,CAAC,EAAE,MAAM,CAAC;CAClC;AAED,oFAAoF;AACpF,MAAM,MAAM,wBAAwB,GAAG;IAAE,KAAK,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,GAAG;IAAE,KAAK,EAAE,KAAK,CAAA;CAAE,CAAC;AAE/F;;;;;;;;;;;;;GAaG;AACH,wBAAgB,sBAAsB,CACpC,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,GAChB,wBAAwB,CA6C1B;AAED,MAAM,WAAW,0BAA0B;IACzC;;;;OAIG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,6EAA6E;IAC7E,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACxB;;;;;OAKG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CACnC,KAAK,GAAE,0BAA+B,GACrC,qBAAqB,CAgDvB"}
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent-attribution provenance sense (mmnto-ai/totem#2629).
|
|
3
|
+
*
|
|
4
|
+
* On a shared multi-seat machine, a seat session that mints no identity of its
|
|
5
|
+
* own can inherit ANOTHER seat's attribution through two ambient channels:
|
|
6
|
+
* a wider-than-launch-shell `TOTEM_SELF_AGENT` (user/machine scope) and the
|
|
7
|
+
* shared `.totem/ledger/.session-id` pointer. A wrong-seat row corrupts two
|
|
8
|
+
* denominators; an unattributed row only under-counts — so the ruled posture
|
|
9
|
+
* (operator, 2026-08-12, on the issue) is:
|
|
10
|
+
*
|
|
11
|
+
* - **Provenance on every row, not only on mismatch:** `agent_source_provenance`
|
|
12
|
+
* is the two-value `'env' | 'absent'` enum — `'env'` iff `agent_source` was
|
|
13
|
+
* stamped from `TOTEM_SELF_AGENT`, `'absent'` iff no attribution was stamped
|
|
14
|
+
* (the honest stamped-absence default, Tenet 4).
|
|
15
|
+
* - **Fail-closed at the stamp seam:** when the ambient env seat and the
|
|
16
|
+
* session pointer's minting seat disagree, stamp NEITHER — no `agent_source`,
|
|
17
|
+
* plus a conflict disclosure naming both candidates. Fail-closed scopes to
|
|
18
|
+
* the *attribution*, never the *operation*: the sense never throws into its
|
|
19
|
+
* caller, so the query/write it decorates is never refused (Tenet 13).
|
|
20
|
+
* - **Never remembered:** identity derives from process env + on-disk state at
|
|
21
|
+
* call time, per call. No cache at any scope — a session-lifetime cache
|
|
22
|
+
* converts a transient contamination into a whole-session one (the
|
|
23
|
+
* `b8d7aa9b` specimen shape).
|
|
24
|
+
*
|
|
25
|
+
* The minting seat is derived from the pointer session's `session_start` row
|
|
26
|
+
* in `events.ndjson` — written by the SessionStart hook's mint block at the
|
|
27
|
+
* #2625 template, so the lookup needs no new producer. Absence of that row
|
|
28
|
+
* (pre-hook ledger, rotated file, unseated mint) is NOT a disagreement: only
|
|
29
|
+
* positive counter-evidence strips attribution.
|
|
30
|
+
*
|
|
31
|
+
* NAMED LIMIT (stated so coverage is never overread): the conflict arm fires
|
|
32
|
+
* only when two PRESENT candidates disagree — a seated process joining a
|
|
33
|
+
* foreign seat's pointer session. The b8d7aa9b specimen itself had an
|
|
34
|
+
* UNSEATED mint row (its minting process's env carried no var), and a
|
|
35
|
+
* machine-wide ambient var makes every mint agree with every process env —
|
|
36
|
+
* both shapes yield an env stamp with no conflict here: the ruling's
|
|
37
|
+
* fail-closed clause covers disagreement between two PRESENT candidates, and
|
|
38
|
+
* the missing-counter-evidence rule (design invariant 3) covers the rest. The
|
|
39
|
+
* sensors for the ambient-scope class are the provenance field (post-hoc
|
|
40
|
+
* partitioning), doctor arm (d)'s registry scope-sense, and the launch-shell
|
|
41
|
+
* scope protocol — not this predicate.
|
|
42
|
+
*/
|
|
43
|
+
import * as fs from 'node:fs';
|
|
44
|
+
import * as path from 'node:path';
|
|
45
|
+
import { TotemError } from './errors.js';
|
|
46
|
+
import { resolveQbdAgentSource } from './qbd/record.js';
|
|
47
|
+
import { readSessionId } from './session-id.js';
|
|
48
|
+
const LEDGER_DIR = 'ledger';
|
|
49
|
+
const EVENTS_FILE = 'events.ndjson';
|
|
50
|
+
/**
|
|
51
|
+
* Errnos that mean "there is no ledger here" rather than "something surprising
|
|
52
|
+
* happened" — the `session-id.ts` absence semantics, minus the permission
|
|
53
|
+
* classes: an EACCES/EPERM/EROFS events file is a real probe failure that must
|
|
54
|
+
* land on the accounting channel, not read as a clean not-instrumented state.
|
|
55
|
+
*/
|
|
56
|
+
const LEDGER_ABSENT_ERRNOS = new Set(['ENOENT', 'ENOTDIR']);
|
|
57
|
+
/** The ruled two-value provenance enum — `'env' | 'absent'`, nothing else. */
|
|
58
|
+
export const AGENT_SOURCE_PROVENANCE_VALUES = ['env', 'absent'];
|
|
59
|
+
/**
|
|
60
|
+
* Derive the minting seat of a session from its `session_start` row in
|
|
61
|
+
* `<totemDir>/ledger/events.ndjson`.
|
|
62
|
+
*
|
|
63
|
+
* Scans BACKWARDS (newest row wins) with a cheap substring prefilter before
|
|
64
|
+
* any `JSON.parse`, so the common case — an active session whose mint row is
|
|
65
|
+
* near the tail — costs a file read plus a short scan. Malformed/torn lines
|
|
66
|
+
* are skipped per line (the `parseSearchLog` tolerance precedent): a torn
|
|
67
|
+
* `session_start` reads as not-found, never as a fabricated conflict.
|
|
68
|
+
*
|
|
69
|
+
* Absent events file (ENOENT class) returns `found: false` — the honest
|
|
70
|
+
* not-instrumented state. Every other read errno throws a `TotemError` so the
|
|
71
|
+
* caller can name the probe failure instead of silently degrading.
|
|
72
|
+
*/
|
|
73
|
+
export function readSessionMintingSeat(totemDir, sessionId) {
|
|
74
|
+
const eventsPath = path.join(totemDir, LEDGER_DIR, EVENTS_FILE);
|
|
75
|
+
let content;
|
|
76
|
+
try {
|
|
77
|
+
content = fs.readFileSync(eventsPath, 'utf-8');
|
|
78
|
+
}
|
|
79
|
+
catch (err) {
|
|
80
|
+
const code = typeof err === 'object' && err !== null ? err.code : undefined;
|
|
81
|
+
if (code !== undefined && LEDGER_ABSENT_ERRNOS.has(code)) {
|
|
82
|
+
return { found: false };
|
|
83
|
+
}
|
|
84
|
+
throw new TotemError('ATTRIBUTION_SENSE_LEDGER_READ_FAILED', 'Unexpected error reading events.ndjson for the minting-seat lookup', 'Check filesystem permissions for the .totem/ledger/events.ndjson file.', err);
|
|
85
|
+
}
|
|
86
|
+
const lines = content.split('\n');
|
|
87
|
+
for (let i = lines.length - 1; i >= 0; i--) {
|
|
88
|
+
const line = lines[i].trim();
|
|
89
|
+
if (line.length === 0)
|
|
90
|
+
continue;
|
|
91
|
+
// Substring prefilter: a JSON row with type "session_start" and this
|
|
92
|
+
// session id must contain both literals. Skips JSON.parse on the ~all
|
|
93
|
+
// non-matching rows of a large ledger.
|
|
94
|
+
if (!line.includes('"session_start"') || !line.includes(sessionId))
|
|
95
|
+
continue;
|
|
96
|
+
let obj;
|
|
97
|
+
// totem-context: intentional malformed-line tolerance — a torn/corrupt NDJSON line is skipped and the scan continues (the parseSearchLog precedent); a torn mint row must read as not-found, never crash the stamp path or fabricate a conflict.
|
|
98
|
+
try {
|
|
99
|
+
obj = JSON.parse(line);
|
|
100
|
+
// totem-context: intentional degradation — see directive above the try; dual placement so the rule fires on either the catch-keyword line or the catch-body line.
|
|
101
|
+
}
|
|
102
|
+
catch {
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
105
|
+
if (typeof obj !== 'object' || obj === null)
|
|
106
|
+
continue;
|
|
107
|
+
const rec = obj;
|
|
108
|
+
if (rec.type !== 'session_start' || rec.session_id !== sessionId)
|
|
109
|
+
continue;
|
|
110
|
+
const seat = typeof rec.agent_source === 'string' && rec.agent_source.trim().length > 0
|
|
111
|
+
? rec.agent_source.trim()
|
|
112
|
+
: null;
|
|
113
|
+
return { found: true, seat };
|
|
114
|
+
}
|
|
115
|
+
return { found: false };
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Derive the agent-attribution stamp for one row. Per call, never cached —
|
|
119
|
+
* see the module contract. Never throws: every probe failure degrades to the
|
|
120
|
+
* env-only stamp with the failure named on `attribution_probe_error`.
|
|
121
|
+
*/
|
|
122
|
+
export function senseAgentAttribution(input = {}) {
|
|
123
|
+
const env = input.env ?? process.env;
|
|
124
|
+
const envSeat = resolveQbdAgentSource(env) ?? null;
|
|
125
|
+
// No env identity → stamped absence. The pointer's minting seat is never
|
|
126
|
+
// promoted into agent_source here: one candidate is not a conflict, and
|
|
127
|
+
// stamping a seat the process did not declare would be a guess (Tenet 4).
|
|
128
|
+
if (envSeat === null) {
|
|
129
|
+
return { agent_source: null, agent_source_provenance: 'absent' };
|
|
130
|
+
}
|
|
131
|
+
if (input.totemDir === undefined) {
|
|
132
|
+
return { agent_source: envSeat, agent_source_provenance: 'env' };
|
|
133
|
+
}
|
|
134
|
+
let probeError;
|
|
135
|
+
// totem-context: intentional degradation — a failed conflict probe keeps the env stamp (a broken probe is not counter-evidence) and names itself on attribution_probe_error; throwing here would let telemetry break the tool call it decorates (Tenet 13, lesson-b1bae311).
|
|
136
|
+
try {
|
|
137
|
+
const pointerId = input.pointerSessionId ?? readSessionId(input.totemDir);
|
|
138
|
+
if (pointerId !== undefined) {
|
|
139
|
+
const minting = readSessionMintingSeat(input.totemDir, pointerId);
|
|
140
|
+
if (minting.found && minting.seat !== null && minting.seat !== envSeat) {
|
|
141
|
+
// Fail-closed: two candidates disagree — stamp neither, name both.
|
|
142
|
+
return {
|
|
143
|
+
agent_source: null,
|
|
144
|
+
agent_source_provenance: 'absent',
|
|
145
|
+
attribution_conflict: {
|
|
146
|
+
env_seat: envSeat,
|
|
147
|
+
minting_seat: minting.seat,
|
|
148
|
+
pointer_session_id: pointerId,
|
|
149
|
+
},
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
// totem-context: intentional degradation — see directive above the try; dual placement so the rule fires on either the catch-keyword line or the catch-body line.
|
|
154
|
+
}
|
|
155
|
+
catch (err) {
|
|
156
|
+
// One level of cause unrolled: the wrapper text names the probe step, the
|
|
157
|
+
// cause names the errno — the half a post-hoc reader actually filters on.
|
|
158
|
+
const causeMsg = err instanceof Error && err.cause instanceof Error ? ` (${err.cause.message})` : '';
|
|
159
|
+
probeError = `${err instanceof Error ? err.message : String(err)}${causeMsg}`;
|
|
160
|
+
}
|
|
161
|
+
return {
|
|
162
|
+
agent_source: envSeat,
|
|
163
|
+
agent_source_provenance: 'env',
|
|
164
|
+
...(probeError !== undefined && { attribution_probe_error: probeError }),
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
//# sourceMappingURL=attribution-sense.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"attribution-sense.js","sourceRoot":"","sources":["../src/attribution-sense.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for the agent-attribution provenance sense (mmnto-ai/totem#2629).
|
|
3
|
+
*
|
|
4
|
+
* The invariants under test come verbatim from the design doc
|
|
5
|
+
* (.totem/specs/2629.md § Invariants):
|
|
6
|
+
*
|
|
7
|
+
* - provenance biconditional: `'env'` iff `agent_source` is non-null;
|
|
8
|
+
* - the fail-closed rule fires ONLY on positive disagreement (env seat and
|
|
9
|
+
* minting seat both present, different) — and then withholds attribution,
|
|
10
|
+
* names both candidates, and never throws into the caller;
|
|
11
|
+
* - missing counter-evidence (no pointer, no mint row, unseated mint row,
|
|
12
|
+
* absent ledger) never strips the env stamp;
|
|
13
|
+
* - a failed probe keeps the env stamp and NAMES itself (never silent,
|
|
14
|
+
* never counter-evidence);
|
|
15
|
+
* - derivation is per call — a conflict introduced between two calls is
|
|
16
|
+
* caught on the second (no cache at any layer);
|
|
17
|
+
* - the minting-seat lookup takes the newest matching `session_start`, skips
|
|
18
|
+
* malformed lines, and treats an absent ledger as not-found.
|
|
19
|
+
*/
|
|
20
|
+
export {};
|
|
21
|
+
//# sourceMappingURL=attribution-sense.test.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"attribution-sense.test.d.ts","sourceRoot":"","sources":["../src/attribution-sense.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG"}
|
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for the agent-attribution provenance sense (mmnto-ai/totem#2629).
|
|
3
|
+
*
|
|
4
|
+
* The invariants under test come verbatim from the design doc
|
|
5
|
+
* (.totem/specs/2629.md § Invariants):
|
|
6
|
+
*
|
|
7
|
+
* - provenance biconditional: `'env'` iff `agent_source` is non-null;
|
|
8
|
+
* - the fail-closed rule fires ONLY on positive disagreement (env seat and
|
|
9
|
+
* minting seat both present, different) — and then withholds attribution,
|
|
10
|
+
* names both candidates, and never throws into the caller;
|
|
11
|
+
* - missing counter-evidence (no pointer, no mint row, unseated mint row,
|
|
12
|
+
* absent ledger) never strips the env stamp;
|
|
13
|
+
* - a failed probe keeps the env stamp and NAMES itself (never silent,
|
|
14
|
+
* never counter-evidence);
|
|
15
|
+
* - derivation is per call — a conflict introduced between two calls is
|
|
16
|
+
* caught on the second (no cache at any layer);
|
|
17
|
+
* - the minting-seat lookup takes the newest matching `session_start`, skips
|
|
18
|
+
* malformed lines, and treats an absent ledger as not-found.
|
|
19
|
+
*/
|
|
20
|
+
import * as os from 'node:os';
|
|
21
|
+
import * as path from 'node:path';
|
|
22
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
23
|
+
// Mock node:fs so spyOn works in ESM — same pattern as session-id.test.ts.
|
|
24
|
+
vi.mock('node:fs', async () => {
|
|
25
|
+
const actual = await vi.importActual('node:fs');
|
|
26
|
+
return { ...actual, default: actual };
|
|
27
|
+
});
|
|
28
|
+
import * as fs from 'node:fs';
|
|
29
|
+
import { readSessionMintingSeat, senseAgentAttribution } from './attribution-sense.js';
|
|
30
|
+
import { TotemError } from './errors.js';
|
|
31
|
+
import { cleanTmpDir } from './test-utils.js';
|
|
32
|
+
const SESSION_A = '550e8400-e29b-41d4-a716-446655440000';
|
|
33
|
+
const SESSION_B = '550e8400-e29b-41d4-a716-446655440009';
|
|
34
|
+
let totemDir;
|
|
35
|
+
function ledgerDir() {
|
|
36
|
+
return path.join(totemDir, 'ledger');
|
|
37
|
+
}
|
|
38
|
+
function writePointer(id) {
|
|
39
|
+
fs.mkdirSync(ledgerDir(), { recursive: true });
|
|
40
|
+
fs.writeFileSync(path.join(ledgerDir(), '.session-id'), id, 'utf-8');
|
|
41
|
+
}
|
|
42
|
+
function eventsPath() {
|
|
43
|
+
return path.join(ledgerDir(), 'events.ndjson');
|
|
44
|
+
}
|
|
45
|
+
function writeEvents(lines) {
|
|
46
|
+
fs.mkdirSync(ledgerDir(), { recursive: true });
|
|
47
|
+
const content = lines
|
|
48
|
+
.map((l) => (typeof l === 'string' ? l : JSON.stringify(l)))
|
|
49
|
+
.join('\n')
|
|
50
|
+
.concat('\n');
|
|
51
|
+
fs.writeFileSync(eventsPath(), content, 'utf-8');
|
|
52
|
+
}
|
|
53
|
+
function sessionStartRow(sessionId, seat) {
|
|
54
|
+
return {
|
|
55
|
+
timestamp: '2026-08-12T03:03:00.000Z',
|
|
56
|
+
type: 'session_start',
|
|
57
|
+
activity_name: 'SessionStart',
|
|
58
|
+
source: 'bot',
|
|
59
|
+
justification: '',
|
|
60
|
+
session_id: sessionId,
|
|
61
|
+
...(seat !== undefined && { agent_source: seat }),
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
beforeEach(() => {
|
|
65
|
+
totemDir = fs.mkdtempSync(path.join(os.tmpdir(), 'attr-sense-'));
|
|
66
|
+
});
|
|
67
|
+
afterEach(() => {
|
|
68
|
+
vi.restoreAllMocks();
|
|
69
|
+
cleanTmpDir(totemDir);
|
|
70
|
+
});
|
|
71
|
+
// ─── readSessionMintingSeat ─────────────────────────────
|
|
72
|
+
describe('readSessionMintingSeat', () => {
|
|
73
|
+
it('finds the mint row and returns its seat', () => {
|
|
74
|
+
writeEvents([sessionStartRow(SESSION_A, 'totem-claude')]);
|
|
75
|
+
expect(readSessionMintingSeat(totemDir, SESSION_A)).toEqual({
|
|
76
|
+
found: true,
|
|
77
|
+
seat: 'totem-claude',
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
it('an unseated mint row is found with seat null (stamped absence, not a fabricated seat)', () => {
|
|
81
|
+
writeEvents([sessionStartRow(SESSION_A)]);
|
|
82
|
+
expect(readSessionMintingSeat(totemDir, SESSION_A)).toEqual({ found: true, seat: null });
|
|
83
|
+
});
|
|
84
|
+
it('a whitespace-only agent_source on the mint row reads as seat null', () => {
|
|
85
|
+
writeEvents([sessionStartRow(SESSION_A, ' ')]);
|
|
86
|
+
expect(readSessionMintingSeat(totemDir, SESSION_A)).toEqual({ found: true, seat: null });
|
|
87
|
+
});
|
|
88
|
+
it('returns not-found when the events file is absent (the honest not-instrumented state)', () => {
|
|
89
|
+
expect(readSessionMintingSeat(totemDir, SESSION_A)).toEqual({ found: false });
|
|
90
|
+
});
|
|
91
|
+
it('returns not-found when no session_start matches the id', () => {
|
|
92
|
+
writeEvents([sessionStartRow(SESSION_B, 'totem-claude')]);
|
|
93
|
+
expect(readSessionMintingSeat(totemDir, SESSION_A)).toEqual({ found: false });
|
|
94
|
+
});
|
|
95
|
+
it('a non-session_start row carrying the same session id is not a mint row', () => {
|
|
96
|
+
writeEvents([
|
|
97
|
+
{
|
|
98
|
+
timestamp: '2026-08-12T03:04:00.000Z',
|
|
99
|
+
type: 'mcp_call',
|
|
100
|
+
activity_name: 'search_knowledge — "session_start"',
|
|
101
|
+
source: 'bot',
|
|
102
|
+
justification: '',
|
|
103
|
+
session_id: SESSION_A,
|
|
104
|
+
},
|
|
105
|
+
]);
|
|
106
|
+
expect(readSessionMintingSeat(totemDir, SESSION_A)).toEqual({ found: false });
|
|
107
|
+
});
|
|
108
|
+
it('the NEWEST matching session_start wins', () => {
|
|
109
|
+
writeEvents([sessionStartRow(SESSION_A, 'seat-old'), sessionStartRow(SESSION_A, 'seat-new')]);
|
|
110
|
+
expect(readSessionMintingSeat(totemDir, SESSION_A)).toEqual({ found: true, seat: 'seat-new' });
|
|
111
|
+
});
|
|
112
|
+
it('skips torn/malformed lines — a torn mint row reads as not-found, an earlier valid one still resolves', () => {
|
|
113
|
+
// The torn line contains both prefilter literals, so it reaches JSON.parse.
|
|
114
|
+
writeEvents([
|
|
115
|
+
sessionStartRow(SESSION_A, 'totem-claude'),
|
|
116
|
+
`{"type":"session_start","session_id":"${SESSION_A}","agent_source":"torn-`,
|
|
117
|
+
]);
|
|
118
|
+
expect(readSessionMintingSeat(totemDir, SESSION_A)).toEqual({
|
|
119
|
+
found: true,
|
|
120
|
+
seat: 'totem-claude',
|
|
121
|
+
});
|
|
122
|
+
writeEvents([`{"type":"session_start","session_id":"${SESSION_A}","agent_source":"torn-`]);
|
|
123
|
+
expect(readSessionMintingSeat(totemDir, SESSION_A)).toEqual({ found: false });
|
|
124
|
+
});
|
|
125
|
+
it('throws a TotemError on a non-benign read failure (EACCES is a probe failure, not absence)', () => {
|
|
126
|
+
writeEvents([sessionStartRow(SESSION_A, 'totem-claude')]);
|
|
127
|
+
vi.spyOn(fs, 'readFileSync').mockImplementation(() => {
|
|
128
|
+
const err = new Error('EACCES: permission denied');
|
|
129
|
+
err.code = 'EACCES';
|
|
130
|
+
throw err;
|
|
131
|
+
});
|
|
132
|
+
try {
|
|
133
|
+
readSessionMintingSeat(totemDir, SESSION_A);
|
|
134
|
+
expect.unreachable('should have thrown');
|
|
135
|
+
}
|
|
136
|
+
catch (err) {
|
|
137
|
+
expect(err).toBeInstanceOf(TotemError);
|
|
138
|
+
expect(err.code).toBe('ATTRIBUTION_SENSE_LEDGER_READ_FAILED');
|
|
139
|
+
}
|
|
140
|
+
});
|
|
141
|
+
});
|
|
142
|
+
// ─── senseAgentAttribution ──────────────────────────────
|
|
143
|
+
describe('senseAgentAttribution', () => {
|
|
144
|
+
it('absent env → stamped absence, even over a foreign-minted pointer (one candidate is not a conflict)', () => {
|
|
145
|
+
writePointer(SESSION_A);
|
|
146
|
+
writeEvents([sessionStartRow(SESSION_A, 'totem-claude')]);
|
|
147
|
+
const sense = senseAgentAttribution({ totemDir, env: {} });
|
|
148
|
+
expect(sense).toEqual({ agent_source: null, agent_source_provenance: 'absent' });
|
|
149
|
+
});
|
|
150
|
+
it('env-only call (no totemDir) stamps env with provenance and touches no fs', () => {
|
|
151
|
+
const readSpy = vi.spyOn(fs, 'readFileSync');
|
|
152
|
+
const statSpy = vi.spyOn(fs, 'statSync');
|
|
153
|
+
const sense = senseAgentAttribution({ env: { TOTEM_SELF_AGENT: 'totem-claude' } });
|
|
154
|
+
expect(sense).toEqual({ agent_source: 'totem-claude', agent_source_provenance: 'env' });
|
|
155
|
+
expect(readSpy).not.toHaveBeenCalled();
|
|
156
|
+
expect(statSpy).not.toHaveBeenCalled();
|
|
157
|
+
});
|
|
158
|
+
it('takes the first non-empty seat from a comma-separated TOTEM_SELF_AGENT', () => {
|
|
159
|
+
const sense = senseAgentAttribution({ env: { TOTEM_SELF_AGENT: ' , totem-gemini , extra' } });
|
|
160
|
+
expect(sense.agent_source).toBe('totem-gemini');
|
|
161
|
+
expect(sense.agent_source_provenance).toBe('env');
|
|
162
|
+
});
|
|
163
|
+
it('no pointer → no counter-candidate — env stamp stands', () => {
|
|
164
|
+
const sense = senseAgentAttribution({ totemDir, env: { TOTEM_SELF_AGENT: 'totem-gemini' } });
|
|
165
|
+
expect(sense).toEqual({ agent_source: 'totem-gemini', agent_source_provenance: 'env' });
|
|
166
|
+
});
|
|
167
|
+
it('pointer minted by the SAME seat → agreement, env stamp, no conflict', () => {
|
|
168
|
+
writePointer(SESSION_A);
|
|
169
|
+
writeEvents([sessionStartRow(SESSION_A, 'totem-claude')]);
|
|
170
|
+
const sense = senseAgentAttribution({ totemDir, env: { TOTEM_SELF_AGENT: 'totem-claude' } });
|
|
171
|
+
expect(sense).toEqual({ agent_source: 'totem-claude', agent_source_provenance: 'env' });
|
|
172
|
+
});
|
|
173
|
+
it('mint row without a seat → no counter-evidence — env stamp stands (the ACTUAL b8d7aa9b specimen shape)', () => {
|
|
174
|
+
// Honest coverage note (leg finding on this PR): the live specimen's mint
|
|
175
|
+
// row carries NO agent_source (minted 03:03Z; the ledger proves only that
|
|
176
|
+
// the minting process's env lacked the var, not why), so in the
|
|
177
|
+
// contamination window this sensor would have stamped the ambient seat
|
|
178
|
+
// with provenance 'env' and NO conflict — ruled item 2 fires only on
|
|
179
|
+
// disagreement between two PRESENT candidates. The ambient-scope class
|
|
180
|
+
// where every mint agrees with the env is sensed by doctor arm (d) + the
|
|
181
|
+
// launch-shell protocol, not by this predicate.
|
|
182
|
+
writePointer(SESSION_A);
|
|
183
|
+
writeEvents([sessionStartRow(SESSION_A)]);
|
|
184
|
+
const sense = senseAgentAttribution({ totemDir, env: { TOTEM_SELF_AGENT: 'totem-gemini' } });
|
|
185
|
+
expect(sense).toEqual({ agent_source: 'totem-gemini', agent_source_provenance: 'env' });
|
|
186
|
+
});
|
|
187
|
+
it('FAIL-CLOSED: env seat ≠ minting seat → stamp neither, name both (a seated foreign-pointer join)', () => {
|
|
188
|
+
writePointer(SESSION_A);
|
|
189
|
+
writeEvents([sessionStartRow(SESSION_A, 'totem-claude')]);
|
|
190
|
+
const sense = senseAgentAttribution({ totemDir, env: { TOTEM_SELF_AGENT: 'totem-gemini' } });
|
|
191
|
+
expect(sense).toEqual({
|
|
192
|
+
agent_source: null,
|
|
193
|
+
agent_source_provenance: 'absent',
|
|
194
|
+
attribution_conflict: {
|
|
195
|
+
env_seat: 'totem-gemini',
|
|
196
|
+
minting_seat: 'totem-claude',
|
|
197
|
+
pointer_session_id: SESSION_A,
|
|
198
|
+
},
|
|
199
|
+
});
|
|
200
|
+
});
|
|
201
|
+
it('a handed pointerSessionId is probed instead of the pointer file (no rotation race)', () => {
|
|
202
|
+
// Pointer file says SESSION_A (minted by the env seat — agreement); the
|
|
203
|
+
// caller hands SESSION_B (minted by a different seat). The probe must
|
|
204
|
+
// check the handed id — the session the caller's row actually joins.
|
|
205
|
+
writePointer(SESSION_A);
|
|
206
|
+
writeEvents([
|
|
207
|
+
sessionStartRow(SESSION_A, 'totem-gemini'),
|
|
208
|
+
sessionStartRow(SESSION_B, 'totem-claude'),
|
|
209
|
+
]);
|
|
210
|
+
const sense = senseAgentAttribution({
|
|
211
|
+
totemDir,
|
|
212
|
+
env: { TOTEM_SELF_AGENT: 'totem-gemini' },
|
|
213
|
+
pointerSessionId: SESSION_B,
|
|
214
|
+
});
|
|
215
|
+
expect(sense.attribution_conflict).toEqual({
|
|
216
|
+
env_seat: 'totem-gemini',
|
|
217
|
+
minting_seat: 'totem-claude',
|
|
218
|
+
pointer_session_id: SESSION_B,
|
|
219
|
+
});
|
|
220
|
+
});
|
|
221
|
+
it('NEVER REMEMBERED: a conflict introduced between two calls is caught on the second', () => {
|
|
222
|
+
writePointer(SESSION_A);
|
|
223
|
+
writeEvents([sessionStartRow(SESSION_A, 'totem-gemini')]);
|
|
224
|
+
const env = { TOTEM_SELF_AGENT: 'totem-gemini' };
|
|
225
|
+
const first = senseAgentAttribution({ totemDir, env });
|
|
226
|
+
expect(first.agent_source).toBe('totem-gemini');
|
|
227
|
+
expect(first.attribution_conflict).toBeUndefined();
|
|
228
|
+
// A foreign seat's SessionStart rotates the shared pointer mid-session.
|
|
229
|
+
writePointer(SESSION_B);
|
|
230
|
+
writeEvents([
|
|
231
|
+
sessionStartRow(SESSION_A, 'totem-gemini'),
|
|
232
|
+
sessionStartRow(SESSION_B, 'totem-claude'),
|
|
233
|
+
]);
|
|
234
|
+
const second = senseAgentAttribution({ totemDir, env });
|
|
235
|
+
expect(second.agent_source).toBeNull();
|
|
236
|
+
expect(second.attribution_conflict).toEqual({
|
|
237
|
+
env_seat: 'totem-gemini',
|
|
238
|
+
minting_seat: 'totem-claude',
|
|
239
|
+
pointer_session_id: SESSION_B,
|
|
240
|
+
});
|
|
241
|
+
});
|
|
242
|
+
it('a failed probe keeps the env stamp and NAMES itself — never silent, never counter-evidence, never a throw', () => {
|
|
243
|
+
writePointer(SESSION_A);
|
|
244
|
+
writeEvents([sessionStartRow(SESSION_A, 'totem-claude')]);
|
|
245
|
+
// Only the events.ndjson read is faked; the pointer read stays real (the
|
|
246
|
+
// qbd/record.test.ts selective-fake pattern).
|
|
247
|
+
const realRead = fs.readFileSync.bind(fs);
|
|
248
|
+
vi.spyOn(fs, 'readFileSync').mockImplementation(((target, ...rest) => {
|
|
249
|
+
if (String(target).endsWith('events.ndjson')) {
|
|
250
|
+
const err = new Error('EACCES: permission denied');
|
|
251
|
+
err.code = 'EACCES';
|
|
252
|
+
throw err;
|
|
253
|
+
}
|
|
254
|
+
return realRead(target, ...rest);
|
|
255
|
+
}));
|
|
256
|
+
const sense = senseAgentAttribution({ totemDir, env: { TOTEM_SELF_AGENT: 'totem-gemini' } });
|
|
257
|
+
expect(sense.agent_source).toBe('totem-gemini');
|
|
258
|
+
expect(sense.agent_source_provenance).toBe('env');
|
|
259
|
+
expect(sense.attribution_conflict).toBeUndefined();
|
|
260
|
+
expect(sense.attribution_probe_error).toContain('EACCES');
|
|
261
|
+
});
|
|
262
|
+
it('provenance biconditional holds across every arm above', () => {
|
|
263
|
+
writePointer(SESSION_A);
|
|
264
|
+
writeEvents([sessionStartRow(SESSION_A, 'totem-claude')]);
|
|
265
|
+
const cases = [
|
|
266
|
+
senseAgentAttribution({ env: {} }),
|
|
267
|
+
senseAgentAttribution({ env: { TOTEM_SELF_AGENT: 'totem-claude' } }),
|
|
268
|
+
senseAgentAttribution({ totemDir, env: {} }),
|
|
269
|
+
senseAgentAttribution({ totemDir, env: { TOTEM_SELF_AGENT: 'totem-claude' } }),
|
|
270
|
+
senseAgentAttribution({ totemDir, env: { TOTEM_SELF_AGENT: 'totem-gemini' } }),
|
|
271
|
+
];
|
|
272
|
+
for (const sense of cases) {
|
|
273
|
+
expect(sense.agent_source_provenance === 'env').toBe(sense.agent_source !== null);
|
|
274
|
+
}
|
|
275
|
+
});
|
|
276
|
+
});
|
|
277
|
+
//# sourceMappingURL=attribution-sense.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"attribution-sense.test.js","sourceRoot":"","sources":["../src/attribution-sense.test.ts"],"names":[],"mappings":"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"}
|
package/dist/errors.d.ts
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* - A recoveryHint telling the user exactly how to fix it
|
|
7
7
|
* - A code for programmatic handling
|
|
8
8
|
*/
|
|
9
|
-
export type TotemErrorCode = 'CONFIG_MISSING' | 'CONFIG_INVALID' | 'DATABASE_CORRUPT' | 'DATABASE_MISMATCH' | 'EMBEDDING_UNAVAILABLE' | 'ORCHESTRATOR_UNAVAILABLE' | 'COMPILE_FAILED' | 'PARSE_FAILED' | 'SYNC_FAILED' | 'GIT_FAILED' | 'NO_LESSONS' | 'NO_LESSONS_DIR' | 'NO_RULES' | 'SHIELD_FAILED' | 'LINT_LESSONS_FAILED' | 'DRIFT_FAILED' | 'TEST_FAILED' | 'CHECK_FAILED' | 'MCP_ERROR' | 'UPGRADE_HASH_NOT_FOUND' | 'UPGRADE_HASH_AMBIGUOUS' | 'STAGED_READ_FAILED' | 'UPGRADE_CLOUD_UNSUPPORTED' | 'STALE_MANIFEST' | 'FLAG_CONFLICT' | 'HOOKS_LOAD_FAILED' | 'SESSION_ID_WRITE_FAILED' | 'SESSION_ID_READ_FAILED' | 'BADGE_VERIFICATION_FAILED' | 'CLAIM_DISCIPLINE_FAILED' | 'GATE_INVALID' | 'PARITY_DRIFT_DETECTED' | 'MAIL_SEND_FAILED' | 'PR_MERGE_FAILED' | 'BASH_RESOLUTION_FAILED'
|
|
9
|
+
export type TotemErrorCode = 'CONFIG_MISSING' | 'CONFIG_INVALID' | 'DATABASE_CORRUPT' | 'DATABASE_MISMATCH' | 'EMBEDDING_UNAVAILABLE' | 'ORCHESTRATOR_UNAVAILABLE' | 'COMPILE_FAILED' | 'PARSE_FAILED' | 'SYNC_FAILED' | 'GIT_FAILED' | 'NO_LESSONS' | 'NO_LESSONS_DIR' | 'NO_RULES' | 'SHIELD_FAILED' | 'LINT_LESSONS_FAILED' | 'DRIFT_FAILED' | 'TEST_FAILED' | 'CHECK_FAILED' | 'MCP_ERROR' | 'UPGRADE_HASH_NOT_FOUND' | 'UPGRADE_HASH_AMBIGUOUS' | 'STAGED_READ_FAILED' | 'UPGRADE_CLOUD_UNSUPPORTED' | 'STALE_MANIFEST' | 'FLAG_CONFLICT' | 'HOOKS_LOAD_FAILED' | 'SESSION_ID_WRITE_FAILED' | 'SESSION_ID_READ_FAILED' | 'ATTRIBUTION_SENSE_LEDGER_READ_FAILED' | 'BADGE_VERIFICATION_FAILED' | 'CLAIM_DISCIPLINE_FAILED' | 'GATE_INVALID' | 'PARITY_DRIFT_DETECTED' | 'MAIL_SEND_FAILED' | 'PR_MERGE_FAILED' | 'BASH_RESOLUTION_FAILED'
|
|
10
10
|
/** Query-before-derive correlation contract breach (mmnto-ai/totem#2510). */
|
|
11
11
|
| 'QBD_CORRELATION_CONTRACT'
|
|
12
12
|
/** Eject's loud backstop: every attempted cleanup mutation failed (mmnto-ai/totem#2620). */
|