@mmnto/totem 1.114.0 → 1.115.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/errors.d.ts +5 -1
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js.map +1 -1
- package/dist/fs-atomic.d.ts +63 -0
- package/dist/fs-atomic.d.ts.map +1 -0
- package/dist/fs-atomic.js +114 -0
- package/dist/fs-atomic.js.map +1 -0
- package/dist/fs-atomic.test.d.ts +2 -0
- package/dist/fs-atomic.test.d.ts.map +1 -0
- package/dist/fs-atomic.test.js +162 -0
- package/dist/fs-atomic.test.js.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -1
- package/dist/ingest/pipeline.d.ts.map +1 -1
- package/dist/ingest/pipeline.js +5 -5
- package/dist/ingest/pipeline.js.map +1 -1
- package/dist/orchestration-resolver.d.ts +19 -0
- package/dist/orchestration-resolver.d.ts.map +1 -1
- package/dist/orchestration-resolver.js +5 -1
- package/dist/orchestration-resolver.js.map +1 -1
- package/dist/seat-lifecycle.d.ts +118 -0
- package/dist/seat-lifecycle.d.ts.map +1 -0
- package/dist/seat-lifecycle.js +197 -0
- package/dist/seat-lifecycle.js.map +1 -0
- package/dist/seat-lifecycle.test.d.ts +11 -0
- package/dist/seat-lifecycle.test.d.ts.map +1 -0
- package/dist/seat-lifecycle.test.js +210 -0
- package/dist/seat-lifecycle.test.js.map +1 -0
- package/dist/selection-manifest.d.ts +416 -0
- package/dist/selection-manifest.d.ts.map +1 -0
- package/dist/selection-manifest.js +444 -0
- package/dist/selection-manifest.js.map +1 -0
- package/dist/selection-manifest.test.d.ts +26 -0
- package/dist/selection-manifest.test.d.ts.map +1 -0
- package/dist/selection-manifest.test.js +331 -0
- package/dist/selection-manifest.test.js.map +1 -0
- package/dist/spine/ledgers.d.ts +12 -12
- package/dist/verification-outcomes.d.ts.map +1 -1
- package/dist/verification-outcomes.js +6 -12
- package/dist/verification-outcomes.js.map +1 -1
- package/dist/worktree-registry.d.ts.map +1 -1
- package/dist/worktree-registry.js +4 -4
- package/dist/worktree-registry.js.map +1 -1
- package/package.json +15 -11
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"selection-manifest.d.ts","sourceRoot":"","sources":["../src/selection-manifest.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,444 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-seat selection manifests — the Prop 308 / strategy#467 M1 instrument
|
|
3
|
+
* (mmnto-ai/totem#2468).
|
|
4
|
+
*
|
|
5
|
+
* A selection manifest is a read-only record of one selection event: which
|
|
6
|
+
* candidates a consumer (session-start hook, `search_knowledge`, `totem
|
|
7
|
+
* orient`) considered on a seat's behalf, which it selected or excluded, what
|
|
8
|
+
* each cost, and the policy's own stated reason. It is a record of what each
|
|
9
|
+
* policy did — never a ranker, and it never adjudicates between policies.
|
|
10
|
+
*
|
|
11
|
+
* ## Contract (binding, from the Prop 308 round + the armed #467 pre-registration)
|
|
12
|
+
*
|
|
13
|
+
* - **No shared relevance score, no common weights** (Prop 308 F1, unanimous;
|
|
14
|
+
* lc-codex refute condition). Both schema levels are `.strict()` so a shared
|
|
15
|
+
* score field cannot be added without failing parse of existing rows — the
|
|
16
|
+
* guard is structural, not a promise. Per-candidate `reason` strings carry
|
|
17
|
+
* each policy's OWN words (a floor value in a search reason is that policy's
|
|
18
|
+
* stated reason, not a common weight).
|
|
19
|
+
* - **Senses, never gates** (Tenet 13). Emission must never block, filter, or
|
|
20
|
+
* alter a selection. Command call sites use {@link senseSelectionManifest};
|
|
21
|
+
* the throwing writer exists so the contract is testable and a programming
|
|
22
|
+
* error is never silent (the `qbd/record.ts` posture, mirrored).
|
|
23
|
+
* - **Named cost basis** (#467 outcome 3's naming requirement): every figure
|
|
24
|
+
* names its aggregation — {@link SELECTION_COST_BASIS} is a fixed literal
|
|
25
|
+
* stamped on every row. What the manifest emits is a PAYLOAD-BYTE census
|
|
26
|
+
* with a declared token approximation; it is NOT the ruled `<total>` basis
|
|
27
|
+
* (measured cache-aware consumption), and whether it may substitute for a
|
|
28
|
+
* `<total>` surface is the pre-registration's call via versioned revision
|
|
29
|
+
* (#467 §9), never this module's claim.
|
|
30
|
+
* - **No fabricated measurement:** a manifest records only what the policy
|
|
31
|
+
* observed. Candidates the policy never read (recency-excluded journals,
|
|
32
|
+
* first-match-losing proposals) are recorded id-only — no fingerprint, no
|
|
33
|
+
* bytes. Emitters never over-fetch to manufacture exclusions, which would
|
|
34
|
+
* alter the thing being measured.
|
|
35
|
+
* - **Recorded absence, never silent skip** (the #2466 clause): the
|
|
36
|
+
* denominator lives in `events.ndjson` rows (`session_start`, `mcp_call`,
|
|
37
|
+
* `derive_action`) that ship independently of this instrument. A denominator
|
|
38
|
+
* row with no matching manifest row IS the recorded absence; a failed write
|
|
39
|
+
* additionally warns on the emitter's accounting channel.
|
|
40
|
+
*/
|
|
41
|
+
import { createHash } from 'node:crypto';
|
|
42
|
+
import * as fs from 'node:fs';
|
|
43
|
+
import * as path from 'node:path';
|
|
44
|
+
import { z } from 'zod';
|
|
45
|
+
import { TotemError } from './errors.js';
|
|
46
|
+
import { resolveQbdAgentSource } from './qbd/record.js';
|
|
47
|
+
import { readSessionId } from './session-id.js';
|
|
48
|
+
// ─── Constants ──────────────────────────────────────────
|
|
49
|
+
const LEDGER_DIR = 'ledger';
|
|
50
|
+
/**
|
|
51
|
+
* Sidecar file, NOT rows in `events.ndjson` (ruled 2026-08-10, #2468 OQ1):
|
|
52
|
+
* per-candidate arrays are bulky, and the event stream's existing consumers
|
|
53
|
+
* (QBD scanner, stats) keep their scan economics. Same directory, same
|
|
54
|
+
* append-only + gitignored conventions; growth is bounded the same way
|
|
55
|
+
* `events.ndjson` is (janitorial rotation is a separate, pre-existing class).
|
|
56
|
+
*/
|
|
57
|
+
export const SELECTION_MANIFESTS_FILE = 'selection-manifests.ndjson';
|
|
58
|
+
/**
|
|
59
|
+
* The named aggregation stamped on every row (#467 outcome-3 requirement:
|
|
60
|
+
* a measure without its basis reports NOT OBSERVED — so the basis rides the
|
|
61
|
+
* data). `bytes` is the UTF-8 byte length of the exact content the policy
|
|
62
|
+
* considered; `approxTokens` is declared as the chars/4 approximation, never
|
|
63
|
+
* presented as a measured token count.
|
|
64
|
+
*/
|
|
65
|
+
export const SELECTION_COST_BASIS = {
|
|
66
|
+
bytes: 'utf8-length',
|
|
67
|
+
approxTokens: 'ceil(bytes/4) approximation',
|
|
68
|
+
};
|
|
69
|
+
/** Emitting consumers — the three selection surfaces named by #2468. */
|
|
70
|
+
export const SELECTION_EMITTERS = ['session-start', 'search_knowledge', 'orient'];
|
|
71
|
+
// ─── Schema ─────────────────────────────────────────────
|
|
72
|
+
/**
|
|
73
|
+
* One candidate the policy observed. `.strict()` — see the module contract:
|
|
74
|
+
* no field can be added silently, which is the structural half of the Prop 308
|
|
75
|
+
* refute-condition guard.
|
|
76
|
+
*/
|
|
77
|
+
export const SelectionCandidateSchema = z
|
|
78
|
+
.object({
|
|
79
|
+
/**
|
|
80
|
+
* Candidate identity: repo-relative path or logical id. The coarse join
|
|
81
|
+
* key for the overlap number — a whole-file fingerprint and a chunk
|
|
82
|
+
* fingerprint of the same source will not collide, so cross-emitter joins
|
|
83
|
+
* anchor on `id` first and use `fingerprint` for exactness. Join policy
|
|
84
|
+
* belongs to the measurement pass, declared here rather than solved.
|
|
85
|
+
*/
|
|
86
|
+
id: z.string().trim().min(1),
|
|
87
|
+
/** Linked-repo origin for federated hits; absent for primary/local. */
|
|
88
|
+
sourceRepo: z.string().trim().min(1).optional(),
|
|
89
|
+
/**
|
|
90
|
+
* sha256 hex, first 16 chars, over the EXACT bytes the policy considered.
|
|
91
|
+
* Absent when the policy never read the content (id-only exclusion) or
|
|
92
|
+
* when hashing failed (which also appends to `warnings` — an absent
|
|
93
|
+
* fingerprint is honest, a fabricated one never is).
|
|
94
|
+
*/
|
|
95
|
+
fingerprint: z
|
|
96
|
+
.string()
|
|
97
|
+
.regex(/^[0-9a-f]{16}$/)
|
|
98
|
+
.optional(),
|
|
99
|
+
/** UTF-8 byte length of the considered content. Absent on id-only rows. */
|
|
100
|
+
bytes: z.number().int().nonnegative().optional(),
|
|
101
|
+
/** ceil(bytes/4), under the declared approximation basis. */
|
|
102
|
+
approxTokens: z.number().int().nonnegative().optional(),
|
|
103
|
+
/**
|
|
104
|
+
* What the policy did with this candidate. `truncated` = selected but
|
|
105
|
+
* partially delivered (`deliveredBytes` carries what actually shipped).
|
|
106
|
+
*/
|
|
107
|
+
disposition: z.enum(['selected', 'truncated', 'excluded']),
|
|
108
|
+
/**
|
|
109
|
+
* The policy's own stated reason — the why-selected / why-excluded half
|
|
110
|
+
* that is invisible today. Free text owned by the emitting policy.
|
|
111
|
+
*/
|
|
112
|
+
reason: z.string().trim().min(1),
|
|
113
|
+
/** For `truncated` rows: bytes actually delivered after the cut. */
|
|
114
|
+
deliveredBytes: z.number().int().nonnegative().optional(),
|
|
115
|
+
})
|
|
116
|
+
.strict()
|
|
117
|
+
// Measured-candidate invariants (PR #2625 CR round): the builders produce
|
|
118
|
+
// exactly two shapes — id-only (nothing measured) or fully measured — so
|
|
119
|
+
// the schema refuses the partial states in between, and `deliveredBytes`
|
|
120
|
+
// is meaningful only on a truncated row and can never exceed the full
|
|
121
|
+
// measurement it was cut from.
|
|
122
|
+
.superRefine((c, ctx) => {
|
|
123
|
+
const measuredPresent = [c.fingerprint, c.bytes, c.approxTokens].filter((v) => v !== undefined).length;
|
|
124
|
+
if (measuredPresent !== 0 && measuredPresent !== 3) {
|
|
125
|
+
ctx.addIssue({
|
|
126
|
+
code: z.ZodIssueCode.custom,
|
|
127
|
+
message: 'measured fields (fingerprint, bytes, approxTokens) travel together — a candidate is id-only or fully measured, never partial',
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
if (c.deliveredBytes !== undefined) {
|
|
131
|
+
if (c.disposition !== 'truncated') {
|
|
132
|
+
ctx.addIssue({
|
|
133
|
+
code: z.ZodIssueCode.custom,
|
|
134
|
+
path: ['deliveredBytes'],
|
|
135
|
+
message: 'deliveredBytes is only meaningful on a truncated candidate',
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
if (c.bytes !== undefined && c.deliveredBytes > c.bytes) {
|
|
139
|
+
ctx.addIssue({
|
|
140
|
+
code: z.ZodIssueCode.custom,
|
|
141
|
+
path: ['deliveredBytes'],
|
|
142
|
+
message: 'deliveredBytes cannot exceed the full measured bytes',
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
});
|
|
147
|
+
/**
|
|
148
|
+
* The CLOSED set of per-emitter context keys. Closing the key space is the
|
|
149
|
+
* context-layer half of the Prop 308 refute-condition guard (PR #2625 CR
|
|
150
|
+
* round): with an open record, `context.sharedRelevanceScore` would have
|
|
151
|
+
* parsed cleanly and the "no shared score without a schema change" claim held
|
|
152
|
+
* only at the row/candidate layers. Adding a context key is now a deliberate
|
|
153
|
+
* schema change, exactly like adding a field.
|
|
154
|
+
*/
|
|
155
|
+
export const SELECTION_CONTEXT_KEYS = [
|
|
156
|
+
// search_knowledge
|
|
157
|
+
'query',
|
|
158
|
+
'boundary',
|
|
159
|
+
'typeFilter',
|
|
160
|
+
'floor',
|
|
161
|
+
'finalLimit',
|
|
162
|
+
'method',
|
|
163
|
+
'status',
|
|
164
|
+
'storesFailed',
|
|
165
|
+
'storesUnavailable',
|
|
166
|
+
// orient
|
|
167
|
+
'render',
|
|
168
|
+
// session-start (V2 hook + published templates)
|
|
169
|
+
'branch',
|
|
170
|
+
'ticket',
|
|
171
|
+
'selfAgent',
|
|
172
|
+
'template',
|
|
173
|
+
];
|
|
174
|
+
/**
|
|
175
|
+
* Per-emitter context: a FLAT scalar map over the closed key set. Flat by
|
|
176
|
+
* design — a nested structure is where a shared relevance model would grow,
|
|
177
|
+
* and the schema refuses it room; closed-keyed so it cannot grow one either.
|
|
178
|
+
*/
|
|
179
|
+
const SelectionContextSchema = z.record(z.enum(SELECTION_CONTEXT_KEYS), z.union([z.string(), z.number(), z.boolean(), z.null()]));
|
|
180
|
+
/** One selection event — one NDJSON row in `selection-manifests.ndjson`. */
|
|
181
|
+
export const SelectionManifestRowSchema = z
|
|
182
|
+
.object({
|
|
183
|
+
schemaVersion: z.literal(1),
|
|
184
|
+
/** ISO 8601 instant of the selection event. */
|
|
185
|
+
timestamp: z.string().datetime(),
|
|
186
|
+
emitter: z.enum(SELECTION_EMITTERS),
|
|
187
|
+
/** Session UUID from `.totem/ledger/.session-id` — the denominator join key. */
|
|
188
|
+
session_id: z.string().uuid().optional(),
|
|
189
|
+
/** Seat id from TOTEM_SELF_AGENT (ADR-078). Absent = stamped absence, never guessed. */
|
|
190
|
+
agent_source: z.string().trim().min(1).optional(),
|
|
191
|
+
/** Emitting package version, when the emitter can resolve it cheaply. */
|
|
192
|
+
cli_version: z.string().trim().min(1).optional(),
|
|
193
|
+
context: SelectionContextSchema,
|
|
194
|
+
/**
|
|
195
|
+
* Names the observable candidate pool (e.g. `store-returned-pool
|
|
196
|
+
* perStoreLimit=5`) — the honest boundary: exclusions are recorded only
|
|
197
|
+
* over candidates the emitter actually saw.
|
|
198
|
+
*/
|
|
199
|
+
universe: z.string().trim().min(1),
|
|
200
|
+
costBasis: z
|
|
201
|
+
.object({
|
|
202
|
+
bytes: z.literal(SELECTION_COST_BASIS.bytes),
|
|
203
|
+
approxTokens: z.literal(SELECTION_COST_BASIS.approxTokens),
|
|
204
|
+
})
|
|
205
|
+
.strict(),
|
|
206
|
+
/**
|
|
207
|
+
* The session-start hook's global char slice — string-level, so its cut
|
|
208
|
+
* is NOT attributed per-candidate (declared limitation, not a gap).
|
|
209
|
+
*/
|
|
210
|
+
finalTruncation: z
|
|
211
|
+
.object({
|
|
212
|
+
cap: z.number().int().positive(),
|
|
213
|
+
totalChars: z.number().int().nonnegative(),
|
|
214
|
+
applied: z.boolean(),
|
|
215
|
+
})
|
|
216
|
+
.strict()
|
|
217
|
+
.optional(),
|
|
218
|
+
candidates: z.array(SelectionCandidateSchema),
|
|
219
|
+
/** Per-row accounting: every non-fatal degradation names itself here. */
|
|
220
|
+
warnings: z.array(z.string()),
|
|
221
|
+
})
|
|
222
|
+
.strict();
|
|
223
|
+
// ─── Measurement helpers ────────────────────────────────
|
|
224
|
+
/**
|
|
225
|
+
* Fingerprint the exact bytes a policy considered: sha256 hex, first 16.
|
|
226
|
+
* 64 bits of collision resistance is ample for a per-workspace corpus join
|
|
227
|
+
* key; the full digest would double every row's bulk for no analytical gain.
|
|
228
|
+
*/
|
|
229
|
+
export function fingerprintContent(content) {
|
|
230
|
+
return createHash('sha256').update(content).digest('hex').slice(0, 16);
|
|
231
|
+
}
|
|
232
|
+
/** Cost of a considered candidate under {@link SELECTION_COST_BASIS}. */
|
|
233
|
+
export function measureCandidateCost(content) {
|
|
234
|
+
const bytes = typeof content === 'string' ? Buffer.byteLength(content, 'utf-8') : content.length;
|
|
235
|
+
return { bytes, approxTokens: Math.ceil(bytes / 4) };
|
|
236
|
+
}
|
|
237
|
+
/**
|
|
238
|
+
* Build a fully-measured candidate from content the policy actually read.
|
|
239
|
+
*
|
|
240
|
+
* TOTAL over its input space (leg round 1, H-7): a non-string/non-Buffer
|
|
241
|
+
* `content` (a store row whose unchecked cast lied) or a hash failure
|
|
242
|
+
* degrades to an ID-ONLY row with the failure named on the returned warning —
|
|
243
|
+
* it never throws into the emitting command, and it never persists a
|
|
244
|
+
* fabricated or NaN measurement.
|
|
245
|
+
*/
|
|
246
|
+
export function buildMeasuredCandidate(input) {
|
|
247
|
+
const idOnly = {
|
|
248
|
+
id: input.id,
|
|
249
|
+
disposition: input.disposition,
|
|
250
|
+
reason: input.reason,
|
|
251
|
+
...(input.sourceRepo !== undefined && { sourceRepo: input.sourceRepo }),
|
|
252
|
+
...(input.deliveredBytes !== undefined && { deliveredBytes: input.deliveredBytes }),
|
|
253
|
+
};
|
|
254
|
+
const content = input.content;
|
|
255
|
+
if (typeof content !== 'string' && !Buffer.isBuffer(content)) {
|
|
256
|
+
return {
|
|
257
|
+
candidate: idOnly,
|
|
258
|
+
warning: `selection-manifest: unmeasurable content for ${input.id} (got ${typeof content}) — recorded id-only`,
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
try {
|
|
262
|
+
const { bytes, approxTokens } = measureCandidateCost(content);
|
|
263
|
+
return {
|
|
264
|
+
candidate: { ...idOnly, bytes, approxTokens, fingerprint: fingerprintContent(content) },
|
|
265
|
+
};
|
|
266
|
+
// totem-context: intentional degradation — a measurement/hash failure records the candidate ID-ONLY and names the failure on the accounting channel; fabricating or dropping the row would both lie (Tenet 13).
|
|
267
|
+
}
|
|
268
|
+
catch (err) {
|
|
269
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
270
|
+
return {
|
|
271
|
+
candidate: idOnly,
|
|
272
|
+
warning: `selection-manifest: measurement failed for ${input.id}: ${msg} — recorded id-only`,
|
|
273
|
+
};
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
/**
|
|
277
|
+
* Is this an instrumented project at all? Same convention as
|
|
278
|
+
* `qbd/record.ts`: an absent `.totem` (ENOENT, or ENOTDIR — a path component
|
|
279
|
+
* being a file, the cross-platform divergence `session-id.ts` documents) is
|
|
280
|
+
* the honest "nothing here to instrument" state — skip silently rather than
|
|
281
|
+
* materialising a stray `.totem/ledger/` in whatever cwd an instrumented
|
|
282
|
+
* command ran from. Every OTHER errno (EACCES, I/O) is a real probe failure
|
|
283
|
+
* that must not masquerade as not-a-project (PR #2625 CR round): it returns a
|
|
284
|
+
* named warning so the skip lands on the accounting channel.
|
|
285
|
+
*/
|
|
286
|
+
function probeInstrumentedProject(totemDir) {
|
|
287
|
+
try {
|
|
288
|
+
return { instrumented: fs.statSync(totemDir).isDirectory() };
|
|
289
|
+
// totem-context: intentional degradation — a missing `.totem` is the honest "not a Totem project" state; any OTHER errno is reported via the returned warning (the accounting channel), never swallowed.
|
|
290
|
+
}
|
|
291
|
+
catch (err) {
|
|
292
|
+
const code = typeof err === 'object' && err !== null ? err.code : undefined;
|
|
293
|
+
if (code === 'ENOENT' || code === 'ENOTDIR')
|
|
294
|
+
return { instrumented: false };
|
|
295
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
296
|
+
return {
|
|
297
|
+
instrumented: false,
|
|
298
|
+
warning: `selection-manifest: .totem probe failed (${msg}) — not recording`,
|
|
299
|
+
};
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
/** Stamp attribution + basis onto an emitter's assembled input. */
|
|
303
|
+
export function buildSelectionManifestRow(input) {
|
|
304
|
+
const env = input.env ?? process.env;
|
|
305
|
+
const nowMs = input.nowMs ?? Date.now();
|
|
306
|
+
// An emitter that MINTED the session id this event belongs to passes it
|
|
307
|
+
// directly (the session-start hooks) — immune to the shared-pointer being
|
|
308
|
+
// rotated by a concurrent session between mint and emission (PR #2625 CR
|
|
309
|
+
// round, partial). Everyone else joins via the pointer.
|
|
310
|
+
const sessionId = input.sessionId ?? readSessionId(input.totemDir);
|
|
311
|
+
const agentSource = resolveQbdAgentSource(env);
|
|
312
|
+
return {
|
|
313
|
+
schemaVersion: 1,
|
|
314
|
+
timestamp: new Date(nowMs).toISOString(),
|
|
315
|
+
emitter: input.emitter,
|
|
316
|
+
...(sessionId !== undefined && { session_id: sessionId }),
|
|
317
|
+
...(agentSource !== undefined && { agent_source: agentSource }),
|
|
318
|
+
...(input.cliVersion !== undefined && { cli_version: input.cliVersion }),
|
|
319
|
+
context: input.context,
|
|
320
|
+
universe: input.universe,
|
|
321
|
+
costBasis: { ...SELECTION_COST_BASIS },
|
|
322
|
+
...(input.finalTruncation !== undefined && { finalTruncation: input.finalTruncation }),
|
|
323
|
+
candidates: input.candidates,
|
|
324
|
+
warnings: input.warnings ?? [],
|
|
325
|
+
};
|
|
326
|
+
}
|
|
327
|
+
// ─── Writers ────────────────────────────────────────────
|
|
328
|
+
/**
|
|
329
|
+
* Validate and append one manifest row.
|
|
330
|
+
*
|
|
331
|
+
* Throws `SELECTION_MANIFEST_CONTRACT` on a schema-invalid row — that is a
|
|
332
|
+
* programming error in an emitter, and persisting it would hand the
|
|
333
|
+
* measurement pass a row `readSelectionManifests` silently skips (data loss
|
|
334
|
+
* dressed as data). I/O failures do not throw; they report through the
|
|
335
|
+
* returned `warnings` (the accounting channel).
|
|
336
|
+
*/
|
|
337
|
+
export function appendSelectionManifest(input) {
|
|
338
|
+
const warnings = [...(input.warnings ?? [])];
|
|
339
|
+
const probe = probeInstrumentedProject(input.totemDir);
|
|
340
|
+
if (!probe.instrumented) {
|
|
341
|
+
if (probe.warning !== undefined)
|
|
342
|
+
warnings.push(probe.warning);
|
|
343
|
+
return { written: false, skipped: true, warnings };
|
|
344
|
+
}
|
|
345
|
+
const row = buildSelectionManifestRow({ ...input, warnings });
|
|
346
|
+
const parsed = SelectionManifestRowSchema.safeParse(row);
|
|
347
|
+
if (!parsed.success) {
|
|
348
|
+
const detail = parsed.error.issues.map((i) => `${i.path.join('.')}: ${i.message}`).join('; ');
|
|
349
|
+
throw new TotemError('SELECTION_MANIFEST_CONTRACT', `Refusing to write a selection-manifest row that violates the schema: ${detail}`, 'This is an emitter programming error — the manifest schema is strict by contract (Prop 308 F1 refute-condition guard); fix the emitter rather than widening the schema.');
|
|
350
|
+
}
|
|
351
|
+
let written = true;
|
|
352
|
+
try {
|
|
353
|
+
const ledgerDir = path.join(input.totemDir, LEDGER_DIR);
|
|
354
|
+
fs.mkdirSync(ledgerDir, { recursive: true });
|
|
355
|
+
fs.appendFileSync(path.join(ledgerDir, SELECTION_MANIFESTS_FILE), JSON.stringify(parsed.data) + '\n', 'utf-8');
|
|
356
|
+
// totem-context: intentional degradation — the manifest is telemetry; an I/O failure is recorded on the `warnings` accounting channel and the absence stays detectable via the denominator ledger rows (never a silent skip).
|
|
357
|
+
}
|
|
358
|
+
catch (err) {
|
|
359
|
+
written = false;
|
|
360
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
361
|
+
warnings.push(`selection-manifest: append failed: ${msg}`);
|
|
362
|
+
}
|
|
363
|
+
return { written, warnings };
|
|
364
|
+
}
|
|
365
|
+
/**
|
|
366
|
+
* Sensor-safe `appendSelectionManifest` — command call sites use this.
|
|
367
|
+
* Every degradation, including a thrown contract breach, surfaces through
|
|
368
|
+
* `onWarn`; there is no path that discards a failure silently, and no path
|
|
369
|
+
* that fails the instrumented command (Tenet 13).
|
|
370
|
+
*/
|
|
371
|
+
/**
|
|
372
|
+
* Deliver a warning to the caller's sink without letting the sink's own
|
|
373
|
+
* failure escape — an accounting-channel exception altering the instrumented
|
|
374
|
+
* command would invert Tenet 13 at the last step (PR #2625 CR round).
|
|
375
|
+
*/
|
|
376
|
+
function safeWarn(onWarn, msg) {
|
|
377
|
+
try {
|
|
378
|
+
onWarn?.(msg);
|
|
379
|
+
// totem-context: intentional swallow — a throwing warning SINK must not fail the instrumented command; the warning also remains in the returned `warnings` array, so nothing is lost.
|
|
380
|
+
}
|
|
381
|
+
catch (err) {
|
|
382
|
+
void err;
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
export function senseSelectionManifest(input, onWarn) {
|
|
386
|
+
try {
|
|
387
|
+
const result = appendSelectionManifest(input);
|
|
388
|
+
for (const warning of result.warnings)
|
|
389
|
+
safeWarn(onWarn, warning);
|
|
390
|
+
return result;
|
|
391
|
+
// totem-context: instrumentation is a sensor — a telemetry failure must never fail the instrumented command (Tenet 13); the failure is reported through onWarn, never discarded.
|
|
392
|
+
}
|
|
393
|
+
catch (err) {
|
|
394
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
395
|
+
const warning = `selection-manifest sensor failed: ${msg}`;
|
|
396
|
+
safeWarn(onWarn, warning);
|
|
397
|
+
return { written: false, warnings: [warning] };
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
/**
|
|
401
|
+
* Read all manifest rows, skipping schema-invalid lines (the generic-consumer
|
|
402
|
+
* convention shared with `readLedgerEvents`; a measurement pass that must
|
|
403
|
+
* count rejects reads the file raw — same division of labor as QBD's
|
|
404
|
+
* compliance scanner).
|
|
405
|
+
*/
|
|
406
|
+
export function readSelectionManifests(totemDir, onWarn) {
|
|
407
|
+
const filePath = path.join(totemDir, LEDGER_DIR, SELECTION_MANIFESTS_FILE);
|
|
408
|
+
let content;
|
|
409
|
+
try {
|
|
410
|
+
content = fs.readFileSync(filePath, 'utf-8');
|
|
411
|
+
// totem-context: intentional degradation — ENOENT is the normal no-manifests-yet state; every other read failure is REPORTED through onWarn (the accounting channel), never swallowed, and the reader degrades to empty rather than failing its caller (Tenet 13 sensor posture, mirroring readLedgerEvents).
|
|
412
|
+
}
|
|
413
|
+
catch (err) {
|
|
414
|
+
const code = typeof err === 'object' && err !== null ? err.code : undefined;
|
|
415
|
+
if (code === 'ENOENT')
|
|
416
|
+
return [];
|
|
417
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
418
|
+
onWarn?.(`selection-manifest read failed: ${msg}`);
|
|
419
|
+
return [];
|
|
420
|
+
}
|
|
421
|
+
const rows = [];
|
|
422
|
+
let malformed = 0;
|
|
423
|
+
for (const line of content.split('\n')) {
|
|
424
|
+
if (line.trim().length === 0)
|
|
425
|
+
continue;
|
|
426
|
+
try {
|
|
427
|
+
const parsed = SelectionManifestRowSchema.safeParse(JSON.parse(line));
|
|
428
|
+
if (parsed.success)
|
|
429
|
+
rows.push(parsed.data);
|
|
430
|
+
// totem-context: intentional skip of malformed lines — generic consumers mirror readLedgerEvents; the AGGREGATE count is reported through onWarn below, and reject-counting consumers read the file raw by contract.
|
|
431
|
+
}
|
|
432
|
+
catch (err) {
|
|
433
|
+
void err;
|
|
434
|
+
malformed += 1;
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
// One aggregate line, not one per row — a torn or hand-edited file must not
|
|
438
|
+
// turn the accounting channel into a firehose (PR #2625 CR round).
|
|
439
|
+
if (malformed > 0) {
|
|
440
|
+
onWarn?.(`selection-manifest: skipped ${malformed} malformed line(s) — reject-counting consumers read the file raw`);
|
|
441
|
+
}
|
|
442
|
+
return rows;
|
|
443
|
+
}
|
|
444
|
+
//# sourceMappingURL=selection-manifest.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"selection-manifest.js","sourceRoot":"","sources":["../src/selection-manifest.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Writer + schema tests for the selection-manifest instrument
|
|
3
|
+
* (mmnto-ai/totem#2468 — the Prop 308 / strategy#467 M1 emitter substrate).
|
|
4
|
+
*
|
|
5
|
+
* The invariants under test come verbatim from the design doc
|
|
6
|
+
* (.totem/specs/2468-selection-manifests.md):
|
|
7
|
+
*
|
|
8
|
+
* - the schema is strict at BOTH levels — the structural refute-condition
|
|
9
|
+
* guard (Prop 308 F1: no shared score field can appear silently);
|
|
10
|
+
* - a fingerprint covers exactly the considered bytes: stable on identical
|
|
11
|
+
* content, changed on changed content, ABSENT (with a named warning) on
|
|
12
|
+
* hash failure — never fabricated;
|
|
13
|
+
* - attribution is stamped absence: no env/seat/session → fields absent,
|
|
14
|
+
* never guessed;
|
|
15
|
+
* - emission is a pure sensor: a failed append leaves the caller unbroken
|
|
16
|
+
* and the failure named on the accounting channel; a schema-invalid row
|
|
17
|
+
* THROWS from the writer (emitter programming error) and is converted to
|
|
18
|
+
* a warning by the sense wrapper;
|
|
19
|
+
* - a `.totem`-less cwd is a silent normal-state skip that materialises no
|
|
20
|
+
* directory.
|
|
21
|
+
*
|
|
22
|
+
* The EACCES induced-failure uses vi.spyOn on `node:fs` (accounting-branch
|
|
23
|
+
* coverage, same scoping rationale as qbd/record.test.ts documents).
|
|
24
|
+
*/
|
|
25
|
+
export {};
|
|
26
|
+
//# sourceMappingURL=selection-manifest.test.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"selection-manifest.test.d.ts","sourceRoot":"","sources":["../src/selection-manifest.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG"}
|