@quolu/lattice 0.35.0 → 0.36.1

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.
@@ -0,0 +1,363 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://github.com/kitepon-rgb/Lattice/blob/main/docs/schemas/lattice.todo_revision_set.v3.schema.json",
4
+ "title": "lattice.todo_revision_set.v3",
5
+ "description": "`lattice todo revise-set`が受理する最新契約。複数planへ跨がるrevisionを1つのatomic書込みへ束ねる。v3のmemberは`lattice.todo_revision.v1/v2`(非phase plan)と`lattice.phase_todo_revision.v1/v2`(phase-audited plan)のいずれかであり、`lattice.phase_todo_revision.v3`はmemberとして受理されない——v3固有のruntime_task_migration/source_cutover_batchはrevise-setの外で単独revise-phaseとして扱う契約になっている。",
6
+ "$comment": "Runtime validation (src/todo-revision.mjs validateTodoRevisionSet) additionally enforces: canonical JSON+LF bytes; revisions.length in [2, 64]; each member's own digest chain (validateTodoRevision / validatePhaseTodoRevision); every member.project_id === project_id; v3はrevisions内に最低1つphase_todo_revision memberを含む必要がある; revisionsが厳密にplan_key昇順でソートされている(重複plan_key禁止); revision_set_digestは他の全top-level fieldへの正準self-digest。",
7
+ "type": "object",
8
+ "additionalProperties": false,
9
+ "required": ["schema", "project_id", "revisions", "revision_set_digest"],
10
+ "properties": {
11
+ "schema": { "const": "lattice.todo_revision_set.v3" },
12
+ "project_id": { "$ref": "#/$defs/identifier" },
13
+ "revisions": {
14
+ "type": "array", "minItems": 2, "maxItems": 64,
15
+ "items": {
16
+ "oneOf": [
17
+ { "$ref": "#/$defs/todoRevisionMember" },
18
+ { "$ref": "#/$defs/phaseTodoRevisionMember" }
19
+ ]
20
+ }
21
+ },
22
+ "revision_set_digest": { "$ref": "#/$defs/digest" }
23
+ },
24
+ "$defs": {
25
+ "identifier": { "type": "string", "pattern": "^[0-9A-Za-z](?:[0-9A-Za-z._-]{0,127})$" },
26
+ "digest": { "type": "string", "pattern": "^[0-9a-f]{64}$" },
27
+ "nullableDigest": { "oneOf": [{ "type": "null" }, { "$ref": "#/$defs/digest" }] },
28
+ "gitSha": { "type": "string", "pattern": "^[0-9a-f]{40}$" },
29
+ "repoRef": {
30
+ "type": "string",
31
+ "minLength": 1,
32
+ "maxLength": 1024,
33
+ "pattern": "^(?!/)(?!.*(?:^|/)\\.{1,2}(?:/|$))(?!.*\\\\)(?!.*[\\u0000-\\u001f\\u007f])[^/]+(?:/[^/]+)*$"
34
+ },
35
+ "sourceLineRef": {
36
+ "type": "string",
37
+ "minLength": 1,
38
+ "maxLength": 1024,
39
+ "pattern": "^(?!/)(?!.*(?:^|/)\\.{1,2}(?:/|$))(?!.*\\\\)(?!.*[\\u0000-\\u001f\\u007f])[^/]+(?:/[^/]+)*#L[1-9][0-9]*$"
40
+ },
41
+ "nullableRepoRef": { "oneOf": [{ "type": "null" }, { "$ref": "#/$defs/repoRef" }] },
42
+ "text": { "type": "string", "minLength": 1, "maxLength": 16384 },
43
+ "nullableText": { "oneOf": [{ "type": "null" }, { "$ref": "#/$defs/text" }] },
44
+ "nodeRef": {
45
+ "type": "object", "additionalProperties": false,
46
+ "required": ["project_id", "plan_key", "task_id"],
47
+ "properties": {
48
+ "project_id": { "$ref": "#/$defs/identifier" },
49
+ "plan_key": { "$ref": "#/$defs/identifier" },
50
+ "task_id": { "$ref": "#/$defs/identifier" },
51
+ "expected_topology_digest": { "$ref": "#/$defs/digest" }
52
+ }
53
+ },
54
+ "phaseRef": {
55
+ "type": "object", "additionalProperties": false,
56
+ "required": ["project_id", "plan_key", "phase_id"],
57
+ "properties": {
58
+ "project_id": { "$ref": "#/$defs/identifier" },
59
+ "plan_key": { "$ref": "#/$defs/identifier" },
60
+ "phase_id": { "$ref": "#/$defs/identifier" },
61
+ "expected_topology_digest": { "$ref": "#/$defs/digest" }
62
+ }
63
+ },
64
+ "edge": {
65
+ "type": "object", "additionalProperties": false, "required": ["from", "to"],
66
+ "properties": { "from": { "$ref": "#/$defs/nodeRef" }, "to": { "$ref": "#/$defs/nodeRef" } }
67
+ },
68
+ "phaseAcceptDependency": {
69
+ "type": "object", "additionalProperties": false, "required": ["from", "to"],
70
+ "properties": { "from": { "$ref": "#/$defs/phaseRef" }, "to": { "$ref": "#/$defs/nodeRef" } }
71
+ },
72
+ "join": {
73
+ "type": "object", "additionalProperties": false, "required": ["id", "after", "before"],
74
+ "properties": {
75
+ "id": { "$ref": "#/$defs/identifier" },
76
+ "after": { "type": "array", "minItems": 1, "maxItems": 512, "items": { "$ref": "#/$defs/nodeRef" } },
77
+ "before": { "$ref": "#/$defs/nodeRef" }
78
+ }
79
+ },
80
+ "narrativeAnchor": {
81
+ "type": "object", "additionalProperties": false,
82
+ "required": ["origin_plan_ref", "origin_line", "source_commit", "source_line_digest"],
83
+ "properties": {
84
+ "origin_plan_ref": { "$ref": "#/$defs/repoRef" },
85
+ "origin_line": { "type": "integer", "minimum": 1 },
86
+ "source_commit": { "type": "string", "pattern": "^(?:[0-9a-f]{40}|[0-9a-f]{64})$" },
87
+ "source_line_digest": { "$ref": "#/$defs/digest" }
88
+ }
89
+ },
90
+ "compileBinding": {
91
+ "oneOf": [
92
+ { "type": "null" },
93
+ {
94
+ "type": "object", "additionalProperties": false,
95
+ "required": ["boundary_manifest_digest", "compiled_plan_digest", "topology_digest", "base_sha"],
96
+ "properties": {
97
+ "boundary_manifest_digest": { "$ref": "#/$defs/digest" },
98
+ "compiled_plan_digest": { "$ref": "#/$defs/digest" },
99
+ "topology_digest": { "$ref": "#/$defs/digest" },
100
+ "base_sha": { "$ref": "#/$defs/gitSha" }
101
+ }
102
+ }
103
+ ]
104
+ },
105
+ "predecessor": {
106
+ "type": "object", "additionalProperties": false,
107
+ "required": ["plan_digest", "journal_head_digest", "plan_version"],
108
+ "properties": {
109
+ "plan_digest": { "$ref": "#/$defs/digest" },
110
+ "journal_head_digest": { "$ref": "#/$defs/digest" },
111
+ "plan_version": { "$ref": "#/$defs/identifier" }
112
+ }
113
+ },
114
+ "taskV3": {
115
+ "type": "object", "additionalProperties": false,
116
+ "required": [
117
+ "task_id", "title", "lane", "narrative_ref", "narrative_anchor", "compile_binding", "parent_task_id"
118
+ ],
119
+ "properties": {
120
+ "task_id": { "$ref": "#/$defs/identifier" },
121
+ "title": { "$ref": "#/$defs/nullableText" },
122
+ "lane": { "$ref": "#/$defs/identifier" },
123
+ "narrative_ref": { "$ref": "#/$defs/nullableRepoRef" },
124
+ "narrative_anchor": { "oneOf": [{ "type": "null" }, { "$ref": "#/$defs/narrativeAnchor" }] },
125
+ "compile_binding": { "$ref": "#/$defs/compileBinding" },
126
+ "parent_task_id": { "oneOf": [{ "type": "null" }, { "$ref": "#/$defs/identifier" }] }
127
+ }
128
+ },
129
+ "taskV4": {
130
+ "type": "object", "additionalProperties": false,
131
+ "required": [
132
+ "task_id", "title", "lane", "narrative_ref", "narrative_anchor", "compile_binding",
133
+ "parent_task_id", "phase_id"
134
+ ],
135
+ "properties": {
136
+ "task_id": { "$ref": "#/$defs/identifier" },
137
+ "title": { "$ref": "#/$defs/nullableText" },
138
+ "lane": { "$ref": "#/$defs/identifier" },
139
+ "narrative_ref": { "$ref": "#/$defs/nullableRepoRef" },
140
+ "narrative_anchor": { "oneOf": [{ "type": "null" }, { "$ref": "#/$defs/narrativeAnchor" }] },
141
+ "compile_binding": { "$ref": "#/$defs/compileBinding" },
142
+ "parent_task_id": { "oneOf": [{ "type": "null" }, { "$ref": "#/$defs/identifier" }] },
143
+ "phase_id": { "$ref": "#/$defs/identifier" }
144
+ }
145
+ },
146
+ "phase": {
147
+ "type": "object", "additionalProperties": false,
148
+ "required": ["phase_id", "title", "gate_policy", "predecessor_phase_ids", "required_evidence_slots"],
149
+ "properties": {
150
+ "phase_id": { "$ref": "#/$defs/identifier" },
151
+ "title": { "$ref": "#/$defs/nullableText" },
152
+ "gate_policy": { "$ref": "#/$defs/identifier" },
153
+ "predecessor_phase_ids": {
154
+ "type": "array", "items": { "$ref": "#/$defs/identifier" }, "uniqueItems": true
155
+ },
156
+ "required_evidence_slots": {
157
+ "type": "array", "minItems": 1, "items": { "$ref": "#/$defs/identifier" }, "uniqueItems": true
158
+ }
159
+ }
160
+ },
161
+ "desiredPlanV3": {
162
+ "type": "object", "additionalProperties": false,
163
+ "required": [
164
+ "schema", "project_id", "plan_key", "plan_version", "predecessor_plan_digest",
165
+ "tasks", "hard_dependencies", "joins", "topology_digest", "plan_digest"
166
+ ],
167
+ "properties": {
168
+ "schema": { "const": "lattice.todo_plan.v3" },
169
+ "project_id": { "$ref": "#/$defs/identifier" },
170
+ "plan_key": { "$ref": "#/$defs/identifier" },
171
+ "plan_version": { "$ref": "#/$defs/identifier" },
172
+ "predecessor_plan_digest": { "$ref": "#/$defs/nullableDigest" },
173
+ "tasks": { "type": "array", "minItems": 1, "maxItems": 512, "items": { "$ref": "#/$defs/taskV3" } },
174
+ "hard_dependencies": { "type": "array", "maxItems": 2048, "items": { "$ref": "#/$defs/edge" } },
175
+ "joins": { "type": "array", "maxItems": 128, "items": { "$ref": "#/$defs/join" } },
176
+ "topology_digest": { "$ref": "#/$defs/digest" },
177
+ "plan_digest": { "$ref": "#/$defs/digest" }
178
+ }
179
+ },
180
+ "desiredPlanV4orV5": {
181
+ "type": "object", "additionalProperties": false,
182
+ "required": [
183
+ "schema", "project_id", "plan_key", "plan_version", "predecessor_plan_digest",
184
+ "tasks", "hard_dependencies", "joins", "phases", "topology_digest", "plan_digest"
185
+ ],
186
+ "$comment": "schemaがv5の時だけphase_accept_dependenciesも必須keyに加わる(runtime-only cross-check、oneOfでは表現しない)。",
187
+ "properties": {
188
+ "schema": { "enum": ["lattice.todo_plan.v4", "lattice.todo_plan.v5"] },
189
+ "project_id": { "$ref": "#/$defs/identifier" },
190
+ "plan_key": { "$ref": "#/$defs/identifier" },
191
+ "plan_version": { "$ref": "#/$defs/identifier" },
192
+ "predecessor_plan_digest": { "$ref": "#/$defs/nullableDigest" },
193
+ "tasks": { "type": "array", "minItems": 1, "maxItems": 512, "items": { "$ref": "#/$defs/taskV4" } },
194
+ "hard_dependencies": { "type": "array", "maxItems": 2048, "items": { "$ref": "#/$defs/edge" } },
195
+ "joins": { "type": "array", "maxItems": 128, "items": { "$ref": "#/$defs/join" } },
196
+ "phases": { "type": "array", "minItems": 1, "maxItems": 512, "items": { "$ref": "#/$defs/phase" } },
197
+ "phase_accept_dependencies": {
198
+ "type": "array", "maxItems": 2048, "items": { "$ref": "#/$defs/phaseAcceptDependency" }
199
+ },
200
+ "topology_digest": { "$ref": "#/$defs/digest" },
201
+ "plan_digest": { "$ref": "#/$defs/digest" }
202
+ },
203
+ "if": { "properties": { "schema": { "const": "lattice.todo_plan.v5" } }, "required": ["schema"] },
204
+ "then": { "required": ["phase_accept_dependencies"] }
205
+ },
206
+ "taskMigrationEntry": {
207
+ "type": "object", "additionalProperties": false,
208
+ "required": ["from_task_id", "to_task_id", "state_policy"],
209
+ "properties": {
210
+ "from_task_id": { "$ref": "#/$defs/identifier" },
211
+ "to_task_id": { "oneOf": [{ "const": "removed" }, { "$ref": "#/$defs/identifier" }] },
212
+ "state_policy": { "enum": ["carry", "carry_reconciled_metadata", "reset_pending", "removed"] }
213
+ }
214
+ },
215
+ "phaseMigrationEntry": {
216
+ "type": "object", "additionalProperties": false,
217
+ "required": ["from_phase_id", "to_phase_id", "state_policy"],
218
+ "properties": {
219
+ "from_phase_id": { "oneOf": [{ "type": "null" }, { "$ref": "#/$defs/identifier" }] },
220
+ "to_phase_id": { "oneOf": [{ "const": "removed" }, { "$ref": "#/$defs/identifier" }] },
221
+ "state_policy": { "enum": ["carry", "reset", "removed"] }
222
+ }
223
+ },
224
+ "sourceInventoryActiveEntry": {
225
+ "type": "object", "additionalProperties": false,
226
+ "required": ["task_id", "source_ref", "source_digest"],
227
+ "properties": {
228
+ "task_id": { "$ref": "#/$defs/identifier" },
229
+ "source_ref": { "$ref": "#/$defs/sourceLineRef" },
230
+ "source_digest": { "$ref": "#/$defs/digest" }
231
+ }
232
+ },
233
+ "sourceInventoryTombstone": {
234
+ "type": "object", "additionalProperties": false,
235
+ "required": ["source_ref", "source_digest", "exclusion_reason"],
236
+ "properties": {
237
+ "source_ref": { "$ref": "#/$defs/sourceLineRef" },
238
+ "source_digest": { "$ref": "#/$defs/digest" },
239
+ "exclusion_reason": { "$ref": "#/$defs/text" }
240
+ }
241
+ },
242
+ "sourceInventory": {
243
+ "type": "object", "additionalProperties": false,
244
+ "required": ["active", "excluded_tombstones"],
245
+ "properties": {
246
+ "active": {
247
+ "type": "array", "maxItems": 512, "items": { "$ref": "#/$defs/sourceInventoryActiveEntry" }
248
+ },
249
+ "excluded_tombstones": {
250
+ "type": "array", "maxItems": 2048, "items": { "$ref": "#/$defs/sourceInventoryTombstone" }
251
+ }
252
+ }
253
+ },
254
+ "sourceCutoverOperation": {
255
+ "type": "object", "additionalProperties": false,
256
+ "required": ["task_id", "disposition", "source_ref", "source_digest", "live_replacement"],
257
+ "properties": {
258
+ "task_id": { "oneOf": [{ "type": "null" }, { "$ref": "#/$defs/identifier" }] },
259
+ "disposition": { "enum": ["active", "excluded"] },
260
+ "source_ref": { "$ref": "#/$defs/sourceLineRef" },
261
+ "source_digest": { "$ref": "#/$defs/digest" },
262
+ "live_replacement": { "type": "string", "minLength": 1, "maxLength": 4096 }
263
+ }
264
+ },
265
+ "sourceCutoverBatch": {
266
+ "type": "object", "additionalProperties": false,
267
+ "required": ["batch_id", "archive_ref", "operations", "batch_digest"],
268
+ "properties": {
269
+ "batch_id": { "$ref": "#/$defs/identifier" },
270
+ "archive_ref": { "allOf": [{ "$ref": "#/$defs/repoRef" }, { "pattern": "\\.md$" }] },
271
+ "operations": {
272
+ "type": "array", "minItems": 1, "maxItems": 512, "items": { "$ref": "#/$defs/sourceCutoverOperation" }
273
+ },
274
+ "batch_digest": { "$ref": "#/$defs/digest" }
275
+ }
276
+ },
277
+ "todoRevisionMember": {
278
+ "description": "非phase planのrevision member。'lattice.todo_revision.v1'(source_cutover_batchなし)と'lattice.todo_revision.v2'(source_cutover_batchあり)のいずれか。",
279
+ "type": "object",
280
+ "if": { "properties": { "schema": { "const": "lattice.todo_revision.v2" } }, "required": ["schema"] },
281
+ "then": {
282
+ "additionalProperties": false,
283
+ "required": [
284
+ "schema", "project_id", "plan_key", "predecessor", "desired_plan", "task_migration",
285
+ "source_inventory", "reconciliation", "source_cutover_batch", "revision_digest"
286
+ ],
287
+ "properties": {
288
+ "schema": { "const": "lattice.todo_revision.v2" },
289
+ "project_id": { "$ref": "#/$defs/identifier" },
290
+ "plan_key": { "$ref": "#/$defs/identifier" },
291
+ "predecessor": { "$ref": "#/$defs/predecessor" },
292
+ "desired_plan": { "$ref": "#/$defs/desiredPlanV3" },
293
+ "task_migration": {
294
+ "type": "array", "minItems": 1, "maxItems": 512, "items": { "$ref": "#/$defs/taskMigrationEntry" }
295
+ },
296
+ "source_inventory": { "$ref": "#/$defs/sourceInventory" },
297
+ "reconciliation": {
298
+ "type": "object", "additionalProperties": false,
299
+ "required": ["predecessor_reconciliation_digest", "source_inventory_digest", "reconciliation_digest"],
300
+ "properties": {
301
+ "predecessor_reconciliation_digest": { "$ref": "#/$defs/digest" },
302
+ "source_inventory_digest": { "$ref": "#/$defs/digest" },
303
+ "reconciliation_digest": { "$ref": "#/$defs/digest" }
304
+ }
305
+ },
306
+ "source_cutover_batch": { "$ref": "#/$defs/sourceCutoverBatch" },
307
+ "revision_digest": { "$ref": "#/$defs/digest" }
308
+ }
309
+ },
310
+ "else": {
311
+ "additionalProperties": false,
312
+ "required": [
313
+ "schema", "project_id", "plan_key", "predecessor", "desired_plan", "task_migration",
314
+ "source_inventory", "reconciliation", "revision_digest"
315
+ ],
316
+ "properties": {
317
+ "schema": { "const": "lattice.todo_revision.v1" },
318
+ "project_id": { "$ref": "#/$defs/identifier" },
319
+ "plan_key": { "$ref": "#/$defs/identifier" },
320
+ "predecessor": { "$ref": "#/$defs/predecessor" },
321
+ "desired_plan": { "$ref": "#/$defs/desiredPlanV3" },
322
+ "task_migration": {
323
+ "type": "array", "minItems": 1, "maxItems": 512, "items": { "$ref": "#/$defs/taskMigrationEntry" }
324
+ },
325
+ "source_inventory": { "$ref": "#/$defs/sourceInventory" },
326
+ "reconciliation": {
327
+ "type": "object", "additionalProperties": false,
328
+ "required": ["predecessor_reconciliation_digest", "source_inventory_digest", "reconciliation_digest"],
329
+ "properties": {
330
+ "predecessor_reconciliation_digest": { "$ref": "#/$defs/digest" },
331
+ "source_inventory_digest": { "$ref": "#/$defs/digest" },
332
+ "reconciliation_digest": { "$ref": "#/$defs/digest" }
333
+ }
334
+ },
335
+ "revision_digest": { "$ref": "#/$defs/digest" }
336
+ }
337
+ }
338
+ },
339
+ "phaseTodoRevisionMember": {
340
+ "description": "phase-audited planのrevision member。'lattice.phase_todo_revision.v1'(desired_planはv4)と'v2'(desired_planはv5)のいずれかだけを受理する——'v3'(runtime_task_migration・source_cutover_batch付き)はrevise-set memberとしては受理されない。",
341
+ "type": "object", "additionalProperties": false,
342
+ "required": [
343
+ "schema", "project_id", "plan_key", "predecessor", "desired_plan",
344
+ "task_migration", "phase_migration", "revision_digest"
345
+ ],
346
+ "properties": {
347
+ "schema": { "enum": ["lattice.phase_todo_revision.v1", "lattice.phase_todo_revision.v2"] },
348
+ "project_id": { "$ref": "#/$defs/identifier" },
349
+ "plan_key": { "$ref": "#/$defs/identifier" },
350
+ "predecessor": { "$ref": "#/$defs/predecessor" },
351
+ "desired_plan": { "$ref": "#/$defs/desiredPlanV4orV5" },
352
+ "task_migration": {
353
+ "type": "array", "minItems": 1, "maxItems": 512, "items": { "$ref": "#/$defs/taskMigrationEntry" }
354
+ },
355
+ "phase_migration": {
356
+ "type": "array", "minItems": 1, "maxItems": 512, "items": { "$ref": "#/$defs/phaseMigrationEntry" }
357
+ },
358
+ "revision_digest": { "$ref": "#/$defs/digest" }
359
+ },
360
+ "$comment": "schemaが'lattice.phase_todo_revision.v1'ならdesired_plan.schemaは'lattice.todo_plan.v4'、'v2'なら'lattice.todo_plan.v5'(runtime cross-check)。"
361
+ }
362
+ }
363
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@quolu/lattice",
3
- "version": "0.35.0",
3
+ "version": "0.36.1",
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",
@@ -27,6 +27,10 @@
27
27
  "docs/schemas/lattice.plan_create_input.v1.schema.json",
28
28
  "docs/schemas/lattice.plan_create_input.v2.schema.json",
29
29
  "docs/schemas/lattice.plan_create_input.v3.schema.json",
30
+ "docs/schemas/lattice.todo_revision.v2.schema.json",
31
+ "docs/schemas/lattice.todo_revision_set.v3.schema.json",
32
+ "docs/schemas/lattice.phase_todo_revision.v3.schema.json",
33
+ "docs/schemas/lattice.todo_extraction.v2.schema.json",
30
34
  "docs/schemas/lattice.run_request.v1.schema.json",
31
35
  "docs/schemas/lattice.executor_packet.v1.schema.json",
32
36
  "docs/schemas/lattice.executor_receipt.v1.schema.json",
package/src/cli-help.mjs CHANGED
@@ -26,8 +26,9 @@ Commands:
26
26
  create --input <file> [--serialization-reviewed]
27
27
  # 依存グラフがほぼ一直線(serialization_ratioが閾値超)なら一度突き返す。
28
28
  # 再考した上でなお直列でよいなら --serialization-reviewed を付けて再実行する
29
- create --schema --json
30
- create --schema-version <2|3> --json
29
+ create --schema --json # 既定は最新版(v3)のJSON Schemaを返す
30
+ create --schema-version <1|2|3> --json
31
+ show <plan_key> --json # task・依存・phase・状態をplan本体から1コマンドで投影する
31
32
  compile --request <request.json>
32
33
  compile --schema --json # lattice.run_request.v1 の JSON Schema を出す
33
34
  verify --request <request.json> --plan <plan.json>
@@ -91,6 +92,9 @@ Write commands:
91
92
  revise --plan <key> --input <file>
92
93
  revise-phase --plan <key> --input <file>
93
94
  revise-set --input <file>
95
+ <revise|revise-phase|revise-set|migrate> --schema --json
96
+ # 実際に受理する最新契約のJSON Schemaを返す(storeを読まない)。
97
+ # 入力が契約に合わないときは、違反フィールドのpathがerror detailへ載る
94
98
  phase review --plan <key> --phase <id> --reason <text>
95
99
  phase <accept|reject> --plan <key> --phase <id> --input <file>
96
100
  phase reopen --plan <key> --phase <id> --reason <text> [--override-reason <text>]
@@ -135,7 +139,8 @@ registerはLATTICE_BRIDGE_REGISTRAR_SSH_HOSTとLATTICE_BRIDGE_REGISTRAR_SCRIPT
135
139
  const SUBCOMMAND_USAGE = Object.freeze({
136
140
  status: 'status --json',
137
141
  'session-context': 'session-context --json',
138
- 'plan create': 'plan create --input <file> [--serialization-reviewed] | --schema --json | --schema-version <2|3> --json',
142
+ 'plan create': 'plan create --input <file> [--serialization-reviewed] | --schema --json | --schema-version <1|2|3> --json',
143
+ 'plan show': 'plan show <plan_key> --json',
139
144
  'plan compile': 'plan compile --request <request.json> | --schema --json',
140
145
  'plan verify': 'plan verify --request <request.json> --plan <plan.json>',
141
146
  'run start': 'run start --request <request.json> --executor <adapter> | --schema --json',
@@ -177,10 +182,10 @@ const SUBCOMMAND_USAGE = Object.freeze({
177
182
  'todo reopen': 'todo reopen --plan <key> --task <id> --reason <text> [--override-reason <text>]',
178
183
  'todo evidence': 'todo evidence promote --plan <key> --task <id> --evidence <file>',
179
184
  'todo evidence promote': 'todo evidence promote --plan <key> --task <id> --evidence <file>',
180
- 'todo revise': 'todo revise --plan <key> --input <file>',
181
- 'todo revise-phase': 'todo revise-phase --plan <key> --input <file>',
182
- 'todo revise-set': 'todo revise-set --input <file>',
183
- 'todo migrate': 'todo migrate --input <extraction.json> [--serialization-reviewed]',
185
+ 'todo revise': 'todo revise --plan <key> --input <file> | --schema --json',
186
+ 'todo revise-phase': 'todo revise-phase --plan <key> --input <file> | --schema --json',
187
+ 'todo revise-set': 'todo revise-set --input <file> | --schema --json',
188
+ 'todo migrate': 'todo migrate --input <extraction.json> [--serialization-reviewed] | --schema --json',
184
189
  'sensor init': 'sensor init [path] --json',
185
190
  'sensor sync': 'sensor sync [path] --json',
186
191
  'runtime-errors snapshot': 'runtime-errors snapshot [--after-cursor <n>] [--limit <n>] --json',
@@ -22,6 +22,7 @@ import {
22
22
  buildTodoPlan,
23
23
  createTodoStoreWriter,
24
24
  initializeAuthoredTodoStore,
25
+ isPhaselessTodoPlanSchema,
25
26
  readTodoIndependenceArtifact,
26
27
  readTodoStore,
27
28
  TodoStoreError,
@@ -481,6 +482,9 @@ export async function runPlanCreate({ cwd, inputRef, stdout, serializationReview
481
482
  max_frontier_width: dispatchShape.max_frontier_width,
482
483
  serialization_ratio: dispatchShape.serialization_ratio,
483
484
  },
485
+ // ADR 0147裁定3: phase無し(v3)のplan createは拒否せず、終端監査が要ることを結果へ
486
+ // 明示するに留める。phase入力(v4/v5)ならfalse——既存のPhase gateがそのまま重監査を担う。
487
+ terminal_audit_required: isPhaselessTodoPlanSchema(member.plan.schema),
484
488
  result_digest: '',
485
489
  };
486
490
  result.result_digest = resultDigest(result);
@@ -488,7 +492,16 @@ export async function runPlanCreate({ cwd, inputRef, stdout, serializationReview
488
492
  return 0;
489
493
  }
490
494
 
491
- export async function runPlanCreateSchema({ stdout, version = 1 }) {
495
+ /**
496
+ * `--schema --json`(版指定なし)の既定はCURRENT_CREATE_INPUT_SCHEMAと同じv3にする。
497
+ * 既定がv1のままだと、素の`--schema`を叩いたAIが古いv1(Phaseを表現できない)を
498
+ * 受け取り、実運用で通らない入力を作ってしまう(実際に踏んだ)。
499
+ * `--schema-version 1`は互換のため引き続き取得できる(bin/lattice.mjs側で許可)。
500
+ *
501
+ * 「どの版を返したか」は返すJSON Schema自身の`title`(例: `lattice.plan_create_input.v3`)が
502
+ * 既に機械可読に持っている。壊さずに追加のkeyを足す理由が無いので足さない。
503
+ */
504
+ export async function runPlanCreateSchema({ stdout, version = 3 }) {
492
505
  if (![1, 2, 3].includes(version)) throw new TypeError('unsupported plan create schema version');
493
506
  const expected = version === 3 ? DECOUPLED_PHASE_CREATE_INPUT_SCHEMA
494
507
  : version === 2 ? PHASE_CREATE_INPUT_SCHEMA : CREATE_INPUT_SCHEMA;
@@ -504,6 +517,95 @@ export async function runPlanCreateSchema({ stdout, version = 1 }) {
504
517
  }
505
518
  }
506
519
 
520
+ const PLAN_SHOW_RESULT_SCHEMA = 'lattice.plan_show_result.v1';
521
+
522
+ /**
523
+ * `todo bindings`はcompile_binding付きtaskだけを投影するので、通常planでは空配列を返す。
524
+ * それを見て「planが空だ」と誤読された実績があるため、plan本体(task・依存・phase・状態)を
525
+ * 1コマンドで読める面を別に持つ。読み出しは既存store readerとtodo status projectionの
526
+ * 再利用に留め、journalを独自に再実装しない。
527
+ */
528
+ export async function runPlanShow({ cwd, planKey, stdout }) {
529
+ const repoRoot = resolveRepoRoot(cwd);
530
+ if (repoRoot === null) throw new TodoStoreError('REPO_UNRESOLVED', 'git_toplevel_unresolved');
531
+ const store = await readTodoStore({ repoRoot });
532
+ const member = store.members.find(({ descriptor }) => descriptor.plan_key === planKey);
533
+ if (member === undefined) {
534
+ // 既存read commands(independence compile等)と同じcode/reasonを踏襲する。
535
+ // 未知のplan_keyを「store不整合」と同じ扱いにするのはこのCLI全体の既定であり、
536
+ // ここだけ別codeへ逸れると呼び出し側の分岐が増える。
537
+ throw new TodoStoreError('STORE_INCONSISTENT', 'plan_not_active', undefined, {
538
+ plan_key: planKey, next_action: 'check_active_plans_via_status',
539
+ });
540
+ }
541
+ const { plan, tasks, phases } = member;
542
+ const phaseInput = ['lattice.todo_plan.v4', 'lattice.todo_plan.v5'].includes(plan.schema);
543
+ const stateByTaskId = new Map(tasks.map((state) => [state.task_id, state]));
544
+ // snapshot artifactの形式(v1にはphasesキーが無い)には縛られない導出ビューを読む
545
+ // (readTodoStoreが常に member.phases として埋める。ADR 0147)。
546
+ const phaseStatusById = new Map((phases ?? []).map((phase) => [phase.phase_id, phase.status]));
547
+
548
+ // 依存の本数: このplanの中でそのtaskへ入ってくるhard_dependencies辺と、joinで
549
+ // 合流するafter辺の合計。cross-plan参照は数えない(plan showは単一planの投影のため)。
550
+ const dependsOnCount = new Map(plan.tasks.map((task) => [task.task_id, 0]));
551
+ for (const edge of plan.hard_dependencies) {
552
+ if (edge.to.plan_key === planKey && dependsOnCount.has(edge.to.task_id)) {
553
+ dependsOnCount.set(edge.to.task_id, dependsOnCount.get(edge.to.task_id) + 1);
554
+ }
555
+ }
556
+ for (const join of plan.joins) {
557
+ if (join.before.plan_key === planKey && dependsOnCount.has(join.before.task_id)) {
558
+ dependsOnCount.set(join.before.task_id, dependsOnCount.get(join.before.task_id) + join.after.length);
559
+ }
560
+ }
561
+
562
+ const taskList = plan.tasks.map((task) => ({
563
+ task_id: task.task_id,
564
+ title: task.title,
565
+ lane: task.lane,
566
+ phase_id: phaseInput ? task.phase_id : null,
567
+ state: stateByTaskId.get(task.task_id).status,
568
+ depends_on_count: dependsOnCount.get(task.task_id) ?? 0,
569
+ }));
570
+
571
+ const phaseList = phaseInput ? plan.phases.map((phase) => ({
572
+ phase_id: phase.phase_id,
573
+ title: phase.title,
574
+ gate_policy: phase.gate_policy,
575
+ predecessor_phase_ids: phase.predecessor_phase_ids,
576
+ status: phaseStatusById.get(phase.phase_id) ?? null,
577
+ })) : [];
578
+
579
+ // dispatch形状はplan create時に既に計算している同じ関数を再利用する。
580
+ // critical path長・frontier幅を独自に計算し直さない。
581
+ const dispatchShape = computeTodoDispatchShapeForPlan({
582
+ projectId: plan.project_id, planKey,
583
+ taskIds: plan.tasks.map(({ task_id: taskId }) => taskId),
584
+ hardDependencies: plan.hard_dependencies, joins: plan.joins,
585
+ });
586
+
587
+ const result = {
588
+ schema: PLAN_SHOW_RESULT_SCHEMA,
589
+ project_id: plan.project_id,
590
+ plan_key: plan.plan_key,
591
+ plan_version: plan.plan_version,
592
+ plan_schema: plan.schema,
593
+ has_phases: phaseInput,
594
+ phases: phaseList,
595
+ tasks: taskList,
596
+ topology: {
597
+ task_count: dispatchShape.task_count,
598
+ critical_path_length: dispatchShape.critical_path_length,
599
+ max_frontier_width: dispatchShape.max_frontier_width,
600
+ serialization_ratio: dispatchShape.serialization_ratio,
601
+ },
602
+ result_digest: '',
603
+ };
604
+ result.result_digest = resultDigest(result);
605
+ stdout.write(`${JSON.stringify(result)}\n`);
606
+ return 0;
607
+ }
608
+
507
609
  export function projectStatusFailure({ cwd, stdout, cliVersion, error }) {
508
610
  const result = invalidStatus({
509
611
  cliVersion, repoRoot: resolveRepoRoot(cwd),