agentic-workflow-manager 9.12.2 → 9.12.4

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.
@@ -19,6 +19,10 @@ const versioning_1 = require("../versioning");
19
19
  const check_1 = require("../currentness/check");
20
20
  const run_1 = require("../../commands/sensors/run");
21
21
  const index_1 = require("./index");
22
+ const store_1 = require("../journal/store");
23
+ const paths_1 = require("../journal/paths");
24
+ const generations_1 = require("../../commands/watch/generations");
25
+ const process_1 = require("../journal/process");
22
26
  const FRAMEWORK_CONTRACTS = ['using-awm', 'writing-plans', 'development-process', 'subagent-driven-development', 'executing-plans', 'post-implementation-qa', 'post-implementation-docs', 'harness-retro', 'finishing-a-development-branch'];
23
27
  const MAX_CONTRACT_BYTES = 1024 * 1024;
24
28
  const MAX_MANIFEST_BYTES = 256 * 1024;
@@ -217,6 +221,56 @@ function registryCompatibility(registries, consumed) {
217
221
  }
218
222
  return diagnostics;
219
223
  }
224
+ /** A controller may ask for admission while its supervisor is writing journal
225
+ * events. The already-launched generation passed sensors before launch; its
226
+ * durable intent plus verified live process identity is the evidence, not a
227
+ * second tree snapshot. A claim alone never certifies process ownership. */
228
+ function hasOwnedGenerationAdmission(input) {
229
+ if (!input.verifySensors || input.freshSensorsRequired || input.executionMode !== 'desatendido' || input.plan.state !== 'valid'
230
+ || input.journalCorrupt || !input.journalState || input.journalState.schema !== 2
231
+ || input.journalState.cycle.status !== 'IN_PROGRESS'
232
+ || input.journalState.admissionContext?.provider !== input.provider
233
+ || input.journalState.admissionContext.controllerAutonomy !== input.controllerAutonomy)
234
+ return false;
235
+ const observed = input.journalState;
236
+ const binding = observed.planBinding;
237
+ if (!binding)
238
+ return false;
239
+ if (input.plan.schema === 'compact-slices/v2' && !observed.admissionContext?.runtime)
240
+ return false;
241
+ let expectedPath;
242
+ try {
243
+ expectedPath = (0, paths_1.bindingPlanPath)(input.planPath ?? '');
244
+ }
245
+ catch {
246
+ return false;
247
+ }
248
+ if (binding.path !== expectedPath || binding.digest !== input.plan.planDigest || binding.schema !== input.plan.schema
249
+ || (binding.executionDigest !== undefined && binding.executionDigest !== input.plan.executionDigest))
250
+ return false;
251
+ let fresh;
252
+ try {
253
+ fresh = (0, store_1.readJournal)(input.cwd, observed.branch);
254
+ }
255
+ catch {
256
+ return false;
257
+ }
258
+ if (fresh.corrupt || !fresh.state || fresh.state.cycle.status !== 'IN_PROGRESS'
259
+ || fresh.state.revision < observed.revision || JSON.stringify(fresh.state.planBinding) !== JSON.stringify(binding)
260
+ || JSON.stringify(fresh.state.admissionContext) !== JSON.stringify(observed.admissionContext))
261
+ return false;
262
+ const generation = (0, generations_1.activeGeneration)(fresh.state);
263
+ if (!generation || generation !== fresh.state.generations.at(-1)
264
+ || generation.provider !== input.provider || !generation.controllerJobId || !generation.launchArgvDigest)
265
+ return false;
266
+ const prior = observed.generations.find(item => item.token === generation.token);
267
+ if (!prior || prior.controllerJobId !== generation.controllerJobId || prior.launchArgvDigest !== generation.launchArgvDigest)
268
+ return false;
269
+ return (generation.processRef !== undefined && generation.processRef.spawnNonce === generation.spawnNonce
270
+ && generation.processRef.argvDigest === generation.launchArgvDigest && (0, process_1.refIsAlive)(generation.processRef))
271
+ || (generation.wrapperRef !== undefined && generation.wrapperRef.spawnNonce === generation.spawnNonce
272
+ && (0, process_1.refIsAlive)(generation.wrapperRef));
273
+ }
220
274
  /** One admission authority for the public command AND actual watch dispatch.
221
275
  * No empirical execution occurs before provenance/currentness/CLI-floor gates. */
222
276
  async function admitRegistryPlan(input, dependencies = {}) {
@@ -253,12 +307,14 @@ async function admitRegistryPlan(input, dependencies = {}) {
253
307
  }
254
308
  return scope.provenance === 'proven';
255
309
  };
310
+ let generationBound = false;
256
311
  const compose = async (fields) => {
257
312
  const report = await admission({ ...input, provenance: scope.provenance, consumedRegistryComponents: scope.consumedRegistryComponents, ...fields });
313
+ const withEvidence = generationBound && report.sensors === 'pass' ? { ...report, sensorEvidence: 'generation-bound' } : report;
258
314
  if (scope.provenanceDiagnostic && report.diagnostics.some(diagnostic => diagnostic.code === 'ADMISSION_CURRENTNESS_PROVENANCE_REQUIRED')) {
259
- return { ...report, diagnostics: report.diagnostics.map(diagnostic => diagnostic.code === 'ADMISSION_CURRENTNESS_PROVENANCE_REQUIRED' ? scope.provenanceDiagnostic : diagnostic) };
315
+ return { ...withEvidence, diagnostics: report.diagnostics.map(diagnostic => diagnostic.code === 'ADMISSION_CURRENTNESS_PROVENANCE_REQUIRED' ? scope.provenanceDiagnostic : diagnostic) };
260
316
  }
261
- return report;
317
+ return withEvidence;
262
318
  };
263
319
  let currentness = input.currentness;
264
320
  let compatibilityDiagnostics;
@@ -277,7 +333,10 @@ async function admitRegistryPlan(input, dependencies = {}) {
277
333
  if (compatibilityGate.currentness !== 'current')
278
334
  return compatibilityGate;
279
335
  }
280
- const sensors = input.verifySensors ? input.sensors ?? await (dependencies.runSensors ?? run_1.runSensors)({ cwd: input.cwd, all: true, readOnly: true }) : undefined;
336
+ generationBound = input.sensors === undefined && hasOwnedGenerationAdmission(input);
337
+ const sensors = input.verifySensors ? input.sensors ?? (generationBound
338
+ ? { sensors: [], overall: 'pass', reason: 'generation-bound-admission' }
339
+ : await (dependencies.runSensors ?? run_1.runSensors)({ cwd: input.cwd, all: true, readOnly: true })) : undefined;
281
340
  if (input.requireCurrent && input.verifySensors) {
282
341
  if (!refresh())
283
342
  return compose({ currentness, sensors });
@@ -286,5 +345,17 @@ async function admitRegistryPlan(input, dependencies = {}) {
286
345
  return currentnessGate;
287
346
  compatibilityDiagnostics = registryCompatibility(registries, scope.consumedRegistryComponents);
288
347
  }
348
+ if (generationBound && input.plan.schema === 'compact-slices/v2') {
349
+ const routing = input.routing ?? await dependencies.readRouting?.();
350
+ const expected = input.journalState?.admissionContext?.runtime;
351
+ if (!expected || !routing?.runtime || routing.runtime.target !== input.provider
352
+ || routing.runtime.kind !== expected.kind || routing.runtime.version !== expected.version
353
+ || routing.runtime.accountScopeDigest !== expected.accountScopeDigest) {
354
+ return compose({ currentness, compatibilityDiagnostics, sensors,
355
+ routing: { ...routing, eventIssue: { code: 'ADMISSION_GENERATION_RUNTIME_MISMATCH',
356
+ message: 'Live runtime identity differs from the admitted controller generation; stop and recover with a fresh admission.' } } });
357
+ }
358
+ return compose({ currentness, compatibilityDiagnostics, sensors, routing });
359
+ }
289
360
  return compose({ currentness, compatibilityDiagnostics, sensors, ...(dependencies.readRouting ? { routingReader: dependencies.readRouting } : {}) });
290
361
  }
@@ -85,9 +85,15 @@ function isWellFormedEnvelope(x) {
85
85
  }
86
86
  function listPendingRequests(repoRoot, branch) {
87
87
  const dir = (0, paths_1.requestsDir)(repoRoot, branch);
88
+ const directory = fs_1.default.lstatSync(dir);
89
+ if (directory.isSymbolicLink() || !directory.isDirectory())
90
+ throw new Error('request directory is not a controlled directory');
88
91
  return fs_1.default.readdirSync(dir).filter((f) => f.endsWith('.json')).sort().map((f) => {
89
92
  const file = path_1.default.join(dir, f);
90
93
  try {
94
+ const stat = fs_1.default.lstatSync(file);
95
+ if (stat.isSymbolicLink() || !stat.isFile())
96
+ return { requestId: f, envelope: null, file, corrupt: true };
91
97
  const parsed = JSON.parse(fs_1.default.readFileSync(file, 'utf8'));
92
98
  // Shape completa antes de cualquier uso downstream. JSON valido no
93
99
  // implica envelope valido: payload/generation/idempotency ausentes
@@ -99,6 +99,8 @@ function isWellFormedState(x) {
99
99
  return false;
100
100
  if (!Array.isArray(x.generations) || !Array.isArray(x.tasks))
101
101
  return false;
102
+ if (!x.tasks.every(isWellFormedTask))
103
+ return false;
102
104
  if (!Array.isArray(x.cycleVerificationPlan) || !Array.isArray(x.verdicts) || !Array.isArray(x.fixes))
103
105
  return false;
104
106
  if (!Array.isArray(x.requiredVerifiers) || !x.requiredVerifiers.every((kind) => VERIFICATION_KINDS.includes(kind))
@@ -108,8 +110,40 @@ function isWellFormedState(x) {
108
110
  return false;
109
111
  if (!isObj(x.appliedRequests) || !Object.values(x.appliedRequests).every(isWellFormedAppliedRequest))
110
112
  return false;
113
+ if (!Object.entries(x.appliedRequests).every(([requestId, ack]) => requestId === ack.requestId))
114
+ return false;
111
115
  if (x.routingAttempts !== undefined && (!Array.isArray(x.routingAttempts) || x.routingAttempts.length > 4096 || !x.routingAttempts.every(isWellFormedRoutingAttempt)))
112
116
  return false;
117
+ // A linked v2 dispatch is evidence of a native handoff. Reject a broken
118
+ // link or a second dispatch for one reservation on journal read as well as
119
+ // at request-application time. Historical unlinked records remain readable
120
+ // but cannot masquerade as this new acknowledged handoff.
121
+ if (x.planBinding?.schema === 'compact-slices/v2') {
122
+ const binding = x.planBinding;
123
+ const attempts = (x.routingAttempts ?? []);
124
+ const dispatches = x.dispatches;
125
+ const linked = new Set();
126
+ for (const dispatch of dispatches) {
127
+ if (!dispatch.routingAttemptId)
128
+ continue;
129
+ if (linked.has(dispatch.routingAttemptId))
130
+ return false;
131
+ linked.add(dispatch.routingAttemptId);
132
+ const attempt = attempts.find(candidate => candidate.id === dispatch.routingAttemptId);
133
+ if (!attempt || !(x.tasks.some(task => task.id === dispatch.taskId))
134
+ || attempt.envelope.role !== 'implementer' || attempt.envelope.sliceId !== dispatch.taskId
135
+ || attempt.envelope.planDigest !== binding.digest || attempt.envelope.executionDigest !== binding.executionDigest
136
+ || !attempt.reservationRequestId || !dispatch.dispatchRequestId)
137
+ return false;
138
+ const acknowledgements = x.appliedRequests;
139
+ const reservationAck = acknowledgements[attempt.reservationRequestId];
140
+ const dispatchAck = acknowledgements[dispatch.dispatchRequestId];
141
+ if (reservationAck?.outcome !== 'applied' || reservationAck.requestId !== attempt.reservationRequestId
142
+ || reservationAck.resultRef !== attempt.id || dispatchAck?.outcome !== 'applied'
143
+ || dispatchAck.requestId !== dispatch.dispatchRequestId || dispatchAck.resultRef !== dispatch.id)
144
+ return false;
145
+ }
146
+ }
113
147
  if (x.routingIncidents !== undefined && (!Array.isArray(x.routingIncidents) || x.routingIncidents.length > 4096 || !x.routingIncidents.every(isWellFormedRoutingIncident)))
114
148
  return false;
115
149
  if (x.implementationLineages !== undefined && (!Array.isArray(x.implementationLineages) || x.implementationLineages.length > 4096 || !x.implementationLineages.every(isWellFormedImplementationLineage)))
@@ -183,7 +217,10 @@ function isWellFormedNextAction(x) {
183
217
  && (x.state === 'pending' || x.state === 'in-progress');
184
218
  }
185
219
  function isWellFormedDispatch(x) {
186
- return isObj(x) && typeof x.id === 'string' && typeof x.taskId === 'string' && typeof x.at === 'string';
220
+ return isObj(x) && Object.keys(x).every(key => ['id', 'taskId', 'at', 'routingAttemptId', 'dispatchRequestId'].includes(key))
221
+ && typeof x.id === 'string' && x.id.length > 0 && typeof x.taskId === 'string' && x.taskId.length > 0
222
+ && typeof x.at === 'string' && (x.routingAttemptId === undefined || (typeof x.routingAttemptId === 'string' && x.routingAttemptId.length > 0))
223
+ && (x.dispatchRequestId === undefined || (typeof x.dispatchRequestId === 'string' && x.dispatchRequestId.length > 0));
187
224
  }
188
225
  function strings(x) {
189
226
  return Array.isArray(x) && x.every((item) => typeof item === 'string');
@@ -235,11 +272,11 @@ function isWellFormedAppliedRequest(x) {
235
272
  && (x.resultRef === undefined || typeof x.resultRef === 'string');
236
273
  }
237
274
  function isWellFormedRoutingAttempt(x) {
238
- return isObj(x) && Object.keys(x).every(key => ['schema', 'id', 'obligationId', 'lineageId', 'attempt', 'envelope', 'envelopeDigest', 'fingerprint', 'state', 'reservedAt', 'nativeEvidenceRequired', 'nativeEventDigest', 'nativeAgentId', 'observed', 'observedBackendModel', 'verdict', 'reasonCode', 'administrativeRepair'].includes(key))
275
+ return isObj(x) && Object.keys(x).every(key => ['schema', 'id', 'obligationId', 'lineageId', 'attempt', 'envelope', 'envelopeDigest', 'fingerprint', 'state', 'reservedAt', 'reservationRequestId', 'nativeEvidenceRequired', 'nativeEventDigest', 'nativeAgentId', 'observed', 'observedBackendModel', 'verdict', 'reasonCode', 'administrativeRepair'].includes(key))
239
276
  && x.schema === 'routing-attempt/v1'
240
277
  && typeof x.id === 'string' && typeof x.obligationId === 'string' && typeof x.lineageId === 'string' && Number.isSafeInteger(x.attempt) && x.attempt > 0
241
278
  && typeof x.envelopeDigest === 'string' && /^[a-f0-9]{64}$/.test(x.envelopeDigest) && typeof x.fingerprint === 'string' && /^[a-f0-9]{64}$/.test(x.fingerprint)
242
- && ['reserved', 'active', 'complete', 'blocked', 'unknown'].includes(String(x.state)) && (x.reservedAt === undefined || (typeof x.reservedAt === 'string' && Number.isFinite(Date.parse(x.reservedAt)))) && (x.nativeEvidenceRequired === undefined || x.nativeEvidenceRequired === true) && (x.nativeEventDigest === undefined || isDigest(x.nativeEventDigest)) && (x.nativeAgentId === undefined || typeof x.nativeAgentId === 'string') && (x.observed === undefined || isRoutingSelection(x.observed)) && (x.observedBackendModel === undefined || (typeof x.observedBackendModel === 'string' && x.observedBackendModel.length > 0 && x.observedBackendModel.length <= 128)) && (x.verdict === undefined || ['pass', 'fail', 'inconclusive'].includes(String(x.verdict))) && (x.reasonCode === undefined || typeof x.reasonCode === 'string') && (x.administrativeRepair === undefined || x.administrativeRepair === true) && isRoutingEnvelope(x.envelope);
279
+ && ['reserved', 'active', 'complete', 'blocked', 'unknown'].includes(String(x.state)) && (x.reservedAt === undefined || (typeof x.reservedAt === 'string' && Number.isFinite(Date.parse(x.reservedAt)))) && (x.reservationRequestId === undefined || (typeof x.reservationRequestId === 'string' && x.reservationRequestId.length > 0 && x.reservationRequestId.length <= 128)) && (x.nativeEvidenceRequired === undefined || x.nativeEvidenceRequired === true) && (x.nativeEventDigest === undefined || isDigest(x.nativeEventDigest)) && (x.nativeAgentId === undefined || typeof x.nativeAgentId === 'string') && (x.observed === undefined || isRoutingSelection(x.observed)) && (x.observedBackendModel === undefined || (typeof x.observedBackendModel === 'string' && x.observedBackendModel.length > 0 && x.observedBackendModel.length <= 128)) && (x.verdict === undefined || ['pass', 'fail', 'inconclusive'].includes(String(x.verdict))) && (x.reasonCode === undefined || typeof x.reasonCode === 'string') && (x.administrativeRepair === undefined || x.administrativeRepair === true) && isRoutingEnvelope(x.envelope);
243
280
  }
244
281
  function isWellFormedRoutingIncident(x) {
245
282
  if (!isObj(x) || Object.keys(x).length !== 12 || !Object.keys(x).every(key => ['schema', 'id', 'runStartedAt', 'target', 'selection', 'reasonCode', 'firstAt', 'lastAt', 'affectedObligations', 'fallbackCount', 'blockedCount', 'alertState'].includes(key)))
@@ -51,6 +51,7 @@ const supervisor_1 = require("../../../src/commands/watch/supervisor");
51
51
  const admission_recovery_1 = require("../../../src/commands/watch/admission-recovery");
52
52
  const watch_1 = require("../../../src/commands/watch");
53
53
  const supervisorModule = __importStar(require("../../../src/commands/watch/supervisor"));
54
+ const routing_approval_1 = require("../../helpers/routing-approval");
54
55
  const DIGEST = 'a'.repeat(64);
55
56
  const identity = { kind: 'native', version: '1.0.0', accountScopeDigest: DIGEST };
56
57
  const input = { provider: 'codex', routingIdentity: identity, controllerAutonomy: 'approval-free',
@@ -157,6 +158,29 @@ describe('issue #196: admission custody recovery', () => {
157
158
  expect((0, store_1.readJournal)(repo, 'main').state.cycle.status).toBe('BLOCKED');
158
159
  expect(await (0, admission_recovery_1.recoverAdmissionCustody)(repo, 'main', input, admitted)).toBe('recovered');
159
160
  });
161
+ test.each([
162
+ 'runtime local no verificable: NATIVE_QUERY_FAILED',
163
+ 'runtime local cambió durante generacion admitida',
164
+ ])('runtime custody %s recovers only after strict offline revalidation', async (reason) => {
165
+ fs_1.default.writeFileSync(path_1.default.join(repo, 'plans', 'cycle.md'), fs_1.default.readFileSync(path_1.default.join(__dirname, '../../core/plan/fixtures/compact-slices-v2/valid.md'), 'utf8'));
166
+ const v2Plan = (0, validate_1.validatePlanFile)('plans/cycle.md', repo);
167
+ if (v2Plan.state !== 'valid' || !v2Plan.executionDigest)
168
+ throw new Error('invalid v2 recovery fixture');
169
+ const state = (0, store_1.readJournal)(repo, 'main').state;
170
+ state.planBinding = { ...state.planBinding, schema: v2Plan.schema, digest: v2Plan.planDigest,
171
+ executionDigest: v2Plan.executionDigest, executionIdentitySchema: 'awm-plan-execution/v1' };
172
+ state.cycle.blockedReason = reason;
173
+ (0, store_1.writeJournal)(repo, 'main', state);
174
+ const revision = (0, store_1.readJournal)(repo, 'main').state.revision;
175
+ await expect((0, admission_recovery_1.recoverAdmissionCustody)(repo, 'main', input, blocked('ADMISSION_SENSORS_BLOCKED', 'current', 'not-certified'))).rejects.toThrow();
176
+ expect((0, store_1.readJournal)(repo, 'main').state.revision).toBe(revision);
177
+ expect(await (0, admission_recovery_1.recoverAdmissionCustody)(repo, 'main', input, admitted)).toBe('recovered');
178
+ const recovered = (0, store_1.readJournal)(repo, 'main').state;
179
+ expect(recovered.cycle.status).toBe('IN_PROGRESS');
180
+ expect(recovered.planBinding?.schema).toBe('compact-slices/v2');
181
+ expect(recovered.admissionRecoveries?.at(-1)?.blockedReason).toBe(reason);
182
+ expect(recovered.generations).toEqual(state.generations);
183
+ });
160
184
  test('changed binding, active request problem, pending job and identity mismatch all fail closed', async () => {
161
185
  const check = async (mutate, pattern) => {
162
186
  const state = (0, store_1.readJournal)(repo, 'main').state;
@@ -181,6 +205,26 @@ describe('issue #196: admission custody recovery', () => {
181
205
  const sJob = (0, store_1.readJournal)(repo, 'main').state;
182
206
  sJob.jobs = {};
183
207
  (0, store_1.writeJournal)(repo, 'main', sJob);
208
+ await check(state => {
209
+ state.routingAttempts = [{ schema: 'routing-attempt/v1', id: 'route-1',
210
+ obligationId: 'S1-implementer', lineageId: 'S1', attempt: 1, envelope: routing_approval_1.routingEnvelope,
211
+ envelopeDigest: 'e'.repeat(64), fingerprint: 'f'.repeat(64), state: 'active',
212
+ nativeAgentId: 'native-child' }];
213
+ }, /nativ|routing|attempt|intento/i);
214
+ const sRouting = (0, store_1.readJournal)(repo, 'main').state;
215
+ sRouting.routingAttempts = [];
216
+ (0, store_1.writeJournal)(repo, 'main', sRouting);
217
+ await check(state => {
218
+ state.routingAttempts = [{ schema: 'routing-attempt/v1', id: 'route-1',
219
+ obligationId: 'S1-implementer', lineageId: 'S1', attempt: 1, envelope: routing_approval_1.routingEnvelope,
220
+ envelopeDigest: 'e'.repeat(64), fingerprint: 'f'.repeat(64), state: 'reserved' }];
221
+ state.dispatches.push({ id: 'linked', taskId: 'S1', at: new Date().toISOString(),
222
+ routingAttemptId: 'route-1', dispatchRequestId: 'linked-ack' });
223
+ }, /nativ|routing|attempt|intento|dispatch/i);
224
+ const sPendingChild = (0, store_1.readJournal)(repo, 'main').state;
225
+ sPendingChild.routingAttempts = [];
226
+ sPendingChild.dispatches = [];
227
+ (0, store_1.writeJournal)(repo, 'main', sPendingChild);
184
228
  await check(state => { state.generations[0].processRef = (0, process_1.captureSelfRef)('recovery-test'); }, /controller/i);
185
229
  const sController = (0, store_1.readJournal)(repo, 'main').state;
186
230
  sController.generations[0].processRef = undefined;
@@ -1,4 +1,37 @@
1
1
  "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
2
35
  var __importDefault = (this && this.__importDefault) || function (mod) {
3
36
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
37
  };
@@ -19,6 +52,7 @@ const supervisor_1 = require("../../../src/commands/watch/supervisor");
19
52
  const exec_wrapper_1 = require("../../../src/commands/job/exec-wrapper");
20
53
  const lock_1 = require("../../../src/commands/watch/lock");
21
54
  const archive_unused_1 = require("../../../src/commands/watch/archive-unused");
55
+ const adapterModule = __importStar(require("../../../src/core/journal/adapter"));
22
56
  jest.setTimeout(30000);
23
57
  function valid(repo) {
24
58
  const report = (0, validate_1.validatePlanFile)('docs/plan.md', repo);
@@ -43,7 +77,7 @@ describe('compact lifecycle proof and unused bootstrap archival', () => {
43
77
  fs_1.default.writeFileSync(path_1.default.join(repo, 'docs/plan.md'), `${fs_1.default.readFileSync(path_1.default.join(__dirname, '../../core/plan/fixtures/compact-slices-v1/valid.md'), 'utf8')}\n- [ ] Reviewed task.\n`);
44
78
  (0, init_1.initWatch)(repo, 'main', { path: 'docs/plan.md', report: valid(repo) });
45
79
  });
46
- afterEach(() => { fs_1.default.rmSync(repo, { recursive: true, force: true }); });
80
+ afterEach(() => { jest.restoreAllMocks(); fs_1.default.rmSync(repo, { recursive: true, force: true }); });
47
81
  test('durable journal retains hash commitments only, never the validated plan source body', () => {
48
82
  const contents = (directory) => fs_1.default.readdirSync(directory, { withFileTypes: true }).map(entry => entry.isDirectory() ? contents(path_1.default.join(directory, entry.name)) : fs_1.default.readFileSync(path_1.default.join(directory, entry.name), 'utf8')).join('\n');
49
83
  expect(contents((0, paths_1.journalDir)(repo, 'main'))).not.toContain('Reviewed task.');
@@ -138,6 +172,18 @@ describe('compact lifecycle proof and unused bootstrap archival', () => {
138
172
  fingerprint: (0, fingerprint_1.computeFingerprint)(repo, argv, ['source.md'], '.').fingerprint, argv, paths: ['source.md'], cwd: '.',
139
173
  } });
140
174
  }
175
+ // This fixture acts as the external controller. Give it an adopted
176
+ // identity before expecting the supervisor to dispatch verification
177
+ // jobs; launch intent alone is deliberately insufficient.
178
+ expect(await supervisor.tick()).toBe('continue');
179
+ const launched = (0, store_1.readJournal)(repo, 'main').state;
180
+ const gen = launched.generations.find(entry => entry.token === generation.token);
181
+ gen.processRef = { pid: process.pid, processGroup: process.pid, startTime: 'fixture',
182
+ spawnNonce: gen.spawnNonce, argvDigest: gen.launchArgvDigest, psArgsDigest: 'b'.repeat(64) };
183
+ (0, store_1.writeJournal)(repo, 'main', launched);
184
+ const adapter = adapterModule.adapterFor('codex');
185
+ jest.spyOn(adapterModule, 'adapterFor').mockReturnValue({ ...adapter,
186
+ activity: () => ({ cpuTime: '0', groupSize: 1 }), safeToReplace: () => 'indeterminate' });
141
187
  for (let i = 0; i < 100; i++) {
142
188
  expect(await supervisor.tick()).toBe('continue');
143
189
  if (Object.values((0, store_1.readJournal)(repo, 'main').state.jobs).some(job => job.verdict === 'pass'))