@quolu/lattice 0.50.1 → 0.52.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/bin/lattice-work-order-adapter.mjs +20 -0
- package/docs/schemas/lattice.runtime_adapter_capabilities.v2.schema.json +55 -0
- package/docs/schemas/lattice.runtime_adapter_registration_input.v2.schema.json +86 -0
- package/package.json +5 -2
- package/src/boundary-observation-compiler-v2.mjs +1 -1
- package/src/cli-help.mjs +33 -2
- package/src/rc3-actual-dogfood.mjs +6 -2
- package/src/rc3-scripted-campaign.mjs +37 -10
- package/src/rc4-stage1-dogfood.mjs +6 -2
- package/src/runtime-adapter-registry.mjs +21 -7
- package/src/runtime-cli.mjs +476 -34
- package/src/runtime-contracts.mjs +59 -13
- package/src/runtime-controller-protocol.mjs +48 -3
- package/src/runtime-decision-verifier.mjs +70 -0
- package/src/runtime-diff-observer.mjs +66 -4
- package/src/runtime-direct-os-observer.mjs +25 -8
- package/src/runtime-driver-state.mjs +162 -0
- package/src/runtime-engine.mjs +37 -6
- package/src/runtime-front-end.mjs +39 -1
- package/src/runtime-managed-supervisor.mjs +80 -14
- package/src/runtime-multi-epoch-store.mjs +87 -14
- package/src/runtime-pull-intake.mjs +1188 -0
- package/src/runtime-work-order-contracts.mjs +91 -0
- package/src/runtime-work-order-controller.mjs +1167 -0
- package/src/seam-proposal-queries.mjs +1 -1
- package/src/todo-cli.mjs +273 -7
- package/src/todo-contracts.mjs +19 -2
- package/src/todo-gantt-html-independence.mjs +3 -2
- package/src/todo-gantt-html-shared.mjs +1 -2
- package/src/todo-gantt-html-style.mjs +13 -0
- package/src/todo-gantt-html.mjs +15 -2
- package/src/todo-gantt-layout.mjs +75 -1
- package/src/todo-gantt-nested.mjs +263 -0
- package/src/todo-gantt-svg.mjs +80 -5
- package/src/todo-independence-contracts.mjs +73 -7
- package/src/todo-independence-guidance.mjs +30 -1
- package/src/todo-independence.mjs +89 -7
- package/src/todo-revision.mjs +1 -1
- package/src/todo-split.mjs +472 -0
- package/src/todo-status.mjs +10 -1
- package/src/todo-store-git-transaction.mjs +418 -0
- package/src/todo-store.mjs +144 -4
package/src/runtime-cli.mjs
CHANGED
|
@@ -32,6 +32,11 @@ import {
|
|
|
32
32
|
compileRuntimePlanV1,
|
|
33
33
|
evidenceFromCollectedOutcomes,
|
|
34
34
|
} from './runtime-front-end.mjs';
|
|
35
|
+
import {
|
|
36
|
+
explainTodoWitnessSet,
|
|
37
|
+
isGitSha,
|
|
38
|
+
synthesizeWitnessRunRequest,
|
|
39
|
+
} from './todo-independence-contracts.mjs';
|
|
35
40
|
import {
|
|
36
41
|
explainRunRequest,
|
|
37
42
|
validateRunRequest,
|
|
@@ -73,15 +78,44 @@ import {
|
|
|
73
78
|
isManagedRunFrozen,
|
|
74
79
|
readCommittedEpochStore,
|
|
75
80
|
recordRuntimeFinding,
|
|
81
|
+
rejectFutureRuntimeStoreSchema,
|
|
76
82
|
stageSuccessorEpoch,
|
|
77
83
|
validateRuntimeEpochBundle,
|
|
78
84
|
validateRuntimeFindingCandidate,
|
|
79
85
|
validateRuntimeFindingRecord,
|
|
80
86
|
} from './runtime-multi-epoch-store.mjs';
|
|
81
|
-
import {
|
|
87
|
+
import {
|
|
88
|
+
acceptsHostDrivenEpoch,
|
|
89
|
+
createRuntimeControlRequest,
|
|
90
|
+
validateRuntimeControlResponse,
|
|
91
|
+
} from './runtime-controller-protocol.mjs';
|
|
82
92
|
import { createRuntimeControlStore } from './runtime-control-store.mjs';
|
|
83
93
|
import { createRuntimeGateStore } from './runtime-gate-store.mjs';
|
|
84
|
-
import {
|
|
94
|
+
import {
|
|
95
|
+
RuntimeLifecycleLockError,
|
|
96
|
+
acquireRuntimeLifecycleLock,
|
|
97
|
+
} from './runtime-lifecycle-lock.mjs';
|
|
98
|
+
import {
|
|
99
|
+
PullRunError,
|
|
100
|
+
acceptPullTask,
|
|
101
|
+
attachPullWorker,
|
|
102
|
+
closePullRun,
|
|
103
|
+
inspectRunMode as inspectPullRunMode,
|
|
104
|
+
intakePullTask,
|
|
105
|
+
interventionPullTask,
|
|
106
|
+
landingPullRun,
|
|
107
|
+
listPullRunEntry,
|
|
108
|
+
observePullRun,
|
|
109
|
+
releasePullTask,
|
|
110
|
+
startPullRun,
|
|
111
|
+
statusPullRun,
|
|
112
|
+
} from './runtime-pull-intake.mjs';
|
|
113
|
+
import {
|
|
114
|
+
RuntimeDriverStateError,
|
|
115
|
+
readRuntimeDriverState,
|
|
116
|
+
replaceRuntimeDriverState,
|
|
117
|
+
} from './runtime-driver-state.mjs';
|
|
118
|
+
|
|
85
119
|
import {
|
|
86
120
|
AdapterRegistryError,
|
|
87
121
|
listRuntimeAdapters,
|
|
@@ -93,9 +127,29 @@ import {
|
|
|
93
127
|
observeManagedProcessStartIdentity,
|
|
94
128
|
prepareManagedSupervisorRestart,
|
|
95
129
|
resolveActiveRuntimePaths,
|
|
96
|
-
|
|
130
|
+
sendRuntimeActivationRequestUntilSettled,
|
|
97
131
|
sendRuntimeControlRequest,
|
|
98
132
|
} from './runtime-managed-supervisor.mjs';
|
|
133
|
+
import { TodoStoreError } from './todo-store.mjs';
|
|
134
|
+
|
|
135
|
+
async function inspectRunMode(runDir) {
|
|
136
|
+
try {
|
|
137
|
+
return await inspectPullRunMode(runDir);
|
|
138
|
+
} catch (error) {
|
|
139
|
+
if (error instanceof PullRunError && error.code === 'UNSUPPORTED_RUN_STORE_SCHEMA') {
|
|
140
|
+
rejectFutureRuntimeStoreSchema(error.detail?.schema, {
|
|
141
|
+
artifact: 'run_meta', family: 'lattice.run_meta', expectedVersion: 2,
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
throw error;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
async function requireLegacyRunMode(runDir, operation) {
|
|
149
|
+
if ((await inspectRunMode(runDir)).mode !== 'legacy') {
|
|
150
|
+
throw new PullRunError('RUN_MODE_MISMATCH', `pull runはlegacy専用${operation}を受け付けない`);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
99
153
|
|
|
100
154
|
/**
|
|
101
155
|
* RC3-D CLI surface(ADR 0044 Decision 8)。
|
|
@@ -103,11 +157,13 @@ import {
|
|
|
103
157
|
* lattice plan compile --request <run-request.json>
|
|
104
158
|
* lattice plan verify --request <run-request.json> --plan <plan.json>
|
|
105
159
|
* lattice run start --request <run-request.json> --executor <adapter>
|
|
160
|
+
* lattice run request synthesize --witness <witness-set.json> --base-sha <sha> --id <request-id>
|
|
106
161
|
* lattice run adapter register --input <descriptor.json>
|
|
107
162
|
* lattice run adapter list --json
|
|
108
163
|
* lattice run observe --run .lattice/runs/<run-id>
|
|
109
164
|
* lattice run status --run .lattice/runs/<run-id>
|
|
110
165
|
* lattice run resume --run .lattice/runs/<run-id>
|
|
166
|
+
* lattice run landing --run .lattice/runs/<run-id>
|
|
111
167
|
* lattice run close --run .lattice/runs/<run-id>
|
|
112
168
|
* lattice run abandon --run .lattice/runs/<run-id> --reason <reason>
|
|
113
169
|
* lattice event verify --run .lattice/runs/<run-id>
|
|
@@ -130,7 +186,9 @@ const CLI_ERROR_SCHEMA = 'lattice.cli_error.v2';
|
|
|
130
186
|
// 現役run storeは対象Git repo内のLattice-owned・ignored rootへ限定する。
|
|
131
187
|
const RUN_STORE_ROOT = ['.lattice', 'runs'];
|
|
132
188
|
const RUN_REF = /^\.lattice\/runs\/([0-9A-Za-z](?:[0-9A-Za-z._-]{0,127}))$/u;
|
|
133
|
-
const KNOWN_ADAPTERS = Object.freeze([
|
|
189
|
+
const KNOWN_ADAPTERS = Object.freeze([
|
|
190
|
+
'scripted', 'isolated-worktree', 'actual-agent', 'work-order',
|
|
191
|
+
]);
|
|
134
192
|
/**
|
|
135
193
|
* 自動escalationがlifecycle lockを待つ上限(ADR 0143)。
|
|
136
194
|
*
|
|
@@ -154,8 +212,7 @@ function ownedResourceId(kind, target) {
|
|
|
154
212
|
function ownedPathResourceId(pathValue) {
|
|
155
213
|
return ownedResourceId('path', pathValue);
|
|
156
214
|
}
|
|
157
|
-
/** 走行中worker
|
|
158
|
-
const SCRIPTED_OBSERVE_TIMEOUT_MS = 120_000;
|
|
215
|
+
/** 走行中workerの完了を観測する間隔。待てないと走行中の観測が成立しない。 */
|
|
159
216
|
const SCRIPTED_OBSERVE_POLL_MS = 20;
|
|
160
217
|
|
|
161
218
|
class CliContractError extends Error {
|
|
@@ -312,11 +369,11 @@ async function runRequestSchema({ stdout }) {
|
|
|
312
369
|
/** 公開登録入力を推測させないため、配布物に同梱したJSON Schemaをそのまま返す(ADR 0125)。 */
|
|
313
370
|
async function runAdapterRegisterSchema({ stdout }) {
|
|
314
371
|
const schemaUrl = new URL(
|
|
315
|
-
'../docs/schemas/lattice.runtime_adapter_registration_input.
|
|
372
|
+
'../docs/schemas/lattice.runtime_adapter_registration_input.v2.schema.json',
|
|
316
373
|
import.meta.url,
|
|
317
374
|
);
|
|
318
375
|
const schema = JSON.parse(await readFile(schemaUrl, 'utf8'));
|
|
319
|
-
if (schema?.title !== 'lattice.runtime_adapter_registration_input.
|
|
376
|
+
if (schema?.title !== 'lattice.runtime_adapter_registration_input.v2') {
|
|
320
377
|
throw new CliContractError('CONTRACT_VIOLATION', '同梱adapter registration input schemaが不正');
|
|
321
378
|
}
|
|
322
379
|
stdout.write(`${JSON.stringify(schema)}\n`);
|
|
@@ -346,6 +403,47 @@ async function runAdapterRegister({ cwd, inputPath, stdout }) {
|
|
|
346
403
|
}
|
|
347
404
|
}
|
|
348
405
|
|
|
406
|
+
/**
|
|
407
|
+
* witness setから`run start`へ渡せるrun requestを組み立てて返す(read-only)。
|
|
408
|
+
*
|
|
409
|
+
* **推測しない面である。** base shaもrequest idもCLIが決めず、呼び手が渡す——どのbaseへ
|
|
410
|
+
* 対する計画なのかは装置が知らないし、HEADを黙って採ると「撮ったつもりのbaseと違う木」で
|
|
411
|
+
* runが始まる。witness setの検査は`explainTodoWitnessSet`が単一の正本なので、ここでは
|
|
412
|
+
* 判定を持たず理由とpathをそのまま返す。
|
|
413
|
+
*
|
|
414
|
+
* request idの旗が`--id`なのは、`--request-id`が末尾2引数のときrun mutationの
|
|
415
|
+
* idempotency keyとして先に横取りされるためである(同じ綴りで別の意味を持たせない)。
|
|
416
|
+
*/
|
|
417
|
+
async function runRequestSynthesize({ cwd, witnessPath, baseSha, requestId, stdout }) {
|
|
418
|
+
if (!isGitSha(baseSha)) {
|
|
419
|
+
throw new CliContractError('INVALID_BASE_SHA', '--base-shaが40桁のgit shaでない', {
|
|
420
|
+
base_sha: baseSha,
|
|
421
|
+
});
|
|
422
|
+
}
|
|
423
|
+
if (!/^[0-9A-Za-z](?:[0-9A-Za-z._-]{0,127})$/u.test(requestId)) {
|
|
424
|
+
throw new CliContractError('INVALID_REQUEST_ID', '--idは128文字以下の識別子でなければならない', {
|
|
425
|
+
request_id: requestId,
|
|
426
|
+
});
|
|
427
|
+
}
|
|
428
|
+
const witnessSet = await readBoundedJson(path.resolve(cwd, witnessPath), 'witness set');
|
|
429
|
+
const verdict = explainTodoWitnessSet(witnessSet);
|
|
430
|
+
if (!verdict.valid) {
|
|
431
|
+
throw new CliContractError('WITNESS_SET_INVALID', 'witness setが契約を満たさない', {
|
|
432
|
+
path: witnessPath, reason: verdict.reason, pointer: verdict.path ?? null,
|
|
433
|
+
});
|
|
434
|
+
}
|
|
435
|
+
const request = synthesizeWitnessRunRequest(witnessSet, { baseSha, requestId });
|
|
436
|
+
const explained = explainRunRequest(request);
|
|
437
|
+
if (!explained.valid) {
|
|
438
|
+
// 生成物が自分の契約を満たさないなら、それは装置の不具合である。黙って出さない
|
|
439
|
+
throw new CliContractError('CONTRACT_VIOLATION', '生成したrun requestがrun_request契約を満たさない', {
|
|
440
|
+
reason: explained.reason, pointer: explained.path ?? null,
|
|
441
|
+
});
|
|
442
|
+
}
|
|
443
|
+
stdout.write(`${JSON.stringify(request)}\n`);
|
|
444
|
+
return 0;
|
|
445
|
+
}
|
|
446
|
+
|
|
349
447
|
async function runAdapterList({ cwd, stdout }) {
|
|
350
448
|
try {
|
|
351
449
|
const repoRoot = await resolveRepoRoot(cwd);
|
|
@@ -523,6 +621,9 @@ async function readRunStore(runDir) {
|
|
|
523
621
|
throw new CliContractError('INVALID_RUN_STORE', 'events.jsonがarrayではない');
|
|
524
622
|
}
|
|
525
623
|
const meta = await readBoundedJson(path.join(runDir, 'run-meta.json'), 'run meta');
|
|
624
|
+
rejectFutureRuntimeStoreSchema(meta?.schema, {
|
|
625
|
+
artifact: 'run_meta', family: 'lattice.run_meta', expectedVersion: 2,
|
|
626
|
+
});
|
|
526
627
|
const compileArtifact = await readBoundedJson(
|
|
527
628
|
path.join(runDir, 'plan-compile-result.json'), 'plan compile result',
|
|
528
629
|
);
|
|
@@ -536,9 +637,11 @@ async function readRunStore(runDir) {
|
|
|
536
637
|
&& meta.run_id === request.request_id
|
|
537
638
|
&& KNOWN_ADAPTERS.includes(meta.executor_adapter)
|
|
538
639
|
&& compileArtifact?.plan?.plan_digest === meta.plan_digest;
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
640
|
+
// v1だけがlegacy alias。その他はmulti-epoch readerへ渡し、既知familyの
|
|
641
|
+
// 将来世代を破損storeへ潰さずtyped診断する。
|
|
642
|
+
const managed = meta?.schema === 'lattice.run_meta.v1'
|
|
643
|
+
? null
|
|
644
|
+
: await readCommittedEpochStore(runDir);
|
|
542
645
|
const managedMetaValid = managed !== null
|
|
543
646
|
&& managed.meta.run_id === request.request_id
|
|
544
647
|
&& KNOWN_ADAPTERS.includes(managed.meta.executor_adapter)
|
|
@@ -806,6 +909,7 @@ async function driveScriptedManagedEpoch({
|
|
|
806
909
|
drainEscalations = null,
|
|
807
910
|
escalateTerminalConflict = null,
|
|
808
911
|
preactivated = null,
|
|
912
|
+
reportDriverState = null,
|
|
809
913
|
}) {
|
|
810
914
|
let events = [...initialEvents];
|
|
811
915
|
const { plan, manifests, executor_packets: packets } = committed.bundle;
|
|
@@ -819,6 +923,7 @@ async function driveScriptedManagedEpoch({
|
|
|
819
923
|
for (;;) {
|
|
820
924
|
const frontier = computeReadyFrontier({ plan, events }).dispatchable;
|
|
821
925
|
if (frontier.length === 0) break;
|
|
926
|
+
await reportDriverState?.({ kind: 'frontier_dispatch', todo_ids: [...frontier].sort() });
|
|
822
927
|
const alreadyRunning = projectRuntimeState({ events }).running;
|
|
823
928
|
const issuedControlDigest = controlEvents().at(-1)?.event_digest;
|
|
824
929
|
if (typeof issuedControlDigest !== 'string') {
|
|
@@ -936,9 +1041,11 @@ async function driveScriptedManagedEpoch({
|
|
|
936
1041
|
process_group_id: response.worker_process.process_group_id,
|
|
937
1042
|
process_start_identity:
|
|
938
1043
|
structuredClone(response.worker_process.process_start_identity),
|
|
939
|
-
// worker
|
|
940
|
-
//
|
|
1044
|
+
// 省略は従来adapterのstatic child集合。外部長寿命workerはroot identityと
|
|
1045
|
+
// PGIDだけを不変にし、barrier時点の全group member静止を検証する。
|
|
941
1046
|
process_children: [],
|
|
1047
|
+
process_membership_policy:
|
|
1048
|
+
response.worker_process.process_membership ?? 'static',
|
|
942
1049
|
// TODOごとの木を指す。ここがrepo rootだった頃、帰属はrootから決まらなかった。
|
|
943
1050
|
worktree_path: worktreeByTodo.get(packet.todo_id),
|
|
944
1051
|
worktree_realpath: worktreeByTodo.get(packet.todo_id),
|
|
@@ -1012,8 +1119,10 @@ async function driveScriptedManagedEpoch({
|
|
|
1012
1119
|
// 回し、その合間に早期警報のescalationを捌く。捌くのは`replaceEventsAtomically`の
|
|
1013
1120
|
// 直後だけ——そこでしかdiskとメモリのeventsが一致していない。
|
|
1014
1121
|
const awaiting = new Set([...dispatched.dispatched, ...alreadyRunning]);
|
|
1122
|
+
if (awaiting.size > 0) {
|
|
1123
|
+
await reportDriverState?.({ kind: 'executor_completion', todo_ids: [...awaiting].sort() });
|
|
1124
|
+
}
|
|
1015
1125
|
const completedReceiptIds = new Set();
|
|
1016
|
-
const observeDeadline = Date.now() + SCRIPTED_OBSERVE_TIMEOUT_MS;
|
|
1017
1126
|
let frozen = false;
|
|
1018
1127
|
while (awaiting.size > 0) {
|
|
1019
1128
|
const drained = await drainEscalations?.(events) ?? null;
|
|
@@ -1088,7 +1197,9 @@ async function driveScriptedManagedEpoch({
|
|
|
1088
1197
|
paths: peerCheckpoint.payload.diff.entries.map((entry) => entry.path) });
|
|
1089
1198
|
}
|
|
1090
1199
|
const actionable = classifyObservedDiff({ plan, manifests, observations,
|
|
1091
|
-
relevantTodoIds }).findings.filter((finding) =>
|
|
1200
|
+
relevantTodoIds }).findings.filter((finding) => [
|
|
1201
|
+
'observed_write_conflict', 'observed_line_change',
|
|
1202
|
+
].includes(finding.kind)
|
|
1092
1203
|
&& finding.todo_ids.includes(todoId));
|
|
1093
1204
|
if (actionable.length > 0) {
|
|
1094
1205
|
if (typeof escalateTerminalConflict !== 'function') {
|
|
@@ -1107,17 +1218,18 @@ async function driveScriptedManagedEpoch({
|
|
|
1107
1218
|
&& event.subject?.kind === 'todo' && event.subject.ref === id)
|
|
1108
1219
|
?.payload?.direct_os_observation_binding?.worktree_path });
|
|
1109
1220
|
awaiting.delete(todoId);
|
|
1221
|
+
if (awaiting.size > 0) {
|
|
1222
|
+
await reportDriverState?.({ kind: 'executor_completion', todo_ids: [...awaiting].sort() });
|
|
1223
|
+
}
|
|
1110
1224
|
progressed = true;
|
|
1111
1225
|
if (frozen) break;
|
|
1112
1226
|
}
|
|
1113
1227
|
if (frozen) break;
|
|
1114
1228
|
if (awaiting.size === 0) break;
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
);
|
|
1120
|
-
}
|
|
1229
|
+
// 長寿命workerの正常な作業時間を固定期限でcontroller障害へ変換しない。
|
|
1230
|
+
// controllerとのroute自体が失敗した時だけtyped errorになり、running応答中は
|
|
1231
|
+
// durable supervisorが観測を継続する。CLIはoutcome unknownで先に戻り得るが、
|
|
1232
|
+
// run resumeは同じstoreをread-only投影し、leaseを再認可しない。
|
|
1121
1233
|
if (!progressed) {
|
|
1122
1234
|
await new Promise((resolve) => { setTimeout(resolve, SCRIPTED_OBSERVE_POLL_MS); });
|
|
1123
1235
|
}
|
|
@@ -1147,8 +1259,14 @@ async function driveScriptedManagedEpoch({
|
|
|
1147
1259
|
return events;
|
|
1148
1260
|
}
|
|
1149
1261
|
|
|
1150
|
-
function
|
|
1151
|
-
|
|
1262
|
+
function isHostDrivenEpochActivation(activation) {
|
|
1263
|
+
const controllerCapabilities = activation?.controllerDescriptor?.capabilities;
|
|
1264
|
+
if (acceptsHostDrivenEpoch(controllerCapabilities)) {
|
|
1265
|
+
return activation?.launchDescriptor?.capabilities_digest
|
|
1266
|
+
=== controllerCapabilities.capabilities_digest;
|
|
1267
|
+
}
|
|
1268
|
+
return controllerCapabilities?.schema === 'lattice.runtime_adapter_capabilities.v1'
|
|
1269
|
+
&& activation?.controllerDescriptor?.adapter_kind === 'scripted'
|
|
1152
1270
|
&& activation?.launchDescriptor?.launch_kind === 'host_binary'
|
|
1153
1271
|
&& activation.launchDescriptor.argv.some((argument) => (
|
|
1154
1272
|
path.basename(argument) === 'lattice-scripted-adapter.mjs'
|
|
@@ -1238,6 +1356,19 @@ async function runStart({ requestPath, executorAdapter, cwd, stdout }) {
|
|
|
1238
1356
|
return 0;
|
|
1239
1357
|
}
|
|
1240
1358
|
|
|
1359
|
+
async function runPullStart({ runId, planKey, equipment, cwd, stdout }) {
|
|
1360
|
+
const repoRoot = await resolveRepoRoot(cwd);
|
|
1361
|
+
await requireSafeRunAncestors(repoRoot);
|
|
1362
|
+
await requireIgnoredRunStore(repoRoot);
|
|
1363
|
+
const runDir = runStorePath(repoRoot, runId);
|
|
1364
|
+
await mkdir(path.dirname(runDir), { recursive: true });
|
|
1365
|
+
const output = await startPullRun({ repoRoot, runDir, runId, planKey, equipment });
|
|
1366
|
+
output.run_dir = path.relative(repoRoot, runDir);
|
|
1367
|
+
output.result_digest = digestArtifact(output);
|
|
1368
|
+
stdout.write(`${JSON.stringify(output)}\n`);
|
|
1369
|
+
return 0;
|
|
1370
|
+
}
|
|
1371
|
+
|
|
1241
1372
|
/**
|
|
1242
1373
|
* 記録済み競合findingを、実際の変換で解消する(請求項8・ADR 0137〜0141)。
|
|
1243
1374
|
*
|
|
@@ -1341,7 +1472,54 @@ async function runSeamProfile({ runDir, repoRoot, findingDigest, inputPath, stdo
|
|
|
1341
1472
|
return 0;
|
|
1342
1473
|
}
|
|
1343
1474
|
|
|
1475
|
+
const stoppedDriverProjection = () => ({ driver_state: 'stopped', waiting_on: null });
|
|
1476
|
+
|
|
1477
|
+
async function runtimeDriverProjection(runDir) {
|
|
1478
|
+
const state = await readRuntimeDriverState({ runDir });
|
|
1479
|
+
if (state === null || state.driver_state === 'stopped') return stoppedDriverProjection();
|
|
1480
|
+
const descriptorPath = path.join(runDir, ...state.supervisor_descriptor_ref.split('/'));
|
|
1481
|
+
let descriptor;
|
|
1482
|
+
try {
|
|
1483
|
+
descriptor = await readBoundedJson(descriptorPath, 'runtime supervisor descriptor');
|
|
1484
|
+
} catch (error) {
|
|
1485
|
+
if (error instanceof CliContractError && error.code === 'INPUT_UNREADABLE') {
|
|
1486
|
+
return stoppedDriverProjection();
|
|
1487
|
+
}
|
|
1488
|
+
throw error;
|
|
1489
|
+
}
|
|
1490
|
+
if (descriptor?.descriptor_digest !== state.supervisor_descriptor_digest
|
|
1491
|
+
|| descriptor?.process_start_identity?.identity_digest
|
|
1492
|
+
!== state.supervisor_process_start_identity_digest) {
|
|
1493
|
+
throw new RuntimeDriverStateError('INVALID_RUN_STORE', 'driver stateがsupervisor descriptorへbindしない');
|
|
1494
|
+
}
|
|
1495
|
+
let observedIdentity;
|
|
1496
|
+
try {
|
|
1497
|
+
observedIdentity = await observeManagedProcessStartIdentity(descriptor.pid);
|
|
1498
|
+
} catch (error) {
|
|
1499
|
+
if (error instanceof ManagedRuntimeError && error.code === 'ADAPTER_CONTROLLER_UNAVAILABLE') {
|
|
1500
|
+
return stoppedDriverProjection();
|
|
1501
|
+
}
|
|
1502
|
+
throw error;
|
|
1503
|
+
}
|
|
1504
|
+
if (observedIdentity.identity_digest !== state.supervisor_process_start_identity_digest) {
|
|
1505
|
+
return stoppedDriverProjection();
|
|
1506
|
+
}
|
|
1507
|
+
return { driver_state: 'driving', waiting_on: structuredClone(state.waiting_on) };
|
|
1508
|
+
}
|
|
1509
|
+
|
|
1510
|
+
async function withRuntimeDriverProjection(output, runDir) {
|
|
1511
|
+
const projected = { ...output, ...await runtimeDriverProjection(runDir) };
|
|
1512
|
+
delete projected.result_digest;
|
|
1513
|
+
projected.result_digest = digestArtifact(projected);
|
|
1514
|
+
return projected;
|
|
1515
|
+
}
|
|
1516
|
+
|
|
1344
1517
|
async function runObserve({ runDir, stdout }) {
|
|
1518
|
+
if ((await inspectRunMode(runDir)).mode === 'pull') {
|
|
1519
|
+
const output = await withRuntimeDriverProjection(await observePullRun(runDir), runDir);
|
|
1520
|
+
stdout.write(`${JSON.stringify(output)}\n`);
|
|
1521
|
+
return 0;
|
|
1522
|
+
}
|
|
1345
1523
|
const { events } = await readRunStore(runDir);
|
|
1346
1524
|
const chain = verifyRunEventChain({ events });
|
|
1347
1525
|
if (!chain.valid) {
|
|
@@ -1359,6 +1537,7 @@ async function runObserve({ runDir, stdout }) {
|
|
|
1359
1537
|
closed: state.closed,
|
|
1360
1538
|
event_count: events.length,
|
|
1361
1539
|
events_digest: digestArtifact(events.map(({ event_digest: digest }) => digest)),
|
|
1540
|
+
...await runtimeDriverProjection(runDir),
|
|
1362
1541
|
};
|
|
1363
1542
|
output.result_digest = digestArtifact(output);
|
|
1364
1543
|
stdout.write(`${JSON.stringify(output)}\n`);
|
|
@@ -1366,6 +1545,11 @@ async function runObserve({ runDir, stdout }) {
|
|
|
1366
1545
|
}
|
|
1367
1546
|
|
|
1368
1547
|
async function runStatus({ runDir, stdout }) {
|
|
1548
|
+
if ((await inspectRunMode(runDir)).mode === 'pull') {
|
|
1549
|
+
const output = await withRuntimeDriverProjection(await statusPullRun(runDir), runDir);
|
|
1550
|
+
stdout.write(`${JSON.stringify(output)}\n`);
|
|
1551
|
+
return 0;
|
|
1552
|
+
}
|
|
1369
1553
|
const { events, meta, compileArtifact, managed } = await readRunStore(runDir);
|
|
1370
1554
|
const chain = verifyRunEventChain({ events });
|
|
1371
1555
|
if (!chain.valid) {
|
|
@@ -1386,6 +1570,7 @@ async function runStatus({ runDir, stdout }) {
|
|
|
1386
1570
|
freeze_active: state.freeze !== null,
|
|
1387
1571
|
closed: state.closed,
|
|
1388
1572
|
event_count: events.length,
|
|
1573
|
+
...await runtimeDriverProjection(runDir),
|
|
1389
1574
|
};
|
|
1390
1575
|
if (managed !== null) {
|
|
1391
1576
|
output.schema = 'lattice.managed_run_status.v1';
|
|
@@ -1425,6 +1610,14 @@ async function runList({ cwd, stdout }) {
|
|
|
1425
1610
|
throw new CliContractError('INVALID_RUN_STORE', `不正なrun store entry: ${entry.name}`);
|
|
1426
1611
|
}
|
|
1427
1612
|
const runDir = path.join(root, entry.name);
|
|
1613
|
+
const mode = await inspectRunMode(runDir);
|
|
1614
|
+
if (mode.mode === 'pull') {
|
|
1615
|
+
const pull = await listPullRunEntry(runDir);
|
|
1616
|
+
if (!pull.closed) activeRuns.push({
|
|
1617
|
+
...pull.entry, run_ref: `.lattice/runs/${entry.name}`,
|
|
1618
|
+
});
|
|
1619
|
+
continue;
|
|
1620
|
+
}
|
|
1428
1621
|
const { events, meta, request } = await readRunStore(runDir);
|
|
1429
1622
|
requireValidEventChain(events);
|
|
1430
1623
|
if (!projectRuntimeState({ events }).closed) {
|
|
@@ -1457,8 +1650,9 @@ async function runResume({ runDir, repoRoot, stdout }) {
|
|
|
1457
1650
|
throw new CliContractError('RUN_CLOSED', 'closed runはresumeできない');
|
|
1458
1651
|
}
|
|
1459
1652
|
await resolveRepoBinding(repoRoot, request);
|
|
1460
|
-
// managed write gateの完全検証とdispatch
|
|
1461
|
-
// resumeがCLIからleaseを再認可しないよう、managed run
|
|
1653
|
+
// managed write gateの完全検証とdispatch・長寿命workerの観測継続はsupervisorだけが
|
|
1654
|
+
// 所有する。read-only resumeがCLIからleaseを再認可しないよう、managed runでは
|
|
1655
|
+
// 常に空frontierを返し、生きたsupervisorが更新するstoreの現在地だけを返す。
|
|
1462
1656
|
const managedFrozen = managed === null ? false : await isManagedRunFrozen(runDir, events);
|
|
1463
1657
|
const frontier = managedFrozen
|
|
1464
1658
|
? { dispatchable: [] }
|
|
@@ -1478,7 +1672,140 @@ async function runResume({ runDir, repoRoot, stdout }) {
|
|
|
1478
1672
|
return 0;
|
|
1479
1673
|
}
|
|
1480
1674
|
|
|
1675
|
+
/**
|
|
1676
|
+
* accepted receiptが束縛したworktree HEADの、remote既定branchへの着地状態を投影する。
|
|
1677
|
+
*
|
|
1678
|
+
* receipt本体はhead_shaを持たない。裁定時にexact bindされたcheckpoint_digestから、
|
|
1679
|
+
* 同じTODOのcheckpoint_observed.diff.head_shaへ辿る。git refsとrun storeを読むだけで、
|
|
1680
|
+
* branchもevent列も動かさない。未着地・upstream無しは判断結果であり失敗ではない。
|
|
1681
|
+
*/
|
|
1682
|
+
async function buildRunLandingReport({ runDir, repoRoot, events: providedEvents = null,
|
|
1683
|
+
runId: providedRunId = null }) {
|
|
1684
|
+
let events = providedEvents;
|
|
1685
|
+
let runId = providedRunId;
|
|
1686
|
+
if (events === null || runId === null) {
|
|
1687
|
+
const stored = await readRunStore(runDir);
|
|
1688
|
+
events = stored.events;
|
|
1689
|
+
runId = stored.meta.run_id;
|
|
1690
|
+
}
|
|
1691
|
+
requireValidEventChain(events);
|
|
1692
|
+
const state = projectRuntimeState({ events });
|
|
1693
|
+
|
|
1694
|
+
const push = await runGit(['rev-parse', '--symbolic-full-name', '@{push}'], repoRoot);
|
|
1695
|
+
let pushState = 'no_upstream';
|
|
1696
|
+
let pushRef = null;
|
|
1697
|
+
let unpushedCommits = null;
|
|
1698
|
+
let remoteName = null;
|
|
1699
|
+
if (push.code === 0 && push.stdout.trim().length > 0) {
|
|
1700
|
+
pushRef = push.stdout.trim();
|
|
1701
|
+
const remoteMatch = /^refs\/remotes\/([^/]+)\/.+$/u.exec(pushRef);
|
|
1702
|
+
remoteName = remoteMatch?.[1] ?? null;
|
|
1703
|
+
const count = await runGit(['rev-list', '--count', `${pushRef}..HEAD`], repoRoot);
|
|
1704
|
+
if (count.code !== 0 || !/^\d+$/u.test(count.stdout.trim())) {
|
|
1705
|
+
throw new CliContractError('LANDING_GIT_UNRESOLVED',
|
|
1706
|
+
`push差分を読めない: ${count.stderr.trim() || count.stdout.trim()}`);
|
|
1707
|
+
}
|
|
1708
|
+
pushState = 'tracked';
|
|
1709
|
+
unpushedCommits = Number(count.stdout.trim());
|
|
1710
|
+
}
|
|
1711
|
+
|
|
1712
|
+
if (remoteName === null) {
|
|
1713
|
+
const remotes = await runGit(['remote'], repoRoot);
|
|
1714
|
+
if (remotes.code !== 0) {
|
|
1715
|
+
throw new CliContractError('LANDING_GIT_UNRESOLVED',
|
|
1716
|
+
`remote一覧を読めない: ${remotes.stderr.trim()}`);
|
|
1717
|
+
}
|
|
1718
|
+
const names = remotes.stdout.split('\n').map((name) => name.trim()).filter(Boolean);
|
|
1719
|
+
if (names.length === 1) [remoteName] = names;
|
|
1720
|
+
}
|
|
1721
|
+
let defaultBranchState = 'unresolved';
|
|
1722
|
+
let defaultBranchRef = null;
|
|
1723
|
+
if (remoteName !== null) {
|
|
1724
|
+
const symbolic = await runGit(
|
|
1725
|
+
['symbolic-ref', '--quiet', `refs/remotes/${remoteName}/HEAD`], repoRoot,
|
|
1726
|
+
);
|
|
1727
|
+
const candidate = symbolic.stdout.trim();
|
|
1728
|
+
if (symbolic.code === 0
|
|
1729
|
+
&& candidate.startsWith(`refs/remotes/${remoteName}/`)
|
|
1730
|
+
&& candidate !== `refs/remotes/${remoteName}/HEAD`) {
|
|
1731
|
+
defaultBranchState = 'resolved';
|
|
1732
|
+
defaultBranchRef = candidate;
|
|
1733
|
+
}
|
|
1734
|
+
}
|
|
1735
|
+
|
|
1736
|
+
const acceptedReceipts = [];
|
|
1737
|
+
const accepted = state.receipts
|
|
1738
|
+
.filter((receipt) => receipt.accepted_sequence !== null)
|
|
1739
|
+
.sort((left, right) => left.todo_id.localeCompare(right.todo_id)
|
|
1740
|
+
|| left.receipt_id.localeCompare(right.receipt_id));
|
|
1741
|
+
for (const receipt of accepted) {
|
|
1742
|
+
const checkpointDigest = receipt.payload?.checkpoint_digest;
|
|
1743
|
+
const checkpoint = state.checkpoints.findLast((entry) => (
|
|
1744
|
+
entry.todo_id === receipt.todo_id
|
|
1745
|
+
&& entry.sequence < receipt.accepted_sequence
|
|
1746
|
+
&& entry.payload?.checkpoint_digest === checkpointDigest
|
|
1747
|
+
));
|
|
1748
|
+
const candidateHead = checkpoint?.payload?.diff?.head_sha;
|
|
1749
|
+
const headSha = typeof candidateHead === 'string' && /^[0-9a-f]{40}$/u.test(candidateHead)
|
|
1750
|
+
? candidateHead : null;
|
|
1751
|
+
let landingState = 'head_unavailable';
|
|
1752
|
+
let landed = false;
|
|
1753
|
+
if (headSha !== null && defaultBranchRef === null) {
|
|
1754
|
+
landingState = 'default_branch_unresolved';
|
|
1755
|
+
} else if (headSha !== null) {
|
|
1756
|
+
const ancestry = await runGit(
|
|
1757
|
+
['merge-base', '--is-ancestor', headSha, defaultBranchRef], repoRoot,
|
|
1758
|
+
);
|
|
1759
|
+
if (ancestry.code === 0) {
|
|
1760
|
+
landingState = 'landed';
|
|
1761
|
+
landed = true;
|
|
1762
|
+
} else if (ancestry.code === 1) {
|
|
1763
|
+
landingState = 'not_landed';
|
|
1764
|
+
} else {
|
|
1765
|
+
throw new CliContractError('LANDING_GIT_UNRESOLVED',
|
|
1766
|
+
`receipt HEADの祖先性を読めない: ${ancestry.stderr.trim()}`);
|
|
1767
|
+
}
|
|
1768
|
+
}
|
|
1769
|
+
acceptedReceipts.push({
|
|
1770
|
+
todo_id: receipt.todo_id,
|
|
1771
|
+
receipt_id: receipt.receipt_id,
|
|
1772
|
+
head_sha: headSha,
|
|
1773
|
+
landing_state: landingState,
|
|
1774
|
+
landed,
|
|
1775
|
+
});
|
|
1776
|
+
}
|
|
1777
|
+
|
|
1778
|
+
const output = {
|
|
1779
|
+
schema: 'lattice.run_landing_report.v1',
|
|
1780
|
+
run_id: runId,
|
|
1781
|
+
landed: acceptedReceipts.length > 0 && acceptedReceipts.every((receipt) => receipt.landed),
|
|
1782
|
+
accepted_receipts: acceptedReceipts,
|
|
1783
|
+
repository: {
|
|
1784
|
+
default_branch_state: defaultBranchState,
|
|
1785
|
+
default_branch_ref: defaultBranchRef,
|
|
1786
|
+
push_state: pushState,
|
|
1787
|
+
push_ref: pushRef,
|
|
1788
|
+
unpushed_commits: unpushedCommits,
|
|
1789
|
+
},
|
|
1790
|
+
};
|
|
1791
|
+
output.result_digest = digestArtifact(output);
|
|
1792
|
+
return output;
|
|
1793
|
+
}
|
|
1794
|
+
|
|
1795
|
+
async function runLanding({ runDir, repoRoot, stdout }) {
|
|
1796
|
+
const output = (await inspectRunMode(runDir)).mode === 'pull'
|
|
1797
|
+
? await landingPullRun({ runDir, repoRoot })
|
|
1798
|
+
: await buildRunLandingReport({ runDir, repoRoot });
|
|
1799
|
+
stdout.write(`${JSON.stringify(output)}\n`);
|
|
1800
|
+
return 0;
|
|
1801
|
+
}
|
|
1802
|
+
|
|
1481
1803
|
async function runClose({ runDir, repoRoot, stdout, requestId = null }) {
|
|
1804
|
+
if ((await inspectRunMode(runDir)).mode === 'pull') {
|
|
1805
|
+
const output = await closePullRun({ runDir, repoRoot });
|
|
1806
|
+
stdout.write(`${JSON.stringify(output)}\n`);
|
|
1807
|
+
return 0;
|
|
1808
|
+
}
|
|
1482
1809
|
return withLifecycleLock(runDir, async () => {
|
|
1483
1810
|
const { events, meta, compileArtifact, request, managed } = await readRunStore(runDir);
|
|
1484
1811
|
requireValidEventChain(events);
|
|
@@ -1489,7 +1816,6 @@ async function runClose({ runDir, repoRoot, stdout, requestId = null }) {
|
|
|
1489
1816
|
if (alreadyClosed && existingClose?.payload?.outcome === 'abandoned') {
|
|
1490
1817
|
throw new CliContractError('RUN_ABANDONED', 'abandoned runは正常closeへ変更できない');
|
|
1491
1818
|
}
|
|
1492
|
-
await resolveRepoBinding(repoRoot, request);
|
|
1493
1819
|
if (!alreadyClosed) {
|
|
1494
1820
|
const closed = closeRunIfComplete({
|
|
1495
1821
|
runId: meta.run_id,
|
|
@@ -1521,6 +1847,9 @@ async function runClose({ runDir, repoRoot, stdout, requestId = null }) {
|
|
|
1521
1847
|
already_closed: alreadyClosed,
|
|
1522
1848
|
event_count: next.length,
|
|
1523
1849
|
events_digest: digestArtifact(next.map(({ event_digest: digest }) => digest)),
|
|
1850
|
+
landing: await buildRunLandingReport({
|
|
1851
|
+
runDir, repoRoot, events: next, runId: meta.run_id,
|
|
1852
|
+
}),
|
|
1524
1853
|
};
|
|
1525
1854
|
output.result_digest = digestArtifact(output);
|
|
1526
1855
|
stdout.write(`${JSON.stringify(output)}\n`);
|
|
@@ -1909,7 +2238,7 @@ async function runActivate({ runDir, runRef, repoRoot, stdout, requestId = null
|
|
|
1909
2238
|
let ambiguous = false;
|
|
1910
2239
|
for (let attempt = 0; attempt < 100; attempt += 1) {
|
|
1911
2240
|
try {
|
|
1912
|
-
response = await
|
|
2241
|
+
response = await sendRuntimeActivationRequestUntilSettled({
|
|
1913
2242
|
socketPath: launched.socketPath, request, expectedPid: launched.pid,
|
|
1914
2243
|
expectedProcessStartIdentity: launched.processStartIdentity,
|
|
1915
2244
|
});
|
|
@@ -2294,12 +2623,13 @@ export async function runManagedSupervisorDaemon({
|
|
|
2294
2623
|
|
|
2295
2624
|
const escalateTerminalConflict = async ({ finding, checkpointDigest }) => {
|
|
2296
2625
|
const activeEpoch = await readCommittedEpochStore(runDir);
|
|
2626
|
+
const resourceFinding = finding.kind === 'observed_line_change';
|
|
2297
2627
|
const candidate = {
|
|
2298
2628
|
schema: 'lattice.runtime_finding_candidate.v1',
|
|
2299
2629
|
proposed_kind: finding.kind,
|
|
2300
2630
|
todo_ids: [...finding.todo_ids].sort(),
|
|
2301
|
-
path: finding.path,
|
|
2302
|
-
resource_id: null,
|
|
2631
|
+
path: resourceFinding ? null : finding.path,
|
|
2632
|
+
resource_id: resourceFinding ? finding.resource_id : null,
|
|
2303
2633
|
evidence_digests: [checkpointDigest],
|
|
2304
2634
|
candidate_digest: '',
|
|
2305
2635
|
};
|
|
@@ -2457,7 +2787,7 @@ export async function runManagedSupervisorDaemon({
|
|
|
2457
2787
|
for (const extra of additionalActivations) {
|
|
2458
2788
|
await extra.registerWithManagedSupervisor(managedSupervisor);
|
|
2459
2789
|
}
|
|
2460
|
-
if (!restarting &&
|
|
2790
|
+
if (!restarting && isHostDrivenEpochActivation(activation)) {
|
|
2461
2791
|
sentinel = createRunSentinel({
|
|
2462
2792
|
packets: committed.bundle.executor_packets,
|
|
2463
2793
|
onWarning: (warning) => {
|
|
@@ -2472,6 +2802,19 @@ export async function runManagedSupervisorDaemon({
|
|
|
2472
2802
|
return settled;
|
|
2473
2803
|
},
|
|
2474
2804
|
});
|
|
2805
|
+
const reportDriverState = (waitingOn) => replaceRuntimeDriverState({
|
|
2806
|
+
runDir,
|
|
2807
|
+
runId: request.request_id,
|
|
2808
|
+
supervisorDescriptorRef: restarting
|
|
2809
|
+
? `supervisor/restart-candidates/${path.basename(candidateDir)}/descriptor.json`
|
|
2810
|
+
: 'supervisor/descriptor.json',
|
|
2811
|
+
supervisorDescriptorDigest: activation.supervisorDescriptor.descriptor_digest,
|
|
2812
|
+
supervisorProcessStartIdentityDigest:
|
|
2813
|
+
activation.supervisorDescriptor.process_start_identity.identity_digest,
|
|
2814
|
+
driverState: 'driving',
|
|
2815
|
+
waitingOn,
|
|
2816
|
+
updatedAt: canonicalNow(),
|
|
2817
|
+
});
|
|
2475
2818
|
epochDriveActive = true;
|
|
2476
2819
|
try {
|
|
2477
2820
|
await driveScriptedManagedEpoch({
|
|
@@ -2487,9 +2830,23 @@ export async function runManagedSupervisorDaemon({
|
|
|
2487
2830
|
preDispatchBindings,
|
|
2488
2831
|
drainEscalations: drainPendingEscalations,
|
|
2489
2832
|
escalateTerminalConflict,
|
|
2833
|
+
reportDriverState,
|
|
2490
2834
|
});
|
|
2491
2835
|
} finally {
|
|
2492
2836
|
epochDriveActive = false;
|
|
2837
|
+
await replaceRuntimeDriverState({
|
|
2838
|
+
runDir,
|
|
2839
|
+
runId: request.request_id,
|
|
2840
|
+
supervisorDescriptorRef: restarting
|
|
2841
|
+
? `supervisor/restart-candidates/${path.basename(candidateDir)}/descriptor.json`
|
|
2842
|
+
: 'supervisor/descriptor.json',
|
|
2843
|
+
supervisorDescriptorDigest: activation.supervisorDescriptor.descriptor_digest,
|
|
2844
|
+
supervisorProcessStartIdentityDigest:
|
|
2845
|
+
activation.supervisorDescriptor.process_start_identity.identity_digest,
|
|
2846
|
+
driverState: 'stopped',
|
|
2847
|
+
waitingOn: null,
|
|
2848
|
+
updatedAt: canonicalNow(),
|
|
2849
|
+
});
|
|
2493
2850
|
}
|
|
2494
2851
|
}
|
|
2495
2852
|
if (restarting) {
|
|
@@ -2590,6 +2947,7 @@ export async function runManagedSupervisorDaemon({
|
|
|
2590
2947
|
if (candidate.path !== null) {
|
|
2591
2948
|
const producer = detectCheckpointFindings({ todoId: observed.subject.ref,
|
|
2592
2949
|
checkpoint: observed.payload, packets: active.bundle.executor_packets,
|
|
2950
|
+
manifests: fresh.manifests,
|
|
2593
2951
|
runningTodoIds: projectRuntimeState({ events }).running }).findings;
|
|
2594
2952
|
const match = producer.find((findingValue) => findingValue.kind === candidate.proposed_kind
|
|
2595
2953
|
&& findingValue.path === candidate.path
|
|
@@ -3199,7 +3557,7 @@ export async function runManagedSupervisorDaemon({
|
|
|
3199
3557
|
event_digest: events.at(-1).event_digest,
|
|
3200
3558
|
});
|
|
3201
3559
|
if (recompileRequest.mode === 'intentional_serial'
|
|
3202
|
-
&&
|
|
3560
|
+
&& isHostDrivenEpochActivation(activation)) {
|
|
3203
3561
|
epochDriveActive = true;
|
|
3204
3562
|
try {
|
|
3205
3563
|
events = await driveScriptedManagedEpoch({
|
|
@@ -3965,6 +4323,14 @@ export async function runRuntimeCli({ argv, cwd, stdout, stderr }) {
|
|
|
3965
4323
|
&& argv[0] === 'run' && argv[1] === 'adapter' && argv[2] === 'register'
|
|
3966
4324
|
&& argv[3] === '--schema' && argv[4] === '--json') {
|
|
3967
4325
|
action = () => runAdapterRegisterSchema({ stdout });
|
|
4326
|
+
} else if (argv.length === 9
|
|
4327
|
+
&& argv[0] === 'run' && argv[1] === 'request' && argv[2] === 'synthesize'
|
|
4328
|
+
&& argv[3] === '--witness' && typeof argv[4] === 'string' && argv[4].length > 0
|
|
4329
|
+
&& argv[5] === '--base-sha' && typeof argv[6] === 'string' && argv[6].length > 0
|
|
4330
|
+
&& argv[7] === '--id' && typeof argv[8] === 'string' && argv[8].length > 0) {
|
|
4331
|
+
action = () => runRequestSynthesize({
|
|
4332
|
+
cwd, witnessPath: argv[4], baseSha: argv[6], requestId: argv[8], stdout,
|
|
4333
|
+
});
|
|
3968
4334
|
} else if (argv.length === 5
|
|
3969
4335
|
&& argv[0] === 'run' && argv[1] === 'adapter' && argv[2] === 'register'
|
|
3970
4336
|
&& argv[3] === '--input' && typeof argv[4] === 'string' && argv[4].length > 0) {
|
|
@@ -4005,21 +4371,84 @@ export async function runRuntimeCli({ argv, cwd, stdout, stderr }) {
|
|
|
4005
4371
|
cwd,
|
|
4006
4372
|
stdout,
|
|
4007
4373
|
});
|
|
4374
|
+
} else if (argv.length === 10
|
|
4375
|
+
&& argv[0] === 'run' && argv[1] === 'start'
|
|
4376
|
+
&& argv[2] === '--selection' && argv[3] === 'pull'
|
|
4377
|
+
&& argv[4] === '--id' && typeof argv[5] === 'string' && argv[5].length > 0
|
|
4378
|
+
&& argv[6] === '--plan' && typeof argv[7] === 'string' && argv[7].length > 0
|
|
4379
|
+
&& argv[8] === '--equipment' && argv[9] === 'detached-worktree') {
|
|
4380
|
+
action = () => runPullStart({
|
|
4381
|
+
runId: argv[5], planKey: argv[7], equipment: argv[9], cwd, stdout,
|
|
4382
|
+
});
|
|
4383
|
+
} else if (argv.length === 6
|
|
4384
|
+
&& argv[0] === 'run' && argv[1] === 'intake'
|
|
4385
|
+
&& argv[2] === '--run' && typeof argv[3] === 'string' && argv[3].length > 0
|
|
4386
|
+
&& argv[4] === '--task' && typeof argv[5] === 'string' && argv[5].length > 0) {
|
|
4387
|
+
action = async () => {
|
|
4388
|
+
const { repoRoot, runDir } = await resolveRunStore(cwd, argv[3]);
|
|
4389
|
+
const output = await intakePullTask({ repoRoot, runDir, taskId: argv[5] });
|
|
4390
|
+
stdout.write(`${JSON.stringify(output)}\n`); return 0;
|
|
4391
|
+
};
|
|
4392
|
+
} else if (argv.length === 7
|
|
4393
|
+
&& argv[0] === 'run' && argv[1] === 'intake' && argv[2] === 'accept'
|
|
4394
|
+
&& argv[3] === '--run' && typeof argv[4] === 'string' && argv[4].length > 0
|
|
4395
|
+
&& argv[5] === '--task' && typeof argv[6] === 'string' && argv[6].length > 0) {
|
|
4396
|
+
action = async () => {
|
|
4397
|
+
const { repoRoot, runDir } = await resolveRunStore(cwd, argv[4]);
|
|
4398
|
+
const output = await acceptPullTask({ repoRoot, runDir, taskId: argv[6] });
|
|
4399
|
+
stdout.write(`${JSON.stringify(output)}\n`); return 0;
|
|
4400
|
+
};
|
|
4401
|
+
} else if (argv.length === 7
|
|
4402
|
+
&& argv[0] === 'run' && argv[1] === 'intake' && argv[2] === 'release'
|
|
4403
|
+
&& argv[3] === '--run' && typeof argv[4] === 'string' && argv[4].length > 0
|
|
4404
|
+
&& argv[5] === '--task' && typeof argv[6] === 'string' && argv[6].length > 0) {
|
|
4405
|
+
action = async () => {
|
|
4406
|
+
const { runDir } = await resolveRunStore(cwd, argv[4]);
|
|
4407
|
+
const output = await releasePullTask({ runDir, taskId: argv[6] });
|
|
4408
|
+
stdout.write(`${JSON.stringify(output)}\n`); return 0;
|
|
4409
|
+
};
|
|
4410
|
+
} else if (argv.length === 9
|
|
4411
|
+
&& argv[0] === 'run' && argv[1] === 'intake' && argv[2] === 'attach'
|
|
4412
|
+
&& argv[3] === '--run' && typeof argv[4] === 'string' && argv[4].length > 0
|
|
4413
|
+
&& argv[5] === '--task' && typeof argv[6] === 'string' && argv[6].length > 0
|
|
4414
|
+
&& argv[7] === '--input' && typeof argv[8] === 'string' && argv[8].length > 0) {
|
|
4415
|
+
action = async () => {
|
|
4416
|
+
const { runDir } = await resolveRunStore(cwd, argv[4]);
|
|
4417
|
+
const input = await readBoundedJson(path.resolve(cwd, argv[8]), 'pull worker attach input');
|
|
4418
|
+
const output = await attachPullWorker({ runDir, taskId: argv[6], input });
|
|
4419
|
+
stdout.write(`${JSON.stringify(output)}\n`); return 0;
|
|
4420
|
+
};
|
|
4421
|
+
} else if (argv.length === 7
|
|
4422
|
+
&& argv[0] === 'run' && argv[1] === 'intake' && argv[2] === 'intervention'
|
|
4423
|
+
&& argv[3] === '--run' && typeof argv[4] === 'string' && argv[4].length > 0
|
|
4424
|
+
&& argv[5] === '--task' && typeof argv[6] === 'string' && argv[6].length > 0) {
|
|
4425
|
+
action = async () => {
|
|
4426
|
+
const { runDir } = await resolveRunStore(cwd, argv[4]);
|
|
4427
|
+
const output = await interventionPullTask(runDir, argv[6]);
|
|
4428
|
+
stdout.write(`${JSON.stringify(output)}\n`); return 0;
|
|
4429
|
+
};
|
|
4008
4430
|
} else if (argv.length === 4
|
|
4009
4431
|
&& argv[0] === 'run' && argv[1] === 'activate'
|
|
4010
4432
|
&& argv[2] === '--run' && typeof argv[3] === 'string' && argv[3].length > 0) {
|
|
4011
4433
|
action = async () => {
|
|
4012
4434
|
const { repoRoot, runDir, runRef } = await resolveRunStore(cwd, argv[3]);
|
|
4435
|
+
await requireLegacyRunMode(runDir, 'activate');
|
|
4013
4436
|
return runActivate({ runDir, runRef, repoRoot, stdout, requestId: requestIdOverride });
|
|
4014
4437
|
};
|
|
4015
4438
|
} else if (argv.length === 4
|
|
4016
|
-
&& argv[0] === 'run' && ['observe', 'status', 'resume', 'close'].includes(argv[1])
|
|
4439
|
+
&& argv[0] === 'run' && ['observe', 'status', 'resume', 'landing', 'close'].includes(argv[1])
|
|
4017
4440
|
&& argv[2] === '--run' && typeof argv[3] === 'string' && argv[3].length > 0) {
|
|
4018
4441
|
action = async () => {
|
|
4019
4442
|
const { repoRoot, runDir } = await resolveRunStore(cwd, argv[3]);
|
|
4020
4443
|
if (argv[1] === 'observe') return runObserve({ runDir, stdout });
|
|
4021
4444
|
if (argv[1] === 'status') return runStatus({ runDir, stdout });
|
|
4022
|
-
if (argv[1] === 'resume')
|
|
4445
|
+
if (argv[1] === 'resume') {
|
|
4446
|
+
if ((await inspectRunMode(runDir)).mode === 'pull') {
|
|
4447
|
+
throw new PullRunError('RUN_MODE_MISMATCH', 'pull runはresumeでleaseを再認可しない');
|
|
4448
|
+
}
|
|
4449
|
+
return runResume({ runDir, repoRoot, stdout });
|
|
4450
|
+
}
|
|
4451
|
+
if (argv[1] === 'landing') return runLanding({ runDir, repoRoot, stdout });
|
|
4023
4452
|
return runClose({ runDir, repoRoot, stdout, requestId: requestIdOverride });
|
|
4024
4453
|
};
|
|
4025
4454
|
} else if (argv.length === 6
|
|
@@ -4028,6 +4457,7 @@ export async function runRuntimeCli({ argv, cwd, stdout, stderr }) {
|
|
|
4028
4457
|
&& argv[4] === '--reason' && typeof argv[5] === 'string') {
|
|
4029
4458
|
action = async () => {
|
|
4030
4459
|
const { runDir } = await resolveRunStore(cwd, argv[3]);
|
|
4460
|
+
await requireLegacyRunMode(runDir, 'abandon');
|
|
4031
4461
|
return runAbandon({ runDir, runRef: argv[3], reason: argv[5], stdout,
|
|
4032
4462
|
requestId: requestIdOverride });
|
|
4033
4463
|
};
|
|
@@ -4037,6 +4467,7 @@ export async function runRuntimeCli({ argv, cwd, stdout, stderr }) {
|
|
|
4037
4467
|
&& argv[4] === '--finding' && /^[0-9a-f]{64}$/u.test(argv[5])) {
|
|
4038
4468
|
action = async () => {
|
|
4039
4469
|
const { runDir, runRef } = await resolveRunStore(cwd, argv[3]);
|
|
4470
|
+
await requireLegacyRunMode(runDir, 'conflict');
|
|
4040
4471
|
return runManagedControl({
|
|
4041
4472
|
runDir, runRef, operation: 'conflict', artifactDigest: argv[5], stdout,
|
|
4042
4473
|
requestId: requestIdOverride,
|
|
@@ -4049,6 +4480,7 @@ export async function runRuntimeCli({ argv, cwd, stdout, stderr }) {
|
|
|
4049
4480
|
&& argv[7] === '--input' && typeof argv[8] === 'string' && argv[8].length > 0) {
|
|
4050
4481
|
action = async () => {
|
|
4051
4482
|
const { repoRoot, runDir } = await resolveRunStore(cwd, argv[4]);
|
|
4483
|
+
await requireLegacyRunMode(runDir, 'seam profile');
|
|
4052
4484
|
return runSeamProfile({ runDir, repoRoot, findingDigest: argv[6],
|
|
4053
4485
|
inputPath: path.resolve(cwd, argv[8]), stdout });
|
|
4054
4486
|
};
|
|
@@ -4059,6 +4491,7 @@ export async function runRuntimeCli({ argv, cwd, stdout, stderr }) {
|
|
|
4059
4491
|
&& argv[7] === '--input' && typeof argv[8] === 'string' && argv[8].length > 0) {
|
|
4060
4492
|
action = async () => {
|
|
4061
4493
|
const { repoRoot, runDir } = await resolveRunStore(cwd, argv[4]);
|
|
4494
|
+
await requireLegacyRunMode(runDir, 'seam resolve');
|
|
4062
4495
|
return runSeamResolve({ runDir, repoRoot, findingDigest: argv[6],
|
|
4063
4496
|
requestPath: path.resolve(cwd, argv[8]), stdout });
|
|
4064
4497
|
};
|
|
@@ -4076,6 +4509,7 @@ export async function runRuntimeCli({ argv, cwd, stdout, stderr }) {
|
|
|
4076
4509
|
&& argv[7] === '--input' && typeof argv[8] === 'string' && argv[8].length > 0) {
|
|
4077
4510
|
action = async () => {
|
|
4078
4511
|
const { runDir, runRef } = await resolveRunStore(cwd, argv[4]);
|
|
4512
|
+
await requireLegacyRunMode(runDir, 'finding record');
|
|
4079
4513
|
if (await readCommittedEpochStore(runDir) === null) {
|
|
4080
4514
|
throw new CliContractError('RUN_NOT_MANAGED', 'runがmanaged storeへactivateされていない');
|
|
4081
4515
|
}
|
|
@@ -4090,6 +4524,7 @@ export async function runRuntimeCli({ argv, cwd, stdout, stderr }) {
|
|
|
4090
4524
|
&& argv[4] === '--input' && typeof argv[5] === 'string' && argv[5].length > 0) {
|
|
4091
4525
|
action = async () => {
|
|
4092
4526
|
const { runDir, runRef } = await resolveRunStore(cwd, argv[3]);
|
|
4527
|
+
await requireLegacyRunMode(runDir, 'recompile');
|
|
4093
4528
|
if (await readCommittedEpochStore(runDir) === null) {
|
|
4094
4529
|
throw new CliContractError('RUN_NOT_MANAGED', 'runがmanaged storeへactivateされていない');
|
|
4095
4530
|
}
|
|
@@ -4102,6 +4537,7 @@ export async function runRuntimeCli({ argv, cwd, stdout, stderr }) {
|
|
|
4102
4537
|
&& argv[2] === '--run' && typeof argv[3] === 'string' && argv[3].length > 0) {
|
|
4103
4538
|
action = async () => {
|
|
4104
4539
|
const { runDir, runRef } = await resolveRunStore(cwd, argv[3]);
|
|
4540
|
+
await requireLegacyRunMode(runDir, argv[1]);
|
|
4105
4541
|
return runManagedControl({
|
|
4106
4542
|
runDir, runRef, operation: argv[1], artifactDigest: null, stdout,
|
|
4107
4543
|
requestId: requestIdOverride,
|
|
@@ -4115,6 +4551,7 @@ export async function runRuntimeCli({ argv, cwd, stdout, stderr }) {
|
|
|
4115
4551
|
&& argv[2] === '--run' && typeof argv[3] === 'string' && argv[3].length > 0) {
|
|
4116
4552
|
action = async () => {
|
|
4117
4553
|
const { runDir } = await resolveRunStore(cwd, argv[3]);
|
|
4554
|
+
await requireLegacyRunMode(runDir, 'event verify');
|
|
4118
4555
|
return eventVerify({ runDir, stdout });
|
|
4119
4556
|
};
|
|
4120
4557
|
}
|
|
@@ -4129,7 +4566,7 @@ export async function runRuntimeCli({ argv, cwd, stdout, stderr }) {
|
|
|
4129
4566
|
return typedFailure(stderr, 'CONTRACT_VIOLATION', error.message);
|
|
4130
4567
|
}
|
|
4131
4568
|
if (error instanceof RuntimeEpochStoreError) {
|
|
4132
|
-
return typedFailure(stderr, error.code, error.message);
|
|
4569
|
+
return typedFailure(stderr, error.code, error.message, error.detail);
|
|
4133
4570
|
}
|
|
4134
4571
|
if (error instanceof AdapterRegistryError) {
|
|
4135
4572
|
return typedFailure(stderr, error.code, error.message, error.detail);
|
|
@@ -4137,6 +4574,11 @@ export async function runRuntimeCli({ argv, cwd, stdout, stderr }) {
|
|
|
4137
4574
|
if (error instanceof ManagedRuntimeError) {
|
|
4138
4575
|
return typedFailure(stderr, error.code, error.message);
|
|
4139
4576
|
}
|
|
4577
|
+
if (error instanceof PullRunError || error instanceof TodoStoreError
|
|
4578
|
+
|| error instanceof RuntimeDriverStateError
|
|
4579
|
+
|| error instanceof RuntimeLifecycleLockError) {
|
|
4580
|
+
return typedFailure(stderr, error.code, error.message, error.detail);
|
|
4581
|
+
}
|
|
4140
4582
|
throw error;
|
|
4141
4583
|
}
|
|
4142
4584
|
}
|