@mmnto/totem 1.122.0 → 1.124.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/artifacts/legs.d.ts +430 -0
- package/dist/artifacts/legs.d.ts.map +1 -0
- package/dist/artifacts/legs.js +577 -0
- package/dist/artifacts/legs.js.map +1 -0
- package/dist/artifacts/legs.test.d.ts +19 -0
- package/dist/artifacts/legs.test.d.ts.map +1 -0
- package/dist/artifacts/legs.test.js +628 -0
- package/dist/artifacts/legs.test.js.map +1 -0
- package/dist/config-schema.d.ts +35 -0
- package/dist/config-schema.d.ts.map +1 -1
- package/dist/config-schema.js +28 -0
- package/dist/config-schema.js.map +1 -1
- package/dist/config-schema.test.js +73 -0
- package/dist/config-schema.test.js.map +1 -1
- package/dist/errors.d.ts +3 -1
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js.map +1 -1
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/dist/routing/legs-owed.d.ts +65 -0
- package/dist/routing/legs-owed.d.ts.map +1 -0
- package/dist/routing/legs-owed.js +72 -0
- package/dist/routing/legs-owed.js.map +1 -0
- package/dist/routing/legs-owed.test.d.ts +10 -0
- package/dist/routing/legs-owed.test.d.ts.map +1 -0
- package/dist/routing/legs-owed.test.js +119 -0
- package/dist/routing/legs-owed.test.js.map +1 -0
- package/dist/semantic-dedup.d.ts.map +1 -1
- package/dist/semantic-dedup.js +2 -5
- package/dist/semantic-dedup.js.map +1 -1
- package/dist/semantic-dedup.test.js +32 -1
- package/dist/semantic-dedup.test.js.map +1 -1
- package/dist/store/lance-store.test.js +23 -0
- package/dist/store/lance-store.test.js.map +1 -1
- package/dist/store/search-lessons.d.ts +18 -0
- package/dist/store/search-lessons.d.ts.map +1 -0
- package/dist/store/search-lessons.js +18 -0
- package/dist/store/search-lessons.js.map +1 -0
- package/dist/store/search-lessons.test.d.ts +2 -0
- package/dist/store/search-lessons.test.d.ts.map +1 -0
- package/dist/store/search-lessons.test.js +48 -0
- package/dist/store/search-lessons.test.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,628 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Leg-deposit store tests (mmnto-ai/totem#2698).
|
|
3
|
+
*
|
|
4
|
+
* These lock the core-owned half of the design's "invariants to lock in via
|
|
5
|
+
* tests": the store is read JSON-AWARE through the schema (a file that merely
|
|
6
|
+
* quotes `diffSha`/`findings` in some other shape is not a deposit), ancestry
|
|
7
|
+
* ranking is exact > nearest ancestor > latest read, a corrupt file is
|
|
8
|
+
* disclosed by name and reason and never hides a valid sibling,
|
|
9
|
+
* `unknown-commit` and `not-ancestor` are DISTINCT stale reasons, the schema
|
|
10
|
+
* refuses control bytes and a non-subset `folded` at write and at read alike,
|
|
11
|
+
* and the writer is create-exclusive with a disclosed replacement.
|
|
12
|
+
*
|
|
13
|
+
* Every hostile string is built with `String.fromCharCode` on purpose: a
|
|
14
|
+
* literal `\u`/`\x` escape authored through an editing tool has landed as a
|
|
15
|
+
* RAW control byte in this repo before, which would make the test assert
|
|
16
|
+
* something other than what it reads as.
|
|
17
|
+
*/
|
|
18
|
+
import * as fs from 'node:fs';
|
|
19
|
+
import * as os from 'node:os';
|
|
20
|
+
import * as path from 'node:path';
|
|
21
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
22
|
+
import { countLegFindings, findLegDepositForHead, LEG_DEPOSIT_KNOWN_MAJOR, LEG_DEPOSIT_SCHEMA_VERSION, LEG_FINDING_SEVERITIES, LegDepositExistsError, legDepositPath, LegDepositSchema, legsDir, loadLegDeposits, renderLegField, saveLegDeposit, } from './legs.js';
|
|
23
|
+
const LF = String.fromCharCode(0x0a);
|
|
24
|
+
const NUL = String.fromCharCode(0x00);
|
|
25
|
+
const NEL = String.fromCharCode(0x85);
|
|
26
|
+
const ESC = String.fromCharCode(0x1b);
|
|
27
|
+
/** Distinct, deterministic 40-hex shas — the store's addresses. */
|
|
28
|
+
function sha(seed) {
|
|
29
|
+
return seed.repeat(40).slice(0, 40);
|
|
30
|
+
}
|
|
31
|
+
const HEAD = sha('a');
|
|
32
|
+
const OLDER = sha('b');
|
|
33
|
+
const OLDEST = sha('c');
|
|
34
|
+
const ELSEWHERE = sha('d');
|
|
35
|
+
function finding(overrides = {}) {
|
|
36
|
+
return {
|
|
37
|
+
id: 'F1',
|
|
38
|
+
severity: 'BLOCKING',
|
|
39
|
+
file: 'packages/core/src/artifacts/legs.ts',
|
|
40
|
+
line: 12,
|
|
41
|
+
claim: 'the loader throws on a corrupt file',
|
|
42
|
+
counterexample: 'loadLegDeposits returns a corrupt row instead',
|
|
43
|
+
...overrides,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
function deposit(overrides = {}) {
|
|
47
|
+
return {
|
|
48
|
+
schemaVersion: LEG_DEPOSIT_SCHEMA_VERSION,
|
|
49
|
+
diffSha: HEAD,
|
|
50
|
+
readAt: '2026-09-03T05:00:00.000Z',
|
|
51
|
+
findings: [finding()],
|
|
52
|
+
folded: ['F1'],
|
|
53
|
+
verdict: 'one blocking finding, folded',
|
|
54
|
+
...overrides,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
/** A git seam that knows a fixed commit set, ancestry map, and per-sha diff. */
|
|
58
|
+
function fakeGit(options) {
|
|
59
|
+
const ancestors = options.ancestors ?? {};
|
|
60
|
+
const reach = options.reach ?? {};
|
|
61
|
+
return {
|
|
62
|
+
isCommit: (candidate) => options.commits.includes(candidate),
|
|
63
|
+
isAncestor: (base) => Object.hasOwn(ancestors, base),
|
|
64
|
+
distance: (base) => ancestors[base] ?? 0,
|
|
65
|
+
changedFiles: (_base, head) => reach[head] ?? [],
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Armed by a test to publish a COMPETING deposit inside the window between the
|
|
70
|
+
* occupancy pre-check and the exclusive publish (mmnto-ai/totem#2745).
|
|
71
|
+
*
|
|
72
|
+
* The seam is the atomic writer rather than `fs.existsSync`: a `node:fs` export
|
|
73
|
+
* cannot be spied under ESM (the namespace is not configurable), and this hook
|
|
74
|
+
* fires at exactly the moment a second process would land — after this writer's
|
|
75
|
+
* temp exists, before it claims the final name.
|
|
76
|
+
*/
|
|
77
|
+
let publishInsideWindow;
|
|
78
|
+
vi.mock('../fs-atomic.js', async (importOriginal) => {
|
|
79
|
+
const actual = await importOriginal();
|
|
80
|
+
return {
|
|
81
|
+
...actual,
|
|
82
|
+
writeFileAtomicSync: (target, data, options) => {
|
|
83
|
+
actual.writeFileAtomicSync(target, data, options);
|
|
84
|
+
const hook = publishInsideWindow;
|
|
85
|
+
// Cleared BEFORE firing: the competing write goes through this same
|
|
86
|
+
// wrapper and must not re-enter.
|
|
87
|
+
publishInsideWindow = undefined;
|
|
88
|
+
hook?.();
|
|
89
|
+
},
|
|
90
|
+
};
|
|
91
|
+
});
|
|
92
|
+
let tmpDir = '';
|
|
93
|
+
beforeEach(() => {
|
|
94
|
+
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'totem-legs-'));
|
|
95
|
+
});
|
|
96
|
+
afterEach(() => {
|
|
97
|
+
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
98
|
+
});
|
|
99
|
+
/** Write raw bytes into the store, bypassing the writer (the hand-edit path). */
|
|
100
|
+
function writeRaw(name, content) {
|
|
101
|
+
fs.mkdirSync(legsDir(tmpDir), { recursive: true });
|
|
102
|
+
fs.writeFileSync(path.join(legsDir(tmpDir), name), content, 'utf-8');
|
|
103
|
+
}
|
|
104
|
+
describe('LegDepositSchema — the parse boundary', () => {
|
|
105
|
+
it('accepts a well-formed deposit and tolerates a forward-minor unknown key', () => {
|
|
106
|
+
const forward = { ...deposit(), schemaVersion: '1.7.0', futureField: 'ignored by this reader' };
|
|
107
|
+
const parsed = LegDepositSchema.safeParse(forward);
|
|
108
|
+
expect(parsed.success).toBe(true);
|
|
109
|
+
// Tolerated means STRIPPED, not preserved (the run-artifact precedent).
|
|
110
|
+
if (parsed.success)
|
|
111
|
+
expect('futureField' in parsed.data).toBe(false);
|
|
112
|
+
});
|
|
113
|
+
it('refuses a newer major BY NAME, not as generic corruption', () => {
|
|
114
|
+
const parsed = LegDepositSchema.safeParse({ ...deposit(), schemaVersion: '2.0.0' });
|
|
115
|
+
expect(parsed.success).toBe(false);
|
|
116
|
+
if (!parsed.success) {
|
|
117
|
+
expect(parsed.error.issues.map((i) => i.message).join(' ')).toContain(`this reader understands major ${LEG_DEPOSIT_KNOWN_MAJOR}.x`);
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
it('requires a full 40-hex lowercase diffSha', () => {
|
|
121
|
+
for (const bad of [HEAD.slice(0, 8), HEAD.toUpperCase(), `${HEAD}0`, 'not-a-sha']) {
|
|
122
|
+
expect(LegDepositSchema.safeParse({ ...deposit(), diffSha: bad }).success, bad).toBe(false);
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
it('requires finding ids to be unique', () => {
|
|
126
|
+
const parsed = LegDepositSchema.safeParse(deposit({
|
|
127
|
+
findings: [finding({ id: 'F1' }), finding({ id: 'F1', claim: 'a second claim' })],
|
|
128
|
+
folded: [],
|
|
129
|
+
}));
|
|
130
|
+
expect(parsed.success).toBe(false);
|
|
131
|
+
if (!parsed.success) {
|
|
132
|
+
expect(parsed.error.issues.some((i) => i.message.includes('duplicate finding id'))).toBe(true);
|
|
133
|
+
}
|
|
134
|
+
});
|
|
135
|
+
it('requires folded to be a SUBSET of the finding ids', () => {
|
|
136
|
+
const parsed = LegDepositSchema.safeParse(deposit({ findings: [finding({ id: 'F1' })], folded: ['F1', 'F9'] }));
|
|
137
|
+
expect(parsed.success).toBe(false);
|
|
138
|
+
if (!parsed.success) {
|
|
139
|
+
expect(parsed.error.issues.some((i) => i.message.includes('"F9"'))).toBe(true);
|
|
140
|
+
expect(parsed.error.issues.some((i) => i.path.join('.') === 'folded.1')).toBe(true);
|
|
141
|
+
}
|
|
142
|
+
});
|
|
143
|
+
it('accepts empty findings and empty folded — a leg that found nothing still deposits', () => {
|
|
144
|
+
expect(LegDepositSchema.safeParse(deposit({ findings: [], folded: [], verdict: 'no findings' }))
|
|
145
|
+
.success).toBe(true);
|
|
146
|
+
});
|
|
147
|
+
it('refuses a multi-line verdict', () => {
|
|
148
|
+
const parsed = LegDepositSchema.safeParse(deposit({ verdict: `line one${LF}line two` }));
|
|
149
|
+
expect(parsed.success).toBe(false);
|
|
150
|
+
if (!parsed.success)
|
|
151
|
+
expect(parsed.error.issues[0]?.path.join('.')).toBe('verdict');
|
|
152
|
+
});
|
|
153
|
+
it('refuses control bytes in verdict, claim, counterexample and file', () => {
|
|
154
|
+
const cases = [
|
|
155
|
+
['verdict', deposit({ verdict: `ok${NUL}` })],
|
|
156
|
+
['findings.0.claim', deposit({ findings: [finding({ claim: `c${NEL}` })], folded: [] })],
|
|
157
|
+
[
|
|
158
|
+
'findings.0.counterexample',
|
|
159
|
+
deposit({ findings: [finding({ counterexample: `${ESC}[2J` })], folded: [] }),
|
|
160
|
+
],
|
|
161
|
+
['findings.0.file', deposit({ findings: [finding({ file: `a.ts${LF}b.ts` })], folded: [] })],
|
|
162
|
+
];
|
|
163
|
+
for (const [expectedPath, candidate] of cases) {
|
|
164
|
+
const parsed = LegDepositSchema.safeParse(candidate);
|
|
165
|
+
expect(parsed.success, expectedPath).toBe(false);
|
|
166
|
+
if (!parsed.success) {
|
|
167
|
+
expect(parsed.error.issues.map((i) => i.path.join('.'))).toContain(expectedPath);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
});
|
|
171
|
+
it('allows an EMPTY counterexample but not an empty claim, file or verdict', () => {
|
|
172
|
+
expect(LegDepositSchema.safeParse(deposit({ findings: [finding({ counterexample: '' })], folded: [] })).success).toBe(true);
|
|
173
|
+
expect(LegDepositSchema.safeParse(deposit({ findings: [finding({ claim: '' })], folded: [] }))
|
|
174
|
+
.success).toBe(false);
|
|
175
|
+
expect(LegDepositSchema.safeParse(deposit({ findings: [finding({ file: '' })], folded: [] }))
|
|
176
|
+
.success).toBe(false);
|
|
177
|
+
expect(LegDepositSchema.safeParse(deposit({ verdict: '' })).success).toBe(false);
|
|
178
|
+
});
|
|
179
|
+
it('refuses a negative or fractional line, accepts 0', () => {
|
|
180
|
+
expect(LegDepositSchema.safeParse(deposit({ findings: [finding({ line: 0 })], folded: [] })).success).toBe(true);
|
|
181
|
+
expect(LegDepositSchema.safeParse(deposit({ findings: [finding({ line: -1 })], folded: [] }))
|
|
182
|
+
.success).toBe(false);
|
|
183
|
+
expect(LegDepositSchema.safeParse(deposit({ findings: [finding({ line: 1.5 })], folded: [] }))
|
|
184
|
+
.success).toBe(false);
|
|
185
|
+
});
|
|
186
|
+
it('pins the severity vocabulary as a set', () => {
|
|
187
|
+
expect([...LEG_FINDING_SEVERITIES]).toEqual(['BLOCKING', 'MATERIAL', 'MINOR']);
|
|
188
|
+
expect(LegDepositSchema.safeParse(deposit({ findings: [finding({ severity: 'CRITICAL' })], folded: [] })).success).toBe(false);
|
|
189
|
+
});
|
|
190
|
+
});
|
|
191
|
+
describe('saveLegDeposit — create-exclusive, atomic, validate-first', () => {
|
|
192
|
+
it('stores the deposit at <diffSha>.json with a diffSha equal to the name', () => {
|
|
193
|
+
const result = saveLegDeposit(tmpDir, deposit());
|
|
194
|
+
expect(result.path).toBe(legDepositPath(tmpDir, HEAD));
|
|
195
|
+
expect(path.basename(result.path)).toBe(`${HEAD}.json`);
|
|
196
|
+
expect(result.replaced).toBeUndefined();
|
|
197
|
+
const onDisk = JSON.parse(fs.readFileSync(result.path, 'utf-8'));
|
|
198
|
+
expect(onDisk.diffSha).toBe(path.basename(result.path, '.json'));
|
|
199
|
+
});
|
|
200
|
+
it('refuses an existing sha without replace, naming the incumbent readAt', () => {
|
|
201
|
+
saveLegDeposit(tmpDir, deposit({ readAt: '2026-09-01T00:00:00.000Z' }));
|
|
202
|
+
let caught;
|
|
203
|
+
try {
|
|
204
|
+
saveLegDeposit(tmpDir, deposit({ readAt: '2026-09-03T00:00:00.000Z' }));
|
|
205
|
+
}
|
|
206
|
+
catch (err) {
|
|
207
|
+
caught = err;
|
|
208
|
+
}
|
|
209
|
+
expect(caught).toBeInstanceOf(LegDepositExistsError);
|
|
210
|
+
const refusal = caught;
|
|
211
|
+
expect(refusal.code).toBe('LEG_DEPOSIT_EXISTS');
|
|
212
|
+
expect(refusal.existingReadAt).toBe('2026-09-01T00:00:00.000Z');
|
|
213
|
+
expect(refusal.message).toContain('2026-09-01T00:00:00.000Z');
|
|
214
|
+
// The refused write did NOT touch the incumbent.
|
|
215
|
+
const onDisk = JSON.parse(fs.readFileSync(legDepositPath(tmpDir, HEAD), 'utf-8'));
|
|
216
|
+
expect(onDisk.readAt).toBe('2026-09-01T00:00:00.000Z');
|
|
217
|
+
});
|
|
218
|
+
it('replaces with the OLD readAt reported', () => {
|
|
219
|
+
saveLegDeposit(tmpDir, deposit({ readAt: '2026-09-01T00:00:00.000Z' }));
|
|
220
|
+
const result = saveLegDeposit(tmpDir, deposit({ readAt: '2026-09-03T00:00:00.000Z' }), {
|
|
221
|
+
replace: true,
|
|
222
|
+
});
|
|
223
|
+
expect(result.replaced).toEqual({ readAt: '2026-09-01T00:00:00.000Z' });
|
|
224
|
+
const onDisk = JSON.parse(fs.readFileSync(result.path, 'utf-8'));
|
|
225
|
+
expect(onDisk.readAt).toBe('2026-09-03T00:00:00.000Z');
|
|
226
|
+
});
|
|
227
|
+
it('replaces a CORRUPT incumbent, disclosing that its instant was unreadable', () => {
|
|
228
|
+
writeRaw(`${HEAD}.json`, 'not json at all');
|
|
229
|
+
const result = saveLegDeposit(tmpDir, deposit(), { replace: true });
|
|
230
|
+
expect(result.replaced).toEqual({ readAt: undefined });
|
|
231
|
+
expect(loadLegDeposits(tmpDir).deposits).toHaveLength(1);
|
|
232
|
+
});
|
|
233
|
+
it('leaves NO file and NO temp behind on a validation failure', () => {
|
|
234
|
+
expect(() => saveLegDeposit(tmpDir, deposit({ verdict: `bad${LF}verdict` }))).toThrow();
|
|
235
|
+
expect(fs.existsSync(legsDir(tmpDir))).toBe(false);
|
|
236
|
+
// …and none beside an existing store either.
|
|
237
|
+
saveLegDeposit(tmpDir, deposit({ diffSha: OLDER }));
|
|
238
|
+
expect(() => saveLegDeposit(tmpDir, deposit({ diffSha: HEAD, folded: ['nope'] }))).toThrow();
|
|
239
|
+
expect(fs.readdirSync(legsDir(tmpDir))).toEqual([`${OLDER}.json`]);
|
|
240
|
+
});
|
|
241
|
+
it('leaves no temp file behind on a successful write either', () => {
|
|
242
|
+
saveLegDeposit(tmpDir, deposit());
|
|
243
|
+
expect(fs.readdirSync(legsDir(tmpDir)).filter((f) => f.endsWith('.tmp'))).toEqual([]);
|
|
244
|
+
});
|
|
245
|
+
});
|
|
246
|
+
describe('loadLegDeposits — tolerant, JSON-aware, never throws', () => {
|
|
247
|
+
it('returns empty results when the store directory does not exist', () => {
|
|
248
|
+
expect(loadLegDeposits(tmpDir)).toEqual({ deposits: [], corrupt: [] });
|
|
249
|
+
});
|
|
250
|
+
it('ignores non-.json entries entirely (not deposits, not corrupt rows)', () => {
|
|
251
|
+
saveLegDeposit(tmpDir, deposit());
|
|
252
|
+
writeRaw('notes.md', 'a leg wrote prose here');
|
|
253
|
+
writeRaw('.gitkeep', '');
|
|
254
|
+
const result = loadLegDeposits(tmpDir);
|
|
255
|
+
expect(result.deposits).toHaveLength(1);
|
|
256
|
+
expect(result.corrupt).toEqual([]);
|
|
257
|
+
});
|
|
258
|
+
it('is JSON-AWARE: a file that merely QUOTES diffSha/findings is corrupt, not a deposit', () => {
|
|
259
|
+
// A review artifact copied into the store: same words, different shape.
|
|
260
|
+
writeRaw(`${HEAD}.json`, JSON.stringify({
|
|
261
|
+
schemaVersion: '1.0.0',
|
|
262
|
+
note: `this record mentions diffSha ${HEAD} and findings but is a verdict artifact`,
|
|
263
|
+
findings: 3,
|
|
264
|
+
lanes: ['anthropic:claude-opus-5'],
|
|
265
|
+
}));
|
|
266
|
+
const result = loadLegDeposits(tmpDir);
|
|
267
|
+
expect(result.deposits).toEqual([]);
|
|
268
|
+
expect(result.corrupt).toHaveLength(1);
|
|
269
|
+
expect(result.corrupt[0]?.file).toBe(`${HEAD}.json`);
|
|
270
|
+
expect(result.corrupt[0]?.reason).toContain('schema-invalid');
|
|
271
|
+
});
|
|
272
|
+
it('discloses each corrupt file by name and reason WITHOUT hiding a valid sibling', () => {
|
|
273
|
+
saveLegDeposit(tmpDir, deposit({ diffSha: HEAD }));
|
|
274
|
+
writeRaw(`${OLDER}.json`, '{ this is not json');
|
|
275
|
+
writeRaw(`${OLDEST}.json`, JSON.stringify({ ...deposit({ diffSha: OLDEST }), verdict: '' }));
|
|
276
|
+
const result = loadLegDeposits(tmpDir);
|
|
277
|
+
expect(result.deposits.map((d) => d.diffSha)).toEqual([HEAD]);
|
|
278
|
+
expect(result.corrupt.map((c) => c.file).sort()).toEqual([`${OLDER}.json`, `${OLDEST}.json`].sort());
|
|
279
|
+
const unreadable = result.corrupt.find((c) => c.file === `${OLDER}.json`);
|
|
280
|
+
expect(unreadable?.reason).toContain('unreadable or not JSON');
|
|
281
|
+
const invalid = result.corrupt.find((c) => c.file === `${OLDEST}.json`);
|
|
282
|
+
expect(invalid?.reason).toContain('verdict');
|
|
283
|
+
});
|
|
284
|
+
it('every corrupt reason is ONE echo-safe line', () => {
|
|
285
|
+
writeRaw(`${HEAD}.json`, JSON.stringify(deposit({ verdict: `a${LF}b${NUL}c` })));
|
|
286
|
+
const [row] = loadLegDeposits(tmpDir).corrupt;
|
|
287
|
+
expect(row).toBeDefined();
|
|
288
|
+
const reason = row?.reason ?? '';
|
|
289
|
+
for (let i = 0; i < reason.length; i++) {
|
|
290
|
+
const code = reason.charCodeAt(i);
|
|
291
|
+
expect(code < 32 || (code >= 127 && code <= 159), `code ${code} at ${i}`).toBe(false);
|
|
292
|
+
}
|
|
293
|
+
});
|
|
294
|
+
it('refuses a newer-major deposit BY NAME rather than as corruption', () => {
|
|
295
|
+
writeRaw(`${HEAD}.json`, JSON.stringify({ ...deposit(), schemaVersion: '2.0.0' }));
|
|
296
|
+
const result = loadLegDeposits(tmpDir);
|
|
297
|
+
expect(result.deposits).toEqual([]);
|
|
298
|
+
expect(result.corrupt[0]?.reason).toContain('written by a newer totem');
|
|
299
|
+
expect(result.corrupt[0]?.reason).toContain('upgrade @mmnto/cli');
|
|
300
|
+
});
|
|
301
|
+
it('refuses a file whose NAME disagrees with its stored diffSha', () => {
|
|
302
|
+
writeRaw(`${OLDER}.json`, JSON.stringify(deposit({ diffSha: HEAD })));
|
|
303
|
+
const result = loadLegDeposits(tmpDir);
|
|
304
|
+
expect(result.deposits).toEqual([]);
|
|
305
|
+
expect(result.corrupt[0]?.reason).toContain('filename does not match its stored diffSha');
|
|
306
|
+
});
|
|
307
|
+
});
|
|
308
|
+
describe('findLegDepositForHead — ancestor-or-equal resolution', () => {
|
|
309
|
+
it('resolves nothing (but still discloses) when the store is empty', () => {
|
|
310
|
+
const resolution = findLegDepositForHead(tmpDir, HEAD, fakeGit({ commits: [] }));
|
|
311
|
+
expect(resolution.winner).toBeUndefined();
|
|
312
|
+
expect(resolution).toMatchObject({ superseded: [], stale: [], corrupt: [] });
|
|
313
|
+
});
|
|
314
|
+
it('EXACT outranks ancestor', () => {
|
|
315
|
+
saveLegDeposit(tmpDir, deposit({ diffSha: HEAD, readAt: '2026-09-01T00:00:00.000Z' }));
|
|
316
|
+
saveLegDeposit(tmpDir, deposit({ diffSha: OLDER, readAt: '2026-09-03T00:00:00.000Z' }));
|
|
317
|
+
const resolution = findLegDepositForHead(tmpDir, HEAD, fakeGit({ commits: [OLDER], ancestors: { [OLDER]: 1 } }));
|
|
318
|
+
expect(resolution.winner?.diffSha).toBe(HEAD);
|
|
319
|
+
expect(resolution.winner?.rank).toBe('exact');
|
|
320
|
+
expect(resolution.winner?.distance).toBe(0);
|
|
321
|
+
expect(resolution.superseded).toEqual([
|
|
322
|
+
{ diffSha: OLDER, readAt: '2026-09-03T00:00:00.000Z', rank: 'ancestor', distance: 1 },
|
|
323
|
+
]);
|
|
324
|
+
});
|
|
325
|
+
it('the NEAREST ancestor outranks a farther one', () => {
|
|
326
|
+
saveLegDeposit(tmpDir, deposit({ diffSha: OLDER, readAt: '2026-09-01T00:00:00.000Z' }));
|
|
327
|
+
saveLegDeposit(tmpDir, deposit({ diffSha: OLDEST, readAt: '2026-09-03T00:00:00.000Z' }));
|
|
328
|
+
const resolution = findLegDepositForHead(tmpDir, HEAD, fakeGit({ commits: [OLDER, OLDEST], ancestors: { [OLDER]: 2, [OLDEST]: 9 } }));
|
|
329
|
+
expect(resolution.winner?.diffSha).toBe(OLDER);
|
|
330
|
+
expect(resolution.winner?.rank).toBe('ancestor');
|
|
331
|
+
expect(resolution.winner?.distance).toBe(2);
|
|
332
|
+
expect(resolution.superseded.map((s) => s.diffSha)).toEqual([OLDEST]);
|
|
333
|
+
});
|
|
334
|
+
it('equal distance resolves to the LATEST readAt', () => {
|
|
335
|
+
saveLegDeposit(tmpDir, deposit({ diffSha: OLDER, readAt: '2026-09-01T00:00:00.000Z' }));
|
|
336
|
+
saveLegDeposit(tmpDir, deposit({ diffSha: OLDEST, readAt: '2026-09-02T00:00:00.000Z' }));
|
|
337
|
+
const resolution = findLegDepositForHead(tmpDir, HEAD, fakeGit({ commits: [OLDER, OLDEST], ancestors: { [OLDER]: 3, [OLDEST]: 3 } }));
|
|
338
|
+
expect(resolution.winner?.diffSha).toBe(OLDEST);
|
|
339
|
+
expect(resolution.superseded.map((s) => s.diffSha)).toEqual([OLDER]);
|
|
340
|
+
});
|
|
341
|
+
it('distinguishes unknown-commit from not-ancestor', () => {
|
|
342
|
+
saveLegDeposit(tmpDir, deposit({ diffSha: ELSEWHERE }));
|
|
343
|
+
saveLegDeposit(tmpDir, deposit({ diffSha: OLDER }));
|
|
344
|
+
const resolution = findLegDepositForHead(tmpDir, HEAD,
|
|
345
|
+
// OLDER is a commit here but on another branch; ELSEWHERE is unknown.
|
|
346
|
+
fakeGit({ commits: [OLDER], ancestors: {} }));
|
|
347
|
+
expect(resolution.winner).toBeUndefined();
|
|
348
|
+
expect(resolution.stale
|
|
349
|
+
.map((s) => ({ diffSha: s.diffSha, reason: s.reason }))
|
|
350
|
+
.sort((a, b) => a.diffSha.localeCompare(b.diffSha))).toEqual([
|
|
351
|
+
{ diffSha: OLDER, reason: 'not-ancestor' },
|
|
352
|
+
{ diffSha: ELSEWHERE, reason: 'unknown-commit' },
|
|
353
|
+
].sort((a, b) => a.diffSha.localeCompare(b.diffSha)));
|
|
354
|
+
});
|
|
355
|
+
it('an EXACT match never consults the git seam', () => {
|
|
356
|
+
saveLegDeposit(tmpDir, deposit({ diffSha: HEAD }));
|
|
357
|
+
const explodes = {
|
|
358
|
+
isCommit: () => {
|
|
359
|
+
throw new Error('isCommit must not be called for an exact match');
|
|
360
|
+
},
|
|
361
|
+
isAncestor: () => {
|
|
362
|
+
throw new Error('isAncestor must not be called for an exact match');
|
|
363
|
+
},
|
|
364
|
+
distance: () => {
|
|
365
|
+
throw new Error('distance must not be called for an exact match');
|
|
366
|
+
},
|
|
367
|
+
changedFiles: () => {
|
|
368
|
+
throw new Error('changedFiles must not be called for an exact match');
|
|
369
|
+
},
|
|
370
|
+
};
|
|
371
|
+
expect(findLegDepositForHead(tmpDir, HEAD, explodes).winner?.rank).toBe('exact');
|
|
372
|
+
// Including under a coverage query: an exact match covers everything BY
|
|
373
|
+
// CONSTRUCTION, so measuring it would be a git call to confirm a tautology
|
|
374
|
+
// (mmnto-ai/totem#2698 fold 3).
|
|
375
|
+
const covered = findLegDepositForHead(tmpDir, HEAD, explodes, {
|
|
376
|
+
base: 'main',
|
|
377
|
+
owedFiles: ['docs/wiki/a.md', 'docs/wiki/b.md'],
|
|
378
|
+
});
|
|
379
|
+
expect(covered.winner?.coverage).toEqual({ covered: 2, owed: 2, missing: [] });
|
|
380
|
+
});
|
|
381
|
+
it('a corrupt file rides the resolution and never masks the valid winner', () => {
|
|
382
|
+
saveLegDeposit(tmpDir, deposit({ diffSha: HEAD }));
|
|
383
|
+
writeRaw(`${OLDER}.json`, 'garbage');
|
|
384
|
+
const resolution = findLegDepositForHead(tmpDir, HEAD, fakeGit({ commits: [] }));
|
|
385
|
+
expect(resolution.winner?.diffSha).toBe(HEAD);
|
|
386
|
+
expect(resolution.corrupt.map((c) => c.file)).toEqual([`${OLDER}.json`]);
|
|
387
|
+
});
|
|
388
|
+
});
|
|
389
|
+
describe('countLegFindings + renderLegField — the covariate v1.2 field', () => {
|
|
390
|
+
it('counts every severity, and folded findings stay counted in their bucket', () => {
|
|
391
|
+
const counts = countLegFindings(deposit({
|
|
392
|
+
findings: [
|
|
393
|
+
finding({ id: 'F1', severity: 'BLOCKING' }),
|
|
394
|
+
finding({ id: 'F2', severity: 'BLOCKING' }),
|
|
395
|
+
finding({ id: 'F3', severity: 'MATERIAL' }),
|
|
396
|
+
finding({ id: 'F4', severity: 'MINOR' }),
|
|
397
|
+
],
|
|
398
|
+
folded: ['F1', 'F3'],
|
|
399
|
+
}));
|
|
400
|
+
expect(counts).toEqual({ blocking: 2, material: 1, minor: 1, folded: 2 });
|
|
401
|
+
});
|
|
402
|
+
it('renders EXACTLY `leg: <sha8> blocking=N material=N folded=N`', () => {
|
|
403
|
+
const rendered = renderLegField(deposit({
|
|
404
|
+
diffSha: HEAD,
|
|
405
|
+
findings: [
|
|
406
|
+
finding({ id: 'F1', severity: 'BLOCKING' }),
|
|
407
|
+
finding({ id: 'F2', severity: 'MATERIAL' }),
|
|
408
|
+
finding({ id: 'F3', severity: 'MINOR' }),
|
|
409
|
+
],
|
|
410
|
+
folded: ['F2'],
|
|
411
|
+
}));
|
|
412
|
+
expect(rendered).toBe(`leg: ${HEAD.slice(0, 8)} blocking=1 material=1 folded=1`);
|
|
413
|
+
});
|
|
414
|
+
it('renders EXACTLY `leg: none` for no deposit', () => {
|
|
415
|
+
expect(renderLegField(undefined)).toBe('leg: none');
|
|
416
|
+
});
|
|
417
|
+
it('renders zeroes for a leg that found nothing', () => {
|
|
418
|
+
expect(renderLegField(deposit({ findings: [], folded: [] }))).toBe(`leg: ${HEAD.slice(0, 8)} blocking=0 material=0 folded=0`);
|
|
419
|
+
});
|
|
420
|
+
});
|
|
421
|
+
// ─── Coverage as a freshness predicate (mmnto-ai/totem#2698 fold 3) ─────────
|
|
422
|
+
//
|
|
423
|
+
// Ancestry alone is not freshness: a deposit against the branch's MERGE BASE
|
|
424
|
+
// satisfies ancestor-or-equal and reports a small distance, while the leg that
|
|
425
|
+
// wrote it saw none of the diff the push proposes. That exhibit is what the
|
|
426
|
+
// operator ruled on, so it is the first case here.
|
|
427
|
+
describe('findLegDepositForHead — coverage (mmnto-ai/totem#2698 fold 3)', () => {
|
|
428
|
+
const OWED = ['docs/wiki/enforcement-model.md', 'docs/wiki/cli-reference.md', 'adr/adr-1.md'];
|
|
429
|
+
const COVERAGE = { base: 'main', owedFiles: OWED };
|
|
430
|
+
it('an ancestor whose diff contains NO owed path is stale, not a winner', () => {
|
|
431
|
+
// The merge-base shape: a real commit, a real ancestor, one commit behind —
|
|
432
|
+
// and its own branch diff touched something else entirely.
|
|
433
|
+
saveLegDeposit(tmpDir, deposit({ diffSha: OLDER }));
|
|
434
|
+
const resolution = findLegDepositForHead(tmpDir, HEAD, fakeGit({
|
|
435
|
+
commits: [OLDER],
|
|
436
|
+
ancestors: { [OLDER]: 1 },
|
|
437
|
+
reach: { [OLDER]: ['src/other.ts'] },
|
|
438
|
+
}), COVERAGE);
|
|
439
|
+
expect(resolution.winner).toBeUndefined();
|
|
440
|
+
expect(resolution.stale).toEqual([
|
|
441
|
+
{ diffSha: OLDER, readAt: expect.any(String), reason: 'no-coverage' },
|
|
442
|
+
]);
|
|
443
|
+
});
|
|
444
|
+
it('the same deposit WINS without a coverage query — the predicate is opt-in', () => {
|
|
445
|
+
// Which is what makes a caller that cannot resolve a branch base (a staged
|
|
446
|
+
// scope) behave exactly as it did before, and say so.
|
|
447
|
+
saveLegDeposit(tmpDir, deposit({ diffSha: OLDER }));
|
|
448
|
+
const resolution = findLegDepositForHead(tmpDir, HEAD, fakeGit({
|
|
449
|
+
commits: [OLDER],
|
|
450
|
+
ancestors: { [OLDER]: 1 },
|
|
451
|
+
reach: { [OLDER]: ['src/other.ts'] },
|
|
452
|
+
}));
|
|
453
|
+
expect(resolution.winner?.diffSha).toBe(OLDER);
|
|
454
|
+
expect(resolution.winner?.coverage).toBeUndefined();
|
|
455
|
+
expect(resolution.stale).toEqual([]);
|
|
456
|
+
});
|
|
457
|
+
it('PARTIAL coverage resolves, and names exactly what the leg could not have read', () => {
|
|
458
|
+
saveLegDeposit(tmpDir, deposit({ diffSha: OLDER }));
|
|
459
|
+
const resolution = findLegDepositForHead(tmpDir, HEAD, fakeGit({
|
|
460
|
+
commits: [OLDER],
|
|
461
|
+
ancestors: { [OLDER]: 4 },
|
|
462
|
+
// It read one of the three owed paths, plus files nobody owes.
|
|
463
|
+
reach: { [OLDER]: ['docs/wiki/cli-reference.md', 'src/unrelated.ts'] },
|
|
464
|
+
}), COVERAGE);
|
|
465
|
+
expect(resolution.winner?.diffSha).toBe(OLDER);
|
|
466
|
+
expect(resolution.winner?.distance).toBe(4);
|
|
467
|
+
// K < N is DISCLOSURE, never a block — the fold re-arm doctrine owes the
|
|
468
|
+
// new read, and this number is what makes that question legible.
|
|
469
|
+
expect(resolution.winner?.coverage).toEqual({
|
|
470
|
+
covered: 1,
|
|
471
|
+
owed: 3,
|
|
472
|
+
missing: ['docs/wiki/enforcement-model.md', 'adr/adr-1.md'],
|
|
473
|
+
});
|
|
474
|
+
});
|
|
475
|
+
it('a duplicated owed path never inflates the denominator', () => {
|
|
476
|
+
// `owedFiles` comes from the BASIS, where one file matching three globs
|
|
477
|
+
// appears three times. `covers 1/3` for a single file would be unreadable.
|
|
478
|
+
saveLegDeposit(tmpDir, deposit({ diffSha: OLDER }));
|
|
479
|
+
const resolution = findLegDepositForHead(tmpDir, HEAD, fakeGit({ commits: [OLDER], ancestors: { [OLDER]: 1 }, reach: { [OLDER]: ['README.md'] } }), { base: 'main', owedFiles: ['README.md', 'README.md', 'README.md'] });
|
|
480
|
+
expect(resolution.winner?.coverage).toEqual({ covered: 1, owed: 1, missing: [] });
|
|
481
|
+
});
|
|
482
|
+
it('nothing owed spends NO reach probe (the intersection cannot change)', () => {
|
|
483
|
+
// mmnto-ai/totem#2698 fold 5, Q3: with an empty owed set the answer is
|
|
484
|
+
// `0/0` whatever the candidate reached, so the probe is a git call whose
|
|
485
|
+
// result is discarded. An adapter that would throw on one proves it is
|
|
486
|
+
// never made.
|
|
487
|
+
saveLegDeposit(tmpDir, deposit({ diffSha: OLDER }));
|
|
488
|
+
const resolution = findLegDepositForHead(tmpDir, HEAD, {
|
|
489
|
+
isCommit: () => true,
|
|
490
|
+
isAncestor: () => true,
|
|
491
|
+
distance: () => 2,
|
|
492
|
+
changedFiles: () => {
|
|
493
|
+
throw new Error('changedFiles must not be called when nothing is owed');
|
|
494
|
+
},
|
|
495
|
+
}, { base: 'main', owedFiles: [] });
|
|
496
|
+
expect(resolution.winner?.coverage).toEqual({ covered: 0, owed: 0, missing: [] });
|
|
497
|
+
expect(resolution.stale).toEqual([]);
|
|
498
|
+
});
|
|
499
|
+
it('nothing owed is 0/0 and NOT stale (vacuous coverage)', () => {
|
|
500
|
+
// Unreachable from the gate — it never consults the store when nothing is
|
|
501
|
+
// owed — so this pins the FUNCTION's own honest behavior rather than a
|
|
502
|
+
// live path: an empty owed set cannot be "covered by none of" anything.
|
|
503
|
+
saveLegDeposit(tmpDir, deposit({ diffSha: OLDER }));
|
|
504
|
+
const resolution = findLegDepositForHead(tmpDir, HEAD, fakeGit({ commits: [OLDER], ancestors: { [OLDER]: 1 }, reach: { [OLDER]: [] } }), { base: 'main', owedFiles: [] });
|
|
505
|
+
expect(resolution.stale).toEqual([]);
|
|
506
|
+
expect(resolution.winner?.coverage).toEqual({ covered: 0, owed: 0, missing: [] });
|
|
507
|
+
});
|
|
508
|
+
it('ranking is unchanged: the nearest ancestor still wins, and both carry coverage', () => {
|
|
509
|
+
// Along one lineage a nearer ancestor's diff is a superset of a farther
|
|
510
|
+
// one's, so nearest-first ALREADY orders by coverage — the ruling's reason
|
|
511
|
+
// for leaving the comparator alone.
|
|
512
|
+
saveLegDeposit(tmpDir, deposit({ diffSha: OLDEST, readAt: '2026-09-01T00:00:00.000Z' }));
|
|
513
|
+
saveLegDeposit(tmpDir, deposit({ diffSha: OLDER, readAt: '2026-09-02T00:00:00.000Z' }));
|
|
514
|
+
const resolution = findLegDepositForHead(tmpDir, HEAD, fakeGit({
|
|
515
|
+
commits: [OLDEST, OLDER],
|
|
516
|
+
ancestors: { [OLDEST]: 9, [OLDER]: 2 },
|
|
517
|
+
reach: { [OLDEST]: [OWED[1]], [OLDER]: [OWED[0], OWED[1]] },
|
|
518
|
+
}), COVERAGE);
|
|
519
|
+
expect(resolution.winner?.diffSha).toBe(OLDER);
|
|
520
|
+
expect(resolution.winner?.coverage?.covered).toBe(2);
|
|
521
|
+
expect(resolution.superseded).toEqual([
|
|
522
|
+
{
|
|
523
|
+
diffSha: OLDEST,
|
|
524
|
+
readAt: '2026-09-01T00:00:00.000Z',
|
|
525
|
+
rank: 'ancestor',
|
|
526
|
+
distance: 9,
|
|
527
|
+
coverage: { covered: 1, owed: 3, missing: [OWED[0], OWED[2]] },
|
|
528
|
+
},
|
|
529
|
+
]);
|
|
530
|
+
});
|
|
531
|
+
it('a no-coverage candidate never hides a covering sibling', () => {
|
|
532
|
+
saveLegDeposit(tmpDir, deposit({ diffSha: OLDEST }));
|
|
533
|
+
saveLegDeposit(tmpDir, deposit({ diffSha: OLDER }));
|
|
534
|
+
const resolution = findLegDepositForHead(tmpDir, HEAD, fakeGit({
|
|
535
|
+
commits: [OLDEST, OLDER],
|
|
536
|
+
ancestors: { [OLDEST]: 9, [OLDER]: 2 },
|
|
537
|
+
reach: { [OLDEST]: ['src/other.ts'], [OLDER]: [OWED[0]] },
|
|
538
|
+
}), COVERAGE);
|
|
539
|
+
expect(resolution.winner?.diffSha).toBe(OLDER);
|
|
540
|
+
expect(resolution.stale).toEqual([
|
|
541
|
+
{ diffSha: OLDEST, readAt: expect.any(String), reason: 'no-coverage' },
|
|
542
|
+
]);
|
|
543
|
+
});
|
|
544
|
+
it('coverage is measured against the caller-supplied base, not a guess', () => {
|
|
545
|
+
// The base must be the SAME one the caller resolved for HEAD, or the
|
|
546
|
+
// measure is of a different diff than the one the push proposes.
|
|
547
|
+
saveLegDeposit(tmpDir, deposit({ diffSha: OLDEST }));
|
|
548
|
+
const seen = [];
|
|
549
|
+
const resolution = findLegDepositForHead(tmpDir, HEAD, {
|
|
550
|
+
isCommit: () => true,
|
|
551
|
+
isAncestor: () => true,
|
|
552
|
+
distance: () => 3,
|
|
553
|
+
changedFiles: (base, head) => {
|
|
554
|
+
seen.push(`${base}...${head}`);
|
|
555
|
+
return OWED;
|
|
556
|
+
},
|
|
557
|
+
}, { base: 'origin/release', owedFiles: OWED });
|
|
558
|
+
expect(seen).toEqual([`origin/release...${OLDEST}`]);
|
|
559
|
+
expect(resolution.winner?.coverage?.covered).toBe(3);
|
|
560
|
+
});
|
|
561
|
+
});
|
|
562
|
+
// ─── The publish is EXCLUSIVE, not merely checked ──────────────────────────
|
|
563
|
+
//
|
|
564
|
+
// Greptile P1 on PR mmnto-ai/totem#2745: `existsSync` then rename is
|
|
565
|
+
// check-then-act. Two no-replace writers for one sha both see "absent", both
|
|
566
|
+
// rename, and the later one wins SILENTLY — one leg's read overwritten by
|
|
567
|
+
// another's, in a store whose whole contract is that a second read at a head is
|
|
568
|
+
// a different observation. These drive the window directly.
|
|
569
|
+
describe('saveLegDeposit publishes exclusively (mmnto-ai/totem#2745, Greptile P1)', () => {
|
|
570
|
+
/** The incumbent's bytes, so a test can prove they were never touched. */
|
|
571
|
+
function incumbentBytes() {
|
|
572
|
+
return fs.readFileSync(legDepositPath(tmpDir, HEAD), 'utf-8');
|
|
573
|
+
}
|
|
574
|
+
afterEach(() => {
|
|
575
|
+
publishInsideWindow = undefined;
|
|
576
|
+
});
|
|
577
|
+
it('a deposit published INSIDE the check-to-publish window is refused, not overwritten', () => {
|
|
578
|
+
const winner = deposit({ diffSha: HEAD, readAt: '2026-09-01T00:00:00.000Z' });
|
|
579
|
+
const loser = deposit({ diffSha: HEAD, readAt: '2026-09-02T00:00:00.000Z' });
|
|
580
|
+
// The store is EMPTY, so the loser's pre-check honestly reports "absent".
|
|
581
|
+
// The competitor then publishes in the window the pre-check opened —
|
|
582
|
+
// after the loser's temp is written, before it claims the final name.
|
|
583
|
+
// That is the interleaving a second process produces, driven here.
|
|
584
|
+
publishInsideWindow = () => {
|
|
585
|
+
saveLegDeposit(tmpDir, winner);
|
|
586
|
+
};
|
|
587
|
+
expect(() => saveLegDeposit(tmpDir, loser)).toThrow(LegDepositExistsError);
|
|
588
|
+
// The incumbent is intact byte for byte: the loser overwrote nothing.
|
|
589
|
+
expect(incumbentBytes()).toBe(JSON.stringify(winner, null, 2));
|
|
590
|
+
expect(JSON.parse(incumbentBytes()).readAt).toBe('2026-09-01T00:00:00.000Z');
|
|
591
|
+
// And the loser left no temp behind — the store holds exactly one file.
|
|
592
|
+
expect(fs.readdirSync(legsDir(tmpDir))).toEqual([`${HEAD}.json`]);
|
|
593
|
+
});
|
|
594
|
+
it('the refusal from that window carries the WINNER instant, not the loser one', () => {
|
|
595
|
+
publishInsideWindow = () => {
|
|
596
|
+
saveLegDeposit(tmpDir, deposit({ diffSha: HEAD, readAt: '2026-09-01T00:00:00.000Z' }));
|
|
597
|
+
};
|
|
598
|
+
let caught;
|
|
599
|
+
try {
|
|
600
|
+
saveLegDeposit(tmpDir, deposit({ diffSha: HEAD, readAt: '2026-09-02T00:00:00.000Z' }));
|
|
601
|
+
}
|
|
602
|
+
catch (err) {
|
|
603
|
+
caught = err;
|
|
604
|
+
}
|
|
605
|
+
expect(caught).toBeInstanceOf(LegDepositExistsError);
|
|
606
|
+
// Read AFTER the loss, so it names the deposit that actually won — the one
|
|
607
|
+
// a seat has to decide whether to `--replace`.
|
|
608
|
+
expect(caught.existingReadAt).toBe('2026-09-01T00:00:00.000Z');
|
|
609
|
+
});
|
|
610
|
+
it('`replace` still overwrites, and leaves one file behind', () => {
|
|
611
|
+
// The exclusive publish must not turn `--replace` into a refusal: it is the
|
|
612
|
+
// one caller allowed to overwrite.
|
|
613
|
+
saveLegDeposit(tmpDir, deposit({ diffSha: HEAD, readAt: '2026-09-01T00:00:00.000Z' }));
|
|
614
|
+
const result = saveLegDeposit(tmpDir, deposit({ diffSha: HEAD, readAt: '2026-09-02T00:00:00.000Z' }), { replace: true });
|
|
615
|
+
expect(result.replaced).toEqual({ readAt: '2026-09-01T00:00:00.000Z' });
|
|
616
|
+
expect(JSON.parse(incumbentBytes()).readAt).toBe('2026-09-02T00:00:00.000Z');
|
|
617
|
+
expect(fs.readdirSync(legsDir(tmpDir))).toEqual([`${HEAD}.json`]);
|
|
618
|
+
});
|
|
619
|
+
it('`replace` with NOTHING to replace reports no replacement', () => {
|
|
620
|
+
const result = saveLegDeposit(tmpDir, deposit({ diffSha: HEAD }), { replace: true });
|
|
621
|
+
expect(result.replaced).toBeUndefined();
|
|
622
|
+
});
|
|
623
|
+
it('an ordinary create leaves no temp beside the deposit', () => {
|
|
624
|
+
saveLegDeposit(tmpDir, deposit({ diffSha: HEAD }));
|
|
625
|
+
expect(fs.readdirSync(legsDir(tmpDir))).toEqual([`${HEAD}.json`]);
|
|
626
|
+
});
|
|
627
|
+
});
|
|
628
|
+
//# sourceMappingURL=legs.test.js.map
|