@quolu/lattice 0.50.1 → 0.51.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/bin/lattice-work-order-adapter.mjs +20 -0
  2. package/docs/schemas/lattice.runtime_adapter_capabilities.v2.schema.json +55 -0
  3. package/docs/schemas/lattice.runtime_adapter_registration_input.v2.schema.json +86 -0
  4. package/package.json +5 -2
  5. package/src/boundary-observation-compiler-v2.mjs +1 -1
  6. package/src/cli-help.mjs +29 -2
  7. package/src/rc3-actual-dogfood.mjs +6 -2
  8. package/src/rc3-scripted-campaign.mjs +37 -10
  9. package/src/rc4-stage1-dogfood.mjs +6 -2
  10. package/src/runtime-adapter-registry.mjs +21 -7
  11. package/src/runtime-cli.mjs +476 -34
  12. package/src/runtime-contracts.mjs +59 -13
  13. package/src/runtime-controller-protocol.mjs +48 -3
  14. package/src/runtime-decision-verifier.mjs +70 -0
  15. package/src/runtime-diff-observer.mjs +66 -4
  16. package/src/runtime-direct-os-observer.mjs +25 -8
  17. package/src/runtime-driver-state.mjs +162 -0
  18. package/src/runtime-engine.mjs +37 -6
  19. package/src/runtime-front-end.mjs +39 -1
  20. package/src/runtime-managed-supervisor.mjs +80 -14
  21. package/src/runtime-multi-epoch-store.mjs +87 -14
  22. package/src/runtime-pull-intake.mjs +1188 -0
  23. package/src/runtime-work-order-contracts.mjs +91 -0
  24. package/src/runtime-work-order-controller.mjs +1167 -0
  25. package/src/seam-proposal-queries.mjs +1 -1
  26. package/src/todo-cli.mjs +199 -5
  27. package/src/todo-contracts.mjs +4 -1
  28. package/src/todo-gantt-html-independence.mjs +3 -2
  29. package/src/todo-gantt-html-shared.mjs +1 -2
  30. package/src/todo-gantt-html-style.mjs +13 -0
  31. package/src/todo-gantt-html.mjs +15 -2
  32. package/src/todo-gantt-layout.mjs +71 -1
  33. package/src/todo-gantt-nested.mjs +243 -0
  34. package/src/todo-gantt-svg.mjs +80 -5
  35. package/src/todo-independence-contracts.mjs +73 -7
  36. package/src/todo-independence-guidance.mjs +30 -1
  37. package/src/todo-independence.mjs +89 -7
  38. package/src/todo-revision.mjs +1 -1
  39. package/src/todo-split.mjs +472 -0
  40. package/src/todo-store-git-transaction.mjs +418 -0
  41. package/src/todo-store.mjs +54 -0
@@ -0,0 +1,20 @@
1
+ #!/usr/bin/env node
2
+
3
+ import {
4
+ runWorkOrderAdapterController,
5
+ WorkOrderAdapterControllerError,
6
+ } from '../src/runtime-work-order-controller.mjs';
7
+
8
+ try {
9
+ await runWorkOrderAdapterController();
10
+ } catch (error) {
11
+ const payload = {
12
+ schema: 'lattice.work_order_adapter_error.v1',
13
+ code: error instanceof WorkOrderAdapterControllerError
14
+ ? error.code
15
+ : 'WORK_ORDER_CONTROLLER_FAILED',
16
+ message: String(error?.message ?? error),
17
+ };
18
+ process.stderr.write(`${JSON.stringify(payload)}\n`);
19
+ process.exitCode = 1;
20
+ }
@@ -0,0 +1,55 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://github.com/kitepon-rgb/Lattice/blob/main/docs/schemas/lattice.runtime_adapter_capabilities.v2.schema.json",
4
+ "title": "lattice.runtime_adapter_capabilities.v2",
5
+ "description": "controller handshakeとlaunch descriptorを束縛するruntime adapter能力契約。host_driven_epochはhostによるmanaged epoch駆動の受入宣言である。",
6
+ "type": "object",
7
+ "additionalProperties": false,
8
+ "required": [
9
+ "schema",
10
+ "operations",
11
+ "process_observation",
12
+ "worktree_fingerprint",
13
+ "staged_write_lease",
14
+ "durable_dispatch",
15
+ "host_driven_epoch",
16
+ "capabilities_digest"
17
+ ],
18
+ "properties": {
19
+ "schema": {
20
+ "const": "lattice.runtime_adapter_capabilities.v2"
21
+ },
22
+ "operations": {
23
+ "const": [
24
+ "dispatch",
25
+ "observe",
26
+ "inventory",
27
+ "barrier",
28
+ "rebind",
29
+ "prepare",
30
+ "activate",
31
+ "release",
32
+ "revoke"
33
+ ]
34
+ },
35
+ "process_observation": {
36
+ "const": true
37
+ },
38
+ "worktree_fingerprint": {
39
+ "const": true
40
+ },
41
+ "staged_write_lease": {
42
+ "const": true
43
+ },
44
+ "durable_dispatch": {
45
+ "const": true
46
+ },
47
+ "host_driven_epoch": {
48
+ "type": "boolean"
49
+ },
50
+ "capabilities_digest": {
51
+ "type": "string",
52
+ "pattern": "^[0-9a-f]{64}$"
53
+ }
54
+ }
55
+ }
@@ -0,0 +1,86 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://github.com/kitepon-rgb/Lattice/blob/main/docs/schemas/lattice.runtime_adapter_registration_input.v2.schema.json",
4
+ "title": "lattice.runtime_adapter_registration_input.v2",
5
+ "description": "lattice run adapter register の入力契約。host_driven_epochでhostによるmanaged epoch駆動への同意を宣言し、digestと固定capabilitiesはCLIが導出する。",
6
+ "oneOf": [
7
+ {
8
+ "type": "object",
9
+ "additionalProperties": false,
10
+ "required": [
11
+ "schema",
12
+ "adapter_kind",
13
+ "launch_kind",
14
+ "binary_path",
15
+ "argv",
16
+ "config_ref",
17
+ "host_driven_epoch"
18
+ ],
19
+ "properties": {
20
+ "schema": {
21
+ "const": "lattice.runtime_adapter_registration_input.v2"
22
+ },
23
+ "adapter_kind": {
24
+ "$ref": "#/$defs/identifier"
25
+ },
26
+ "launch_kind": {
27
+ "const": "host_binary"
28
+ },
29
+ "binary_path": {
30
+ "type": "string",
31
+ "pattern": "^/"
32
+ },
33
+ "argv": {
34
+ "type": "array",
35
+ "maxItems": 64,
36
+ "items": {
37
+ "type": "string",
38
+ "pattern": "^[^\\u0000]*$"
39
+ }
40
+ },
41
+ "config_ref": {
42
+ "type": "string",
43
+ "minLength": 1
44
+ },
45
+ "host_driven_epoch": {
46
+ "type": "boolean"
47
+ }
48
+ }
49
+ },
50
+ {
51
+ "type": "object",
52
+ "additionalProperties": false,
53
+ "required": [
54
+ "schema",
55
+ "adapter_kind",
56
+ "launch_kind",
57
+ "endpoint",
58
+ "host_driven_epoch"
59
+ ],
60
+ "properties": {
61
+ "schema": {
62
+ "const": "lattice.runtime_adapter_registration_input.v2"
63
+ },
64
+ "adapter_kind": {
65
+ "$ref": "#/$defs/identifier"
66
+ },
67
+ "launch_kind": {
68
+ "const": "existing_endpoint"
69
+ },
70
+ "endpoint": {
71
+ "type": "string",
72
+ "minLength": 1
73
+ },
74
+ "host_driven_epoch": {
75
+ "type": "boolean"
76
+ }
77
+ }
78
+ }
79
+ ],
80
+ "$defs": {
81
+ "identifier": {
82
+ "type": "string",
83
+ "pattern": "^[0-9A-Za-z](?:[0-9A-Za-z._-]{0,127})$"
84
+ }
85
+ }
86
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@quolu/lattice",
3
- "version": "0.50.1",
3
+ "version": "0.51.0",
4
4
  "description": "Schedulability compiler for multi-agent development: observe real code boundaries, refactor the conflicting seam, recompile the plan for parallel execution",
5
5
  "author": {
6
6
  "name": "Quo / クオ at kitepon.dev",
@@ -18,7 +18,8 @@
18
18
  "bin": {
19
19
  "lattice": "bin/lattice.mjs",
20
20
  "lattice-mcp": "bin/lattice-mcp.mjs",
21
- "lattice-scripted-adapter": "bin/lattice-scripted-adapter.mjs"
21
+ "lattice-scripted-adapter": "bin/lattice-scripted-adapter.mjs",
22
+ "lattice-work-order-adapter": "bin/lattice-work-order-adapter.mjs"
22
23
  },
23
24
  "files": [
24
25
  "bin",
@@ -37,6 +38,8 @@
37
38
  "docs/schemas/lattice.executor_packet.v1.schema.json",
38
39
  "docs/schemas/lattice.executor_receipt.v1.schema.json",
39
40
  "docs/schemas/lattice.runtime_adapter_registration_input.v1.schema.json",
41
+ "docs/schemas/lattice.runtime_adapter_registration_input.v2.schema.json",
42
+ "docs/schemas/lattice.runtime_adapter_capabilities.v2.schema.json",
40
43
  "docs/bridge-setup.md",
41
44
  "sensor/dist",
42
45
  "!sensor/dist/bin/lattice-sensor.*",
@@ -6,7 +6,7 @@ const GRAPH_SCHEMA = 'lattice.normalized_boundary_graph.v2';
6
6
  const MAX_TODOS = 256;
7
7
  const IDENTIFIER = /^[0-9A-Za-z](?:[0-9A-Za-z._-]{0,127})$/;
8
8
  const DIGEST = /^[0-9a-f]{64}$/;
9
- const RESOURCE_KINDS = new Set(['symbol', 'path', 'state', 'effect', 'dynamic']);
9
+ const RESOURCE_KINDS = new Set(['symbol', 'path', 'state', 'effect', 'line', 'dynamic']);
10
10
  const PROVENANCE_SOURCES = new Set([
11
11
  'sensor',
12
12
  'manual_candidate_spec',
package/src/cli-help.mjs CHANGED
@@ -38,12 +38,22 @@ Commands:
38
38
 
39
39
  Commands:
40
40
  start --request <request.json> --executor <adapter>
41
+ start --selection pull --id <id> --plan <key> --equipment detached-worktree
41
42
  start --schema --json # lattice.run_request.v1 の JSON Schema を出す
43
+ intake --run .lattice/runs/<id> --task <task_id>
44
+ intake release --run .lattice/runs/<id> --task <task_id>
45
+ intake attach --run .lattice/runs/<id> --task <task_id> --input <worker.json>
46
+ intake intervention --run .lattice/runs/<id> --task <task_id>
47
+ intake accept --run .lattice/runs/<id> --task <task_id>
42
48
  adapter register --input <descriptor.json>
43
49
  adapter register --schema --json # 登録入力の JSON Schema を出す
44
50
  adapter list --json
51
+ activate --run .lattice/runs/<id>
52
+ # 全waveが完了するまで戻らないforeground driver。別processのobserve/statusは
53
+ # driver_stateとwaiting_onで、駆動中か・何を待っているかを投影する。
45
54
  observe --run .lattice/runs/<id>
46
55
  status --run .lattice/runs/<id>
56
+ landing --run .lattice/runs/<id>
47
57
  resume --run .lattice/runs/<id>
48
58
  close --run .lattice/runs/<id>
49
59
  abandon --run .lattice/runs/<id> --reason <reason>
@@ -95,6 +105,7 @@ Write commands:
95
105
  seam-proposal compile --plan <key> # 並列可否記録と実sensorからseam提案を記録する
96
106
  seam-proposal apply --plan <key> # 記録済み提案を隔離worktreeで適用し五条件で採否を決める
97
107
  seam-proposal land --plan <key> --names <file> # 採用された変換を本ツリーへ着地させる
108
+ split --plan <key> --input <file> # in-progress ToDoを抽出群とpending残差へrevisionする
98
109
  revise --plan <key> --input <file>
99
110
  revise-phase --plan <key> --input <file>
100
111
  revise-set --input <file>
@@ -114,6 +125,11 @@ Write commands:
114
125
  Write commands require LATTICE_TODO_ACTOR_HOST, LATTICE_TODO_ACTOR_SESSION,
115
126
  and LATTICE_TODO_ACTOR_AGENT.
116
127
 
128
+ storeだけを書き換えるcommandの末尾へ--commit-storeを付けると、共有Git lockを取得し、
129
+ 生じた.lattice/todoの変更だけをcommitしてreceiptを返す。
130
+ dirtyなsourceとstore外の既存stageは保持し、store自身がdirtyなら拒否する。
131
+ ignoredな再生成artifactだけを作るindependence/seam-proposal compileは対象外。
132
+
117
133
  並列可否(依存線の不在は、書き込み境界が干渉しないことを意味しない):
118
134
  ${TODO_INDEPENDENCE_WORKFLOW.join('\n')}
119
135
 
@@ -171,16 +187,25 @@ const SUBCOMMAND_USAGE = Object.freeze({
171
187
  'plan show': 'plan show <plan_key> --json',
172
188
  'plan compile': 'plan compile --request <request.json> | --schema --json',
173
189
  'plan verify': 'plan verify --request <request.json> --plan <plan.json>',
174
- 'run start': 'run start --request <request.json> --executor <adapter> | --schema --json',
190
+ 'run start': 'run start --request <request.json> --executor <adapter>'
191
+ + ' | --selection pull --id <id> --plan <key> --equipment detached-worktree | --schema --json',
192
+ 'run intake': 'run intake --run .lattice/runs/<id> --task <task_id>',
193
+ 'run intake release': 'run intake release --run .lattice/runs/<id> --task <task_id>',
194
+ 'run intake attach': 'run intake attach --run .lattice/runs/<id> --task <task_id> --input <worker.json>',
195
+ 'run intake intervention': 'run intake intervention --run .lattice/runs/<id> --task <task_id>',
196
+ 'run intake accept': 'run intake accept --run .lattice/runs/<id> --task <task_id>',
175
197
  'run adapter register': 'run adapter register --input <descriptor.json> | --schema --json',
176
198
  'run adapter list': 'run adapter list --json',
177
199
  'run observe': 'run observe --run .lattice/runs/<id>',
178
200
  'run status': 'run status --run .lattice/runs/<id>',
179
201
  'run resume': 'run resume --run .lattice/runs/<id>',
202
+ 'run landing': 'run landing --run .lattice/runs/<id>',
180
203
  'run close': 'run close --run .lattice/runs/<id>',
181
204
  'run abandon': 'run abandon --run .lattice/runs/<id> --reason <reason>',
182
205
  'run list': 'run list --json',
183
- 'run activate': 'run activate --run .lattice/runs/<id>',
206
+ 'run activate': 'run activate --run .lattice/runs/<id>\n\n'
207
+ + '全waveが完了するまで戻らないforeground driver。別processのrun observe/statusは\n'
208
+ + 'driver_stateとwaiting_onで、駆動中か・何を待っているかを投影する。',
184
209
  'run conflict': 'run conflict --run .lattice/runs/<id> --finding <digest>',
185
210
  'run hold': 'run hold --run .lattice/runs/<id> --finding <digest>',
186
211
  'run recompile': 'run recompile --run .lattice/runs/<id> --input <recompile-request.json>',
@@ -215,12 +240,14 @@ const SUBCOMMAND_USAGE = Object.freeze({
215
240
  'todo phase close-unaudited': 'todo phase close-unaudited --plan <key> --phase <id> --reason <text>',
216
241
  'todo phase baseline': 'todo phase baseline --reason <text> [--except <plan_key>]...',
217
242
  'todo start': 'todo start --plan <key> --task <id> [--parallel-frontier|--override-reason <text>]',
243
+ 'todo retract': 'todo retract --plan <key> --task <id> --reason <text>',
218
244
  'todo block': 'todo block --plan <key> --task <id> --reason <text>',
219
245
  'todo unblock': 'todo unblock --plan <key> --task <id>',
220
246
  'todo done': 'todo done --plan <key> --task <id> --evidence <file>',
221
247
  'todo reopen': 'todo reopen --plan <key> --task <id> --reason <text> [--override-reason <text>]',
222
248
  'todo evidence': 'todo evidence promote --plan <key> --task <id> --evidence <file>',
223
249
  'todo evidence promote': 'todo evidence promote --plan <key> --task <id> --evidence <file>',
250
+ 'todo split': 'todo split --plan <key> --input <file>',
224
251
  'todo revise': 'todo revise --plan <key> --input <file> | --schema --json',
225
252
  'todo revise-phase': 'todo revise-phase --plan <key> --input <file> | --schema --json',
226
253
  'todo revise-set': 'todo revise-set --input <file> | --schema --json',
@@ -263,12 +263,16 @@ export async function observeActualCheckpoint({ stateDir, todoId, planKey = 'pla
263
263
  const classifyPackets = { ...state.packets, ...(state.redispatchPackets ?? {}) };
264
264
  void packets;
265
265
  const classified = classifyCheckpointObservation({
266
- runId: RUN_ID, plan, events, packets: classifyPackets, todoId,
266
+ runId: RUN_ID, plan, events, packets: classifyPackets, manifests: state.manifests, todoId,
267
267
  detect: detectCheckpointFindings, recordedAt: RUN_TIMESTAMP,
268
268
  });
269
269
  state.events = classified.events;
270
270
  await saveState(stateDir, state);
271
- return { findings: classified.findings, checkpoint_digest: checkpoint.checkpoint_digest };
271
+ return {
272
+ findings: classified.findings,
273
+ scope_expanded: classified.scope_expanded,
274
+ checkpoint_digest: checkpoint.checkpoint_digest,
275
+ };
272
276
  }
273
277
 
274
278
  /** hold裁定→recompile→(carried TODOの)rebind packet発行までを一括で行う。 */
@@ -149,8 +149,9 @@ function docWriter(docPath, body) {
149
149
  }
150
150
 
151
151
  /** 全running executorを1回ずつ観測し、checkpointがあれば分類する共通driver。 */
152
- async function observeAndClassifyAll({ runId, plan, events, packets, adapter }) {
152
+ async function observeAndClassifyAll({ runId, plan, events, packets, manifests, adapter }) {
153
153
  let next = events;
154
+ const scopeExpanded = [];
154
155
  const state = projectRuntimeState({ events: next });
155
156
  for (const todoId of state.running) {
156
157
  const observed = await observeExecutor({
@@ -159,33 +160,40 @@ async function observeAndClassifyAll({ runId, plan, events, packets, adapter })
159
160
  next = observed.events;
160
161
  if (observed.observation.state === 'checkpoint_ready') {
161
162
  const classified = classifyCheckpointObservation({
162
- runId, plan, events: next, packets, todoId,
163
+ runId, plan, events: next, packets, manifests, todoId,
163
164
  detect: detectCheckpointFindings, recordedAt: RUN_TIMESTAMP,
164
165
  });
165
166
  next = classified.events;
166
- if (classified.findings.length > 0) return { events: next, frozen: true };
167
+ scopeExpanded.push(...classified.scope_expanded);
168
+ if (classified.findings.length > 0) {
169
+ return { events: next, frozen: true, scope_expanded: scopeExpanded };
170
+ }
167
171
  }
168
172
  }
169
- return { events: next, frozen: false };
173
+ return { events: next, frozen: false, scope_expanded: scopeExpanded };
170
174
  }
171
175
 
172
176
  /** 競合なし前提でclosed loopまで回す(clean/irreducible串行の完走用)。 */
173
177
  async function driveToClose({ runId, plan, events, packets, manifests, adapter, maxRounds = 16 }) {
174
178
  let next = events;
179
+ const scopeExpanded = [];
175
180
  for (let round = 0; round < maxRounds; round += 1) {
176
181
  const dispatched = await dispatchReadyFrontier({
177
182
  runId, plan, events: next, packets, manifests, adapter, recordedAt: RUN_TIMESTAMP,
178
183
  });
179
184
  if (dispatched.failure) fail(`dispatch失敗: ${dispatched.failure.message}`);
180
185
  next = dispatched.events;
181
- const observed = await observeAndClassifyAll({ runId, plan, events: next, packets, adapter });
186
+ const observed = await observeAndClassifyAll({
187
+ runId, plan, events: next, packets, manifests, adapter,
188
+ });
182
189
  if (observed.frozen) fail('競合なし条件でfreezeが発生した');
183
190
  next = observed.events;
191
+ scopeExpanded.push(...observed.scope_expanded);
184
192
  const adjudicated = adjudicatePendingReceipts({ runId, plan, events: next, recordedAt: RUN_TIMESTAMP });
185
193
  next = adjudicated.events;
186
194
  const closed = closeRunIfComplete({ runId, plan, events: next, recordedAt: RUN_TIMESTAMP });
187
195
  next = closed.events;
188
- if (closed.closed) return next;
196
+ if (closed.closed) return { events: next, scope_expanded: scopeExpanded };
189
197
  }
190
198
  fail('closed loopがmaxRounds内に完走しない');
191
199
  return null;
@@ -292,12 +300,14 @@ async function runCleanParallel({ scaffold }) {
292
300
  },
293
301
  });
294
302
  let events = initializeRunEvents({ runId, request, plan, manifests, recordedAt: RUN_TIMESTAMP });
295
- events = await driveToClose({ runId, plan, events, packets, manifests, adapter });
303
+ const driven = await driveToClose({ runId, plan, events, packets, manifests, adapter });
304
+ events = driven.events;
296
305
  const state = projectRuntimeState({ events });
297
306
  return {
298
307
  request,
299
308
  plan,
300
309
  events,
310
+ scope_expanded: driven.scope_expanded,
301
311
  record: conditionRecord({
302
312
  condition: 'clean_parallel',
303
313
  expected: { hold: [], continue: ['TA', 'TB', 'TC'], accepted: ['TA', 'TB', 'TC'], closed: true },
@@ -352,7 +362,8 @@ async function runLateConflict({ scaffold }) {
352
362
  const observed = await observeExecutor({ runId, plan, events, adapter, todoId, recordedAt: RUN_TIMESTAMP });
353
363
  events = observed.events;
354
364
  const classified = classifyCheckpointObservation({
355
- runId, plan, events, packets, todoId, detect: detectCheckpointFindings, recordedAt: RUN_TIMESTAMP,
365
+ runId, plan, events, packets, manifests, todoId,
366
+ detect: detectCheckpointFindings, recordedAt: RUN_TIMESTAMP,
356
367
  });
357
368
  events = classified.events;
358
369
  }
@@ -464,22 +475,36 @@ async function runScopeViolation({ scaffold }) {
464
475
  const observed = await observeExecutor({ runId, plan, events, adapter, todoId: 'TA', recordedAt: RUN_TIMESTAMP });
465
476
  events = observed.events;
466
477
  const classified = classifyCheckpointObservation({
467
- runId, plan, events, packets, todoId: 'TA', detect: detectCheckpointFindings, recordedAt: RUN_TIMESTAMP,
478
+ runId, plan, events, packets, manifests, todoId: 'TA',
479
+ detect: detectCheckpointFindings, recordedAt: RUN_TIMESTAMP,
468
480
  });
469
481
  events = classified.events;
470
- events = await driveToClose({ runId, plan, events, packets, manifests, adapter });
482
+ const driven = await driveToClose({ runId, plan, events, packets, manifests, adapter });
483
+ events = driven.events;
484
+ const scopeExpanded = [...classified.scope_expanded, ...driven.scope_expanded];
471
485
  const state = projectRuntimeState({ events });
472
486
  return {
473
487
  request,
474
488
  plan,
475
489
  manifests,
476
490
  events,
491
+ scope_expanded: scopeExpanded,
477
492
  record: conditionRecord({
478
493
  condition: 'scope_violation',
479
494
  // directory名はRC3 artifact互換のため維持する。現契約では単独の予測超過は
480
495
  // conflictではなく、観測を残したまま有効なreceiptを受理する。
481
496
  expected: {
482
497
  observation_kinds: ['prediction_excess'],
498
+ scope_expanded: [{
499
+ task_id: 'TA',
500
+ compared_witness_digest: null,
501
+ first_seen_path_count: 1,
502
+ path_count: 2,
503
+ added_paths: ['docs/rogue.md'],
504
+ removed_paths: [],
505
+ growth_events: 1,
506
+ gate_shape: false,
507
+ }],
483
508
  conflict_finding_kinds: [],
484
509
  frozen: false,
485
510
  accepted: ['TA', 'TB'],
@@ -487,6 +512,7 @@ async function runScopeViolation({ scaffold }) {
487
512
  },
488
513
  actual: {
489
514
  observation_kinds: [...new Set(classified.observations.map(({ kind }) => kind))],
515
+ scope_expanded: scopeExpanded,
490
516
  conflict_finding_kinds: [...new Set(classified.findings.map(({ kind }) => kind))],
491
517
  frozen: state.freeze !== null,
492
518
  accepted: state.accepted,
@@ -1352,5 +1378,6 @@ export async function runRc3ScriptedCampaign(options = {}) {
1352
1378
  campaignManifest,
1353
1379
  verification,
1354
1380
  conditions: results.map(({ record }) => record.condition),
1381
+ scope_expanded: results.flatMap((result) => result.scope_expanded ?? []),
1355
1382
  };
1356
1383
  }
@@ -264,12 +264,16 @@ export async function observeStage1Checkpoint({ stateDir, todoId, planKey = 'pla
264
264
  }));
265
265
  const classifyPackets = { ...state.packets, ...(state.redispatchPackets ?? {}) };
266
266
  const classified = classifyCheckpointObservation({
267
- runId: RUN_ID, plan, events, packets: classifyPackets, todoId,
267
+ runId: RUN_ID, plan, events, packets: classifyPackets, manifests: state.manifests, todoId,
268
268
  detect: detectCheckpointFindings, recordedAt: RUN_TIMESTAMP,
269
269
  });
270
270
  state.events = classified.events;
271
271
  await saveState(stateDir, state);
272
- return { findings: classified.findings, checkpoint_digest: checkpoint.checkpoint_digest };
272
+ return {
273
+ findings: classified.findings,
274
+ scope_expanded: classified.scope_expanded,
275
+ checkpoint_digest: checkpoint.checkpoint_digest,
276
+ };
273
277
  }
274
278
 
275
279
  /** hold裁定→recompile→(carried TODOの)rebind packet発行までを一括で行う受け皿。 */
@@ -21,7 +21,8 @@ import {
21
21
  import { selfDigest } from './runtime-contracts.mjs';
22
22
  import { observeMacosBinaryIdentity } from './runtime-managed-supervisor.mjs';
23
23
 
24
- const INPUT_SCHEMA = 'lattice.runtime_adapter_registration_input.v1';
24
+ const INPUT_SCHEMA = 'lattice.runtime_adapter_registration_input.v2';
25
+ const LEGACY_INPUT_SCHEMA = 'lattice.runtime_adapter_registration_input.v1';
25
26
  const REGISTRY_SCHEMA = 'lattice.runtime_adapter_registry.v1';
26
27
  const DESCRIPTOR_SCHEMA = 'lattice.runtime_adapter_launch_descriptor.v1';
27
28
  const REGISTRY_REF = '.lattice/runtime/adapter-registry/registry.json';
@@ -68,10 +69,17 @@ function inputFailure(reason, inputPath = '') {
68
69
 
69
70
  function validateRegistrationInput(value) {
70
71
  if (!plain(value)) inputFailure('input_must_be_object');
71
- if (value.schema !== INPUT_SCHEMA) inputFailure('unsupported_schema', '/schema');
72
+ if (![LEGACY_INPUT_SCHEMA, INPUT_SCHEMA].includes(value.schema)) {
73
+ inputFailure('unsupported_schema', '/schema');
74
+ }
72
75
  if (!ID.test(value.adapter_kind ?? '')) inputFailure('invalid_adapter_kind', '/adapter_kind');
76
+ const hostDrivenFields = value.schema === INPUT_SCHEMA ? ['host_driven_epoch'] : [];
77
+ if (value.schema === INPUT_SCHEMA && typeof value.host_driven_epoch !== 'boolean') {
78
+ inputFailure('host_driven_epoch_must_be_boolean', '/host_driven_epoch');
79
+ }
73
80
  if (value.launch_kind === 'host_binary') {
74
- if (!exact(value, ['schema', 'adapter_kind', 'launch_kind', 'binary_path', 'argv', 'config_ref'])) {
81
+ if (!exact(value, ['schema', 'adapter_kind', 'launch_kind', 'binary_path', 'argv',
82
+ 'config_ref', ...hostDrivenFields])) {
75
83
  inputFailure('unexpected_or_missing_keys');
76
84
  }
77
85
  if (typeof value.binary_path !== 'string' || !path.isAbsolute(value.binary_path)) {
@@ -87,7 +95,8 @@ function validateRegistrationInput(value) {
87
95
  return;
88
96
  }
89
97
  if (value.launch_kind === 'existing_endpoint') {
90
- if (!exact(value, ['schema', 'adapter_kind', 'launch_kind', 'endpoint'])) {
98
+ if (!exact(value, ['schema', 'adapter_kind', 'launch_kind', 'endpoint',
99
+ ...hostDrivenFields])) {
91
100
  inputFailure('unexpected_or_missing_keys');
92
101
  }
93
102
  if (typeof value.endpoint !== 'string' || value.endpoint.length === 0) {
@@ -104,9 +113,11 @@ function validateRegistrationInput(value) {
104
113
  inputFailure('unsupported_launch_kind', '/launch_kind');
105
114
  }
106
115
 
107
- function createCapabilities() {
116
+ function createCapabilities(input = { schema: LEGACY_INPUT_SCHEMA }) {
108
117
  const capabilities = {
109
- schema: 'lattice.runtime_adapter_capabilities.v1',
118
+ schema: input.schema === INPUT_SCHEMA
119
+ ? 'lattice.runtime_adapter_capabilities.v2'
120
+ : 'lattice.runtime_adapter_capabilities.v1',
110
121
  operations: [...CONTROLLER_OPERATIONS],
111
122
  process_observation: true,
112
123
  worktree_fingerprint: true,
@@ -114,6 +125,9 @@ function createCapabilities() {
114
125
  durable_dispatch: true,
115
126
  capabilities_digest: '',
116
127
  };
128
+ if (input.schema === INPUT_SCHEMA) {
129
+ capabilities.host_driven_epoch = input.host_driven_epoch;
130
+ }
117
131
  capabilities.capabilities_digest = selfDigest(capabilities, 'capabilities_digest');
118
132
  if (!validateRuntimeAdapterCapabilities(capabilities)) {
119
133
  throw new TypeError('runtime adapter capabilities生成不正');
@@ -326,7 +340,7 @@ async function resolveHostBinary(repoRoot, input, binaryIdentityObserver) {
326
340
  }
327
341
 
328
342
  async function buildLaunchDescriptor(repoRoot, input, binaryIdentityObserver) {
329
- const capabilities = createCapabilities();
343
+ const capabilities = createCapabilities(input);
330
344
  let observation = { status: 'not_applicable', reason: null, message: null };
331
345
  let descriptor;
332
346
  if (input.launch_kind === 'host_binary') {