great-cto 3.76.1 → 3.78.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.
@@ -0,0 +1,478 @@
1
+ import { readFileSync, writeFileSync, lstatSync, realpathSync, existsSync, mkdirSync, renameSync, unlinkSync, readdirSync } from 'node:fs';
2
+ import { join, resolve, relative, isAbsolute, sep, dirname } from 'node:path';
3
+ import { execFileSync } from 'node:child_process';
4
+ import { createHash, randomUUID } from 'node:crypto';
5
+ import { stableDigest, evaluateBoundShipEvidence, normalizeLegacyOrigins } from './ship-evidence-identity.mjs';
6
+ import { readShipPolicy, captureShipContext, recheckShipContext, assertCommittedShipArtifacts } from './ship-evidence-context.mjs';
7
+ import { codexRunStore } from './codex-host-state.mjs';
8
+ import { normalizeTask } from './gate-check.mjs';
9
+ import { parseVerdictLine } from './verdict-record.mjs';
10
+ import { canonicalAgent, verdictKind } from './ship-evidence.mjs';
11
+ import { invocationIdentity } from './invocation-identity.mjs';
12
+ import { isMainModule } from './is-main.mjs';
13
+
14
+ const brands = new WeakMap();
15
+ const caps = new WeakMap();
16
+ const sha = x => createHash('sha256').update(x).digest('hex');
17
+ const fail = (code, extra = {}) => { throw Object.assign(new Error(code), {code, ...extra}); };
18
+ const uuid = x => typeof x === 'string' && /^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/.test(x);
19
+ const hex = x => typeof x === 'string' && /^[a-f0-9]{64}$/.test(x);
20
+ const positive = x => Number.isSafeInteger(x) && x > 0;
21
+ const id = x => typeof x === 'string' && Buffer.byteLength(x) > 0 && Buffer.byteLength(x) <= 256 && !/[\x00-\x1f\x7f-\x9f]/u.test(x);
22
+ const exact = (x, keys) => x && Object.getPrototypeOf(x) === Object.prototype && Object.keys(x).sort().join('|') === [...keys].sort().join('|');
23
+ const clone = x => structuredClone(x);
24
+ const inside = (a, b) => { const r = relative(a, b); return r === '' || (!isAbsolute(r) && r !== '..' && !r.startsWith(`..${sep}`)); };
25
+ const codepointOrder = (a, b) => {
26
+ const aa = Array.from(a, c => c.codePointAt(0)), bb = Array.from(b, c => c.codePointAt(0));
27
+ for (let i = 0; i < Math.min(aa.length, bb.length); i++) if (aa[i] !== bb[i]) return aa[i] - bb[i];
28
+ return aa.length - bb.length;
29
+ };
30
+ // JSON.stringify on a reconstructed object would reorder integer-looking keys.
31
+ // Serialize the ordered key/value pairs directly, matching stableDigest exactly.
32
+ const bytesOf = x => Array.isArray(x) ? `[${x.map(bytesOf).join(',')}]`
33
+ : x !== null && typeof x === 'object' ? `{${Object.keys(x).sort(codepointOrder).map(k => `${JSON.stringify(k)}:${bytesOf(x[k])}`).join(',')}}`
34
+ : JSON.stringify(x);
35
+ function privatePath(path, kind, limit = 16777216) {
36
+ if (!isAbsolute(path) || resolve(path) !== path || realpathSync(path) !== path) fail('SHIP_PATH_UNSAFE');
37
+ const st = lstatSync(path);
38
+ if (st.isSymbolicLink() || (kind === 'dir' ? !st.isDirectory() : !st.isFile()) || st.uid !== process.getuid() || (st.mode & 0o777) !== (kind === 'dir' ? 0o700 : 0o600) || (kind === 'file' && st.size > limit)) fail('SHIP_PATH_UNSAFE');
39
+ return path;
40
+ }
41
+ function safeStores(root, store, runStore) {
42
+ root = realpathSync(root); privatePath(store, 'dir'); privatePath(runStore, 'dir');
43
+ if (inside(root, store) || inside(root, runStore) || inside(store, runStore) || inside(runStore, store)) fail('SHIP_PATH_UNSAFE');
44
+ return root;
45
+ }
46
+ function readJSON(path) {
47
+ privatePath(path, 'file');
48
+ const value = JSON.parse(readFileSync(path, 'utf8')); stableDigest(value); return value;
49
+ }
50
+ function ensureDirectory(path) {
51
+ if (!existsSync(path)) mkdirSync(path, {mode: 0o700});
52
+ privatePath(path, 'dir'); return path;
53
+ }
54
+ function runDirectory(store, runId) {
55
+ if (!uuid(runId)) fail('SHIP_UNBOUND');
56
+ return join(store, 'runs', runId);
57
+ }
58
+ function evidenceDirectories(store, runId) {
59
+ ensureDirectory(join(store, 'runs'));
60
+ const dir = ensureDirectory(runDirectory(store, runId));
61
+ ensureDirectory(join(dir, 'generations')); ensureDirectory(join(dir, 'blobs')); return dir;
62
+ }
63
+ function readBinding(state) {
64
+ const b = state?.shipEvidence;
65
+ if (!b) fail('SHIP_UNBOUND');
66
+ if (!exact(b, ['version', 'revision', 'phase', 'policyPath', 'policyDigest', 'registrationDigest', 'storeDigest', 'contextDigest', 'assignmentStoreDigest', 'originRegistryDigest', 'generationDigest']) || b.version !== 'ship-bound-run/v1' || !positive(b.revision) || !['PROVISIONAL', 'NEEDS_REASSESSMENT', 'REVIEWING', 'READY', 'INVALIDATED'].includes(b.phase) || typeof b.policyPath !== 'string' || ![b.policyDigest, b.registrationDigest, b.storeDigest, b.assignmentStoreDigest, b.originRegistryDigest, b.generationDigest].every(hex) || !(b.contextDigest === null || hex(b.contextDigest))) fail('SHIP_INVALID_REVISION');
67
+ return b;
68
+ }
69
+ function linked(state, descriptor, store, {pointer = true} = {}) {
70
+ const b = readBinding(state);
71
+ if (!uuid(state.id) || !uuid(state.taskId) || state.version !== 1 || realpathSync(state.root) !== descriptor.root || b.policyPath !== descriptor.policyPath || b.policyDigest !== descriptor.policyDigest || b.registrationDigest !== descriptor.registrationDigest || b.storeDigest !== sha(store)) fail('SHIP_UNBOUND');
72
+ const dir = runDirectory(store, state.id); privatePath(join(store, 'runs'), 'dir'); privatePath(dir, 'dir'); privatePath(join(dir, 'generations'), 'dir');
73
+ privatePath(join(dir, 'blobs'), 'dir');
74
+ const path = join(dir, 'generations', `${b.revision}.json`), gen = readJSON(path);
75
+ if (!exact(gen, ['version', 'runId', 'taskId', 'revision', 'phase', 'policyDigest', 'registrationDigest', 'expected', 'assignments', 'records', 'originRegistry', 'obligations', 'resolutions', 'readStatus', 'taskSnapshotDigest', 'journalSnapshotDigest', 'completionDigests', 'previousGenerationDigest']) || gen.version !== 'ship-generation/v1' || gen.runId !== state.id || gen.taskId !== state.taskId || gen.revision !== b.revision || gen.phase !== b.phase || gen.policyDigest !== b.policyDigest || gen.registrationDigest !== b.registrationDigest || stableDigest(gen) !== b.generationDigest || sha(readFileSync(path)) !== b.generationDigest || stableDigest(gen.assignments) !== b.assignmentStoreDigest || stableDigest(gen.originRegistry) !== b.originRegistryDigest || (gen.expected === null ? b.contextDigest !== null : stableDigest(gen.expected) !== b.contextDigest)) fail('SHIP_EVIDENCE_UNKNOWN');
76
+ for (const name of ['assignments', 'records', 'obligations', 'resolutions', 'completionDigests']) if (!Array.isArray(gen[name]) || gen[name].length > 10000) fail('SHIP_EVIDENCE_UNKNOWN');
77
+ if (gen.resolutions.some(r => r.kind === 'OWNER_SUPERSESSION')) fail('SHIP_EVIDENCE_UNKNOWN');
78
+ for (const field of ['taskSnapshotDigest', 'journalSnapshotDigest']) {
79
+ if (gen[field] === null) continue;
80
+ if (!hex(gen[field]) || stableDigest(readJSON(join(dir, 'blobs', `${gen[field]}.json`))) !== gen[field]) fail('SHIP_EVIDENCE_UNKNOWN');
81
+ }
82
+ for (const digest of gen.completionDigests) {
83
+ if (!hex(digest)) fail('SHIP_EVIDENCE_UNKNOWN');
84
+ const blob = readJSON(join(dir, 'blobs', `${digest}.json`));
85
+ if (stableDigest(blob) !== digest || blob.version !== 'ship-completion/v1' || !gen.records.some(r => r.recordId === blob.recordId && r.reviewerInvocationId === blob.reviewerInvocationId && r.rawRecordDigest === digest) || !gen.assignments.some(a => a.reviewerInvocationId === blob.reviewerInvocationId && (stableDigest(a) === blob.assignmentDigest || a.revoked && stableDigest({...a, revoked: false}) === blob.assignmentDigest))) fail('SHIP_EVIDENCE_UNKNOWN');
86
+ }
87
+ if (gen.records.some(r => !gen.completionDigests.includes(r.rawRecordDigest))) fail('SHIP_EVIDENCE_UNKNOWN');
88
+ if (pointer) {
89
+ const p = readJSON(join(dir, 'current.json'));
90
+ if (!exact(p, ['version', 'generation', 'digest']) || p.version !== 'ship-store-pointer/v1' || p.generation !== b.revision || p.digest !== b.generationDigest) fail('SHIP_EVIDENCE_UNKNOWN');
91
+ }
92
+ return gen;
93
+ }
94
+ function now(clock) { const n = clock(); if (!Number.isSafeInteger(n) || n < 0) fail('SHIP_EVIDENCE_UNKNOWN'); return n; }
95
+ const unknownRead = subject => ({state: 'UNKNOWN', snapshotDigest: null, diagnostics: [{code: 'READ_UNKNOWN', subject}]});
96
+ function productionTasks({root}) {
97
+ try {
98
+ const raw = execFileSync('bd', ['--readonly', '--sandbox', 'list', '--all', '--limit', '0', '--include-gates', '--include-infra', '--include-templates', '--include-ephemeral', '--max-rows', '10000', '--json'], {cwd: root, encoding: 'utf8', timeout: 20000, maxBuffer: 16777216, stdio: ['ignore', 'pipe', 'ignore'], env: {...process.env, BD_DISABLE_METRICS: '1', BD_DISABLE_EVENT_FLUSH: '1', BD_DOLT_AUTO_PUSH: 'false'}});
99
+ const parsed = JSON.parse(raw), tasks = Array.isArray(parsed) ? parsed : parsed.issues || parsed.tasks;
100
+ if (!Array.isArray(tasks) || tasks.length > 10000) throw Error();
101
+ return {state: 'COMPLETE', tasks, snapshotDigest: stableDigest(tasks), diagnostics: []};
102
+ } catch { return {...unknownRead('tasks'), tasks: []}; }
103
+ }
104
+ function productionJournals({journalRoot, originId}) {
105
+ const journals = []; let total = 0, complete = true;
106
+ try {
107
+ privatePath(journalRoot, 'dir');
108
+ const files = readdirSync(journalRoot).sort();
109
+ if (files.length > 10000) throw Error();
110
+ for (const file of files) {
111
+ if (!file.endsWith('.log')) continue;
112
+ if (!id(file)) throw Error();
113
+ const path = privatePath(join(journalRoot, file), 'file'), raw = readFileSync(path);
114
+ if ((total += raw.length) > 16777216) throw Error();
115
+ const records = [];
116
+ for (const line of raw.toString('utf8').split('\n')) {
117
+ if (!line.trim()) continue;
118
+ if (records.length >= 10000 || Buffer.byteLength(line) > 262144) throw Error();
119
+ const parsed = parseVerdictLine(line), rawDigest = sha(line);
120
+ let outcome = 'UNKNOWN', role = canonicalAgent(file);
121
+ if (parsed.ok && Number.isFinite(Date.parse(parsed.rec.ts))) {
122
+ role = canonicalAgent(parsed.rec.agent || file);
123
+ const kind = verdictKind(parsed.rec.verdict);
124
+ outcome = kind === 'negative' ? 'FAIL' : kind === 'positive' ? 'PASS' : 'UNKNOWN';
125
+ if (kind === 'neutral') complete = false;
126
+ } else complete = false;
127
+ if (!/^[a-z][a-z0-9-]{0,63}$/.test(role)) { role = 'unknown-reviewer'; complete = false; }
128
+ records.push({rawDigest, outcome, role, rawRecord: parsed.ok ? parsed.rec : {rejectedDigest: rawDigest}});
129
+ }
130
+ journals.push({journalId: file, originId, snapshotId: sha(raw), complete: true, records});
131
+ }
132
+ return {state: complete ? 'COMPLETE' : 'UNKNOWN', journals, snapshotDigest: stableDigest(journals), diagnostics: complete ? [] : [{code: 'JOURNAL_REJECTED', subject: 'journals'}]};
133
+ } catch { return {...unknownRead('journals'), journals}; }
134
+ }
135
+ function snapshots(root, descriptor, registry, clock, readTasks, readJournals) {
136
+ let t, j, invalid = false;
137
+ try { t = (readTasks || productionTasks)({root}); stableDigest(t); } catch { t = {...unknownRead('tasks'), tasks: []}; }
138
+ try { j = (readJournals || productionJournals)({root, journalRoot: descriptor.policy.journalRoot, originId: descriptor.registration.journalOriginId}); stableDigest(j); } catch { j = {...unknownRead('journals'), journals: []}; }
139
+ const tasks = [];
140
+ if (!Array.isArray(t.tasks) || t.tasks.length > 10000) invalid = true;
141
+ else for (const raw of t.tasks) {
142
+ if (!raw || !id(raw.id) || typeof raw.status !== 'string' || !Array.isArray(raw.labels) || raw.labels.some(l => typeof l !== 'string')) { invalid = true; continue; }
143
+ const {id: taskId, state} = normalizeTask(raw);
144
+ if (!['open', 'in_progress', 'closed', 'blocked', 'failed', 'unverified', 'not_run', 'not-run'].includes(state)) { invalid = true; continue; }
145
+ tasks.push({id: taskId, state});
146
+ }
147
+ let journals = [];
148
+ try {
149
+ if (!Array.isArray(j.journals) || j.journals.length > 10000) throw Error();
150
+ journals = j.journals.map(v => {
151
+ if (v.originId !== descriptor.registration.journalOriginId || !Array.isArray(v.records)) throw Error();
152
+ return {journalId: v.journalId, originId: v.originId, snapshotId: v.snapshotId, complete: v.complete, records: v.records.map(r => ({rawDigest: r.rawDigest, outcome: r.outcome, role: r.role}))};
153
+ });
154
+ } catch { invalid = true; }
155
+ const normalized = normalizeLegacyOrigins({registry, journals});
156
+ const status = {version: 'ship-read-status/v1', nowMs: now(clock), context: 'COMPLETE', assignments: 'COMPLETE', journals: j.state === 'COMPLETE' && !invalid ? 'COMPLETE' : 'UNKNOWN', tasks: t.state === 'COMPLETE' && !invalid ? 'COMPLETE' : 'UNKNOWN', policy: 'COMPLETE', origins: normalized.state === 'UNKNOWN' ? 'UNKNOWN' : 'COMPLETE', trustedOwnerDecisions: []};
157
+ const taskSnapshotDigest = Array.isArray(t.tasks) && stableDigest(t.tasks) === t.snapshotDigest ? t.snapshotDigest : null;
158
+ const journalSnapshotDigest = Array.isArray(j.journals) && stableDigest(j.journals) === j.snapshotDigest ? j.snapshotDigest : null;
159
+ if (!hex(taskSnapshotDigest)) status.tasks = 'UNKNOWN';
160
+ if (!hex(journalSnapshotDigest)) status.journals = 'UNKNOWN';
161
+ return {tasks, normalized, status, taskSnapshotDigest, journalSnapshotDigest, rawTasks: t, rawJournals: j};
162
+ }
163
+ function requirementsFor(descriptor, state) {
164
+ const actors = [...new Set((state.attempts || []).filter(a => a.role === 'senior-dev' && id(a.invocationId)).map(a => a.invocationId))].sort();
165
+ return descriptor.requirements.map(r => ({...r, independentOf: actors}));
166
+ }
167
+ function innerEvaluation(state, descriptor, gen, options, {allowAppend = false} = {}) {
168
+ const snaps = snapshots(descriptor.root, descriptor, gen.originRegistry, options.clock, options.readTasks, options.readJournals);
169
+ if (!gen.expected || !['REVIEWING', 'READY'].includes(gen.phase)) snaps.status.context = 'UNKNOWN';
170
+ else {
171
+ if (recheckShipContext({root: descriptor.root, registration: descriptor.registration, expected: gen.expected}).state !== 'MATCH') snaps.status.context = 'UNKNOWN';
172
+ if (stableDigest(requirementsFor(descriptor, state)) !== gen.expected.requirementsDigest) snaps.status.policy = 'UNKNOWN';
173
+ }
174
+ if (!allowAppend && stableDigest(snaps.normalized.registry) !== stableDigest(gen.originRegistry)) snaps.status.origins = 'UNKNOWN';
175
+ const result = evaluateBoundShipEvidence({expected: gen.expected, invocations: gen.assignments, records: gen.records, obligations: [...gen.obligations, ...snaps.normalized.obligations], resolutions: gen.resolutions, tasks: snaps.tasks, readStatus: snaps.status});
176
+ return {result, snaps};
177
+ }
178
+ const unknownReport = () => ({version: 'ship-bound-gate/v1', state: 'UNKNOWN', contextDigest: null, revision: null, result: evaluateBoundShipEvidence(null), publicationEligibility: 'NOT_CHECKED'});
179
+ function knownHistoricalFindings({root, store, runId, expectedRevision}, {runStore, clock}) {
180
+ const report = unknownReport();
181
+ try {
182
+ root = safeStores(root, store, runStore);
183
+ if (!uuid(runId) || !positive(expectedRevision)) return report;
184
+ const state = readJSON(join(runStore, `${runId}.json`)), binding = readBinding(state);
185
+ if (state.id !== runId || state.version !== 1 || !uuid(state.taskId) || realpathSync(state.root) !== root || binding.revision !== expectedRevision || binding.storeDigest !== sha(store) || binding.policyPath !== join(store, 'policy.json')) return report;
186
+ const dir = runDirectory(store, runId);
187
+ for (const path of [join(store, 'runs'), dir, join(dir, 'generations')]) privatePath(path, 'dir');
188
+ const path = join(dir, 'generations', `${binding.revision}.json`), generation = readJSON(path);
189
+ if (sha(readFileSync(path)) !== binding.generationDigest || stableDigest(generation) !== binding.generationDigest || generation.version !== 'ship-generation/v1' || generation.runId !== runId || generation.taskId !== state.taskId || generation.revision !== binding.revision || generation.policyDigest !== binding.policyDigest || generation.registrationDigest !== binding.registrationDigest || stableDigest(generation.assignments) !== binding.assignmentStoreDigest || stableDigest(generation.originRegistry) !== binding.originRegistryDigest) return report;
190
+ // A controller-linked immutable history is useful when current policy/pointer
191
+ // cannot be assessed. It never supplies current source, tasks or resolutions.
192
+ const result = evaluateBoundShipEvidence({expected: null, invocations: [], records: generation.records, obligations: generation.obligations, resolutions: [], tasks: [], readStatus: {version: 'ship-read-status/v1', nowMs: now(clock), context: 'UNKNOWN', assignments: 'UNKNOWN', journals: 'UNKNOWN', tasks: 'UNKNOWN', policy: 'UNKNOWN', origins: 'UNKNOWN', trustedOwnerDecisions: []}});
193
+ return {...report, revision: binding.revision, result};
194
+ } catch { return report; }
195
+ }
196
+ export function evaluateBoundGate({root, store, runId, expectedRevision}, {runStore, clock = Date.now, readTasks = null, readJournals = null} = {}) {
197
+ try {
198
+ root = safeStores(root, store, runStore);
199
+ if (!uuid(runId) || !positive(expectedRevision)) fail('SHIP_INVALID_REVISION');
200
+ const state = readJSON(join(runStore, `${runId}.json`)), b = readBinding(state);
201
+ if (b.revision !== expectedRevision) fail('SHIP_STALE_REVISION');
202
+ const descriptor = readShipPolicy({root, path: b.policyPath});
203
+ if (descriptor.store !== store) fail('SHIP_UNBOUND');
204
+ const gen = linked(state, descriptor, store), {result} = innerEvaluation(state, descriptor, gen, {clock, readTasks, readJournals});
205
+ return {version: 'ship-bound-gate/v1', state: result.state, contextDigest: b.contextDigest, revision: b.revision, result, publicationEligibility: 'NOT_CHECKED'};
206
+ } catch { return knownHistoricalFindings({root, store, runId, expectedRevision}, {runStore, clock}); }
207
+ }
208
+
209
+ export function assertShipControllerBoundary(boundary, state) {
210
+ const owned = brands.get(boundary);
211
+ if (!owned || owned.root !== state.root || owned.runId !== state.id || owned.taskId !== state.taskId) fail('SHIP_CAPABILITY_INVALID');
212
+ return boundary;
213
+ }
214
+ function makeBoundary({root, store, lease, readPersistedState, writeGeneration}, options, seed = null) {
215
+ const {runStore, clock = Date.now, readTasks = null, readJournals = null} = options;
216
+ root = safeStores(root, store, runStore);
217
+ if (!lease || typeof lease.assertOwned !== 'function') fail('SHIP_LEASE_LOST');
218
+ const assertLease = () => { try { lease.assertOwned(); } catch { fail('SHIP_LEASE_LOST'); } };
219
+ assertLease();
220
+ const read = readPersistedState || (runId => readJSON(join(runStore, `${runId}.json`)));
221
+ const write = writeGeneration || (({runId, generation, digest, bytes}) => {
222
+ const dir = evidenceDirectories(store, runId), path = join(dir, 'generations', `${generation}.json`);
223
+ if (existsSync(path)) {
224
+ privatePath(path, 'file'); if (sha(readFileSync(path)) !== digest) fail('SHIP_GENERATION_CONFLICT');
225
+ return {created: false, digest};
226
+ }
227
+ writeFileSync(path, bytes, {mode: 0o600, flag: 'wx'});
228
+ return {created: true, digest: sha(readFileSync(privatePath(path, 'file')))};
229
+ });
230
+ let runId = seed?.state.id || null, taskId = seed?.state.taskId || null, revision = seed ? 0 : null, gen = seed?.generation || null, descriptor = seed?.descriptor || null, priorDigest = null, poisoned = false;
231
+ const activeCapabilities = new Set(), callIds = new Set();
232
+ const load = state => {
233
+ if (poisoned) fail('SHIP_COMMITTED_OBSERVATION_UNKNOWN', {persistedRevision: revision});
234
+ assertLease();
235
+ if (!runId) {
236
+ const b = readBinding(state);
237
+ descriptor = readShipPolicy({root, path: b.policyPath});
238
+ if (descriptor.store !== store) fail('SHIP_UNBOUND');
239
+ const saved = read(state.id); if (saved?.then) fail('SHIP_EVIDENCE_UNKNOWN');
240
+ gen = clone(linked(saved, descriptor, store)); revision = saved.shipEvidence.revision; priorDigest = saved.shipEvidence.generationDigest;
241
+ runId = saved.id; taskId = saved.taskId;
242
+ brands.set(boundary, {root, runId, taskId});
243
+ }
244
+ if (state.id !== runId || state.taskId !== taskId || realpathSync(state.root) !== root) fail('SHIP_CAPABILITY_INVALID');
245
+ if (revision === 0) return null;
246
+ const saved = read(runId); if (saved?.then) fail('SHIP_EVIDENCE_UNKNOWN');
247
+ const b = readBinding(saved);
248
+ if (b.revision !== revision || state.shipEvidence?.revision !== revision || b.generationDigest !== priorDigest || state.shipEvidence.generationDigest !== priorDigest) fail('SHIP_STALE_REVISION');
249
+ const currentDescriptor = readShipPolicy({root, path: b.policyPath});
250
+ linked(saved, currentDescriptor, store);
251
+ if (currentDescriptor.policyDigest !== descriptor.policyDigest || currentDescriptor.registrationDigest !== descriptor.registrationDigest) fail('SHIP_EVIDENCE_UNKNOWN');
252
+ return saved;
253
+ };
254
+ const assertSource = () => {
255
+ if (gen.expected && recheckShipContext({root, registration: descriptor.registration, expected: gen.expected}).state !== 'MATCH') fail('SHIP_CONTEXT_DRIFT');
256
+ };
257
+ const persistBlob = value => {
258
+ const digest = stableDigest(value), dir = evidenceDirectories(store, runId), path = join(dir, 'blobs', `${digest}.json`), bytes = bytesOf(value);
259
+ if (sha(bytes) !== digest) fail('SHIP_EVIDENCE_UNKNOWN');
260
+ if (existsSync(path)) { privatePath(path, 'file'); if (sha(readFileSync(path)) !== digest) fail('SHIP_GENERATION_CONFLICT'); }
261
+ else writeFileSync(path, bytes, {flag: 'wx', mode: 0o600});
262
+ return digest;
263
+ };
264
+ const retainCallFinding = (cap, digest, category) => {
265
+ const ordinal = gen.obligations.length;
266
+ gen.obligations.push({findingId: `controller:${randomUUID()}`, originRecordDigest: digest, originContextDigest: cap.assignment.expectedContextDigest, role: cap.assignment.role, severity: 'UNKNOWN', category, origin: {journalId: `controller:${runId}`, ordinal, rawDigest: digest}});
267
+ };
268
+ const boundary = {
269
+ assertCurrent(state, {effect} = {}) {
270
+ if (!['metadata', 'worker', 'checks', 'verifier', 'ship', 'devops', 'release'].includes(effect)) fail('SHIP_CAPABILITY_INVALID');
271
+ load(state); assertSource();
272
+ const requiredRole = descriptor.requirements.some(r => r.role === (state.active || state.queue?.[0]));
273
+ if (effect !== 'metadata' && (requiredRole || ['ship', 'devops', 'release'].includes(effect)) && !['REVIEWING', 'READY'].includes(gen.phase)) fail('SHIP_REASSESS_REQUIRED');
274
+ if (['ship', 'devops', 'release'].includes(effect)) {
275
+ const report = evaluateBoundGate({root, store, runId, expectedRevision: revision}, {runStore, clock, readTasks, readJournals});
276
+ if (report.state !== 'PASS') fail(report.state === 'BLOCKED' ? 'SHIP_EVIDENCE_BLOCKED' : 'SHIP_EVIDENCE_UNKNOWN');
277
+ }
278
+ return {revision, contextDigest: gen.expected ? stableDigest(gen.expected) : null};
279
+ },
280
+ assertReady(state, {role, attemptId, host}) {
281
+ load(state);
282
+ if (!['REVIEWING', 'READY'].includes(gen.phase)) {
283
+ if (gen.phase === 'PROVISIONAL') { gen.phase = 'NEEDS_REASSESSMENT'; state.shipEvidence.phase = gen.phase; }
284
+ fail('SHIP_REASSESS_REQUIRED');
285
+ }
286
+ boundary.assertCurrent(state, {effect: 'worker'});
287
+ const required = gen.expected.requiredReviewers.find(r => r.role === role);
288
+ if (!required || !id(attemptId) || !['codex', 'claude-code'].includes(host) || !state.graph[role] || (state.hostRoutes?.[role] || 'codex') !== host) fail('SHIP_CAPABILITY_INVALID');
289
+ const issuedAtMs = now(clock), expiresAtMs = issuedAtMs + descriptor.policy.assignmentLifetimeMs;
290
+ if (!Number.isSafeInteger(expiresAtMs)) fail('SHIP_EVIDENCE_UNKNOWN');
291
+ const reviewerActorId = invocationIdentity({session_id: runId, agent_id: attemptId});
292
+ if (!reviewerActorId) fail('SHIP_CAPABILITY_INVALID');
293
+ const assignment = {version: 'ship-assignment/v1', reviewerInvocationId: randomUUID(), role, expectedContextDigest: stableDigest(gen.expected), taskId, runId, reviewerActorId, independentOf: [...required.independentOf], requiredChecks: [...required.requiredChecks], issuedAtMs, expiresAtMs, revoked: false, origin: 'CONTROLLER'};
294
+ if (required.independentOf.includes(assignment.reviewerActorId)) fail('SHIP_CAPABILITY_INVALID');
295
+ gen.assignments.push(assignment);
296
+ const capability = Object.freeze({});
297
+ caps.set(capability, {boundary, assignment, state, attemptId, host, calls: new Map(), startedAtMs: issuedAtMs, completed: false});
298
+ activeCapabilities.add(capability); return capability;
299
+ },
300
+ async runCall(capability, {kind, callId, invoke}) {
301
+ const cap = caps.get(capability);
302
+ if (!cap || cap.boundary !== boundary || cap.completed || !activeCapabilities.has(capability) || cap.assignment.revoked || cap.assignment.expectedContextDigest !== (gen.expected && stableDigest(gen.expected))) fail('SHIP_CAPABILITY_INVALID');
303
+ if (!['worker', 'verifier', 'checks'].includes(kind) || !id(callId) || typeof invoke !== 'function') fail('SHIP_CAPABILITY_INVALID');
304
+ if (cap.calls.has(kind) || callIds.has(callId)) fail('SHIP_REPLAY');
305
+ boundary.assertCurrent(cap.state, {effect: kind});
306
+ callIds.add(callId); const call = {callId, result: null, digest: null, returned: false}; cap.calls.set(kind, call);
307
+ try {
308
+ const result = await invoke();
309
+ boundary.assertCurrent(cap.state, {effect: kind});
310
+ call.result = result; call.digest = stableDigest(result); call.returned = true;
311
+ if (kind === 'worker') {
312
+ let parsed; try { parsed = JSON.parse(result.finalText ?? result.text); } catch {}
313
+ if (result.state !== 'ok' || result.code !== 0 || result.errors?.length || !parsed || verdictKind(parsed.verdict) !== 'positive') retainCallFinding(cap, call.digest, 'worker-return');
314
+ }
315
+ if (kind === 'checks' && (result?.state !== 'passed' || result?.code !== 0)) retainCallFinding(cap, call.digest, 'checks-return');
316
+ return result;
317
+ } catch (error) {
318
+ call.failed = true;
319
+ // This is an unresolved call finding, not a fabricated review completion.
320
+ retainCallFinding(cap, stableDigest({kind, callId, code: String(error.code || 'CALL_UNKNOWN')}), 'transport-unknown');
321
+ throw error;
322
+ }
323
+ },
324
+ completeReview(state, {capability, proposal, verification, checks = null}) {
325
+ const cap = caps.get(capability);
326
+ if (!cap || cap.boundary !== boundary || cap.state !== state || cap.completed || !activeCapabilities.has(capability)) fail('SHIP_CAPABILITY_INVALID');
327
+ boundary.assertCurrent(state, {effect: 'verifier'});
328
+ const worker = cap.calls.get('worker'), verifier = cap.calls.get('verifier'), check = cap.calls.get('checks');
329
+ if (!worker?.returned || stableDigest(worker.result) !== worker.digest || !verifier?.returned || verifier.result !== verification || stableDigest(verification) !== verifier.digest || (checks !== null && (!check?.returned || check.result !== checks || stableDigest(checks) !== check.digest))) fail('SHIP_CAPABILITY_INVALID');
330
+ let parsed;
331
+ try { parsed = JSON.parse(worker.result.finalText ?? worker.result.text); } catch { fail('SHIP_CAPABILITY_INVALID'); }
332
+ if (stableDigest(parsed) !== stableDigest(proposal)) fail('SHIP_CAPABILITY_INVALID');
333
+ if (!Array.isArray(proposal.files) || proposal.files.length) fail('UNSUPPORTED_REVIEW_OUTPUT');
334
+ const rule = state.graph[`${cap.assignment.role}.${proposal.verdict}`] || state.graph[cap.assignment.role];
335
+ assertCommittedShipArtifacts({root, allowed: state.allowed, rule, proposal});
336
+ const workerClean = worker.result.state === 'ok' && worker.result.code === 0 && Array.isArray(worker.result.errors) && worker.result.errors.length === 0;
337
+ const verifierClean = verification.state === 'verified' && Array.isArray(verification.findings) && verification.findings.length === 0 && Array.isArray(verification.checks) && verification.checks.length > 0;
338
+ const checkEvidence = cap.assignment.requiredChecks.map(checkId => ({checkId, state: (checkId === 'independent-verifier' ? verifierClean : check?.returned && checks?.state === 'passed' && checks?.code === 0) ? 'PASS' : 'FAIL', evidenceDigest: checkId === 'independent-verifier' ? verifier.digest : check?.digest || stableDigest(null)}));
339
+ const outcome = workerClean && verifierClean && rule?.on?.includes(proposal.verdict) && checkEvidence.every(c => c.state === 'PASS') ? 'PASS' : ['FAIL', 'REJECTED', 'BLOCKED'].includes(proposal.verdict) ? 'FAIL' : 'UNKNOWN';
340
+ const recordId = randomUUID(), finishedAtMs = now(clock), contextDigest = stableDigest(gen.expected);
341
+ if (finishedAtMs >= cap.assignment.expiresAtMs) fail('SHIP_EVIDENCE_UNKNOWN');
342
+ const envelope = {version: 'ship-completion/v1', assignmentDigest: stableDigest(cap.assignment), reviewerInvocationId: cap.assignment.reviewerInvocationId, attemptId: cap.attemptId, role: cap.assignment.role, host: cap.host, expectedContextDigest: contextDigest, sourceBeforeDigest: contextDigest, sourceAfterDigest: contextDigest, workerCallId: worker.callId, workerTransportDigest: worker.digest, proposalDigest: stableDigest(proposal), verifierCallId: verifier.callId, verifierTransportDigest: verifier.digest, verificationDigest: stableDigest(verification), checkEvidence, startedAtMs: cap.startedAtMs, finishedAtMs, outcome, recordId};
343
+ const completionDigest = persistBlob(envelope);
344
+ gen.records.push({version: 'ship-evidence/v2', recordId, reviewerInvocationId: cap.assignment.reviewerInvocationId, expectedContextDigest: contextDigest, role: cap.assignment.role, outcome, checkEvidence, reviewedEvidenceDigest: stableDigest({proposalDigest: envelope.proposalDigest, verificationDigest: envelope.verificationDigest}), authority: 'CONTROLLED_REVIEW', releaseEligibility: 'SOURCE_GATE_ONLY', evidenceClass: 'SOURCE_REVIEW', findingIds: [], startedAtMs: cap.startedAtMs, finishedAtMs, rawRecordDigest: completionDigest, origin: {journalId: `controller:${runId}`, ordinal: gen.records.length, rawDigest: completionDigest}});
345
+ gen.completionDigests.push(completionDigest); gen.completionDigests.sort(); cap.completed = true; activeCapabilities.delete(capability);
346
+ return {recordId, completionDigest, outcome};
347
+ },
348
+ assertApproval(state, {token}) {
349
+ const saved = load(state);
350
+ if (!saved?.pending || token !== saved.pending.token || token !== state.pending?.token) fail('SHIP_STALE_REVISION');
351
+ if (state.pending.gates.includes('gate:ship') && (state.pending.shipEvidenceRevision !== revision || state.pending.shipEvidenceContextDigest !== state.shipEvidence.contextDigest || saved.pending.shipEvidenceRevision !== revision || saved.pending.shipEvidenceContextDigest !== state.shipEvidence.contextDigest)) fail('SHIP_STALE_REVISION');
352
+ return boundary.assertCurrent(state, {effect: state.pending.gates.includes('gate:ship') ? 'ship' : 'metadata'});
353
+ },
354
+ save(state) {
355
+ const saved = load(state);
356
+ if (revision >= Number.MAX_SAFE_INTEGER) fail('SHIP_INVALID_REVISION');
357
+ privatePath(join(runStore, `${runId}.lock`), 'dir');
358
+ assertSource();
359
+ const protectedEffect = saved && (
360
+ state.approvals.some(a => a.gate === 'gate:ship' && !saved.approvals.some(old => stableDigest(old) === stableDigest(a)))
361
+ || state.released.some(role => !saved.released.includes(role) && (state.graph[role]?.gate === 'gate:ship' || state.graph[role]?.next?.includes('devops')))
362
+ || stableDigest(state.release || null) !== stableDigest(saved.release || null)
363
+ || state.results.devops && !saved.results.devops);
364
+ const assertProtectedEffect = () => {
365
+ if (!protectedEffect) return;
366
+ const {result} = innerEvaluation(state, descriptor, gen, {clock, readTasks, readJournals});
367
+ if (result.state !== 'PASS') fail(result.state === 'BLOCKED' ? 'SHIP_EVIDENCE_BLOCKED' : 'SHIP_EVIDENCE_UNKNOWN');
368
+ };
369
+ assertProtectedEffect();
370
+ const next = clone(gen), stagedState = clone(state), nextRevision = revision + 1;
371
+ if (gen.expected) {
372
+ const evaluation = innerEvaluation(state, descriptor, gen, {clock, readTasks, readJournals}, {allowAppend: true});
373
+ next.originRegistry = evaluation.snaps.normalized.registry;
374
+ next.obligations = evaluation.result.openObligations.concat(evaluation.result.resolvedObligations, evaluation.result.supersededObligations);
375
+ next.readStatus = evaluation.snaps.status; next.taskSnapshotDigest = evaluation.snaps.taskSnapshotDigest; next.journalSnapshotDigest = evaluation.snaps.journalSnapshotDigest;
376
+ if (next.taskSnapshotDigest && persistBlob(evaluation.snaps.rawTasks.tasks) !== next.taskSnapshotDigest) fail('SHIP_EVIDENCE_UNKNOWN');
377
+ if (next.journalSnapshotDigest && persistBlob(evaluation.snaps.rawJournals.journals) !== next.journalSnapshotDigest) fail('SHIP_EVIDENCE_UNKNOWN');
378
+ persistBlob(evaluation.snaps.rawTasks); persistBlob(evaluation.snaps.rawJournals);
379
+ if (evaluation.result.state === 'PASS') next.phase = 'READY';
380
+ else if (next.phase === 'READY') next.phase = 'REVIEWING';
381
+ }
382
+ next.revision = nextRevision; next.previousGenerationDigest = revision === 0 ? null : priorDigest;
383
+ const digest = stableDigest(next), bytes = bytesOf(next);
384
+ if (sha(bytes) !== digest) fail('SHIP_EVIDENCE_UNKNOWN');
385
+ const response = write({runId, generation: nextRevision, digest, bytes});
386
+ if (response?.then || response?.digest !== digest) fail('SHIP_EVIDENCE_UNKNOWN');
387
+ const generationPath = join(runDirectory(store, runId), 'generations', `${nextRevision}.json`);
388
+ if (stableDigest(readJSON(generationPath)) !== digest || sha(readFileSync(generationPath)) !== digest) fail('SHIP_EVIDENCE_UNKNOWN');
389
+ const binding = {version: 'ship-bound-run/v1', revision: nextRevision, phase: next.phase, policyPath: descriptor.policyPath, policyDigest: descriptor.policyDigest, registrationDigest: descriptor.registrationDigest, storeDigest: sha(store), contextDigest: next.expected ? stableDigest(next.expected) : null, assignmentStoreDigest: stableDigest(next.assignments), originRegistryDigest: stableDigest(next.originRegistry), generationDigest: digest};
390
+ stagedState.shipEvidence = binding;
391
+ const isNewPending = stagedState.pending && (!saved?.pending || stagedState.pending.token !== saved.pending.token);
392
+ if (stagedState.pending?.gates.includes('gate:ship') && isNewPending) {
393
+ stagedState.pending.shipEvidenceRevision = nextRevision; stagedState.pending.shipEvidenceContextDigest = binding.contextDigest;
394
+ }
395
+ assertLease(); assertSource(); assertProtectedEffect();
396
+ const currentDescriptor = readShipPolicy({root, path: descriptor.policyPath});
397
+ if (currentDescriptor.policyDigest !== descriptor.policyDigest || currentDescriptor.registrationDigest !== descriptor.registrationDigest) fail('SHIP_EVIDENCE_UNKNOWN');
398
+ if (revision !== 0) {
399
+ const final = read(runId);
400
+ if (readBinding(final).revision !== revision || final.shipEvidence.generationDigest !== priorDigest) fail('SHIP_STALE_REVISION');
401
+ } else if (existsSync(join(runStore, `${runId}.json`))) fail('SHIP_STALE_REVISION');
402
+ const file = join(runStore, `${runId}.json`), temp = `${file}.${randomUUID()}.tmp`;
403
+ try { writeFileSync(temp, JSON.stringify(stagedState, null, 2), {flag: 'wx', mode: 0o600}); assertLease(); assertSource(); renameSync(temp, file); }
404
+ catch (error) { try { if (existsSync(temp)) unlinkSync(temp); } catch {} throw error; }
405
+ revision = nextRevision; priorDigest = digest; gen = next; state.shipEvidence = clone(binding);
406
+ if (state.pending && stagedState.pending) state.pending = clone(stagedState.pending);
407
+ try { writeFileSync(join(runDirectory(store, runId), 'current.json'), bytesOf({version: 'ship-store-pointer/v1', generation: revision, digest}), {mode: 0o600}); }
408
+ catch { poisoned = true; fail('SHIP_COMMITTED_OBSERVATION_UNKNOWN', {persistedRevision: revision}); }
409
+ return {revision, contextDigest: binding.contextDigest, generationDigest: digest, committed: true};
410
+ },
411
+ reassess(state, {expectedRevision}) {
412
+ load(state);
413
+ if (expectedRevision !== revision) fail('SHIP_STALE_REVISION');
414
+ if (state.active || state.wave || ['done', 'cancelled'].includes(state.status) || state.release && ['publishing', 'published', 'verified'].includes(state.release.status)) fail('SHIP_REASSESS_REQUIRED');
415
+ const freshDescriptor = readShipPolicy({root, path: descriptor.policyPath});
416
+ const requirements = requirementsFor(freshDescriptor, state);
417
+ for (const r of requirements) if (!state.graph[r.role] || (state.attempts || []).filter(a => a.role === r.role).length >= state.maxAttempts || r.requiredChecks.includes('configured-checks') && !state.checkPolicy) fail('SHIP_REQUIREMENTS_UNKNOWN');
418
+ const captured = captureShipContext({root, registration: descriptor.registration, taskId, runId, gateId: 'gate:ship', policy: descriptor.policy, requirements});
419
+ if (captured.state !== 'COMPLETE') fail('SHIP_CONTEXT_DRIFT');
420
+ const staged = clone(state), roles = new Set(requirements.map(r => r.role));
421
+ staged.invalidations ??= []; staged.invalidations.push({kind: 'ship-reassessment', results: Object.fromEntries(Object.entries(staged.results).filter(([r]) => roles.has(r))), approvals: staged.approvals.filter(a => roles.has(a.role)), at: new Date(now(clock)).toISOString()});
422
+ for (const role of roles) delete staged.results[role];
423
+ staged.approvals = staged.approvals.filter(a => !roles.has(a.role)); staged.released = staged.released.filter(r => !roles.has(r));
424
+ const orderedRoles = Object.keys(state.graph).filter(r => roles.has(r));
425
+ const first = orderedRoles[0], joined = state.graph[first]?.join || [];
426
+ staged.queue = [...new Set([first, ...joined.filter(r => roles.has(r))])]; staged.pending = null; staged.active = null; staged.status = 'ready'; delete staged.reason;
427
+ gen = {...gen, expected: captured.expected, phase: 'REVIEWING', assignments: gen.assignments.map(a => ({...a, revoked: true}))};
428
+ // New generations retain negative history; prior positive assignments cannot satisfy.
429
+ for (const cap of activeCapabilities) caps.get(cap).assignment.revoked = true;
430
+ activeCapabilities.clear();
431
+ const saved = boundary.save(staged); return {state: staged, revision: saved.revision, contextDigest: saved.contextDigest};
432
+ },
433
+ invalidate(state, {reason}) {
434
+ load(state); if (!id(reason)) fail('SHIP_CAPABILITY_INVALID');
435
+ gen.phase = 'INVALIDATED'; gen.expected = null; gen.assignments = gen.assignments.map(a => ({...a, revoked: true}));
436
+ for (const cap of activeCapabilities) caps.get(cap).assignment.revoked = true;
437
+ activeCapabilities.clear(); state.shipEvidence.phase = 'INVALIDATED'; state.shipEvidence.contextDigest = null;
438
+ },
439
+ };
440
+ brands.set(boundary, {root, runId, taskId});
441
+ // Existing bindings are linked lazily on their first state-bearing method.
442
+ const originalLoadBrand = state => { load(state); brands.set(boundary, {root, runId, taskId}); };
443
+ if (seed) brands.set(boundary, {root, runId, taskId});
444
+ else {
445
+ const assert = boundary.assertCurrent;
446
+ boundary.assertCurrent = (state, request) => { originalLoadBrand(state); return assert(state, request); };
447
+ }
448
+ return boundary;
449
+ }
450
+ export function createShipControllerBoundary(input, options = {}) { return makeBoundary(input, options); }
451
+ export function initializeShipBinding({state, policyPath, lease}, options = {}) {
452
+ if (state.shipEvidence || !uuid(state.id) || !uuid(state.taskId) || state.version !== 1) fail('SHIP_UNBOUND');
453
+ const descriptor = readShipPolicy({root: state.root, path: policyPath}), clock = options.clock || Date.now;
454
+ const generation = {version: 'ship-generation/v1', runId: state.id, taskId: state.taskId, revision: 1, phase: 'PROVISIONAL', policyDigest: descriptor.policyDigest, registrationDigest: descriptor.registrationDigest, expected: null, assignments: [], records: [], originRegistry: {version: 'ship-origin-registry/v1', journals: []}, obligations: [], resolutions: [], readStatus: {version: 'ship-read-status/v1', nowMs: now(clock), context: 'UNKNOWN', assignments: 'UNKNOWN', journals: 'UNKNOWN', tasks: 'UNKNOWN', policy: 'UNKNOWN', origins: 'UNKNOWN', trustedOwnerDecisions: []}, taskSnapshotDigest: null, journalSnapshotDigest: null, completionDigests: [], previousGenerationDigest: null};
455
+ const staged = clone(state), boundary = makeBoundary({root: state.root, store: descriptor.store, lease}, options, {state: staged, descriptor, generation});
456
+ boundary.save(staged); return {state: staged, boundary};
457
+ }
458
+ export function reassessShipBinding({state, expectedRevision, lease}, options = {}) {
459
+ const b = readBinding(state), descriptor = readShipPolicy({root: state.root, path: b.policyPath});
460
+ const boundary = createShipControllerBoundary({root: state.root, store: descriptor.store, lease}, options);
461
+ const result = boundary.reassess(state, {expectedRevision});
462
+ return {state: result.state, boundary};
463
+ }
464
+ function main() {
465
+ const args = process.argv.slice(2), values = {}, allowed = new Set(['--dir', '--store', '--run', '--revision']); let diagnostic = false;
466
+ try {
467
+ for (let i = 0; i < args.length; i++) {
468
+ const key = args[i]; if (key === '--json') continue;
469
+ if (key === '--as') { diagnostic = true; i++; continue; }
470
+ if (!allowed.has(key) || Object.hasOwn(values, key) || !args[i + 1] || args[i + 1].startsWith('--')) fail('SHIP_CAPABILITY_INVALID');
471
+ values[key] = args[++i];
472
+ }
473
+ if (!/^\d+$/.test(values['--revision'] || '')) fail('SHIP_INVALID_REVISION');
474
+ const report = evaluateBoundGate({root: values['--dir'], store: values['--store'], runId: values['--run'], expectedRevision: Number(values['--revision'])}, {runStore: codexRunStore()});
475
+ console.log(JSON.stringify(report)); process.exitCode = report.state === 'PASS' && !diagnostic ? 0 : 2;
476
+ } catch { console.log(JSON.stringify(unknownReport())); process.exitCode = 2; }
477
+ }
478
+ if (isMainModule(import.meta.url)) main();