agentic-workflow-manager 9.7.0 → 9.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -1
- package/dist/src/commands/doctor.js +4 -2
- package/dist/src/commands/export.js +3 -0
- package/dist/src/commands/init.js +10 -2
- package/dist/src/commands/job/exec-wrapper.js +53 -3
- package/dist/src/commands/plan/index.js +124 -6
- package/dist/src/commands/registry/add.js +2 -2
- package/dist/src/commands/registry/index.js +7 -5
- package/dist/src/commands/registry/install-bundles.js +4 -4
- package/dist/src/commands/registry/status.js +4 -4
- package/dist/src/commands/sensors/bootstrap.js +36 -5
- package/dist/src/commands/sensors/compatibility/contract.js +19 -4
- package/dist/src/commands/sensors/compatibility/discovery.js +23 -3
- package/dist/src/commands/sensors/compatibility/live.js +18 -1
- package/dist/src/commands/sensors/compatibility/probe.js +8 -2
- package/dist/src/commands/sensors/compatibility/resolve.js +5 -2
- package/dist/src/commands/sensors/coverage/evidence.js +19 -10
- package/dist/src/commands/sensors/coverage/index.js +11 -2
- package/dist/src/commands/sensors/coverage/resolve.js +10 -0
- package/dist/src/commands/sensors/exec.js +16 -1
- package/dist/src/commands/sensors/migrate.js +62 -2
- package/dist/src/commands/sensors/prepare.js +2 -0
- package/dist/src/commands/sensors/result.js +2 -0
- package/dist/src/commands/sensors/run.js +64 -1
- package/dist/src/commands/sensors/status.js +6 -4
- package/dist/src/commands/sync.js +2 -1
- package/dist/src/commands/update.js +3 -1
- package/dist/src/commands/watch/archive-unused.js +138 -0
- package/dist/src/commands/watch/generations.js +32 -9
- package/dist/src/commands/watch/index.js +108 -2
- package/dist/src/commands/watch/init.js +149 -18
- package/dist/src/commands/watch/supervisor.js +167 -13
- package/dist/src/commands/watch/teardown-driver.js +13 -13
- package/dist/src/commands/watch/track-state.js +59 -0
- package/dist/src/commands/watch/tracks.js +38 -102
- package/dist/src/core/admission/index.js +181 -0
- package/dist/src/core/admission/registry-contracts.js +290 -0
- package/dist/src/core/bundle-install.js +2 -2
- package/dist/src/core/bundles.js +92 -19
- package/dist/src/core/diagnostics/context.js +1 -1
- package/dist/src/core/export/index.js +1 -1
- package/dist/src/core/export/resolve.js +2 -2
- package/dist/src/core/journal/fingerprint.js +35 -1
- package/dist/src/core/journal/paths.js +11 -0
- package/dist/src/core/journal/store.js +146 -4
- package/dist/src/core/journal/types.js +35 -1
- package/dist/src/core/migration/historical-provenance.js +18 -0
- package/dist/src/core/migration/index.js +322 -0
- package/dist/src/core/plan/identity.js +75 -0
- package/dist/src/core/plan/validate.js +178 -64
- package/dist/src/core/reconciliation.js +1 -1
- package/dist/src/index.js +46 -39
- package/dist/src/utils/grouping.js +1 -1
- package/dist/src/utils/registry-view.js +1 -1
- package/dist/tests/commands/bundle-diagnostics.test.js +145 -0
- package/dist/tests/commands/export.test.js +28 -2
- package/dist/tests/commands/job/exec-wrapper.test.js +16 -0
- package/dist/tests/commands/job/gate-reconcile.test.js +24 -10
- package/dist/tests/commands/multi-agent-targeting.test.js +1 -1
- package/dist/tests/commands/pin.test.js +23 -23
- package/dist/tests/commands/plan/index.test.js +630 -14
- package/dist/tests/commands/sensors/bootstrap.test.js +57 -4
- package/dist/tests/commands/sensors/compatibility/contract.test.js +21 -0
- package/dist/tests/commands/sensors/compatibility/discovery.test.js +19 -0
- package/dist/tests/commands/sensors/compatibility/probe.test.js +14 -1
- package/dist/tests/commands/sensors/compatibility/resolve.test.js +17 -0
- package/dist/tests/commands/sensors/coverage/evidence.test.js +51 -27
- package/dist/tests/commands/sensors/coverage/resolve.test.js +51 -0
- package/dist/tests/commands/sensors/exec.test.js +112 -0
- package/dist/tests/commands/sensors/run-changed.test.js +11 -0
- package/dist/tests/commands/sensors/run.test.js +15 -2
- package/dist/tests/commands/sensors/status.test.js +3 -3
- package/dist/tests/commands/watch/e2e-crash.test.js +126 -11
- package/dist/tests/commands/watch/generations.test.js +113 -4
- package/dist/tests/commands/watch/plan-lifecycle.test.js +263 -0
- package/dist/tests/commands/watch/supervisor-loop.test.js +247 -49
- package/dist/tests/commands/watch/track-finalize.test.js +20 -3
- package/dist/tests/commands/watch/track-freeze.test.js +43 -30
- package/dist/tests/commands/watch/track-state-dependency.test.js +15 -0
- package/dist/tests/commands/watch/watch-init.test.js +244 -0
- package/dist/tests/core/admission/index.test.js +87 -0
- package/dist/tests/core/bundle-install.test.js +36 -0
- package/dist/tests/core/bundles-multiroot.test.js +24 -2
- package/dist/tests/core/bundles-overrides.test.js +10 -7
- package/dist/tests/core/bundles.test.js +75 -6
- package/dist/tests/core/diagnostics/context.test.js +2 -2
- package/dist/tests/core/export/engine.test.js +1 -1
- package/dist/tests/core/export/resolve.test.js +1 -1
- package/dist/tests/core/init/mutation-targets.test.js +1 -1
- package/dist/tests/core/init/orchestrator.test.js +1 -1
- package/dist/tests/core/init/steps.test.js +1 -1
- package/dist/tests/core/journal/fingerprint.test.js +22 -0
- package/dist/tests/core/journal/store.test.js +53 -0
- package/dist/tests/core/journal/types.test.js +7 -0
- package/dist/tests/core/migration/facts.test.js +289 -0
- package/dist/tests/core/migration/historical-provenance.test.js +25 -0
- package/dist/tests/core/plan/validate.test.js +320 -6
- package/dist/tests/core/reconciliation.test.js +1 -1
- package/dist/tests/core/sync-profile-atomicity.test.js +1 -1
- package/dist/tests/integration/pack-e2e.test.js +4 -1
- package/dist/tests/integration/preflight-json-pipe.e2e.test.js +7 -3
- package/dist/tests/integration/sensor-bootstrap.e2e.test.js +37 -0
- package/dist/tests/integration/sensor-structured-run.e2e.test.js +12 -10
- package/dist/tests/providers/injection-config.test.js +14 -6
- package/dist/tests/structural/bundle-skill-reference-contract.test.js +56 -0
- package/dist/tests/structural/jest-environment-is-isolated.test.js +29 -24
- package/dist/tests/structural/native-release-artifacts.test.js +16 -0
- package/dist/tests/utils/grouping.test.js +2 -2
- package/dist/tests/utils/registry-view.test.js +7 -7
- package/package.json +2 -2
- package/prebuilds/darwin-arm64/secure_fs.node +0 -0
- package/prebuilds/darwin-x64/secure_fs.node +0 -0
- package/prebuilds/win32-arm64/secure_fs.node +0 -0
- 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,181 @@
|
|
|
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 UNKNOWN = {
|
|
9
|
+
artifactDelivery: 'unverified', interactiveExecution: 'unverified', unattendedController: 'unverified', nativeSubagents: 'unverified',
|
|
10
|
+
modelOverride: 'unverified', effortOverride: 'unverified', observedModelEvidence: 'unverified', durableResume: 'unverified',
|
|
11
|
+
};
|
|
12
|
+
/** Deliberate R1 evidence table. Artifact delivery is never used as execution evidence. */
|
|
13
|
+
exports.PROVIDER_EXECUTION_CAPABILITIES = {
|
|
14
|
+
antigravity: { ...UNKNOWN },
|
|
15
|
+
opencode: { ...UNKNOWN },
|
|
16
|
+
'claude-code': { ...UNKNOWN, interactiveExecution: 'supported', unattendedController: 'supported', durableResume: 'supported' },
|
|
17
|
+
codex: { ...UNKNOWN, interactiveExecution: 'supported', unattendedController: 'supported', durableResume: 'supported' },
|
|
18
|
+
cursor: { ...UNKNOWN },
|
|
19
|
+
copilot: { ...UNKNOWN },
|
|
20
|
+
};
|
|
21
|
+
const MAX_DIAGNOSTICS = 20;
|
|
22
|
+
const MAX_DIAGNOSTIC_LENGTH = 4096;
|
|
23
|
+
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); }
|
|
24
|
+
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) } : {}) })); }
|
|
25
|
+
function diagnostic(code, message) { return { code: safe(code), message: safe(message) }; }
|
|
26
|
+
function blocked(input, diagnostics, extras = {}) {
|
|
27
|
+
const { journal = 'not-required', currentness = 'not-checked', sensors = 'not-required', ...rest } = extras;
|
|
28
|
+
return { state: 'blocked', planState: input.plan.state, journal, currentness, sensors, ...rest, diagnostics: sanitizeDiagnostics(diagnostics) };
|
|
29
|
+
}
|
|
30
|
+
function forecast(slices) {
|
|
31
|
+
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 };
|
|
32
|
+
return { kind: 'topology', slices, roles, total: Object.values(roles).reduce((sum, count) => sum + count, 0) };
|
|
33
|
+
}
|
|
34
|
+
function currentness(report, consumedRegistryComponents) {
|
|
35
|
+
const consumed = new Set(['cli', ...consumedRegistryComponents]);
|
|
36
|
+
const present = new Set(report.components.map(component => component.component));
|
|
37
|
+
const missing = [...consumed].filter(component => !present.has(component));
|
|
38
|
+
if (missing.length > 0)
|
|
39
|
+
return { status: 'unverifiable', diagnostics: [diagnostic('ADMISSION_CURRENTNESS_MISSING_COMPONENT', `Currentness evidence is missing consumed components: ${missing.join(', ')}.`)] };
|
|
40
|
+
const failed = report.components.filter(component => consumed.has(component.component) && component.status !== 'current');
|
|
41
|
+
if (failed.length === 0)
|
|
42
|
+
return { status: 'current', diagnostics: [] };
|
|
43
|
+
const names = failed.map(component => component.component).join(', ');
|
|
44
|
+
const status = failed.some(component => component.status === 'unverifiable') ? 'unverifiable' : 'stale';
|
|
45
|
+
return { status, diagnostics: [diagnostic('ADMISSION_CURRENTNESS_BLOCKED', `Consumed contract currentness is ${status}: ${names}.`)] };
|
|
46
|
+
}
|
|
47
|
+
function sensorVerdict(report) { return report.overall === 'not_certified' ? 'not-certified' : report.overall === 'pass' ? 'pass' : 'fail'; }
|
|
48
|
+
function journalStatus(input, plan) {
|
|
49
|
+
if (input.journalCorrupt)
|
|
50
|
+
return 'corrupt';
|
|
51
|
+
const journal = input.journalState;
|
|
52
|
+
if (!journal)
|
|
53
|
+
return 'missing';
|
|
54
|
+
if (journal.schema !== 2 || !journal.planBinding)
|
|
55
|
+
return 'stale';
|
|
56
|
+
let planPath;
|
|
57
|
+
try {
|
|
58
|
+
planPath = (0, paths_1.bindingPlanPath)(input.planPath ?? '');
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
return 'stale';
|
|
62
|
+
}
|
|
63
|
+
const binding = journal.planBinding;
|
|
64
|
+
return binding.path === planPath && binding.digest === plan.planDigest && binding.schema === plan.schema && binding.executionMode === 'desatendido'
|
|
65
|
+
? 'current' : 'stale';
|
|
66
|
+
}
|
|
67
|
+
const PLAN_STATES = new Set(['valid', 'migration-required', 'invalid', 'unsupported']);
|
|
68
|
+
const JOURNALS = new Set(['not-required', 'current', 'missing', 'corrupt', 'stale']);
|
|
69
|
+
const CURRENTNESS = new Set(['current', 'stale', 'unverifiable', 'not-checked']);
|
|
70
|
+
const SENSORS = new Set(['pass', 'fail', 'not-certified', 'not-required']);
|
|
71
|
+
const CAPABILITY = new Set(['supported', 'unsupported', 'unverified']);
|
|
72
|
+
function invalidAdmission() { throw new Error('admission returned an invalid report'); }
|
|
73
|
+
function validCapabilities(value) {
|
|
74
|
+
if (!value || typeof value !== 'object' || Array.isArray(value))
|
|
75
|
+
return false;
|
|
76
|
+
const record = value;
|
|
77
|
+
const keys = ['artifactDelivery', 'interactiveExecution', 'unattendedController', 'nativeSubagents', 'modelOverride', 'effortOverride', 'observedModelEvidence', 'durableResume'];
|
|
78
|
+
return Object.keys(record).length === keys.length && keys.every(key => CAPABILITY.has(record[key]));
|
|
79
|
+
}
|
|
80
|
+
function validForecast(value) {
|
|
81
|
+
if (!value || typeof value !== 'object' || Array.isArray(value))
|
|
82
|
+
return false;
|
|
83
|
+
const record = value;
|
|
84
|
+
const roles = ['implementer', 'specification-reviewer', 'code-quality-reviewer', 'final-reviewer', 'track-a-qa', 'track-b-qa', 'documentation', 'retro', 'finishing'];
|
|
85
|
+
const slices = record.slices;
|
|
86
|
+
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 };
|
|
87
|
+
return record.kind === 'topology' && Number.isSafeInteger(slices) && slices >= 0 && Number.isSafeInteger(record.total) && record.total >= 0
|
|
88
|
+
&& !!record.roles && typeof record.roles === 'object' && !Array.isArray(record.roles) && Object.keys(record.roles).length === roles.length
|
|
89
|
+
&& roles.every(role => record.roles[role] === topology[role])
|
|
90
|
+
&& record.total === Object.values(topology).reduce((sum, count) => sum + count, 0);
|
|
91
|
+
}
|
|
92
|
+
function validDiagnostics(value) {
|
|
93
|
+
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'));
|
|
94
|
+
}
|
|
95
|
+
/** Defensive public-output boundary for injected dependencies as well as core reports. */
|
|
96
|
+
function sanitizeAdmissionReport(report) {
|
|
97
|
+
if (!report || typeof report !== 'object' || Array.isArray(report))
|
|
98
|
+
invalidAdmission();
|
|
99
|
+
const value = report;
|
|
100
|
+
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))
|
|
101
|
+
invalidAdmission();
|
|
102
|
+
if (value.planDigest !== undefined && (typeof value.planDigest !== 'string' || !/^[a-f0-9]{64}$/.test(value.planDigest)))
|
|
103
|
+
invalidAdmission();
|
|
104
|
+
if (value.executionMode !== undefined && value.executionMode !== 'interactivo' && value.executionMode !== 'desatendido')
|
|
105
|
+
invalidAdmission();
|
|
106
|
+
if (value.provider !== undefined && !(0, providers_1.isAgentTarget)(value.provider))
|
|
107
|
+
invalidAdmission();
|
|
108
|
+
if (value.capabilityResolution !== undefined) {
|
|
109
|
+
const resolution = value.capabilityResolution;
|
|
110
|
+
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))
|
|
111
|
+
invalidAdmission();
|
|
112
|
+
}
|
|
113
|
+
if (value.forecast !== undefined && !validForecast(value.forecast))
|
|
114
|
+
invalidAdmission();
|
|
115
|
+
const capabilityResolution = value.capabilityResolution === undefined ? undefined : { ...value.capabilityResolution, diagnostics: sanitizeDiagnostics(value.capabilityResolution.diagnostics) };
|
|
116
|
+
return { ...value, ...(capabilityResolution ? { capabilityResolution } : {}), diagnostics: sanitizeDiagnostics(value.diagnostics) };
|
|
117
|
+
}
|
|
118
|
+
/** Pure, read-only composition. Callers own every I/O dependency and no dispatch API is exposed here. */
|
|
119
|
+
async function admitPlan(input) {
|
|
120
|
+
if (!input || typeof input !== 'object' || typeof input.cwd !== 'string' || input.cwd.length === 0)
|
|
121
|
+
throw new Error('admission requires a cwd');
|
|
122
|
+
const plan = input.plan;
|
|
123
|
+
if (!plan || typeof plan !== 'object')
|
|
124
|
+
throw new Error('admission requires a plan report');
|
|
125
|
+
if (plan.state !== 'valid') {
|
|
126
|
+
if (plan.state === 'unsupported')
|
|
127
|
+
return blocked(input, [diagnostic('ADMISSION_PLAN_UNSUPPORTED', 'This compact plan schema is not supported for admission.'), ...plan.diagnostics]);
|
|
128
|
+
const code = plan.state === 'migration-required' ? 'ADMISSION_PLAN_MIGRATION_REQUIRED' : 'ADMISSION_PLAN_INVALID';
|
|
129
|
+
return blocked(input, [diagnostic(code, 'Compact plan validation must succeed before admission.'), ...(plan.state === 'invalid' ? plan.diagnostics : [])]);
|
|
130
|
+
}
|
|
131
|
+
if (!(0, providers_1.isAgentTarget)(input.provider))
|
|
132
|
+
return blocked(input, [diagnostic('ADMISSION_PROVIDER_INVALID', `Unknown provider: ${String(input.provider)}.`)], { planDigest: plan.planDigest });
|
|
133
|
+
const provider = input.provider;
|
|
134
|
+
if (input.enabledAgents && !input.enabledAgents.includes(provider)) {
|
|
135
|
+
return blocked(input, [diagnostic('ADMISSION_PROVIDER_DISABLED', `Provider ${provider} is not enabled.`)], { planDigest: plan.planDigest, provider });
|
|
136
|
+
}
|
|
137
|
+
// v1 manifests do not yet carry executionMode in their typed schema. Accept only
|
|
138
|
+
// the exact future field when an already-validated caller supplies it; otherwise
|
|
139
|
+
// preserve the safe interactive default until S3 owns durable unattended binding.
|
|
140
|
+
const manifestMode = plan.manifest.executionMode;
|
|
141
|
+
const mode = input.executionMode ?? (manifestMode === 'desatendido' ? 'desatendido' : 'interactivo');
|
|
142
|
+
if (mode !== 'interactivo' && mode !== 'desatendido')
|
|
143
|
+
return blocked(input, [diagnostic('ADMISSION_EXECUTION_MODE_INVALID', 'Execution mode must be interactivo or desatendido.')], { planDigest: plan.planDigest, provider, executionMode: 'interactivo' });
|
|
144
|
+
if (input.requireCurrent) {
|
|
145
|
+
if (input.provenance !== 'proven')
|
|
146
|
+
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' });
|
|
147
|
+
if (!input.currentness)
|
|
148
|
+
return blocked(input, [diagnostic('ADMISSION_CURRENTNESS_REQUIRED', 'Currentness evidence was required but not supplied.')], { planDigest: plan.planDigest, provider, executionMode: mode });
|
|
149
|
+
const gate = currentness(input.currentness, input.consumedRegistryComponents ?? []);
|
|
150
|
+
if (gate.diagnostics.length)
|
|
151
|
+
return blocked(input, gate.diagnostics, { planDigest: plan.planDigest, provider, executionMode: mode, currentness: gate.status });
|
|
152
|
+
if (input.compatibilityDiagnostics?.length)
|
|
153
|
+
return blocked(input, input.compatibilityDiagnostics, { planDigest: plan.planDigest, provider, executionMode: mode, currentness: 'unverifiable' });
|
|
154
|
+
}
|
|
155
|
+
if (input.verifySensors) {
|
|
156
|
+
if (!input.sensors)
|
|
157
|
+
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' });
|
|
158
|
+
const sensors = sensorVerdict(input.sensors);
|
|
159
|
+
if (sensors !== 'pass')
|
|
160
|
+
return blocked(input, [diagnostic('ADMISSION_SENSORS_BLOCKED', `Sensor verdict is ${sensors}.`)], { planDigest: plan.planDigest, provider, executionMode: mode, currentness: input.requireCurrent ? 'current' : 'not-checked', sensors });
|
|
161
|
+
}
|
|
162
|
+
if (mode === 'desatendido') {
|
|
163
|
+
const journal = journalStatus(input, plan);
|
|
164
|
+
if (journal !== 'current')
|
|
165
|
+
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' });
|
|
166
|
+
const capabilities = exports.PROVIDER_EXECUTION_CAPABILITIES[provider];
|
|
167
|
+
const resolution = { outcome: capabilities.unattendedController === 'supported' ? 'native' : 'blocked', provider, capabilities, evidenceVersion: 'r1-v1', diagnostics: [] };
|
|
168
|
+
if (resolution.outcome === 'blocked')
|
|
169
|
+
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 });
|
|
170
|
+
return { state: 'admitted', planState: 'valid', planDigest: plan.planDigest, provider, executionMode: mode, journal, currentness: input.requireCurrent ? 'current' : 'not-checked', sensors: input.verifySensors ? 'pass' : 'not-required', capabilityResolution: resolution, forecast: forecast(plan.manifest.slices.length), diagnostics: [] };
|
|
171
|
+
}
|
|
172
|
+
const capabilities = exports.PROVIDER_EXECUTION_CAPABILITIES[provider];
|
|
173
|
+
const resolution = { outcome: capabilities.interactiveExecution === 'supported' ? 'native' : 'blocked', provider, capabilities, evidenceVersion: 'r1-v1', diagnostics: [] };
|
|
174
|
+
if (resolution.outcome === 'blocked')
|
|
175
|
+
return blocked(input, [diagnostic('ADMISSION_CAPABILITY_UNVERIFIED', `Provider ${provider} has no verified interactive execution capability.`)], { planDigest: plan.planDigest, provider, executionMode: mode, capabilityResolution: resolution });
|
|
176
|
+
return { state: 'admitted', planState: 'valid', planDigest: plan.planDigest, provider, executionMode: mode, journal: 'not-required', currentness: input.requireCurrent ? 'current' : 'not-checked', sensors: input.verifySensors ? 'pass' : 'not-required', capabilityResolution: resolution, forecast: forecast(plan.manifest.slices.length), diagnostics: [] };
|
|
177
|
+
}
|
|
178
|
+
// Structural exhaustiveness guard: adding an AgentTarget requires an explicit entry above.
|
|
179
|
+
const capabilityTargets = providers_1.AGENT_TARGETS;
|
|
180
|
+
if (Object.keys(exports.PROVIDER_EXECUTION_CAPABILITIES).length !== capabilityTargets.length || !capabilityTargets.every(target => target in exports.PROVIDER_EXECUTION_CAPABILITIES))
|
|
181
|
+
throw new Error('provider capability table must be exhaustive');
|