ultra-builder-pro-cli 0.26.2 → 3.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. package/.ultra-template/contexts/TEMPLATE.md +64 -29
  2. package/.ultra-template/north-star.md +35 -18
  3. package/.ultra-template/tasks.json +1 -0
  4. package/.ultra-template/test-report.json +17 -1
  5. package/CHANGELOG.md +163 -1
  6. package/README.md +108 -38
  7. package/adapters/_shared/host-profile.cjs +68 -12
  8. package/adapters/_shared/plugin-core.cjs +4 -5
  9. package/adapters/_shared/runtime-assets.cjs +11 -2
  10. package/adapters/zcode.js +375 -0
  11. package/bin/delegate-worker.cjs +20 -0
  12. package/bin/delegate.cjs +94 -25
  13. package/bin/install.js +11 -6
  14. package/docs/ARCHITECTURE.md +84 -23
  15. package/docs/ARTIFACT-AUTHORITY.md +167 -22
  16. package/docs/DECISIONS.md +131 -17
  17. package/docs/PHILOSOPHY.md +58 -7
  18. package/docs/PLUGIN-ISOLATION-CONTRACT.md +6 -0
  19. package/docs/RUNTIME-COMPAT-MATRIX.md +106 -13
  20. package/docs/SKILL-AUTHORING.md +4 -3
  21. package/docs/WORKFLOW-LIFECYCLE.md +126 -28
  22. package/docs/evals/adversarial-review-2026-08-14.md +84 -0
  23. package/docs/evals/zcode-automation-2026-08-14.md +103 -0
  24. package/hooks/README.md +27 -4
  25. package/hooks/_common.py +1678 -61
  26. package/hooks/adapters/zcode.py +227 -0
  27. package/hooks/compact_context.py +176 -17
  28. package/hooks/mid_workflow_recall.py +27 -5
  29. package/hooks/post_edit_guard.py +133 -23
  30. package/hooks/session_context.py +707 -22
  31. package/package.json +6 -3
  32. package/skills/ultra-change/SKILL.md +40 -9
  33. package/skills/ultra-change/references/change-contract.md +93 -27
  34. package/skills/ultra-change/references/execution-grant.md +109 -0
  35. package/skills/ultra-change/references/primary-transfer.md +273 -0
  36. package/skills/ultra-change/scripts/validate_primary_transfer.cjs +1928 -0
  37. package/skills/ultra-delegate/SKILL.md +24 -2
  38. package/skills/ultra-delegate/references/delegation-contract.md +21 -1
  39. package/skills/ultra-deliver/SKILL.md +85 -11
  40. package/skills/ultra-dev/SKILL.md +171 -41
  41. package/skills/ultra-grilling/SKILL.md +10 -7
  42. package/skills/ultra-grilling/references/reframing.md +7 -3
  43. package/skills/ultra-init/SKILL.md +44 -7
  44. package/skills/ultra-init/scripts/init_project.cjs +968 -21
  45. package/skills/ultra-plan/SKILL.md +70 -44
  46. package/skills/ultra-plan/references/task-evidence-v2.md +300 -0
  47. package/skills/ultra-plan/scripts/validate_task_evidence.cjs +859 -0
  48. package/skills/ultra-research/SKILL.md +38 -10
  49. package/skills/ultra-research/references/04-product-strategy.md +11 -2
  50. package/skills/ultra-research/references/21-features-scope.md +8 -0
  51. package/skills/ultra-research/references/22-success-metrics.md +4 -3
  52. package/skills/ultra-research/references/99-synthesis.md +19 -2
  53. package/skills/ultra-research/references/north-star-v2.md +168 -0
  54. package/skills/ultra-research/references/wayfinding.md +8 -0
  55. package/skills/ultra-research/scripts/validate_north_star.cjs +1174 -0
  56. package/skills/ultra-review/SKILL.md +201 -27
  57. package/skills/ultra-review/references/adversarial-evaluation.md +31 -0
  58. package/skills/ultra-review/references/spec.md +5 -0
  59. package/skills/ultra-review/references/unified-schema.md +146 -11
  60. package/skills/ultra-review/references/worker-packet.md +151 -8
  61. package/skills/ultra-review/scripts/review_wait.py +2653 -63
  62. package/skills/ultra-status/SKILL.md +95 -11
  63. package/skills/ultra-tdd/SKILL.md +11 -4
  64. package/skills/ultra-tdd/references/test-execution.md +6 -1
  65. package/skills/ultra-test/SKILL.md +145 -17
  66. package/skills/ultra-test/scripts/validate_review_transport.cjs +549 -0
  67. package/skills/ultra-test/scripts/worktree_digest.cjs +518 -30
@@ -6,13 +6,57 @@ const fs = require('node:fs');
6
6
  const path = require('node:path');
7
7
  const { execFileSync } = require('node:child_process');
8
8
 
9
+ const MAX_SNAPSHOT_BYTES = 8 * 1024 * 1024;
10
+ const SNAPSHOT_CHUNK_BYTES = 64 * 1024;
11
+ const MAX_OBSERVATION_FILES = 256;
12
+ const MAX_OBSERVATION_BYTES = 16 * 1024 * 1024;
13
+ const GIT_TIMEOUT_MS = 5000;
14
+ const GIT_MAX_OUTPUT_BYTES = 64 * 1024 * 1024;
15
+ const PRODUCT_PATHSPEC = [
16
+ '--', '.',
17
+ ':(exclude).ultra/test-report.json',
18
+ ':(exclude).ultra/evidence/**',
19
+ ':(exclude).ultra/reviews/**',
20
+ ':(exclude).ultra/.runtime/**',
21
+ ':(exclude).ultra/progress/**',
22
+ ':(exclude).ultra/changes/active/**',
23
+ ':(exclude).ultra/changes/archive/**',
24
+ ':(exclude).ultra/changes/abandoned/**',
25
+ ];
26
+
9
27
  function git(cwd, args, encoding = 'utf8') {
10
- return execFileSync('git', args, {
11
- cwd,
12
- encoding,
13
- maxBuffer: 64 * 1024 * 1024,
14
- stdio: ['ignore', 'pipe', 'pipe'],
15
- });
28
+ try {
29
+ return execFileSync('git', args, {
30
+ cwd,
31
+ encoding,
32
+ maxBuffer: GIT_MAX_OUTPUT_BYTES,
33
+ stdio: ['ignore', 'pipe', 'pipe'],
34
+ timeout: GIT_TIMEOUT_MS,
35
+ });
36
+ } catch (error) {
37
+ const operation = args.slice(0, 2).join(' ');
38
+ if (error && (error.code === 'ETIMEDOUT' || (error.killed && error.signal))) {
39
+ throw new Error(
40
+ `ULTRA_SNAPSHOT_GIT_TIMEOUT: Git ${JSON.stringify(operation)} exceeded the `
41
+ + `${GIT_TIMEOUT_MS}ms physical observation limit. Restore a responsive repository `
42
+ + 'and Git process, then retry worktree_digest.',
43
+ );
44
+ }
45
+ if (error && error.code === 'ENOBUFS') {
46
+ throw new Error(
47
+ `ULTRA_SNAPSHOT_GIT_OUTPUT_LIMIT: Git ${JSON.stringify(operation)} exceeded the `
48
+ + `${GIT_MAX_OUTPUT_BYTES}-byte output limit. Reduce or split the physical product `
49
+ + 'observation, then retry worktree_digest.',
50
+ );
51
+ }
52
+ const stderr = error && error.stderr
53
+ ? Buffer.from(error.stderr).toString('utf8').trim().split('\n')[0]
54
+ : 'Git returned an unknown failure';
55
+ throw new Error(
56
+ `ULTRA_SNAPSHOT_GIT_FAILED: Git ${JSON.stringify(operation)} failed (${stderr}). `
57
+ + 'Repair the repository and Git operation, then retry worktree_digest.',
58
+ );
59
+ }
16
60
  }
17
61
 
18
62
  function optionsFromArgs(argv) {
@@ -31,58 +75,502 @@ function optionsFromArgs(argv) {
31
75
  return options;
32
76
  }
33
77
 
78
+ function snapshotError(code, relative, observation) {
79
+ return new Error(
80
+ `${code}: cannot snapshot ${JSON.stringify(relative)}: ${observation}. `
81
+ + 'Restore the repository path as an ordinary regular non-symlink file within the '
82
+ + '8 MiB limit, then retry worktree_digest.',
83
+ );
84
+ }
85
+
86
+ function repositoryPath(root, relative) {
87
+ if (typeof relative !== 'string'
88
+ || relative.length === 0
89
+ || relative.includes('\0')
90
+ || path.posix.isAbsolute(relative)
91
+ || path.win32.isAbsolute(relative)) {
92
+ throw snapshotError('ULTRA_SNAPSHOT_PATH_ESCAPE', String(relative), 'path is not repository-relative');
93
+ }
94
+ const parts = relative.split('/');
95
+ if (parts.some((part) => part === '' || part === '.' || part === '..')
96
+ || (path.sep === '\\' && parts.some((part) => part.includes('\\')))) {
97
+ throw snapshotError('ULTRA_SNAPSHOT_PATH_ESCAPE', relative, 'path escapes its repository root');
98
+ }
99
+ const absolute = path.join(root, ...parts);
100
+ const resolvedRelative = path.relative(root, absolute);
101
+ if (resolvedRelative === ''
102
+ || resolvedRelative === '..'
103
+ || resolvedRelative.startsWith(`..${path.sep}`)
104
+ || path.isAbsolute(resolvedRelative)) {
105
+ throw snapshotError('ULTRA_SNAPSHOT_PATH_ESCAPE', relative, 'path escapes its repository root');
106
+ }
107
+ return { absolute, parts };
108
+ }
109
+
110
+ function lstatSnapshot(absolute, relative) {
111
+ try {
112
+ return fs.lstatSync(absolute, { bigint: true });
113
+ } catch (error) {
114
+ if (error && (error.code === 'ENOENT' || error.code === 'ENOTDIR')) {
115
+ throw snapshotError('ULTRA_SNAPSHOT_MISSING', relative, 'path disappeared during observation');
116
+ }
117
+ throw snapshotError(
118
+ 'ULTRA_SNAPSHOT_UNREADABLE',
119
+ relative,
120
+ `lstat failed with ${error && error.code ? error.code : 'an unknown error'}`,
121
+ );
122
+ }
123
+ }
124
+
125
+ function sameNode(left, right) {
126
+ return left.dev === right.dev
127
+ && left.ino === right.ino
128
+ && left.mode === right.mode;
129
+ }
130
+
131
+ function sameFileVersion(left, right) {
132
+ return sameNode(left, right)
133
+ && left.size === right.size
134
+ && left.mtimeNs === right.mtimeNs
135
+ && left.ctimeNs === right.ctimeNs;
136
+ }
137
+
138
+ function walkSnapshotPath(root, relative) {
139
+ const resolved = repositoryPath(root, relative);
140
+ const parents = [];
141
+ let current = root;
142
+ const rootStat = lstatSnapshot(root, '.');
143
+ if (rootStat.isSymbolicLink()) {
144
+ throw snapshotError('ULTRA_SNAPSHOT_SYMLINK', relative, 'repository root is a symlink');
145
+ }
146
+ if (!rootStat.isDirectory()) {
147
+ throw snapshotError('ULTRA_SNAPSHOT_NOT_DIRECTORY', relative, 'repository root is not a directory');
148
+ }
149
+ parents.push({ absolute: root, relative: '.', stat: rootStat });
150
+
151
+ for (const part of resolved.parts.slice(0, -1)) {
152
+ current = path.join(current, part);
153
+ const observedRelative = path.relative(root, current) || '.';
154
+ const stat = lstatSnapshot(current, observedRelative);
155
+ if (stat.isSymbolicLink()) {
156
+ throw snapshotError(
157
+ 'ULTRA_SNAPSHOT_SYMLINK',
158
+ relative,
159
+ `parent ${JSON.stringify(observedRelative)} is a symlink`,
160
+ );
161
+ }
162
+ if (!stat.isDirectory()) {
163
+ throw snapshotError(
164
+ 'ULTRA_SNAPSHOT_NOT_DIRECTORY',
165
+ relative,
166
+ `parent ${JSON.stringify(observedRelative)} is not a directory`,
167
+ );
168
+ }
169
+ parents.push({ absolute: current, relative: observedRelative, stat });
170
+ }
171
+
172
+ const finalStat = lstatSnapshot(resolved.absolute, relative);
173
+ if (finalStat.isSymbolicLink()) {
174
+ throw snapshotError('ULTRA_SNAPSHOT_SYMLINK', relative, 'final entry is a symlink');
175
+ }
176
+ if (!finalStat.isFile()) {
177
+ throw snapshotError('ULTRA_SNAPSHOT_NOT_REGULAR', relative, 'final entry is not a regular file');
178
+ }
179
+ if (finalStat.size > BigInt(MAX_SNAPSHOT_BYTES)) {
180
+ throw snapshotError(
181
+ 'ULTRA_SNAPSHOT_TOO_LARGE',
182
+ relative,
183
+ `regular file exceeds the ${MAX_SNAPSHOT_BYTES}-byte physical ceiling`,
184
+ );
185
+ }
186
+ return {
187
+ absolute: resolved.absolute,
188
+ final: { absolute: resolved.absolute, relative, stat: finalStat },
189
+ parents,
190
+ };
191
+ }
192
+
193
+ function verifyFreshRewalk(root, relative, initial, phase) {
194
+ let current;
195
+ try {
196
+ current = walkSnapshotPath(root, relative);
197
+ } catch (error) {
198
+ throw snapshotError(
199
+ 'ULTRA_SNAPSHOT_REPLACED',
200
+ relative,
201
+ `${phase} rewalk no longer resolves the initial identities (${error.message})`,
202
+ );
203
+ }
204
+ const parentsStable = current.parents.length === initial.parents.length
205
+ && current.parents.every((entry, index) => sameNode(entry.stat, initial.parents[index].stat));
206
+ if (!parentsStable || !sameFileVersion(current.final.stat, initial.final.stat)) {
207
+ throw snapshotError(
208
+ 'ULTRA_SNAPSHOT_REPLACED',
209
+ relative,
210
+ `${phase} rewalk observed a replaced root, parent, or final identity`,
211
+ );
212
+ }
213
+ }
214
+
215
+ function openSnapshotFile(absolute, relative) {
216
+ const { O_NOFOLLOW, O_NONBLOCK, O_RDONLY } = fs.constants;
217
+ if (![O_NOFOLLOW, O_NONBLOCK, O_RDONLY].every(Number.isInteger)) {
218
+ throw snapshotError(
219
+ 'ULTRA_SNAPSHOT_UNSUPPORTED',
220
+ relative,
221
+ 'this host cannot enforce O_NONBLOCK and O_NOFOLLOW',
222
+ );
223
+ }
224
+ try {
225
+ return fs.openSync(absolute, O_RDONLY | O_NONBLOCK | O_NOFOLLOW);
226
+ } catch (error) {
227
+ const observed = error && error.code ? error.code : 'an unknown error';
228
+ if (['ELOOP', 'ENOENT', 'ENOTDIR', 'ENXIO'].includes(observed)) {
229
+ throw snapshotError(
230
+ 'ULTRA_SNAPSHOT_REPLACED',
231
+ relative,
232
+ `no-follow nonblocking open observed ${observed}`,
233
+ );
234
+ }
235
+ throw snapshotError(
236
+ 'ULTRA_SNAPSHOT_UNREADABLE',
237
+ relative,
238
+ `no-follow nonblocking open failed with ${observed}`,
239
+ );
240
+ }
241
+ }
242
+
243
+ function streamStableRepositoryFile(root, relative, consume, budget = null) {
244
+ const initial = walkSnapshotPath(root, relative);
245
+ if (budget) budget.reserve(relative, initial.final.stat.size);
246
+ let descriptor;
247
+ try {
248
+ descriptor = openSnapshotFile(initial.absolute, relative);
249
+ const opened = fs.fstatSync(descriptor, { bigint: true });
250
+ if (!opened.isFile() || !sameFileVersion(opened, initial.final.stat)) {
251
+ throw snapshotError(
252
+ 'ULTRA_SNAPSHOT_REPLACED',
253
+ relative,
254
+ 'opened descriptor does not match the initial regular-file identity',
255
+ );
256
+ }
257
+ verifyFreshRewalk(root, relative, initial, 'pre-read');
258
+
259
+ const buffer = Buffer.allocUnsafe(SNAPSHOT_CHUNK_BYTES);
260
+ let offset = 0;
261
+ for (;;) {
262
+ const remaining = MAX_SNAPSHOT_BYTES - offset;
263
+ const requested = Math.min(buffer.length, remaining + 1);
264
+ let bytesRead;
265
+ try {
266
+ bytesRead = fs.readSync(descriptor, buffer, 0, requested, offset);
267
+ } catch (error) {
268
+ throw snapshotError(
269
+ 'ULTRA_SNAPSHOT_UNREADABLE',
270
+ relative,
271
+ `stream read failed with ${error && error.code ? error.code : 'an unknown error'}`,
272
+ );
273
+ }
274
+ if (bytesRead === 0) break;
275
+ offset += bytesRead;
276
+ if (offset > MAX_SNAPSHOT_BYTES) {
277
+ throw snapshotError(
278
+ 'ULTRA_SNAPSHOT_TOO_LARGE',
279
+ relative,
280
+ `stream exceeded the ${MAX_SNAPSHOT_BYTES}-byte physical ceiling`,
281
+ );
282
+ }
283
+ consume(buffer.subarray(0, bytesRead));
284
+ }
285
+
286
+ const finished = fs.fstatSync(descriptor, { bigint: true });
287
+ if (!finished.isFile()
288
+ || !sameFileVersion(finished, initial.final.stat)
289
+ || BigInt(offset) !== finished.size) {
290
+ throw snapshotError(
291
+ 'ULTRA_SNAPSHOT_REPLACED',
292
+ relative,
293
+ 'file identity or byte extent changed during the streamed read',
294
+ );
295
+ }
296
+ verifyFreshRewalk(root, relative, initial, 'post-read');
297
+ return initial;
298
+ } finally {
299
+ if (descriptor !== undefined) fs.closeSync(descriptor);
300
+ }
301
+ }
302
+
303
+ function resourceError(relative, nextFiles, nextBytes) {
304
+ return new Error(
305
+ `ULTRA_SNAPSHOT_RESOURCE_LIMIT: cannot snapshot ${JSON.stringify(relative)}: aggregate `
306
+ + `product observation would reach ${nextFiles} files and ${nextBytes} bytes, above the `
307
+ + `${MAX_OBSERVATION_FILES}-file or ${MAX_OBSERVATION_BYTES}-byte physical ceiling. `
308
+ + 'Reduce or split the included physical product observation, then retry worktree_digest; '
309
+ + 'this ceiling is not a semantic quality verdict.',
310
+ );
311
+ }
312
+
313
+ function observationBudget() {
314
+ let files = 0;
315
+ let bytes = 0;
316
+ return {
317
+ reserve(relative, size) {
318
+ const nextFiles = files + 1;
319
+ const nextBytes = bytes + Number(size);
320
+ if (nextFiles > MAX_OBSERVATION_FILES || nextBytes > MAX_OBSERVATION_BYTES) {
321
+ throw resourceError(relative, nextFiles, nextBytes);
322
+ }
323
+ files = nextFiles;
324
+ bytes = nextBytes;
325
+ },
326
+ };
327
+ }
328
+
329
+ function candidateExists(file) {
330
+ try {
331
+ fs.lstatSync(file);
332
+ return true;
333
+ } catch (error) {
334
+ if (error && (error.code === 'ENOENT' || error.code === 'ENOTDIR')) return false;
335
+ throw error;
336
+ }
337
+ }
338
+
34
339
  function intentSnapshot(root, changeId) {
35
340
  if (!changeId) return { change_id: null, intent_digest: null };
36
341
  const matches = ['active', 'archive', 'abandoned']
37
342
  .map((state) => path.join(root, '.ultra', 'changes', state, changeId, 'intent.md'))
38
- .filter((file) => fs.existsSync(file));
343
+ .filter(candidateExists);
39
344
  if (matches.length !== 1) {
40
345
  throw new Error(`expected exactly one intent.md for Change ${changeId}; found ${matches.length}`);
41
346
  }
42
- const intent = fs.readFileSync(matches[0]);
347
+ const intentHash = crypto.createHash('sha256');
348
+ const relative = path.relative(root, matches[0]).split(path.sep).join('/');
349
+ const snapshot = streamStableRepositoryFile(root, relative, (chunk) => intentHash.update(chunk));
43
350
  return {
44
351
  change_id: changeId,
45
- intent_digest: crypto.createHash('sha256').update(intent).digest('hex'),
352
+ intent_digest: intentHash.digest('hex'),
353
+ relative,
354
+ identity: snapshotIdentity(snapshot),
46
355
  };
47
356
  }
48
357
 
358
+ function snapshotIdentity(snapshot) {
359
+ const statIdentity = (stat) => [
360
+ stat.dev, stat.ino, stat.mode, stat.size, stat.mtimeNs, stat.ctimeNs,
361
+ ].map((value) => value.toString()).join(':');
362
+ return [
363
+ ...snapshot.parents.map((entry) => statIdentity(entry.stat)),
364
+ statIdentity(snapshot.final.stat),
365
+ ].join('|');
366
+ }
367
+
49
368
  function excluded(file) {
50
369
  return file === '.ultra/test-report.json'
370
+ || /^\.ultra\/evidence(?:\/|$)/.test(file)
371
+ || /^\.ultra\/(?:reviews|\.runtime|progress)(?:\/|$)/.test(file)
51
372
  || /^\.ultra\/changes\/(?:active|archive|abandoned)(?:\/|$)/.test(file);
52
373
  }
53
374
 
375
+ function changedDuringObservation(observation) {
376
+ return new Error(
377
+ `ULTRA_SNAPSHOT_CHANGED_DURING_OBSERVATION: ${observation}. `
378
+ + 'Finish or restore the concurrent repository change, then retry worktree_digest once; '
379
+ + 'no digest was accepted.',
380
+ );
381
+ }
382
+
383
+ function splitNul(buffer, label) {
384
+ if (buffer.length === 0) return [];
385
+ if (buffer[buffer.length - 1] !== 0) {
386
+ throw new Error(
387
+ `ULTRA_SNAPSHOT_GIT_FAILED: ${label} was not NUL terminated. `
388
+ + 'Repair the Git observation, then retry worktree_digest.',
389
+ );
390
+ }
391
+ const values = [];
392
+ let start = 0;
393
+ for (let index = 0; index < buffer.length; index += 1) {
394
+ if (buffer[index] !== 0) continue;
395
+ values.push(buffer.subarray(start, index));
396
+ start = index + 1;
397
+ }
398
+ return values;
399
+ }
400
+
401
+ function decodeGitPath(bytes, label) {
402
+ const relative = bytes.toString('utf8');
403
+ if (!Buffer.from(relative, 'utf8').equals(bytes)) {
404
+ throw snapshotError(
405
+ 'ULTRA_SNAPSHOT_PATH_ENCODING',
406
+ label,
407
+ 'Git returned a path that is not valid UTF-8',
408
+ );
409
+ }
410
+ return relative;
411
+ }
412
+
413
+ function trackedManifest(root, head) {
414
+ const raw = git(root, [
415
+ 'diff', '--raw', '-z', '--no-renames', '--abbrev=64', head, ...PRODUCT_PATHSPEC,
416
+ ], 'buffer');
417
+ const fields = splitNul(raw, 'tracked manifest');
418
+ if (fields.length % 2 !== 0) {
419
+ throw new Error(
420
+ 'ULTRA_SNAPSHOT_GIT_FAILED: tracked manifest has an incomplete raw record. '
421
+ + 'Repair the Git observation, then retry worktree_digest.',
422
+ );
423
+ }
424
+ const files = [];
425
+ for (let index = 0; index < fields.length; index += 2) {
426
+ const header = fields[index].toString('ascii');
427
+ const match = header.match(
428
+ /^:([0-7]{6}) ([0-7]{6}) ([0-9a-f]+) ([0-9a-f]+) ([A-Z])(?:[0-9]+)?$/u,
429
+ );
430
+ if (!match) {
431
+ throw new Error(
432
+ `ULTRA_SNAPSHOT_GIT_FAILED: tracked manifest record ${JSON.stringify(header)} is invalid. `
433
+ + 'Repair the Git observation, then retry worktree_digest.',
434
+ );
435
+ }
436
+ const relative = decodeGitPath(fields[index + 1], 'tracked manifest path');
437
+ files.push({
438
+ path: relative,
439
+ status: match[5],
440
+ present: match[5] !== 'D',
441
+ old_mode: match[1],
442
+ new_mode: match[2],
443
+ });
444
+ }
445
+ return { raw, files };
446
+ }
447
+
448
+ function untrackedManifest(root) {
449
+ const raw = git(root, ['ls-files', '--others', '--exclude-standard', '-z'], 'buffer');
450
+ const entries = splitNul(raw, 'untracked manifest')
451
+ .map((bytes) => ({ bytes, path: decodeGitPath(bytes, 'untracked manifest path') }))
452
+ .filter((entry) => !excluded(entry.path))
453
+ .sort((left, right) => Buffer.compare(left.bytes, right.bytes));
454
+ return {
455
+ files: entries.map((entry) => entry.path),
456
+ raw: Buffer.concat(entries.flatMap((entry) => [entry.bytes, Buffer.from([0])])),
457
+ };
458
+ }
459
+
460
+ function exactDiff(root, head) {
461
+ return git(root, [
462
+ 'diff', '--binary', '--no-ext-diff', head, ...PRODUCT_PATHSPEC,
463
+ ], 'buffer');
464
+ }
465
+
466
+ function verifyObservedPaths(root, records) {
467
+ for (const record of records) {
468
+ try {
469
+ verifyFreshRewalk(root, record.relative, record.snapshot, 'final');
470
+ } catch (error) {
471
+ throw changedDuringObservation(
472
+ `included path ${JSON.stringify(record.relative)} changed after its stable read (${error.message})`,
473
+ );
474
+ }
475
+ }
476
+ }
477
+
478
+ function sameIntent(left, right) {
479
+ return left.change_id === right.change_id
480
+ && left.intent_digest === right.intent_digest
481
+ && left.relative === right.relative
482
+ && left.identity === right.identity;
483
+ }
484
+
485
+ function verifyExpectedObservation(root, expected) {
486
+ const observedHead = git(root, ['rev-parse', 'HEAD']).trim();
487
+ const observedTracked = trackedManifest(root, expected.head);
488
+ const observedUntracked = untrackedManifest(root);
489
+ const observedDiff = exactDiff(root, expected.head);
490
+ if (observedHead !== expected.head
491
+ || !observedTracked.raw.equals(expected.tracked.raw)
492
+ || !observedUntracked.raw.equals(expected.untracked.raw)
493
+ || !observedDiff.equals(expected.diff)) {
494
+ throw changedDuringObservation(
495
+ 'HEAD, the exact tracked manifest or diff, or the untracked manifest changed during capture',
496
+ );
497
+ }
498
+
499
+ const observedIntent = intentSnapshot(root, expected.intent.change_id);
500
+ if (!sameIntent(expected.intent, observedIntent)) {
501
+ throw changedDuringObservation('the Change intent identity or bytes changed during capture');
502
+ }
503
+ verifyObservedPaths(root, expected.records);
504
+ }
505
+
506
+ function verifyClosingObservation(root, expected) {
507
+ verifyExpectedObservation(root, expected);
508
+ verifyExpectedObservation(root, expected);
509
+ }
510
+
54
511
  function main(options) {
55
512
  const root = fs.realpathSync(git(options.project, ['rev-parse', '--show-toplevel']).trim());
56
513
  const head = git(root, ['rev-parse', 'HEAD']).trim();
57
- const diff = git(root, [
58
- 'diff', '--binary', '--no-ext-diff', 'HEAD', '--', '.',
59
- ':(exclude).ultra/test-report.json',
60
- ':(exclude).ultra/changes/active/**',
61
- ':(exclude).ultra/changes/archive/**',
62
- ':(exclude).ultra/changes/abandoned/**',
63
- ], 'buffer');
64
- const untracked = git(root, ['ls-files', '--others', '--exclude-standard', '-z'], 'buffer')
65
- .toString('utf8').split('\0').filter(Boolean)
66
- .filter((file) => !excluded(file))
67
- .sort();
514
+ const tracked = trackedManifest(root, head);
515
+ const untracked = untrackedManifest(root);
516
+ const intent = intentSnapshot(root, options.changeId);
517
+ const budget = observationBudget();
518
+ const records = [];
519
+
520
+ for (const file of tracked.files) {
521
+ if (!file.present) {
522
+ budget.reserve(file.path, 0n);
523
+ continue;
524
+ }
525
+ const snapshot = streamStableRepositoryFile(root, file.path, () => {}, budget);
526
+ records.push({ relative: file.path, snapshot });
527
+ }
528
+
529
+ const untrackedBytes = [];
530
+ for (const file of untracked.files) {
531
+ const chunks = [];
532
+ const snapshot = streamStableRepositoryFile(
533
+ root,
534
+ file,
535
+ (chunk) => chunks.push(Buffer.from(chunk)),
536
+ budget,
537
+ );
538
+ records.push({ relative: file, snapshot });
539
+ untrackedBytes.push(chunks);
540
+ }
541
+
542
+ const diff = exactDiff(root, head);
543
+ const closingObservation = { diff, head, intent, records, tracked, untracked };
544
+ verifyClosingObservation(root, closingObservation);
545
+
68
546
  const hash = crypto.createHash('sha256');
69
547
  hash.update('ultra-worktree-digest-v1\0').update(head).update('\0').update(diff);
70
- for (const file of untracked) {
71
- hash.update(file).update('\0').update(fs.readFileSync(path.join(root, file))).update('\0');
548
+ for (let index = 0; index < untracked.files.length; index += 1) {
549
+ hash.update(untracked.files[index]).update('\0');
550
+ for (const chunk of untrackedBytes[index]) hash.update(chunk);
551
+ hash.update('\0');
72
552
  }
73
553
  return {
74
554
  $schema: 'ultra-worktree-digest-v1',
75
- ...intentSnapshot(root, options.changeId),
555
+ change_id: intent.change_id,
556
+ intent_digest: intent.intent_digest,
76
557
  head,
77
- dirty: diff.length > 0 || untracked.length > 0,
558
+ dirty: diff.length > 0 || untracked.files.length > 0,
78
559
  diff_digest: hash.digest('hex'),
79
- untracked_files: untracked,
560
+ untracked_files: untracked.files,
80
561
  };
81
562
  }
82
563
 
83
- try {
84
- process.stdout.write(`${JSON.stringify(main(optionsFromArgs(process.argv.slice(2))), null, 2)}\n`);
85
- } catch (error) {
86
- process.stderr.write(`${error.message}\n`);
87
- process.exitCode = 1;
564
+ if (require.main === module) {
565
+ try {
566
+ process.stdout.write(`${JSON.stringify(main(optionsFromArgs(process.argv.slice(2))), null, 2)}\n`);
567
+ } catch (error) {
568
+ process.stderr.write(`${error.message}\n`);
569
+ process.exitCode = 1;
570
+ }
88
571
  }
572
+
573
+ // The product-subject pathspec and the stable file-snapshot primitive are the
574
+ // single bounded mechanical definitions shared with the primary-transfer
575
+ // validator, so receipt reads and digest subject bytes can never drift.
576
+ module.exports = { PRODUCT_PATHSPEC, streamStableRepositoryFile };