agentic-workflow-manager 9.7.1 → 9.9.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.
Files changed (107) hide show
  1. package/README.md +5 -1
  2. package/dist/src/commands/job/exec-wrapper.js +53 -3
  3. package/dist/src/commands/job/gate.js +18 -0
  4. package/dist/src/commands/job/index.js +61 -0
  5. package/dist/src/commands/model-policy/index.js +73 -0
  6. package/dist/src/commands/plan/index.js +182 -7
  7. package/dist/src/commands/sensors/bootstrap.js +36 -5
  8. package/dist/src/commands/sensors/compatibility/contract.js +19 -4
  9. package/dist/src/commands/sensors/compatibility/discovery.js +23 -3
  10. package/dist/src/commands/sensors/compatibility/live.js +18 -1
  11. package/dist/src/commands/sensors/compatibility/probe.js +8 -2
  12. package/dist/src/commands/sensors/compatibility/resolve.js +5 -2
  13. package/dist/src/commands/sensors/coverage/evidence.js +19 -10
  14. package/dist/src/commands/sensors/coverage/index.js +11 -2
  15. package/dist/src/commands/sensors/coverage/resolve.js +10 -0
  16. package/dist/src/commands/sensors/exec.js +16 -1
  17. package/dist/src/commands/sensors/migrate.js +62 -2
  18. package/dist/src/commands/sensors/prepare.js +2 -0
  19. package/dist/src/commands/sensors/result.js +2 -0
  20. package/dist/src/commands/sensors/run.js +64 -1
  21. package/dist/src/commands/sensors/status.js +6 -4
  22. package/dist/src/commands/watch/apply.js +32 -0
  23. package/dist/src/commands/watch/archive-unused.js +138 -0
  24. package/dist/src/commands/watch/generations.js +32 -9
  25. package/dist/src/commands/watch/index.js +108 -2
  26. package/dist/src/commands/watch/init.js +149 -18
  27. package/dist/src/commands/watch/supervisor.js +167 -13
  28. package/dist/src/commands/watch/teardown-driver.js +13 -13
  29. package/dist/src/commands/watch/track-state.js +59 -0
  30. package/dist/src/commands/watch/tracks.js +38 -102
  31. package/dist/src/core/admission/index.js +231 -0
  32. package/dist/src/core/admission/registry-contracts.js +290 -0
  33. package/dist/src/core/journal/fingerprint.js +35 -1
  34. package/dist/src/core/journal/paths.js +11 -0
  35. package/dist/src/core/journal/requests.js +1 -1
  36. package/dist/src/core/journal/store.js +146 -4
  37. package/dist/src/core/journal/types.js +57 -2
  38. package/dist/src/core/migration/historical-provenance.js +18 -0
  39. package/dist/src/core/migration/index.js +322 -0
  40. package/dist/src/core/model-policy/canonical.js +18 -0
  41. package/dist/src/core/model-policy/capabilities.js +197 -0
  42. package/dist/src/core/model-policy/capability-types.js +2 -0
  43. package/dist/src/core/model-policy/journal.js +124 -0
  44. package/dist/src/core/model-policy/paths.js +12 -0
  45. package/dist/src/core/model-policy/resolve.js +116 -0
  46. package/dist/src/core/model-policy/store.js +182 -0
  47. package/dist/src/core/model-policy/types.js +2 -0
  48. package/dist/src/core/model-policy/validate.js +112 -0
  49. package/dist/src/core/plan/identity.js +77 -0
  50. package/dist/src/core/plan/validate.js +191 -78
  51. package/dist/src/index.js +2 -0
  52. package/dist/tests/commands/job/exec-wrapper.test.js +16 -0
  53. package/dist/tests/commands/job/gate-reconcile.test.js +40 -10
  54. package/dist/tests/commands/job/gate.test.js +9 -0
  55. package/dist/tests/commands/job/routing-report-cli.test.js +46 -0
  56. package/dist/tests/commands/job/routing.test.js +11 -0
  57. package/dist/tests/commands/model-policy/index.test.js +57 -0
  58. package/dist/tests/commands/pin.test.js +23 -23
  59. package/dist/tests/commands/plan/index.test.js +682 -14
  60. package/dist/tests/commands/sensors/bootstrap.test.js +57 -4
  61. package/dist/tests/commands/sensors/compatibility/contract.test.js +21 -0
  62. package/dist/tests/commands/sensors/compatibility/discovery.test.js +19 -0
  63. package/dist/tests/commands/sensors/compatibility/probe.test.js +14 -1
  64. package/dist/tests/commands/sensors/compatibility/resolve.test.js +17 -0
  65. package/dist/tests/commands/sensors/coverage/evidence.test.js +51 -27
  66. package/dist/tests/commands/sensors/coverage/resolve.test.js +51 -0
  67. package/dist/tests/commands/sensors/exec.test.js +112 -0
  68. package/dist/tests/commands/sensors/run-changed.test.js +11 -0
  69. package/dist/tests/commands/sensors/run.test.js +15 -2
  70. package/dist/tests/commands/sensors/status.test.js +3 -3
  71. package/dist/tests/commands/watch/apply.test.js +12 -0
  72. package/dist/tests/commands/watch/e2e-crash.test.js +126 -11
  73. package/dist/tests/commands/watch/generations.test.js +113 -4
  74. package/dist/tests/commands/watch/plan-lifecycle.test.js +263 -0
  75. package/dist/tests/commands/watch/supervisor-loop.test.js +247 -49
  76. package/dist/tests/commands/watch/track-finalize.test.js +20 -3
  77. package/dist/tests/commands/watch/track-freeze.test.js +43 -30
  78. package/dist/tests/commands/watch/track-state-dependency.test.js +15 -0
  79. package/dist/tests/commands/watch/watch-init.test.js +244 -0
  80. package/dist/tests/core/admission/index.test.js +140 -0
  81. package/dist/tests/core/journal/fingerprint.test.js +22 -0
  82. package/dist/tests/core/journal/store.test.js +53 -0
  83. package/dist/tests/core/journal/types.test.js +7 -0
  84. package/dist/tests/core/migration/facts.test.js +289 -0
  85. package/dist/tests/core/migration/historical-provenance.test.js +25 -0
  86. package/dist/tests/core/model-policy/capabilities.test.js +365 -0
  87. package/dist/tests/core/model-policy/journal.test.js +46 -0
  88. package/dist/tests/core/model-policy/policy.test.js +47 -0
  89. package/dist/tests/core/model-policy/resolve.test.js +111 -0
  90. package/dist/tests/core/model-policy/store.test.js +177 -0
  91. package/dist/tests/core/plan/validate.test.js +360 -8
  92. package/dist/tests/core/secure-fs/native-bridge.test.js +81 -4
  93. package/dist/tests/integration/pack-e2e.test.js +4 -1
  94. package/dist/tests/integration/preflight-json-pipe.e2e.test.js +7 -3
  95. package/dist/tests/integration/sensor-bootstrap.e2e.test.js +37 -0
  96. package/dist/tests/integration/sensor-structured-run.e2e.test.js +12 -10
  97. package/dist/tests/providers/injection-config.test.js +14 -6
  98. package/dist/tests/structural/directory-fsync-is-single-source.test.js +97 -0
  99. package/dist/tests/structural/jest-environment-is-isolated.test.js +29 -24
  100. package/dist/tests/structural/native-release-artifacts.test.js +30 -0
  101. package/package.json +4 -3
  102. package/prebuilds/darwin-arm64/secure_fs.node +0 -0
  103. package/prebuilds/darwin-x64/secure_fs.node +0 -0
  104. package/prebuilds/linux-arm64/secure_fs.node +0 -0
  105. package/prebuilds/linux-x64/secure_fs.node +0 -0
  106. package/prebuilds/win32-arm64/secure_fs.node +0 -0
  107. package/prebuilds/win32-x64/secure_fs.node +0 -0
@@ -3,11 +3,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.withRef = exports.refOf = exports.persist = exports.applyProtocolToState = void 0;
6
7
  exports.canCompleteCohort = canCompleteCohort;
7
- exports.applyProtocolToState = applyProtocolToState;
8
- exports.persist = persist;
9
- exports.refOf = refOf;
10
- exports.withRef = withRef;
11
8
  exports.reconcileTracks = reconcileTracks;
12
9
  exports.reconcileOpenJoin = reconcileOpenJoin;
13
10
  exports.observeSupervisorFromDisk = observeSupervisorFromDisk;
@@ -34,6 +31,12 @@ const ownership_1 = require("../../core/tracks/ownership");
34
31
  const descriptor_1 = require("../../core/tracks/descriptor");
35
32
  const join_1 = require("../../core/tracks/join");
36
33
  const teardown_driver_1 = require("./teardown-driver");
34
+ const track_state_1 = require("./track-state");
35
+ var track_state_2 = require("./track-state");
36
+ Object.defineProperty(exports, "applyProtocolToState", { enumerable: true, get: function () { return track_state_2.applyProtocolToState; } });
37
+ Object.defineProperty(exports, "persist", { enumerable: true, get: function () { return track_state_2.persist; } });
38
+ Object.defineProperty(exports, "refOf", { enumerable: true, get: function () { return track_state_2.refOf; } });
39
+ Object.defineProperty(exports, "withRef", { enumerable: true, get: function () { return track_state_2.withRef; } });
37
40
  const request_1 = require("../job/request");
38
41
  const gate_1 = require("../job/gate");
39
42
  const process_2 = require("../../core/journal/process");
@@ -119,73 +122,6 @@ function toProtocol(state, maxParallel) {
119
122
  fallbackReason: state.cohortFallbackReason,
120
123
  };
121
124
  }
122
- /** Vuelca las decisiones de `protocol.ts` de regreso al `TrackRef[]` real —
123
- * nunca al revés: esta función jamás decide, solo transcribe. Exportada
124
- * (junto con `persist`/`refOf`/`withRef`/`EffectRunResult` más abajo) para
125
- * que `./teardown-driver` — que ejecuta el mismo patrón "gather -> decide ->
126
- * tocar el mundo real como mucho una vez -> persistir -> stop" que todo
127
- * `run*` de este archivo — reutilice estos helpers sin duplicarlos. */
128
- function applyProtocolToState(state, protocol) {
129
- const next = structuredClone(state);
130
- next.cohortPhase = protocol.cohortPhase;
131
- // R6.2/R6.3/C7 (Task 11): persistir el HEAD del plan avanzado por
132
- // `reconcileProtocol`'s `accept-merge` (`out.planHeadSha = decision.
133
- // joinedCommitSha`) — sin esto, el siguiente `toProtocol` volvería a leer
134
- // `cohortBaseSha` (stale) y el segundo join de la cohorte usaría un
135
- // `expectedPlanHeadSha` incorrecto.
136
- if (protocol.planHeadSha !== undefined)
137
- next.cohortPlanHeadSha = protocol.planHeadSha;
138
- // R7/C3 (Task 12): los valores finales normalmente solo avanzan, salvo
139
- // que fallback abandone explícitamente esa ruta. En ese caso el reducer
140
- // ya invalidó su evidencia y el mirror durable DEBE borrarla también;
141
- // conservarla permitiría que un restart reconstruyera un protocolo que
142
- // contradice los tracks en teardown.
143
- if (protocol.cohortPhase === 'FALLBACK_PENDING') {
144
- next.globalQaHeadSha = undefined;
145
- next.finalIntegrationJobId = undefined;
146
- next.qaFinalizeRequested = undefined;
147
- }
148
- else {
149
- if (protocol.globalQaHeadSha !== undefined)
150
- next.globalQaHeadSha = protocol.globalQaHeadSha;
151
- if (protocol.finalIntegrationJobId !== undefined)
152
- next.finalIntegrationJobId = protocol.finalIntegrationJobId;
153
- }
154
- if (protocol.fallbackReason !== undefined)
155
- next.cohortFallbackReason = protocol.fallbackReason;
156
- next.tracks = (next.tracks ?? []).map((ref) => {
157
- const t = protocol.tracks[ref.trackId];
158
- if (t === undefined)
159
- return ref;
160
- return {
161
- ...ref,
162
- phase: t.phase,
163
- frozenHeadSha: t.frozenHeadSha,
164
- blockedReason: t.blockedReason,
165
- joinedCommitSha: t.joinedCommitSha,
166
- joinIntent: t.expectedPlanHeadSha !== undefined && t.expectedTrackHeadSha !== undefined
167
- ? { expectedPlanHeadSha: t.expectedPlanHeadSha, expectedTrackHeadSha: t.expectedTrackHeadSha, strategy: types_1.JOIN_STRATEGY_NO_FF }
168
- : ref.joinIntent,
169
- };
170
- });
171
- return next;
172
- }
173
- function persist(planRoot, branch, s) {
174
- (0, store_1.writeJournal)(planRoot, branch, s);
175
- const r = (0, store_1.readJournal)(planRoot, branch);
176
- if (r.corrupt || r.state === null)
177
- throw new Error('journal corrupto tras persistir tracks (R1.6)');
178
- return r.state;
179
- }
180
- function refOf(s, trackId) {
181
- const ref = s.tracks?.find((t) => t.trackId === trackId);
182
- if (ref === undefined)
183
- throw new Error(`invariante rota: TrackRef ausente para ${trackId}`);
184
- return ref;
185
- }
186
- function withRef(s, trackId, patch) {
187
- return { ...s, tracks: (s.tracks ?? []).map((t) => (t.trackId === trackId ? { ...t, ...patch } : t)) };
188
- }
189
125
  function mustBaseSha(s) {
190
126
  if (s.cohortBaseSha === undefined)
191
127
  throw new Error('invariante rota: cohortBaseSha ausente para una cohorte en PREPARING/ACTIVE');
@@ -193,7 +129,7 @@ function mustBaseSha(s) {
193
129
  }
194
130
  function runCreateWorktree(planRoot, branch, s, protocol, effect, runtime) {
195
131
  const trackId = effect.trackId;
196
- const ref = refOf(s, trackId);
132
+ const ref = (0, track_state_1.refOf)(s, trackId);
197
133
  // Task 9 (R4.2/R4.6/C11): observaciones read-only del mundo real ANTES de
198
134
  // tocar `runtime` — ninguna de las dos cuenta como el side effect del
199
135
  // tick, igual que documentaba la versión anterior de este comentario.
@@ -209,13 +145,13 @@ function runCreateWorktree(planRoot, branch, s, protocol, effect, runtime) {
209
145
  const decision = (0, protocol_1.decidePrepare)(protocol.tracks[trackId], observed);
210
146
  if (decision === 'accept-worktree') {
211
147
  const applied = (0, protocol_1.observeProtocolEffect)(protocol, effect, { kind: 'worktree-observed', trackId, owned: true });
212
- const next = persist(planRoot, branch, applyProtocolToState(s, applied));
148
+ const next = (0, track_state_1.persist)(planRoot, branch, (0, track_state_1.applyProtocolToState)(s, applied));
213
149
  (0, store_1.appendEvent)(planRoot, branch, { kind: 'track-effect', trackId, effect: effect.kind });
214
150
  return { state: next, stop: true, executed: effect.kind };
215
151
  }
216
152
  if (decision === 'block-foreign') {
217
153
  const blocked = (0, protocol_1.observeProtocolEffect)(protocol, effect, { kind: 'worktree-observed', trackId, owned: false });
218
- const next = persist(planRoot, branch, applyProtocolToState(s, blocked));
154
+ const next = (0, track_state_1.persist)(planRoot, branch, (0, track_state_1.applyProtocolToState)(s, blocked));
219
155
  (0, store_1.appendEvent)(planRoot, branch, { kind: 'track-blocked', trackId, reason: 'worktree preexistente ajeno' });
220
156
  // Post-review fix (Task 8): bloquear un track es en sí mismo UNA
221
157
  // frontera — dejar `stop:false` acá permitía que el MISMO call
@@ -232,18 +168,18 @@ function runCreateWorktree(planRoot, branch, s, protocol, effect, runtime) {
232
168
  }
233
169
  catch (error) {
234
170
  const failed = (0, protocol_1.observeProtocolEffect)(protocol, effect, { kind: 'effect-failed', trackId, effect: effect.kind, detail: error.message });
235
- const next = persist(planRoot, branch, applyProtocolToState(s, failed));
171
+ const next = (0, track_state_1.persist)(planRoot, branch, (0, track_state_1.applyProtocolToState)(s, failed));
236
172
  (0, store_1.appendEvent)(planRoot, branch, { kind: 'track-effect-failed', trackId, effect: effect.kind, detail: error.message });
237
173
  return { state: next, stop: true, executed: effect.kind };
238
174
  }
239
175
  const applied = (0, protocol_1.observeProtocolEffect)(protocol, effect, { kind: 'worktree-observed', trackId, owned: true });
240
- const next = persist(planRoot, branch, applyProtocolToState(s, applied));
176
+ const next = (0, track_state_1.persist)(planRoot, branch, (0, track_state_1.applyProtocolToState)(s, applied));
241
177
  (0, store_1.appendEvent)(planRoot, branch, { kind: 'track-effect', trackId, effect: effect.kind });
242
178
  return { state: next, stop: true, executed: effect.kind };
243
179
  }
244
180
  function runCreateTrackJournal(planRoot, branch, s, protocol, effect, runtime) {
245
181
  const trackId = effect.trackId;
246
- const ref = refOf(s, trackId);
182
+ const ref = (0, track_state_1.refOf)(s, trackId);
247
183
  // GAP CONOCIDO Y DELIBERADAMENTE DIFERIDO (ver el comentario largo en
248
184
  // `apply.ts` junto a `track-prepare-request`): `taskIds`/`planDigest`
249
185
  // deberían nacer del plan .md parseado, pero NINGÚN task (1-8) provee un
@@ -270,12 +206,12 @@ function runCreateTrackJournal(planRoot, branch, s, protocol, effect, runtime) {
270
206
  }
271
207
  catch (error) {
272
208
  const failed = (0, protocol_1.observeProtocolEffect)(protocol, effect, { kind: 'effect-failed', trackId, effect: effect.kind, detail: error.message });
273
- const next = persist(planRoot, branch, applyProtocolToState(s, failed));
209
+ const next = (0, track_state_1.persist)(planRoot, branch, (0, track_state_1.applyProtocolToState)(s, failed));
274
210
  (0, store_1.appendEvent)(planRoot, branch, { kind: 'track-effect-failed', trackId, effect: effect.kind, detail: error.message });
275
211
  return { state: next, stop: true, executed: effect.kind };
276
212
  }
277
213
  const applied = (0, protocol_1.observeProtocolEffect)(protocol, effect, { kind: 'effect-applied', effect });
278
- const next = persist(planRoot, branch, applyProtocolToState(s, applied));
214
+ const next = (0, track_state_1.persist)(planRoot, branch, (0, track_state_1.applyProtocolToState)(s, applied));
279
215
  (0, store_1.appendEvent)(planRoot, branch, { kind: 'track-effect', trackId, effect: effect.kind });
280
216
  return { state: next, stop: true, executed: effect.kind };
281
217
  }
@@ -300,7 +236,7 @@ function runCreateTrackJournal(planRoot, branch, s, protocol, effect, runtime) {
300
236
  * `Supervisor.tick()` del track, en su journal, en su propio proceso). */
301
237
  function runFreezeTrack(planRoot, branch, s, protocol, effect, runtime) {
302
238
  const trackId = effect.trackId;
303
- const ref = refOf(s, trackId);
239
+ const ref = (0, track_state_1.refOf)(s, trackId);
304
240
  const r = (0, store_1.readJournal)(ref.worktreePath, ref.branch);
305
241
  if (r.corrupt || r.state === null) {
306
242
  // Journal del track todavía no legible (crash entre `create-track-
@@ -331,7 +267,7 @@ function runFreezeTrack(planRoot, branch, s, protocol, effect, runtime) {
331
267
  const observed = (0, protocol_1.observeProtocolEffect)(protocol, effect, {
332
268
  kind: 'freeze-observation', trackId, frozenHeadSha: trackState.frozen.headSha,
333
269
  });
334
- let next = applyProtocolToState(s, observed);
270
+ let next = (0, track_state_1.applyProtocolToState)(s, observed);
335
271
  // R5.7/R5.8/R5.9/C5 (Step 6): ownership REAL post-hoc, desde commits
336
272
  // YA congelados — nunca `git status` para esto (esa es la
337
273
  // herramienta de limpieza, no de ownership). `ref.ownership` es el
@@ -361,7 +297,7 @@ function runFreezeTrack(planRoot, branch, s, protocol, effect, runtime) {
361
297
  cohortParallelInvalidatedBy: [...new Set([...(next.cohortParallelInvalidatedBy ?? []), ...marks])],
362
298
  };
363
299
  }
364
- const persisted = persist(planRoot, branch, next);
300
+ const persisted = (0, track_state_1.persist)(planRoot, branch, next);
365
301
  (0, store_1.appendEvent)(planRoot, branch, { kind: 'track-frozen-observed', trackId, frozenHeadSha: trackState.frozen.headSha });
366
302
  if (actual.outsideOwnership.length > 0) {
367
303
  (0, store_1.appendEvent)(planRoot, branch, { kind: 'track-ownership-violation', trackId, paths: actual.outsideOwnership });
@@ -490,7 +426,7 @@ async function runMergeTrack(planRoot, branch, s, protocol, effect, runtime) {
490
426
  }
491
427
  const after = gatherJoinObservation(planRoot, intent.expectedTrackHeadSha);
492
428
  const observed = (0, protocol_1.observeProtocolEffect)(protocol, effect, { kind: 'join-observation', trackId, ...after });
493
- const next = persist(planRoot, branch, applyProtocolToState(s, observed));
429
+ const next = (0, track_state_1.persist)(planRoot, branch, (0, track_state_1.applyProtocolToState)(s, observed));
494
430
  (0, store_1.appendEvent)(planRoot, branch, { kind: 'track-merge-attempted', trackId, detail });
495
431
  return { state: next, stop: true, executed: effect.kind };
496
432
  }
@@ -508,7 +444,7 @@ async function runMergeTrack(planRoot, branch, s, protocol, effect, runtime) {
508
444
  }
509
445
  const after = gatherJoinObservation(planRoot, intent.expectedTrackHeadSha);
510
446
  const observed = (0, protocol_1.observeProtocolEffect)(protocol, effect, { kind: 'join-observation', trackId, ...after });
511
- const next = persist(planRoot, branch, applyProtocolToState(s, observed));
447
+ const next = (0, track_state_1.persist)(planRoot, branch, (0, track_state_1.applyProtocolToState)(s, observed));
512
448
  (0, store_1.appendEvent)(planRoot, branch, { kind: 'track-merge-aborted', trackId, detail });
513
449
  return { state: next, stop: true, executed: effect.kind };
514
450
  }
@@ -516,7 +452,7 @@ async function runMergeTrack(planRoot, branch, s, protocol, effect, runtime) {
516
452
  // ningún efecto git nuevo que ejecutar este tick (mismo criterio que
517
453
  // `runCreateWorktree`'s ramas 'accept-worktree'/'block-foreign').
518
454
  const observed = (0, protocol_1.observeProtocolEffect)(protocol, effect, { kind: 'join-observation', trackId, ...before });
519
- const next = persist(planRoot, branch, applyProtocolToState(s, observed));
455
+ const next = (0, track_state_1.persist)(planRoot, branch, (0, track_state_1.applyProtocolToState)(s, observed));
520
456
  (0, store_1.appendEvent)(planRoot, branch, {
521
457
  kind: decision.action === 'accept-merge' ? 'track-merged' : 'track-blocked',
522
458
  trackId, ...(decision.action === 'block' ? { reason: decision.reason } : {}),
@@ -555,7 +491,7 @@ function runRequestGlobalQa(planRoot, branch, s, protocol, effect) {
555
491
  }
556
492
  if (actualHead === reported.headSha && clean) {
557
493
  const observed = (0, protocol_1.observeProtocolEffect)(protocol, effect, { kind: 'global-qa-pass', headSha: actualHead, clean: true });
558
- const next = persist(planRoot, branch, applyProtocolToState(s, observed));
494
+ const next = (0, track_state_1.persist)(planRoot, branch, (0, track_state_1.applyProtocolToState)(s, observed));
559
495
  (0, store_1.appendEvent)(planRoot, branch, { kind: 'global-qa-passed', headSha: actualHead });
560
496
  return { state: next, stop: true, executed: effect.kind };
561
497
  }
@@ -573,7 +509,7 @@ function runRequestGlobalQa(planRoot, branch, s, protocol, effect) {
573
509
  nextAction: { actionId: 'finalize-global-qa', type: 'run-global-qa', target: 'cycle', preconditions: [], attempt: 0, state: 'pending' },
574
510
  },
575
511
  };
576
- const persisted = persist(planRoot, branch, next);
512
+ const persisted = (0, track_state_1.persist)(planRoot, branch, next);
577
513
  (0, store_1.appendEvent)(planRoot, branch, { kind: 'global-qa-requested' });
578
514
  return { state: persisted, stop: true, executed: effect.kind };
579
515
  }
@@ -601,7 +537,7 @@ async function runRequestFinalIntegration(planRoot, branch, s, protocol, effect,
601
537
  return { state: s, stop: true, executed: null };
602
538
  }
603
539
  const observed = (0, protocol_1.observeProtocolEffect)(protocol, effect, { kind: 'integration-pass', jobId: existing.id, headSha: headNow });
604
- const next = persist(planRoot, branch, applyProtocolToState(s, observed));
540
+ const next = (0, track_state_1.persist)(planRoot, branch, (0, track_state_1.applyProtocolToState)(s, observed));
605
541
  (0, store_1.appendEvent)(planRoot, branch, { kind: 'track-integration-passed', jobId: existing.id });
606
542
  return { state: next, stop: true, executed: effect.kind };
607
543
  }
@@ -659,7 +595,7 @@ function runRunFinalInterlock(planRoot, branch, s, protocol, effect, runtime) {
659
595
  if (protocol.globalQaHeadSha === undefined)
660
596
  throw new Error('invariante rota: globalQaHeadSha ausente en FINAL_INTERLOCK');
661
597
  const observed = (0, protocol_1.observeProtocolEffect)(protocol, effect, { kind: 'interlock-pass', headSha: protocol.globalQaHeadSha });
662
- const next = persist(planRoot, branch, applyProtocolToState(s, observed));
598
+ const next = (0, track_state_1.persist)(planRoot, branch, (0, track_state_1.applyProtocolToState)(s, observed));
663
599
  runtime.releaseIntegrationLockIfHeld();
664
600
  (0, store_1.appendEvent)(planRoot, branch, { kind: 'cohort-complete' });
665
601
  return { state: next, stop: true, executed: effect.kind };
@@ -671,7 +607,7 @@ function defaultCliEntry() {
671
607
  }
672
608
  function runSpawnTrackSupervisor(planRoot, branch, s, protocol, effect, runtime) {
673
609
  const trackId = effect.trackId;
674
- const ref = refOf(s, trackId);
610
+ const ref = (0, track_state_1.refOf)(s, trackId);
675
611
  if (ref.supervisorIntent === undefined) {
676
612
  // R1.8/C11: el intent (nonce + argv + claimPath) se persiste ANTES de
677
613
  // spawnear nada — si el supervisor del plan muere justo acá, el
@@ -688,7 +624,7 @@ function runSpawnTrackSupervisor(planRoot, branch, s, protocol, effect, runtime)
688
624
  claimPath: path_1.default.join(ref.worktreePath, '.awm', 'supervisor.claim'),
689
625
  };
690
626
  const applied = (0, protocol_1.observeProtocolEffect)(protocol, effect, { kind: 'effect-applied', effect });
691
- const next = persist(planRoot, branch, withRef(applyProtocolToState(s, applied), trackId, { supervisorIntent: intent }));
627
+ const next = (0, track_state_1.persist)(planRoot, branch, (0, track_state_1.withRef)((0, track_state_1.applyProtocolToState)(s, applied), trackId, { supervisorIntent: intent }));
692
628
  (0, store_1.appendEvent)(planRoot, branch, { kind: 'track-supervisor-intent', trackId });
693
629
  // Post-review fix: persistir el supervisorIntent es en sí mismo UNA
694
630
  // frontera durable (R1.8/C11) — con `stop:false` el MISMO call podía
@@ -721,7 +657,7 @@ function runSpawnTrackSupervisor(planRoot, branch, s, protocol, effect, runtime)
721
657
  const decision = (0, protocol_1.decidePrepare)(protocol.tracks[trackId], { supervisorArtifact: observation.kind });
722
658
  if (decision === 'block-foreign') {
723
659
  const blocked = (0, protocol_1.observeProtocolEffect)(protocol, effect, { kind: 'supervisor-observed', trackId, identity: 'other' });
724
- const next = persist(planRoot, branch, applyProtocolToState(s, blocked));
660
+ const next = (0, track_state_1.persist)(planRoot, branch, (0, track_state_1.applyProtocolToState)(s, blocked));
725
661
  (0, store_1.appendEvent)(planRoot, branch, { kind: 'track-blocked', trackId, reason: 'identidad de supervisor ajena' });
726
662
  return { state: next, stop: true, executed: null };
727
663
  }
@@ -733,7 +669,7 @@ function runSpawnTrackSupervisor(planRoot, branch, s, protocol, effect, runtime)
733
669
  // mutante por invocación — igual patrón que
734
670
  // `foreignPathExists` -> `addWorktree` en `runCreateWorktree`.
735
671
  const pr = runtime.spawnSupervisor(ref);
736
- const next = pr !== undefined ? persist(planRoot, branch, withRef(s, trackId, { supervisorProcessRef: pr })) : s;
672
+ const next = pr !== undefined ? (0, track_state_1.persist)(planRoot, branch, (0, track_state_1.withRef)(s, trackId, { supervisorProcessRef: pr })) : s;
737
673
  (0, store_1.appendEvent)(planRoot, branch, { kind: 'track-effect', trackId, effect: effect.kind });
738
674
  return { state: next, stop: true, executed: effect.kind };
739
675
  }
@@ -755,7 +691,7 @@ function runSpawnTrackSupervisor(planRoot, branch, s, protocol, effect, runtime)
755
691
  const observed = (0, protocol_1.observeProtocolEffect)(protocol, effect, {
756
692
  kind: 'supervisor-observed', trackId, identity: 'expected', readinessNonce: observation.readinessNonce,
757
693
  });
758
- const next = persist(planRoot, branch, applyProtocolToState(s, observed));
694
+ const next = (0, track_state_1.persist)(planRoot, branch, (0, track_state_1.applyProtocolToState)(s, observed));
759
695
  (0, store_1.appendEvent)(planRoot, branch, { kind: 'track-armed-or-blocked', trackId });
760
696
  return { state: next, stop: true, executed: null };
761
697
  }
@@ -839,7 +775,7 @@ async function reconcileTracks(planRoot, branch, state, runtime, maxParallel) {
839
775
  for (const track of next.tracks ?? [])
840
776
  if (track.trackId === pendingTeardown.trackId)
841
777
  track.teardownRequested = undefined;
842
- s = persist(planRoot, branch, next);
778
+ s = (0, track_state_1.persist)(planRoot, branch, next);
843
779
  (0, store_1.appendEvent)(planRoot, branch, {
844
780
  kind: 'track-teardown-moot', trackId: pendingTeardown.trackId, phase: s.cohortPhase,
845
781
  });
@@ -847,11 +783,11 @@ async function reconcileTracks(planRoot, branch, state, runtime, maxParallel) {
847
783
  }
848
784
  const protocolBefore = toProtocol(s, maxParallel);
849
785
  const observed = (0, protocol_1.reconcileProtocol)(protocolBefore, { kind: 'teardown-requested', trackId: pendingTeardown.trackId });
850
- const next = applyProtocolToState(s, observed);
786
+ const next = (0, track_state_1.applyProtocolToState)(s, observed);
851
787
  for (const track of next.tracks ?? [])
852
788
  if (track.trackId === pendingTeardown.trackId)
853
789
  track.teardownRequested = undefined;
854
- s = persist(planRoot, branch, next);
790
+ s = (0, track_state_1.persist)(planRoot, branch, next);
855
791
  // El lock pertenece al proceso vivo que alcanzó la integración.
856
792
  // Al abandonar FINAL_INTERLOCK debe soltarse en este mismo proceso;
857
793
  // tras un crash el lease muerto se reclama por identidad al iniciar.
@@ -886,11 +822,11 @@ async function reconcileTracks(planRoot, branch, state, runtime, maxParallel) {
886
822
  if (pendingJoin !== undefined) {
887
823
  const protocolBefore = toProtocol(s, maxParallel);
888
824
  const observed = (0, protocol_1.reconcileProtocol)(protocolBefore, { kind: 'join-requested', trackId: pendingJoin.trackId });
889
- const next = applyProtocolToState(s, observed);
825
+ const next = (0, track_state_1.applyProtocolToState)(s, observed);
890
826
  for (const t of next.tracks ?? [])
891
827
  if (t.trackId === pendingJoin.trackId)
892
828
  t.joinRequested = undefined;
893
- s = persist(planRoot, branch, next);
829
+ s = (0, track_state_1.persist)(planRoot, branch, next);
894
830
  (0, store_1.appendEvent)(planRoot, branch, {
895
831
  kind: 'track-join-observed', trackId: pendingJoin.trackId,
896
832
  phase: s.tracks?.find((t) => t.trackId === pendingJoin.trackId)?.phase,
@@ -908,7 +844,7 @@ async function reconcileTracks(planRoot, branch, state, runtime, maxParallel) {
908
844
  for (const t of next.tracks ?? [])
909
845
  if (t.trackId === mootJoin.trackId)
910
846
  t.joinRequested = undefined;
911
- s = persist(planRoot, branch, next);
847
+ s = (0, track_state_1.persist)(planRoot, branch, next);
912
848
  (0, store_1.appendEvent)(planRoot, branch, { kind: 'track-join-moot', trackId: mootJoin.trackId, phase: mootJoin.phase });
913
849
  continue;
914
850
  }
@@ -918,7 +854,7 @@ async function reconcileTracks(planRoot, branch, state, runtime, maxParallel) {
918
854
  return { state: s, effectExecuted: null };
919
855
  if (!RUNTIME_EFFECTS.has(effect.kind)) {
920
856
  const applied = (0, protocol_1.observeProtocolEffect)(protocol, effect, { kind: 'effect-applied', effect });
921
- const candidate = applyProtocolToState(s, applied);
857
+ const candidate = (0, track_state_1.applyProtocolToState)(s, applied);
922
858
  // Defensivo: un ProtocolEffect sin fase mapeada en `EFFECT_APPLIED_PHASE`
923
859
  // (ej. `merge-track`/`request-global-qa`, todavía sin driver propio en
924
860
  // esta task — llegan en Tasks 11/12) no debe hacer que este loop gire
@@ -926,7 +862,7 @@ async function reconcileTracks(planRoot, branch, state, runtime, maxParallel) {
926
862
  if (candidate.cohortPhase === s.cohortPhase && JSON.stringify(candidate.tracks) === JSON.stringify(s.tracks)) {
927
863
  return { state: s, effectExecuted: null };
928
864
  }
929
- s = persist(planRoot, branch, candidate);
865
+ s = (0, track_state_1.persist)(planRoot, branch, candidate);
930
866
  const trackId = 'trackId' in effect ? effect.trackId : undefined;
931
867
  const label = trackId !== undefined ? (s.tracks?.find((t) => t.trackId === trackId)?.phase ?? effect.kind) : s.cohortPhase;
932
868
  // `enter-serial` es el ÚNICO efecto de este branch que lleva una causa: la
@@ -0,0 +1,231 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PROVIDER_EXECUTION_CAPABILITIES = void 0;
4
+ exports.sanitizeAdmissionReport = sanitizeAdmissionReport;
5
+ exports.admitPlan = admitPlan;
6
+ const providers_1 = require("../../providers");
7
+ const paths_1 = require("../journal/paths");
8
+ const resolve_1 = require("../model-policy/resolve");
9
+ const capabilities_1 = require("../model-policy/capabilities");
10
+ const UNKNOWN = {
11
+ artifactDelivery: 'unverified', interactiveExecution: 'unverified', unattendedController: 'unverified', nativeSubagents: 'unverified',
12
+ modelOverride: 'unverified', effortOverride: 'unverified', observedModelEvidence: 'unverified', durableResume: 'unverified',
13
+ };
14
+ /** Deliberate R1 evidence table. Artifact delivery is never used as execution evidence. */
15
+ exports.PROVIDER_EXECUTION_CAPABILITIES = {
16
+ antigravity: { ...UNKNOWN },
17
+ opencode: { ...UNKNOWN },
18
+ 'claude-code': { ...UNKNOWN, interactiveExecution: 'supported', unattendedController: 'supported', durableResume: 'supported' },
19
+ codex: { ...UNKNOWN, interactiveExecution: 'supported', unattendedController: 'supported', durableResume: 'supported' },
20
+ cursor: { ...UNKNOWN },
21
+ copilot: { ...UNKNOWN },
22
+ };
23
+ const MAX_DIAGNOSTICS = 20;
24
+ const MAX_DIAGNOSTIC_LENGTH = 4096;
25
+ function safe(value) { return value.replace(/[\u0000-\u001F\u007F-\u009F]/g, character => `\\u${character.codePointAt(0).toString(16).padStart(4, '0')}`).slice(0, MAX_DIAGNOSTIC_LENGTH); }
26
+ function sanitizeDiagnostics(diagnostics) { return diagnostics.slice(0, MAX_DIAGNOSTICS).map(item => ({ code: safe(String(item.code)), message: safe(String(item.message)), ...(typeof item.field === 'string' ? { field: safe(item.field) } : {}) })); }
27
+ function diagnostic(code, message) { return { code: safe(code), message: safe(message) }; }
28
+ function blocked(input, diagnostics, extras = {}) {
29
+ const { journal = 'not-required', currentness = 'not-checked', sensors = 'not-required', ...rest } = extras;
30
+ return { state: 'blocked', planState: input.plan.state, journal, currentness, sensors, ...rest, diagnostics: sanitizeDiagnostics(diagnostics) };
31
+ }
32
+ function forecast(slices) {
33
+ const roles = { implementer: slices, 'specification-reviewer': slices, 'code-quality-reviewer': slices, 'final-reviewer': 1, 'track-a-qa': 1, 'track-b-qa': 1, documentation: 1, retro: 1, finishing: 1 };
34
+ return { kind: 'topology', slices, roles, total: Object.values(roles).reduce((sum, count) => sum + count, 0) };
35
+ }
36
+ function routingForecast(plan, routing) {
37
+ const profiles = { mechanical: 0, integration: 0, judgment: 0 };
38
+ for (const slice of plan.manifest.slices)
39
+ profiles[slice.implementerProfile] += 1;
40
+ const qa = routing.qaLens;
41
+ if (qa !== undefined && (!Array.isArray(qa) || qa.length > 64 || new Set(qa).size !== qa.length || qa.some(id => typeof id !== 'string' || !/^[A-Za-z0-9][A-Za-z0-9_.-]{0,63}$/.test(id))))
42
+ throw new Error('routing qaLens must be unique validated lens ids');
43
+ if (routing.controllerCount !== undefined && (!Number.isSafeInteger(routing.controllerCount) || routing.controllerCount < 0 || routing.controllerCount > 1))
44
+ throw new Error('routing controllerCount must be 0 or 1');
45
+ return { kind: 'routing-v1', slices: plan.manifest.slices.length, implementerProfiles: profiles, roles: { 'specification-reviewer': plan.manifest.slices.length, 'code-quality-reviewer': plan.manifest.slices.length, 'final-reviewer': 1, 'track-a-qa': 1, documentation: 1, retro: 1, finishing: 1 }, trackB: qa === undefined ? { state: 'unavailable', lowerBound: 1 } : { state: 'known', count: qa.length }, controller: routing.controllerCount === undefined ? { state: 'unavailable', lowerBound: 0 } : { state: 'known', count: routing.controllerCount } };
46
+ }
47
+ function currentness(report, consumedRegistryComponents) {
48
+ const consumed = new Set(['cli', ...consumedRegistryComponents]);
49
+ const present = new Set(report.components.map(component => component.component));
50
+ const missing = [...consumed].filter(component => !present.has(component));
51
+ if (missing.length > 0)
52
+ return { status: 'unverifiable', diagnostics: [diagnostic('ADMISSION_CURRENTNESS_MISSING_COMPONENT', `Currentness evidence is missing consumed components: ${missing.join(', ')}.`)] };
53
+ const failed = report.components.filter(component => consumed.has(component.component) && component.status !== 'current');
54
+ if (failed.length === 0)
55
+ return { status: 'current', diagnostics: [] };
56
+ const names = failed.map(component => component.component).join(', ');
57
+ const status = failed.some(component => component.status === 'unverifiable') ? 'unverifiable' : 'stale';
58
+ return { status, diagnostics: [diagnostic('ADMISSION_CURRENTNESS_BLOCKED', `Consumed contract currentness is ${status}: ${names}.`)] };
59
+ }
60
+ function sensorVerdict(report) { return report.overall === 'not_certified' ? 'not-certified' : report.overall === 'pass' ? 'pass' : 'fail'; }
61
+ function journalStatus(input, plan) {
62
+ if (input.journalCorrupt)
63
+ return 'corrupt';
64
+ const journal = input.journalState;
65
+ if (!journal)
66
+ return 'missing';
67
+ if (journal.schema !== 2 || !journal.planBinding)
68
+ return 'stale';
69
+ let planPath;
70
+ try {
71
+ planPath = (0, paths_1.bindingPlanPath)(input.planPath ?? '');
72
+ }
73
+ catch {
74
+ return 'stale';
75
+ }
76
+ const binding = journal.planBinding;
77
+ return binding.path === planPath && binding.digest === plan.planDigest && binding.schema === plan.schema && binding.executionMode === 'desatendido'
78
+ ? 'current' : 'stale';
79
+ }
80
+ const PLAN_STATES = new Set(['valid', 'migration-required', 'invalid', 'unsupported']);
81
+ const JOURNALS = new Set(['not-required', 'current', 'missing', 'corrupt', 'stale']);
82
+ const CURRENTNESS = new Set(['current', 'stale', 'unverifiable', 'not-checked']);
83
+ const SENSORS = new Set(['pass', 'fail', 'not-certified', 'not-required']);
84
+ const CAPABILITY = new Set(['supported', 'unsupported', 'unverified']);
85
+ function invalidAdmission() { throw new Error('admission returned an invalid report'); }
86
+ function validCapabilities(value) {
87
+ if (!value || typeof value !== 'object' || Array.isArray(value))
88
+ return false;
89
+ const record = value;
90
+ const keys = ['artifactDelivery', 'interactiveExecution', 'unattendedController', 'nativeSubagents', 'modelOverride', 'effortOverride', 'observedModelEvidence', 'durableResume'];
91
+ return Object.keys(record).length === keys.length && keys.every(key => CAPABILITY.has(record[key]));
92
+ }
93
+ function validForecast(value) {
94
+ if (!value || typeof value !== 'object' || Array.isArray(value))
95
+ return false;
96
+ const record = value;
97
+ const roles = ['implementer', 'specification-reviewer', 'code-quality-reviewer', 'final-reviewer', 'track-a-qa', 'track-b-qa', 'documentation', 'retro', 'finishing'];
98
+ const slices = record.slices;
99
+ const topology = { implementer: slices, 'specification-reviewer': slices, 'code-quality-reviewer': slices, 'final-reviewer': 1, 'track-a-qa': 1, 'track-b-qa': 1, documentation: 1, retro: 1, finishing: 1 };
100
+ return record.kind === 'topology' && Number.isSafeInteger(slices) && slices >= 0 && Number.isSafeInteger(record.total) && record.total >= 0
101
+ && !!record.roles && typeof record.roles === 'object' && !Array.isArray(record.roles) && Object.keys(record.roles).length === roles.length
102
+ && roles.every(role => record.roles[role] === topology[role])
103
+ && record.total === Object.values(topology).reduce((sum, count) => sum + count, 0);
104
+ }
105
+ function validRoutingForecast(value) {
106
+ if (!value || typeof value !== 'object' || Array.isArray(value))
107
+ return false;
108
+ const record = value;
109
+ const profiles = record.implementerProfiles;
110
+ const roles = record.roles;
111
+ const count = (item) => Number.isSafeInteger(item) && item >= 0;
112
+ const roleKeys = ['specification-reviewer', 'code-quality-reviewer', 'final-reviewer', 'track-a-qa', 'documentation', 'retro', 'finishing'];
113
+ return record.kind === 'routing-v1' && Object.keys(record).length === 6 && count(record.slices) && !!profiles && Object.keys(profiles).length === 3 && ['mechanical', 'integration', 'judgment'].every(key => count(profiles[key])) && Object.values(profiles).reduce((sum, profile) => sum + profile, 0) === record.slices && !!roles && typeof roles === 'object' && Object.keys(roles).length === roleKeys.length && roleKeys.every(key => key in roles) && roles['specification-reviewer'] === record.slices && roles['code-quality-reviewer'] === record.slices && ['final-reviewer', 'track-a-qa', 'documentation', 'retro', 'finishing'].every(key => roles[key] === 1)
114
+ && !!record.trackB && typeof record.trackB === 'object' && ((record.trackB.state === 'known' && count(record.trackB.count)) || (record.trackB.state === 'unavailable' && record.trackB.lowerBound === 1))
115
+ && !!record.controller && typeof record.controller === 'object' && ((record.controller.state === 'known' && count(record.controller.count)) || (record.controller.state === 'unavailable' && record.controller.lowerBound === 0));
116
+ }
117
+ function validDiagnostics(value) {
118
+ return Array.isArray(value) && value.every(item => item && typeof item === 'object' && typeof item.code === 'string' && typeof item.message === 'string' && (item.field === undefined || typeof item.field === 'string'));
119
+ }
120
+ /** Defensive public-output boundary for injected dependencies as well as core reports. */
121
+ function sanitizeAdmissionReport(report) {
122
+ if (!report || typeof report !== 'object' || Array.isArray(report))
123
+ invalidAdmission();
124
+ const value = report;
125
+ if ((value.state !== 'admitted' && value.state !== 'blocked') || !PLAN_STATES.has(value.planState) || !JOURNALS.has(value.journal) || !CURRENTNESS.has(value.currentness) || !SENSORS.has(value.sensors) || !validDiagnostics(value.diagnostics))
126
+ invalidAdmission();
127
+ if (value.planDigest !== undefined && (typeof value.planDigest !== 'string' || !/^[a-f0-9]{64}$/.test(value.planDigest)))
128
+ invalidAdmission();
129
+ if (value.executionMode !== undefined && value.executionMode !== 'interactivo' && value.executionMode !== 'desatendido')
130
+ invalidAdmission();
131
+ if (value.provider !== undefined && !(0, providers_1.isAgentTarget)(value.provider))
132
+ invalidAdmission();
133
+ if (value.capabilityResolution !== undefined) {
134
+ const resolution = value.capabilityResolution;
135
+ if (!resolution || typeof resolution !== 'object' || !['native', 'degraded', 'blocked'].includes(resolution.outcome) || !(0, providers_1.isAgentTarget)(resolution.provider) || !validCapabilities(resolution.capabilities) || resolution.evidenceVersion !== 'r1-v1' || !validDiagnostics(resolution.diagnostics))
136
+ invalidAdmission();
137
+ }
138
+ if (value.forecast !== undefined && !validForecast(value.forecast))
139
+ invalidAdmission();
140
+ if (value.routingForecast !== undefined && !validRoutingForecast(value.routingForecast))
141
+ invalidAdmission();
142
+ const capabilityResolution = value.capabilityResolution === undefined ? undefined : { ...value.capabilityResolution, diagnostics: sanitizeDiagnostics(value.capabilityResolution.diagnostics) };
143
+ return { ...value, ...(capabilityResolution ? { capabilityResolution } : {}), diagnostics: sanitizeDiagnostics(value.diagnostics) };
144
+ }
145
+ /** Pure, read-only composition. Callers own every I/O dependency and no dispatch API is exposed here. */
146
+ async function admitPlan(input) {
147
+ if (!input || typeof input !== 'object' || typeof input.cwd !== 'string' || input.cwd.length === 0)
148
+ throw new Error('admission requires a cwd');
149
+ const plan = input.plan;
150
+ if (!plan || typeof plan !== 'object')
151
+ throw new Error('admission requires a plan report');
152
+ if (plan.state !== 'valid') {
153
+ if (plan.state === 'unsupported')
154
+ return blocked(input, [diagnostic('ADMISSION_PLAN_UNSUPPORTED', 'This compact plan schema is not supported for admission.'), ...plan.diagnostics]);
155
+ const code = plan.state === 'migration-required' ? 'ADMISSION_PLAN_MIGRATION_REQUIRED' : 'ADMISSION_PLAN_INVALID';
156
+ return blocked(input, [diagnostic(code, 'Compact plan validation must succeed before admission.'), ...(plan.state === 'invalid' ? plan.diagnostics : [])]);
157
+ }
158
+ if (!(0, providers_1.isAgentTarget)(input.provider))
159
+ return blocked(input, [diagnostic('ADMISSION_PROVIDER_INVALID', `Unknown provider: ${String(input.provider)}.`)], { planDigest: plan.planDigest });
160
+ const provider = input.provider;
161
+ if (input.enabledAgents && !input.enabledAgents.includes(provider)) {
162
+ return blocked(input, [diagnostic('ADMISSION_PROVIDER_DISABLED', `Provider ${provider} is not enabled.`)], { planDigest: plan.planDigest, provider });
163
+ }
164
+ // v1 manifests do not yet carry executionMode in their typed schema. Accept only
165
+ // the exact future field when an already-validated caller supplies it; otherwise
166
+ // preserve the safe interactive default until S3 owns durable unattended binding.
167
+ const manifestMode = plan.manifest.executionMode;
168
+ const mode = input.executionMode ?? (manifestMode === 'desatendido' ? 'desatendido' : 'interactivo');
169
+ if (mode !== 'interactivo' && mode !== 'desatendido')
170
+ return blocked(input, [diagnostic('ADMISSION_EXECUTION_MODE_INVALID', 'Execution mode must be interactivo or desatendido.')], { planDigest: plan.planDigest, provider, executionMode: 'interactivo' });
171
+ if (input.requireCurrent) {
172
+ if (input.provenance !== 'proven')
173
+ return blocked(input, [diagnostic('ADMISSION_CURRENTNESS_PROVENANCE_REQUIRED', 'Consumed registry contracts cannot be proven from validated plan sources.')], { planDigest: plan.planDigest, provider, executionMode: mode, currentness: 'unverifiable' });
174
+ if (!input.currentness)
175
+ return blocked(input, [diagnostic('ADMISSION_CURRENTNESS_REQUIRED', 'Currentness evidence was required but not supplied.')], { planDigest: plan.planDigest, provider, executionMode: mode });
176
+ const gate = currentness(input.currentness, input.consumedRegistryComponents ?? []);
177
+ if (gate.diagnostics.length)
178
+ return blocked(input, gate.diagnostics, { planDigest: plan.planDigest, provider, executionMode: mode, currentness: gate.status });
179
+ if (input.compatibilityDiagnostics?.length)
180
+ return blocked(input, input.compatibilityDiagnostics, { planDigest: plan.planDigest, provider, executionMode: mode, currentness: 'unverifiable' });
181
+ }
182
+ if (input.verifySensors) {
183
+ if (!input.sensors)
184
+ return blocked(input, [diagnostic('ADMISSION_SENSORS_REQUIRED', 'Sensor evidence was required but not supplied.')], { planDigest: plan.planDigest, provider, executionMode: mode, currentness: input.requireCurrent ? 'current' : 'not-checked' });
185
+ const sensors = sensorVerdict(input.sensors);
186
+ if (sensors !== 'pass')
187
+ return blocked(input, [diagnostic('ADMISSION_SENSORS_BLOCKED', `Sensor verdict is ${sensors}.`)], { planDigest: plan.planDigest, provider, executionMode: mode, currentness: input.requireCurrent ? 'current' : 'not-checked', sensors });
188
+ }
189
+ if (mode === 'desatendido') {
190
+ const journal = journalStatus(input, plan);
191
+ if (journal !== 'current')
192
+ return blocked(input, [diagnostic('ADMISSION_JOURNAL_BINDING_REQUIRED', 'Unattended execution requires a healthy schema-2 journal binding for this exact plan; run watch --init --plan.')], { planDigest: plan.planDigest, provider, executionMode: mode, journal, currentness: input.requireCurrent ? 'current' : 'not-checked', sensors: input.verifySensors ? 'pass' : 'not-required' });
193
+ const capabilities = exports.PROVIDER_EXECUTION_CAPABILITIES[provider];
194
+ const resolution = { outcome: capabilities.unattendedController === 'supported' ? 'native' : 'blocked', provider, capabilities, evidenceVersion: 'r1-v1', diagnostics: [] };
195
+ if (resolution.outcome === 'blocked')
196
+ return blocked(input, [diagnostic('ADMISSION_CAPABILITY_UNVERIFIED', `Provider ${provider} has no verified unattended execution capability.`)], { planDigest: plan.planDigest, provider, executionMode: mode, journal, currentness: input.requireCurrent ? 'current' : 'not-checked', sensors: input.verifySensors ? 'pass' : 'not-required', capabilityResolution: resolution });
197
+ return completeAdmission(input, plan, provider, mode, journal, input.requireCurrent ? 'current' : 'not-checked', input.verifySensors ? 'pass' : 'not-required', resolution);
198
+ }
199
+ const capabilities = exports.PROVIDER_EXECUTION_CAPABILITIES[provider];
200
+ const resolution = { outcome: capabilities.interactiveExecution === 'supported' ? 'native' : 'blocked', provider, capabilities, evidenceVersion: 'r1-v1', diagnostics: [] };
201
+ if (resolution.outcome === 'blocked')
202
+ return blocked(input, [diagnostic('ADMISSION_CAPABILITY_UNVERIFIED', `Provider ${provider} has no verified interactive execution capability.`)], { planDigest: plan.planDigest, provider, executionMode: mode, capabilityResolution: resolution });
203
+ return completeAdmission(input, plan, provider, mode, 'not-required', input.requireCurrent ? 'current' : 'not-checked', input.verifySensors ? 'pass' : 'not-required', resolution);
204
+ }
205
+ function completeAdmission(input, plan, provider, executionMode, journal, currentness, sensors, capabilityResolution) {
206
+ if (plan.schema === 'compact-slices/v1')
207
+ return { state: 'admitted', planState: 'valid', planDigest: plan.planDigest, provider, executionMode, journal, currentness, sensors, capabilityResolution, forecast: forecast(plan.manifest.slices.length), diagnostics: [] };
208
+ const routing = input.routing ?? input.routingReader?.();
209
+ if (!routing?.runtime || !routing.policy || !routing.capabilities)
210
+ return blocked(input, [diagnostic('ADMISSION_ROUTING_FACTS_REQUIRED', 'Compact v2 requires an approved policy, current capability receipt, and runtime identity.')], { planDigest: plan.planDigest, provider, executionMode, journal, currentness, sensors, capabilityResolution });
211
+ let runtime;
212
+ try {
213
+ runtime = (0, capabilities_1.validateRuntimeKey)(routing.runtime);
214
+ }
215
+ catch {
216
+ return blocked(input, [diagnostic('ADMISSION_ROUTING_RUNTIME_INVALID', 'Compact v2 routing runtime identity is invalid.')], { planDigest: plan.planDigest, provider, executionMode, journal, currentness, sensors, capabilityResolution });
217
+ }
218
+ if (runtime.target !== provider)
219
+ return blocked(input, [diagnostic('ADMISSION_ROUTING_PROVIDER_MISMATCH', 'Compact v2 routing runtime target must exactly match the admitted provider.')], { planDigest: plan.planDigest, provider, executionMode, journal, currentness, sensors, capabilityResolution });
220
+ const now = routing.now ?? new Date();
221
+ for (const slice of plan.manifest.slices) {
222
+ const resolved = (0, resolve_1.resolveSelection)({ role: 'implementer', requestedProfile: slice.implementerProfile, policy: routing.policy, capabilities: routing.capabilities, runtime, now });
223
+ if (resolved.state === 'blocked')
224
+ return blocked(input, resolved.diagnostics, { planDigest: plan.planDigest, provider, executionMode, journal, currentness, sensors, capabilityResolution });
225
+ }
226
+ return { state: 'admitted', planState: 'valid', planDigest: plan.planDigest, provider, executionMode, journal, currentness, sensors, capabilityResolution, forecast: forecast(plan.manifest.slices.length), routingForecast: routingForecast(plan, routing), diagnostics: [] };
227
+ }
228
+ // Structural exhaustiveness guard: adding an AgentTarget requires an explicit entry above.
229
+ const capabilityTargets = providers_1.AGENT_TARGETS;
230
+ if (Object.keys(exports.PROVIDER_EXECUTION_CAPABILITIES).length !== capabilityTargets.length || !capabilityTargets.every(target => target in exports.PROVIDER_EXECUTION_CAPABILITIES))
231
+ throw new Error('provider capability table must be exhaustive');