@quolu/lattice 0.52.0 → 0.52.2

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 (182) hide show
  1. package/LICENSE +147 -147
  2. package/README.ja.md +355 -355
  3. package/README.md +258 -258
  4. package/bin/lattice-mcp.mjs +0 -0
  5. package/bin/lattice-scripted-adapter.mjs +0 -0
  6. package/bin/lattice-scripted-worker.mjs +0 -0
  7. package/bin/lattice-work-order-adapter.mjs +0 -0
  8. package/bin/lattice.mjs +0 -0
  9. package/docs/bridge-setup.md +132 -132
  10. package/docs/schemas/lattice.executor_packet.v1.schema.json +57 -57
  11. package/docs/schemas/lattice.executor_receipt.v1.schema.json +66 -66
  12. package/docs/schemas/lattice.phase_todo_revision.v3.schema.json +360 -360
  13. package/docs/schemas/lattice.plan_create_input.v1.schema.json +56 -56
  14. package/docs/schemas/lattice.plan_create_input.v2.schema.json +72 -72
  15. package/docs/schemas/lattice.plan_create_input.v3.schema.json +81 -81
  16. package/docs/schemas/lattice.plan_create_input.v4.schema.json +85 -85
  17. package/docs/schemas/lattice.run_request.v1.schema.json +238 -238
  18. package/docs/schemas/lattice.runtime_adapter_capabilities.v2.schema.json +55 -55
  19. package/docs/schemas/lattice.runtime_adapter_registration_input.v1.schema.json +78 -78
  20. package/docs/schemas/lattice.runtime_adapter_registration_input.v2.schema.json +86 -86
  21. package/docs/schemas/lattice.todo_extraction.v2.schema.json +298 -298
  22. package/docs/schemas/lattice.todo_extraction.v3.schema.json +146 -146
  23. package/docs/schemas/lattice.todo_revision.v2.schema.json +260 -260
  24. package/docs/schemas/lattice.todo_revision_set.v3.schema.json +363 -363
  25. package/package.json +103 -103
  26. package/sensor/LICENSE +21 -21
  27. package/sensor/NOTICE +19 -19
  28. package/sensor/dist/bin/lattice-sensor.js +9 -9
  29. package/sensor/dist/db/index.js +24 -24
  30. package/sensor/dist/db/migrations.js +41 -41
  31. package/sensor/dist/db/queries.js +164 -164
  32. package/sensor/dist/db/schema.sql +205 -205
  33. package/sensor/dist/directory.js +5 -5
  34. package/sensor/dist/extraction/wasm/tree-sitter-c_sharp.wasm +0 -0
  35. package/sensor/dist/extraction/wasm/tree-sitter-cfml.wasm +0 -0
  36. package/sensor/dist/extraction/wasm/tree-sitter-cfquery.wasm +0 -0
  37. package/sensor/dist/extraction/wasm/tree-sitter-cfscript.wasm +0 -0
  38. package/sensor/dist/extraction/wasm/tree-sitter-cobol.wasm +0 -0
  39. package/sensor/dist/extraction/wasm/tree-sitter-erlang.wasm +0 -0
  40. package/sensor/dist/extraction/wasm/tree-sitter-go.wasm +0 -0
  41. package/sensor/dist/extraction/wasm/tree-sitter-java.wasm +0 -0
  42. package/sensor/dist/extraction/wasm/tree-sitter-javascript.wasm +0 -0
  43. package/sensor/dist/extraction/wasm/tree-sitter-nix.wasm +0 -0
  44. package/sensor/dist/extraction/wasm/tree-sitter-pascal.wasm +0 -0
  45. package/sensor/dist/extraction/wasm/tree-sitter-python.wasm +0 -0
  46. package/sensor/dist/extraction/wasm/tree-sitter-tsx.wasm +0 -0
  47. package/sensor/dist/extraction/wasm/tree-sitter-typescript.wasm +0 -0
  48. package/sensor/dist/extraction/wasm/tree-sitter-vbnet.wasm +0 -0
  49. package/sensor/dist/mcp/liveness-watchdog.js +53 -53
  50. package/sensor/dist/mcp/server-instructions.js +95 -95
  51. package/sensor/package.json +56 -56
  52. package/src/artifact-contracts-v2.mjs +325 -325
  53. package/src/artifact-contracts.mjs +895 -895
  54. package/src/boundary-compiler.mjs +712 -712
  55. package/src/boundary-observation-compiler-v2.mjs +344 -344
  56. package/src/bounded-seam.mjs +230 -230
  57. package/src/bridge-address.mjs +107 -107
  58. package/src/bridge-cli.mjs +297 -297
  59. package/src/bridge-config.mjs +346 -346
  60. package/src/bridge-daemon.mjs +378 -378
  61. package/src/bridge-launch-agent.mjs +323 -323
  62. package/src/bridge-registrar.mjs +102 -102
  63. package/src/bridge-server.mjs +376 -376
  64. package/src/cli-help.mjs +308 -308
  65. package/src/cli-stdio.mjs +40 -40
  66. package/src/control-compiler.mjs +532 -532
  67. package/src/dag-chain.mjs +261 -261
  68. package/src/factory-diagnostics.mjs +188 -188
  69. package/src/hash-chain.mjs +76 -76
  70. package/src/hooks-cli.mjs +1057 -1053
  71. package/src/isolation-runner.mjs +409 -409
  72. package/src/node-version-guard.mjs +44 -44
  73. package/src/project-cli.mjs +697 -697
  74. package/src/project-identity.mjs +130 -130
  75. package/src/rc1-black-box-oracle.mjs +906 -906
  76. package/src/rc1-comparison.mjs +154 -154
  77. package/src/rc1-evidence-bundle.mjs +456 -456
  78. package/src/rc1-v4-campaign.mjs +708 -708
  79. package/src/rc1-v4-transform.mjs +467 -467
  80. package/src/rc1-v5-artifact-set.mjs +855 -855
  81. package/src/rc1-v5-behavior-evidence.mjs +594 -594
  82. package/src/rc1-v5-campaign.mjs +797 -797
  83. package/src/rc1-v5-transform.mjs +421 -421
  84. package/src/rc1-v6-artifact-set.mjs +807 -807
  85. package/src/rc1-v6-behavior-evidence.mjs +273 -273
  86. package/src/rc1-v6-campaign.mjs +623 -623
  87. package/src/rc1-v6-causal-binding.mjs +473 -473
  88. package/src/rc1-v6-measurement.mjs +313 -313
  89. package/src/rc2-artifact-set.mjs +1584 -1584
  90. package/src/rc2-campaign.mjs +1506 -1502
  91. package/src/rc2-delivery-policy-front-end.mjs +1079 -1079
  92. package/src/rc2-delivery-policy-oracle.mjs +134 -134
  93. package/src/rc2-delivery-policy-transform.mjs +1127 -1127
  94. package/src/rc2-rc1-transfer-front-end.mjs +511 -511
  95. package/src/rc3-actual-dogfood.mjs +652 -652
  96. package/src/rc3-dogfood-scaffold.mjs +315 -315
  97. package/src/rc3-scripted-campaign.mjs +1383 -1383
  98. package/src/rc4-stage1-dogfood.mjs +673 -673
  99. package/src/runtime-adapter-registry.mjs +524 -520
  100. package/src/runtime-cli.mjs +4588 -4584
  101. package/src/runtime-contracts.mjs +824 -824
  102. package/src/runtime-control-store.mjs +604 -600
  103. package/src/runtime-controller-protocol.mjs +587 -587
  104. package/src/runtime-decision-verifier.mjs +701 -701
  105. package/src/runtime-diff-observer.mjs +361 -361
  106. package/src/runtime-direct-os-observer.mjs +301 -301
  107. package/src/runtime-driver-state.mjs +166 -162
  108. package/src/runtime-engine.mjs +779 -779
  109. package/src/runtime-errors.mjs +356 -356
  110. package/src/runtime-event-store.mjs +189 -189
  111. package/src/runtime-front-end.mjs +925 -925
  112. package/src/runtime-gate-store.mjs +481 -477
  113. package/src/runtime-hold-recompile.mjs +916 -916
  114. package/src/runtime-io-sentinel.mjs +391 -391
  115. package/src/runtime-lifecycle-lock.mjs +294 -290
  116. package/src/runtime-managed-supervisor.mjs +1490 -1490
  117. package/src/runtime-multi-epoch-store.mjs +838 -834
  118. package/src/runtime-projection.mjs +269 -269
  119. package/src/runtime-pull-intake.mjs +1192 -1188
  120. package/src/runtime-scripted-adapter-controller.mjs +1160 -1156
  121. package/src/runtime-scripted-executor.mjs +163 -163
  122. package/src/runtime-scripted-worktree.mjs +104 -104
  123. package/src/runtime-seam-resolve.mjs +428 -428
  124. package/src/runtime-seam-treatment.mjs +173 -173
  125. package/src/runtime-socket-owner.mjs +125 -125
  126. package/src/runtime-work-order-contracts.mjs +91 -91
  127. package/src/runtime-work-order-controller.mjs +1171 -1167
  128. package/src/runtime-worktree-executor.mjs +199 -199
  129. package/src/schedulability-compiler-v2.mjs +303 -303
  130. package/src/schedulability-verifier-v2.mjs +317 -317
  131. package/src/seam-apply.mjs +549 -549
  132. package/src/seam-commit-shared.mjs +22 -22
  133. package/src/seam-commit-transform.mjs +81 -81
  134. package/src/seam-commit.mjs +18 -18
  135. package/src/seam-cost.mjs +322 -322
  136. package/src/seam-derivation.mjs +188 -188
  137. package/src/seam-gate.mjs +146 -146
  138. package/src/seam-proposal-contracts.mjs +446 -446
  139. package/src/seam-proposal-queries.mjs +521 -521
  140. package/src/seam-proposal.mjs +2011 -2011
  141. package/src/seam-ref.mjs +33 -33
  142. package/src/seam-rewrite.mjs +286 -286
  143. package/src/seam-transform.mjs +554 -554
  144. package/src/seam-verification.mjs +260 -260
  145. package/src/sensor-adapter.mjs +432 -432
  146. package/src/sensor-cli.mjs +139 -139
  147. package/src/sensor-diff.mjs +661 -661
  148. package/src/sensor-node-runtime.mjs +53 -53
  149. package/src/sensor-runtime.mjs +52 -52
  150. package/src/timestamp-contract.mjs +8 -8
  151. package/src/todo-audit-pending.mjs +91 -91
  152. package/src/todo-chain.mjs +178 -178
  153. package/src/todo-cli.mjs +3141 -3126
  154. package/src/todo-contracts.mjs +728 -728
  155. package/src/todo-dashboard-registry.mjs +573 -573
  156. package/src/todo-dispatch-shape.mjs +190 -190
  157. package/src/todo-gantt-html-independence.mjs +239 -239
  158. package/src/todo-gantt-html-shared.mjs +226 -226
  159. package/src/todo-gantt-html-style.mjs +131 -131
  160. package/src/todo-gantt-html.mjs +248 -248
  161. package/src/todo-gantt-layout.mjs +974 -974
  162. package/src/todo-gantt-live.mjs +361 -361
  163. package/src/todo-gantt-nested.mjs +263 -263
  164. package/src/todo-gantt-presentation.mjs +217 -217
  165. package/src/todo-gantt-scope.mjs +123 -123
  166. package/src/todo-gantt-svg.mjs +353 -353
  167. package/src/todo-independence-contracts.mjs +595 -595
  168. package/src/todo-independence-guidance.mjs +322 -322
  169. package/src/todo-independence.mjs +640 -640
  170. package/src/todo-markdown-renderer.mjs +260 -260
  171. package/src/todo-migration.mjs +448 -448
  172. package/src/todo-narrative-anchor.mjs +130 -130
  173. package/src/todo-note-store.mjs +629 -625
  174. package/src/todo-parallel-candidates.mjs +114 -114
  175. package/src/todo-revision.mjs +995 -995
  176. package/src/todo-split.mjs +472 -472
  177. package/src/todo-status.mjs +690 -690
  178. package/src/todo-store-git-transaction.mjs +418 -418
  179. package/src/todo-store.mjs +4322 -4314
  180. package/src/treatment-compiler.mjs +728 -728
  181. package/src/treatment-runner.mjs +656 -656
  182. package/src/witness-scaffold.mjs +180 -180
@@ -1,322 +1,322 @@
1
- /**
2
- * 並列可否の案内(ADR 0130)。
3
- *
4
- * 状況から`{code, message, next_action}`を引く単一正本。advisory・投影・typed error・helpは
5
- * すべてここから引く。面ごとに文言を書けば必ずずれ、同じ状況に別の説明が付く。
6
- *
7
- * 文は事実と次の一歩だけを述べ、指示しない。dispatchの意思決定はhostが所有する
8
- * (ADR 0063 Decision 5)。命令形にするとLatticeがagentを統制する面へ滑る。
9
- */
10
-
11
- export const TODO_INDEPENDENCE_GUIDANCE_CODES = Object.freeze([
12
- 'independence_no_ready_frontier',
13
- // ob03: 調整方式の宣言に関する案内。witnessが全planの暗黙義務だった時、未compileの督促は
14
- // 誰の受入条件でもない作業を指しており、正確なまま素通りされた。まず「どちらで行くか」を
15
- // 選ばせ、選択の後にだけ督促する。
16
- 'coordination_mode_undeclared',
17
- 'coordination_conversation',
18
- 'independence_unrecorded',
19
- 'independence_task_undeclared',
20
- 'independence_contract_superseded',
21
- 'independence_superseded',
22
- 'independence_stale_for_task',
23
- 'independence_conflict_with_active',
24
- 'independence_conflict_between_ready',
25
- 'independence_verdicts_absent',
26
- 'independence_verified',
27
- ]);
28
-
29
- export const SEAM_PROPOSAL_GUIDANCE_CODES = Object.freeze([
30
- 'seam_proposal_unrecorded',
31
- 'seam_proposal_superseded',
32
- 'seam_proposal_stale',
33
- 'seam_proposal_binding_overlap',
34
- 'seam_proposal_binding_outside_resource',
35
- 'seam_proposal_binding_symbol_unresolved',
36
- 'seam_proposal_binding_resource_unresolved',
37
- 'seam_proposal_binding_ambiguous',
38
- 'seam_proposal_binding_missing',
39
- 'seam_proposal_verified',
40
- ]);
41
-
42
- /**
43
- * 束縛失敗のunknown種別から案内codeを引く表。並びは優先順で、先頭ほど次の一歩が具体的である。
44
- *
45
- * 宣言が壊れている状況を、宣言が無い状況より上に置く。壊れている方は直す対象が
46
- * 一意に決まるのに対し、無い方は何をどう宣言するかから決めることになるからである。
47
- */
48
- const SEAM_PROPOSAL_BINDING_GUIDANCE = Object.freeze([
49
- Object.freeze(['concern_anchor_overlap', 'seam_proposal_binding_overlap']),
50
- Object.freeze(['concern_anchor_outside_resource', 'seam_proposal_binding_outside_resource']),
51
- Object.freeze(['concern_anchor_unresolved', 'seam_proposal_binding_symbol_unresolved']),
52
- Object.freeze(['concern_anchor_resource_unresolved', 'seam_proposal_binding_resource_unresolved']),
53
- Object.freeze(['semantic_owner_binding_ambiguous', 'seam_proposal_binding_ambiguous']),
54
- Object.freeze(['semantic_owner_binding_missing', 'seam_proposal_binding_missing']),
55
- ]);
56
-
57
- /**
58
- * 束縛の案内へ共通で添える、記録が更新される条件。
59
- *
60
- * concern_anchorsはwitness setにあり、seam提案はindependence記録のwitness_set_digestと
61
- * 一致する宣言しか読まない。宣言を直しただけでは提案は変わらないので、そこまで述べないと
62
- * 「直したのに同じunknownが出る」で止まる。
63
- */
64
- const BINDING_RECOMPILE_HINT = '宣言はwitness setにあり、independence compileとseam-proposal compileを通し直すまで提案へ写らない。';
65
-
66
- const CATALOG = Object.freeze({
67
- independence_no_ready_frontier: Object.freeze({
68
- message: '着手候補が無いため、並列可否を述べる対象が無い。',
69
- next_action: 'none',
70
- }),
71
- coordination_mode_undeclared: Object.freeze({
72
- message: 'このplanは調整方式をまだ選んでいない。witness検証で並列するか、会話で調整するかが決まっていない。',
73
- next_action: 'declare_coordination_mode',
74
- }),
75
- coordination_conversation: Object.freeze({
76
- message: 'このplanは会話調整を選んでいる。並列可否は宣言と判定ではなく、卓の合意が持つ。',
77
- next_action: 'none',
78
- }),
79
- independence_unrecorded: Object.freeze({
80
- message: 'このplanの並列可否はまだ判定していない。競合が無いのではなく、記録が存在しない。',
81
- next_action: 'declare_witness_set_then_compile',
82
- }),
83
- independence_task_undeclared: Object.freeze({
84
- message: 'この工程はwitness setで宣言されていないため、記録には含まれていない。',
85
- next_action: 'add_task_to_witness_set_then_compile',
86
- }),
87
- independence_contract_superseded: Object.freeze({
88
- message: '記録は旧契約versionで書かれており、現在の並列可否の判定としては読めない。現在の契約での再compileが次の一歩になる。',
89
- next_action: 'recompile_independence',
90
- }),
91
- independence_superseded: Object.freeze({
92
- message: 'planが改訂され、記録は別のtopologyについての判定になっている。',
93
- next_action: 'migrate_witness_set_then_compile',
94
- }),
95
- independence_stale_for_task: Object.freeze({
96
- message: '記録後にこの工程の宣言境界が変更されたため、記録時点の判定は現在のcodeを指していない。',
97
- next_action: 'recompile_independence',
98
- }),
99
- independence_conflict_with_active: Object.freeze({
100
- message: '作業中の工程と同じ資源を書く記録がある。並行すると衝突する。',
101
- next_action: 'serialize_or_split_boundary',
102
- }),
103
- independence_conflict_between_ready: Object.freeze({
104
- message: '他のready工程と同じ資源を書く記録がある。同時に着手すると衝突する。',
105
- next_action: 'serialize_or_split_boundary',
106
- }),
107
- independence_verdicts_absent: Object.freeze({
108
- message: '判定が途中で止まっており、記録はどの組についてもverdictを持たない。自分にunknownが無いことは、他と干渉しない証拠にならない。',
109
- next_action: 'resolve_unknowns_then_recompile',
110
- }),
111
- independence_verified: Object.freeze({
112
- message: '記録時点の宣言境界では、他のready工程と干渉しない。',
113
- next_action: 'none',
114
- }),
115
- seam_proposal_unrecorded: Object.freeze({
116
- message: 'このplanのseam提案はまだ生成していない。提案対象が無いのではなく、記録が存在しない。',
117
- next_action: 'compile_seam_proposal',
118
- }),
119
- seam_proposal_superseded: Object.freeze({
120
- message: '参照元のplanまたは並列可否記録が更新され、このseam提案は現在の競合についての記録ではない。',
121
- next_action: 'compile_seam_proposal',
122
- }),
123
- seam_proposal_stale: Object.freeze({
124
- message: 'seam提案の生成後にHEADが進み、記録時点の構造証拠は現在のcodeを指していない。',
125
- next_action: 'compile_seam_proposal',
126
- }),
127
- seam_proposal_binding_overlap: Object.freeze({
128
- message: `同じ資源について、複数のToDoが同じsymbolを自分の担当として宣言している。どちら側へ切るか決まらないため、切断候補を束縛できない。${BINDING_RECOMPILE_HINT}`,
129
- next_action: 'split_overlapping_concern_anchors_then_recompile',
130
- }),
131
- seam_proposal_binding_outside_resource: Object.freeze({
132
- message: `concern_anchorsが挙げたsymbolが、withinで指した資源の外にある。その資源の分割を説明しないので束縛へ使えない。${BINDING_RECOMPILE_HINT}`,
133
- next_action: 'correct_concern_anchors_then_recompile',
134
- }),
135
- seam_proposal_binding_symbol_unresolved: Object.freeze({
136
- message: `concern_anchorsが挙げたsymbolを、sensorが同名同pathで解決できなかった。近い別symbolへは寄せないため、束縛は成立していない。${BINDING_RECOMPILE_HINT}`,
137
- next_action: 'correct_concern_anchors_then_recompile',
138
- }),
139
- seam_proposal_binding_resource_unresolved: Object.freeze({
140
- message: `concern_anchorsのwithinが指す資源をsensorで解決できなかった。宣言がどの資源についてのものか確定しない。${BINDING_RECOMPILE_HINT}`,
141
- next_action: 'correct_concern_anchors_then_recompile',
142
- }),
143
- seam_proposal_binding_ambiguous: Object.freeze({
144
- message: `宣言したsymbolが切断候補の複数側へ当たるため、どのToDoがどちらを所有するか一意に決まらない。${BINDING_RECOMPILE_HINT}`,
145
- next_action: 'narrow_concern_anchors_then_recompile',
146
- }),
147
- seam_proposal_binding_missing: Object.freeze({
148
- message: `係争資源の中でどのToDoが何を触るかが宣言されていないため、切断候補を所有者へ束縛できない。依存線でも呼び出し辺でも所有は決まらない。${BINDING_RECOMPILE_HINT}`,
149
- next_action: 'declare_concern_anchors_then_recompile',
150
- }),
151
- seam_proposal_verified: Object.freeze({
152
- message: 'seam提案の記録は現在のplan、並列可否記録、HEADと一致している。',
153
- next_action: 'none',
154
- }),
155
- });
156
-
157
- /** 切断可能性の言い換え。conflictの案内へ添える。 */
158
- const SEVERABILITY_HINT = Object.freeze({
159
- code_seam: 'symbol/pathの衝突なので、境界を分けるrefactorで並列化しうる。記録済みの競合からseam-proposal compileを検討できる。',
160
- serial: '共有stateまたはeffectの衝突なので、分割では切り離せない。',
161
- });
162
-
163
- export function todoIndependenceGuidance(code, { severability = null } = {}) {
164
- const entry = CATALOG[code];
165
- if (entry === undefined) {
166
- throw new TypeError(`unknown independence guidance code: ${code}`);
167
- }
168
- const hint = severability === null ? null : SEVERABILITY_HINT[severability] ?? null;
169
- return {
170
- code,
171
- message: hint === null ? entry.message : `${entry.message}${hint}`,
172
- next_action: entry.next_action,
173
- };
174
- }
175
-
176
- /**
177
- * 記録済みの宣言膨張を、AIが分割を検討するための助言へ写す。
178
- *
179
- * 判断はしない。膨張回数が既知かつ1回以上という事実だけを入口にし、gate形なら
180
- * 依存の合流点という構造事実を強い材料として添える。subset gapで累計がnullのtaskや
181
- * 初回宣言は、回数を主張できないので助言を出さない。
182
- */
183
- export function scopeExpansionRecommendations(scopeExpanded) {
184
- if (!Array.isArray(scopeExpanded)) {
185
- throw new TypeError('scope_expanded must be an array');
186
- }
187
- return scopeExpanded
188
- .filter((entry) => Number.isSafeInteger(entry?.growth_events)
189
- && entry.growth_events > 0)
190
- .map((entry) => ({
191
- code: 'scope_expanded_consider_split',
192
- task_id: entry.task_id,
193
- growth_events: entry.growth_events,
194
- first_seen_path_count: entry.first_seen_path_count,
195
- path_count: entry.path_count,
196
- gate_shape: entry.gate_shape,
197
- message: `宣言が${entry.growth_events}回膨張している。${entry.gate_shape
198
- ? 'このtaskは依存の合流点であり、'
199
- : ''}内側にgateのリストが生えているなら、A1..Anと残余A'への分割を検討できる。`,
200
- next_action: 'consider_todo_split',
201
- }))
202
- .sort((left, right) => left.task_id.localeCompare(right.task_id));
203
- }
204
-
205
- /**
206
- * advisoryや投影の状態から、最も行動を要する案内を1つ選ぶ。
207
- *
208
- * 複数の状況が重なることはあるが、案内を並べると読み手はどれから手を付けるか決められない。
209
- * 「記録が無い」より「記録が古い」より「衝突している」を上に置く——後者ほど
210
- * 次の一歩が具体的だからである。
211
- */
212
- export function selectIndependenceGuidance({
213
- coverage, taskDeclared, taskStale, conflictWithActive = null, conflictBetweenReady = null,
214
- contractSuperseded = false, readyCount = null, verdictsAbsent = false,
215
- coordinationMode = 'witness',
216
- }) {
217
- // 着手候補が無いなら述べる対象が無い。ここを通さないと、readyが空のとき
218
- // 「未検査taskが1件も無い」が空虚に真になり、記録が古くても検証済みへ倒れる。
219
- if (readyCount === 0) return todoIndependenceGuidance('independence_no_ready_frontier');
220
- if (conflictWithActive !== null) {
221
- return todoIndependenceGuidance('independence_conflict_with_active', {
222
- severability: conflictWithActive,
223
- });
224
- }
225
- if (conflictBetweenReady !== null) {
226
- return todoIndependenceGuidance('independence_conflict_between_ready', {
227
- severability: conflictBetweenReady,
228
- });
229
- }
230
- if (contractSuperseded) {
231
- return todoIndependenceGuidance('independence_contract_superseded');
232
- }
233
- // 記録が無い時に何を言うかは、planがどちらの方式を選んだかで変わる(ob03・裁定C①)。
234
- // 会話調整を選んだplanへ未compileを督促するのは、選択を尊重しないことになる。未宣言の
235
- // planへ督促するのは、誰の受入条件でもない作業を指すことになる——それが8件で素通りされた
236
- // 当のものである。督促が一級で出るのはwitnessを選んだplanだけとする。
237
- // 既定を`witness`にしてあるのは、宣言を渡さない既存の呼び出し側の挙動を変えないためである。
238
- if (coverage === 'missing') {
239
- if (coordinationMode === 'conversation') return todoIndependenceGuidance('coordination_conversation');
240
- if (coordinationMode === null) return todoIndependenceGuidance('coordination_mode_undeclared');
241
- return todoIndependenceGuidance('independence_unrecorded');
242
- }
243
- if (coverage === 'superseded') return todoIndependenceGuidance('independence_superseded');
244
- if (!taskDeclared) return todoIndependenceGuidance('independence_task_undeclared');
245
- if (taskStale) return todoIndependenceGuidance('independence_stale_for_task');
246
- // 記録は新しく、この工程自身にも問題が無い。それでもverdictが1つも無いなら、
247
- // 述べられるのは「干渉しない」ではなく「まだ何も判定していない」である。
248
- if (verdictsAbsent) return todoIndependenceGuidance('independence_verdicts_absent');
249
- return todoIndependenceGuidance('independence_verified');
250
- }
251
-
252
- /**
253
- * seam提案の状況から案内codeを引く。投影の生成側と契約の検査側が同じ規則を読むための正本。
254
- *
255
- * 鮮度(coverage)を束縛失敗より先に見る。記録が古い・別topologyについてのものである時、
256
- * そこに載っているunknownは現在のcodeについての事実ではないため、先に再compileが要る。
257
- * 記録が現在と一致している時だけ、束縛失敗が「今直せる状況」になる。
258
- */
259
- export function seamProposalGuidanceCode({ coverage, unknownKinds = [] }) {
260
- if (coverage === 'missing') return 'seam_proposal_unrecorded';
261
- if (coverage === 'superseded') return 'seam_proposal_superseded';
262
- if (coverage === 'stale') return 'seam_proposal_stale';
263
- if (coverage !== 'verified') throw new TypeError(`unknown seam proposal coverage: ${coverage}`);
264
- const kinds = new Set(unknownKinds);
265
- const binding = SEAM_PROPOSAL_BINDING_GUIDANCE.find(([kind]) => kinds.has(kind));
266
- return binding === undefined ? 'seam_proposal_verified' : binding[1];
267
- }
268
-
269
- export function selectSeamProposalGuidance({ coverage, unknownKinds = [] }) {
270
- return todoIndependenceGuidance(seamProposalGuidanceCode({ coverage, unknownKinds }));
271
- }
272
-
273
- /**
274
- * 宣言からcompileを経て読むまでの順序。helpとMCP instructionsが同じ手順を語るための正本。
275
- * 面ごとに手順を書き直すと、片方だけが古くなる。
276
- *
277
- * 宣言できる欄を挙げる面はここだけなので、witness契約へ欄を足したらここへも足す。
278
- * 足さないと、能力はあるのに機械が黙る面が残る(ADR 0130)。
279
- */
280
- export const TODO_INDEPENDENCE_WORKFLOW = Object.freeze([
281
- '1. 宣言する: .lattice/todo/witness/<plan_key>.json へ、ToDoごとのowns/reads/writes/affected_testsを書く',
282
- ' 係争資源しか所有していないToDoは、その資源の中で自分が触るsymbolをconcern_anchorsへ宣言できる(witness set v2)。並列可否の判定には写らず、切断候補の束縛だけに効く',
283
- '2. 判定する: lattice todo independence compile --plan <key> --input <ref>(実sensorを引き、clean worktreeが要る)',
284
- '3. 読む: lattice todo independence --plan <key> --json(sensorを引かず、記録とHEAD照合だけで返る)',
285
- '4. 追従する: plan改訂後は lattice todo independence witness migrate --plan <key> で宣言を写してから再compileする',
286
- ]);
287
-
288
- /**
289
- * 宣言の下書きが受理されなかった時の案内(`witness scaffold`)。
290
- *
291
- * 理由コードは具体的なのに次の一手が「宣言を直して再実行」のままだと、何をどう直すのかが
292
- * 伝わらない。一番必要な瞬間——機械が「作れませんでした」と言った瞬間——に解決法を知らせない
293
- * のは、ADR 0130が禁じたものそのものである。
294
- */
295
- const SCAFFOLD_CATALOG = Object.freeze({
296
- anchor_outside_owned: {
297
- message: 'concern_anchorのwithinが、自分の所有pathを指していない。所有していない資源の内側に担当を主張できない。',
298
- next_action: 'align_anchor_with_owns',
299
- },
300
- draft_invalid: {
301
- message: '下書きがlattice.todo_witness_draft契約を満たしていない。',
302
- next_action: 'fix_draft_schema_then_retry',
303
- },
304
- });
305
-
306
- /**
307
- * 理由の集合から、最も行動を要する案内を1つ選ぶ。
308
- *
309
- * 並べると読み手はどれから手を付けるか決められない。「形が壊れている」を最優先にし、
310
- * 以下、宣言の直し方が具体的なものほど上へ置く。
311
- */
312
- export function selectWitnessScaffoldGuidance(reasons = []) {
313
- const codes = reasons.map((reason) => String(reason).split(':')[0]);
314
- const order = [
315
- 'draft_invalid', 'anchor_outside_owned',
316
- ];
317
- const code = order.find((candidate) => codes.includes(candidate));
318
- if (code === undefined) {
319
- return { code: 'witness_scaffold_incomplete', message: '下書きから宣言を組めなかった。', next_action: 'resolve_declaration_then_retry' };
320
- }
321
- return { code, ...SCAFFOLD_CATALOG[code] };
322
- }
1
+ /**
2
+ * 並列可否の案内(ADR 0130)。
3
+ *
4
+ * 状況から`{code, message, next_action}`を引く単一正本。advisory・投影・typed error・helpは
5
+ * すべてここから引く。面ごとに文言を書けば必ずずれ、同じ状況に別の説明が付く。
6
+ *
7
+ * 文は事実と次の一歩だけを述べ、指示しない。dispatchの意思決定はhostが所有する
8
+ * (ADR 0063 Decision 5)。命令形にするとLatticeがagentを統制する面へ滑る。
9
+ */
10
+
11
+ export const TODO_INDEPENDENCE_GUIDANCE_CODES = Object.freeze([
12
+ 'independence_no_ready_frontier',
13
+ // ob03: 調整方式の宣言に関する案内。witnessが全planの暗黙義務だった時、未compileの督促は
14
+ // 誰の受入条件でもない作業を指しており、正確なまま素通りされた。まず「どちらで行くか」を
15
+ // 選ばせ、選択の後にだけ督促する。
16
+ 'coordination_mode_undeclared',
17
+ 'coordination_conversation',
18
+ 'independence_unrecorded',
19
+ 'independence_task_undeclared',
20
+ 'independence_contract_superseded',
21
+ 'independence_superseded',
22
+ 'independence_stale_for_task',
23
+ 'independence_conflict_with_active',
24
+ 'independence_conflict_between_ready',
25
+ 'independence_verdicts_absent',
26
+ 'independence_verified',
27
+ ]);
28
+
29
+ export const SEAM_PROPOSAL_GUIDANCE_CODES = Object.freeze([
30
+ 'seam_proposal_unrecorded',
31
+ 'seam_proposal_superseded',
32
+ 'seam_proposal_stale',
33
+ 'seam_proposal_binding_overlap',
34
+ 'seam_proposal_binding_outside_resource',
35
+ 'seam_proposal_binding_symbol_unresolved',
36
+ 'seam_proposal_binding_resource_unresolved',
37
+ 'seam_proposal_binding_ambiguous',
38
+ 'seam_proposal_binding_missing',
39
+ 'seam_proposal_verified',
40
+ ]);
41
+
42
+ /**
43
+ * 束縛失敗のunknown種別から案内codeを引く表。並びは優先順で、先頭ほど次の一歩が具体的である。
44
+ *
45
+ * 宣言が壊れている状況を、宣言が無い状況より上に置く。壊れている方は直す対象が
46
+ * 一意に決まるのに対し、無い方は何をどう宣言するかから決めることになるからである。
47
+ */
48
+ const SEAM_PROPOSAL_BINDING_GUIDANCE = Object.freeze([
49
+ Object.freeze(['concern_anchor_overlap', 'seam_proposal_binding_overlap']),
50
+ Object.freeze(['concern_anchor_outside_resource', 'seam_proposal_binding_outside_resource']),
51
+ Object.freeze(['concern_anchor_unresolved', 'seam_proposal_binding_symbol_unresolved']),
52
+ Object.freeze(['concern_anchor_resource_unresolved', 'seam_proposal_binding_resource_unresolved']),
53
+ Object.freeze(['semantic_owner_binding_ambiguous', 'seam_proposal_binding_ambiguous']),
54
+ Object.freeze(['semantic_owner_binding_missing', 'seam_proposal_binding_missing']),
55
+ ]);
56
+
57
+ /**
58
+ * 束縛の案内へ共通で添える、記録が更新される条件。
59
+ *
60
+ * concern_anchorsはwitness setにあり、seam提案はindependence記録のwitness_set_digestと
61
+ * 一致する宣言しか読まない。宣言を直しただけでは提案は変わらないので、そこまで述べないと
62
+ * 「直したのに同じunknownが出る」で止まる。
63
+ */
64
+ const BINDING_RECOMPILE_HINT = '宣言はwitness setにあり、independence compileとseam-proposal compileを通し直すまで提案へ写らない。';
65
+
66
+ const CATALOG = Object.freeze({
67
+ independence_no_ready_frontier: Object.freeze({
68
+ message: '着手候補が無いため、並列可否を述べる対象が無い。',
69
+ next_action: 'none',
70
+ }),
71
+ coordination_mode_undeclared: Object.freeze({
72
+ message: 'このplanは調整方式をまだ選んでいない。witness検証で並列するか、会話で調整するかが決まっていない。',
73
+ next_action: 'declare_coordination_mode',
74
+ }),
75
+ coordination_conversation: Object.freeze({
76
+ message: 'このplanは会話調整を選んでいる。並列可否は宣言と判定ではなく、卓の合意が持つ。',
77
+ next_action: 'none',
78
+ }),
79
+ independence_unrecorded: Object.freeze({
80
+ message: 'このplanの並列可否はまだ判定していない。競合が無いのではなく、記録が存在しない。',
81
+ next_action: 'declare_witness_set_then_compile',
82
+ }),
83
+ independence_task_undeclared: Object.freeze({
84
+ message: 'この工程はwitness setで宣言されていないため、記録には含まれていない。',
85
+ next_action: 'add_task_to_witness_set_then_compile',
86
+ }),
87
+ independence_contract_superseded: Object.freeze({
88
+ message: '記録は旧契約versionで書かれており、現在の並列可否の判定としては読めない。現在の契約での再compileが次の一歩になる。',
89
+ next_action: 'recompile_independence',
90
+ }),
91
+ independence_superseded: Object.freeze({
92
+ message: 'planが改訂され、記録は別のtopologyについての判定になっている。',
93
+ next_action: 'migrate_witness_set_then_compile',
94
+ }),
95
+ independence_stale_for_task: Object.freeze({
96
+ message: '記録後にこの工程の宣言境界が変更されたため、記録時点の判定は現在のcodeを指していない。',
97
+ next_action: 'recompile_independence',
98
+ }),
99
+ independence_conflict_with_active: Object.freeze({
100
+ message: '作業中の工程と同じ資源を書く記録がある。並行すると衝突する。',
101
+ next_action: 'serialize_or_split_boundary',
102
+ }),
103
+ independence_conflict_between_ready: Object.freeze({
104
+ message: '他のready工程と同じ資源を書く記録がある。同時に着手すると衝突する。',
105
+ next_action: 'serialize_or_split_boundary',
106
+ }),
107
+ independence_verdicts_absent: Object.freeze({
108
+ message: '判定が途中で止まっており、記録はどの組についてもverdictを持たない。自分にunknownが無いことは、他と干渉しない証拠にならない。',
109
+ next_action: 'resolve_unknowns_then_recompile',
110
+ }),
111
+ independence_verified: Object.freeze({
112
+ message: '記録時点の宣言境界では、他のready工程と干渉しない。',
113
+ next_action: 'none',
114
+ }),
115
+ seam_proposal_unrecorded: Object.freeze({
116
+ message: 'このplanのseam提案はまだ生成していない。提案対象が無いのではなく、記録が存在しない。',
117
+ next_action: 'compile_seam_proposal',
118
+ }),
119
+ seam_proposal_superseded: Object.freeze({
120
+ message: '参照元のplanまたは並列可否記録が更新され、このseam提案は現在の競合についての記録ではない。',
121
+ next_action: 'compile_seam_proposal',
122
+ }),
123
+ seam_proposal_stale: Object.freeze({
124
+ message: 'seam提案の生成後にHEADが進み、記録時点の構造証拠は現在のcodeを指していない。',
125
+ next_action: 'compile_seam_proposal',
126
+ }),
127
+ seam_proposal_binding_overlap: Object.freeze({
128
+ message: `同じ資源について、複数のToDoが同じsymbolを自分の担当として宣言している。どちら側へ切るか決まらないため、切断候補を束縛できない。${BINDING_RECOMPILE_HINT}`,
129
+ next_action: 'split_overlapping_concern_anchors_then_recompile',
130
+ }),
131
+ seam_proposal_binding_outside_resource: Object.freeze({
132
+ message: `concern_anchorsが挙げたsymbolが、withinで指した資源の外にある。その資源の分割を説明しないので束縛へ使えない。${BINDING_RECOMPILE_HINT}`,
133
+ next_action: 'correct_concern_anchors_then_recompile',
134
+ }),
135
+ seam_proposal_binding_symbol_unresolved: Object.freeze({
136
+ message: `concern_anchorsが挙げたsymbolを、sensorが同名同pathで解決できなかった。近い別symbolへは寄せないため、束縛は成立していない。${BINDING_RECOMPILE_HINT}`,
137
+ next_action: 'correct_concern_anchors_then_recompile',
138
+ }),
139
+ seam_proposal_binding_resource_unresolved: Object.freeze({
140
+ message: `concern_anchorsのwithinが指す資源をsensorで解決できなかった。宣言がどの資源についてのものか確定しない。${BINDING_RECOMPILE_HINT}`,
141
+ next_action: 'correct_concern_anchors_then_recompile',
142
+ }),
143
+ seam_proposal_binding_ambiguous: Object.freeze({
144
+ message: `宣言したsymbolが切断候補の複数側へ当たるため、どのToDoがどちらを所有するか一意に決まらない。${BINDING_RECOMPILE_HINT}`,
145
+ next_action: 'narrow_concern_anchors_then_recompile',
146
+ }),
147
+ seam_proposal_binding_missing: Object.freeze({
148
+ message: `係争資源の中でどのToDoが何を触るかが宣言されていないため、切断候補を所有者へ束縛できない。依存線でも呼び出し辺でも所有は決まらない。${BINDING_RECOMPILE_HINT}`,
149
+ next_action: 'declare_concern_anchors_then_recompile',
150
+ }),
151
+ seam_proposal_verified: Object.freeze({
152
+ message: 'seam提案の記録は現在のplan、並列可否記録、HEADと一致している。',
153
+ next_action: 'none',
154
+ }),
155
+ });
156
+
157
+ /** 切断可能性の言い換え。conflictの案内へ添える。 */
158
+ const SEVERABILITY_HINT = Object.freeze({
159
+ code_seam: 'symbol/pathの衝突なので、境界を分けるrefactorで並列化しうる。記録済みの競合からseam-proposal compileを検討できる。',
160
+ serial: '共有stateまたはeffectの衝突なので、分割では切り離せない。',
161
+ });
162
+
163
+ export function todoIndependenceGuidance(code, { severability = null } = {}) {
164
+ const entry = CATALOG[code];
165
+ if (entry === undefined) {
166
+ throw new TypeError(`unknown independence guidance code: ${code}`);
167
+ }
168
+ const hint = severability === null ? null : SEVERABILITY_HINT[severability] ?? null;
169
+ return {
170
+ code,
171
+ message: hint === null ? entry.message : `${entry.message}${hint}`,
172
+ next_action: entry.next_action,
173
+ };
174
+ }
175
+
176
+ /**
177
+ * 記録済みの宣言膨張を、AIが分割を検討するための助言へ写す。
178
+ *
179
+ * 判断はしない。膨張回数が既知かつ1回以上という事実だけを入口にし、gate形なら
180
+ * 依存の合流点という構造事実を強い材料として添える。subset gapで累計がnullのtaskや
181
+ * 初回宣言は、回数を主張できないので助言を出さない。
182
+ */
183
+ export function scopeExpansionRecommendations(scopeExpanded) {
184
+ if (!Array.isArray(scopeExpanded)) {
185
+ throw new TypeError('scope_expanded must be an array');
186
+ }
187
+ return scopeExpanded
188
+ .filter((entry) => Number.isSafeInteger(entry?.growth_events)
189
+ && entry.growth_events > 0)
190
+ .map((entry) => ({
191
+ code: 'scope_expanded_consider_split',
192
+ task_id: entry.task_id,
193
+ growth_events: entry.growth_events,
194
+ first_seen_path_count: entry.first_seen_path_count,
195
+ path_count: entry.path_count,
196
+ gate_shape: entry.gate_shape,
197
+ message: `宣言が${entry.growth_events}回膨張している。${entry.gate_shape
198
+ ? 'このtaskは依存の合流点であり、'
199
+ : ''}内側にgateのリストが生えているなら、A1..Anと残余A'への分割を検討できる。`,
200
+ next_action: 'consider_todo_split',
201
+ }))
202
+ .sort((left, right) => left.task_id.localeCompare(right.task_id));
203
+ }
204
+
205
+ /**
206
+ * advisoryや投影の状態から、最も行動を要する案内を1つ選ぶ。
207
+ *
208
+ * 複数の状況が重なることはあるが、案内を並べると読み手はどれから手を付けるか決められない。
209
+ * 「記録が無い」より「記録が古い」より「衝突している」を上に置く——後者ほど
210
+ * 次の一歩が具体的だからである。
211
+ */
212
+ export function selectIndependenceGuidance({
213
+ coverage, taskDeclared, taskStale, conflictWithActive = null, conflictBetweenReady = null,
214
+ contractSuperseded = false, readyCount = null, verdictsAbsent = false,
215
+ coordinationMode = 'witness',
216
+ }) {
217
+ // 着手候補が無いなら述べる対象が無い。ここを通さないと、readyが空のとき
218
+ // 「未検査taskが1件も無い」が空虚に真になり、記録が古くても検証済みへ倒れる。
219
+ if (readyCount === 0) return todoIndependenceGuidance('independence_no_ready_frontier');
220
+ if (conflictWithActive !== null) {
221
+ return todoIndependenceGuidance('independence_conflict_with_active', {
222
+ severability: conflictWithActive,
223
+ });
224
+ }
225
+ if (conflictBetweenReady !== null) {
226
+ return todoIndependenceGuidance('independence_conflict_between_ready', {
227
+ severability: conflictBetweenReady,
228
+ });
229
+ }
230
+ if (contractSuperseded) {
231
+ return todoIndependenceGuidance('independence_contract_superseded');
232
+ }
233
+ // 記録が無い時に何を言うかは、planがどちらの方式を選んだかで変わる(ob03・裁定C①)。
234
+ // 会話調整を選んだplanへ未compileを督促するのは、選択を尊重しないことになる。未宣言の
235
+ // planへ督促するのは、誰の受入条件でもない作業を指すことになる——それが8件で素通りされた
236
+ // 当のものである。督促が一級で出るのはwitnessを選んだplanだけとする。
237
+ // 既定を`witness`にしてあるのは、宣言を渡さない既存の呼び出し側の挙動を変えないためである。
238
+ if (coverage === 'missing') {
239
+ if (coordinationMode === 'conversation') return todoIndependenceGuidance('coordination_conversation');
240
+ if (coordinationMode === null) return todoIndependenceGuidance('coordination_mode_undeclared');
241
+ return todoIndependenceGuidance('independence_unrecorded');
242
+ }
243
+ if (coverage === 'superseded') return todoIndependenceGuidance('independence_superseded');
244
+ if (!taskDeclared) return todoIndependenceGuidance('independence_task_undeclared');
245
+ if (taskStale) return todoIndependenceGuidance('independence_stale_for_task');
246
+ // 記録は新しく、この工程自身にも問題が無い。それでもverdictが1つも無いなら、
247
+ // 述べられるのは「干渉しない」ではなく「まだ何も判定していない」である。
248
+ if (verdictsAbsent) return todoIndependenceGuidance('independence_verdicts_absent');
249
+ return todoIndependenceGuidance('independence_verified');
250
+ }
251
+
252
+ /**
253
+ * seam提案の状況から案内codeを引く。投影の生成側と契約の検査側が同じ規則を読むための正本。
254
+ *
255
+ * 鮮度(coverage)を束縛失敗より先に見る。記録が古い・別topologyについてのものである時、
256
+ * そこに載っているunknownは現在のcodeについての事実ではないため、先に再compileが要る。
257
+ * 記録が現在と一致している時だけ、束縛失敗が「今直せる状況」になる。
258
+ */
259
+ export function seamProposalGuidanceCode({ coverage, unknownKinds = [] }) {
260
+ if (coverage === 'missing') return 'seam_proposal_unrecorded';
261
+ if (coverage === 'superseded') return 'seam_proposal_superseded';
262
+ if (coverage === 'stale') return 'seam_proposal_stale';
263
+ if (coverage !== 'verified') throw new TypeError(`unknown seam proposal coverage: ${coverage}`);
264
+ const kinds = new Set(unknownKinds);
265
+ const binding = SEAM_PROPOSAL_BINDING_GUIDANCE.find(([kind]) => kinds.has(kind));
266
+ return binding === undefined ? 'seam_proposal_verified' : binding[1];
267
+ }
268
+
269
+ export function selectSeamProposalGuidance({ coverage, unknownKinds = [] }) {
270
+ return todoIndependenceGuidance(seamProposalGuidanceCode({ coverage, unknownKinds }));
271
+ }
272
+
273
+ /**
274
+ * 宣言からcompileを経て読むまでの順序。helpとMCP instructionsが同じ手順を語るための正本。
275
+ * 面ごとに手順を書き直すと、片方だけが古くなる。
276
+ *
277
+ * 宣言できる欄を挙げる面はここだけなので、witness契約へ欄を足したらここへも足す。
278
+ * 足さないと、能力はあるのに機械が黙る面が残る(ADR 0130)。
279
+ */
280
+ export const TODO_INDEPENDENCE_WORKFLOW = Object.freeze([
281
+ '1. 宣言する: .lattice/todo/witness/<plan_key>.json へ、ToDoごとのowns/reads/writes/affected_testsを書く',
282
+ ' 係争資源しか所有していないToDoは、その資源の中で自分が触るsymbolをconcern_anchorsへ宣言できる(witness set v2)。並列可否の判定には写らず、切断候補の束縛だけに効く',
283
+ '2. 判定する: lattice todo independence compile --plan <key> --input <ref>(実sensorを引き、clean worktreeが要る)',
284
+ '3. 読む: lattice todo independence --plan <key> --json(sensorを引かず、記録とHEAD照合だけで返る)',
285
+ '4. 追従する: plan改訂後は lattice todo independence witness migrate --plan <key> で宣言を写してから再compileする',
286
+ ]);
287
+
288
+ /**
289
+ * 宣言の下書きが受理されなかった時の案内(`witness scaffold`)。
290
+ *
291
+ * 理由コードは具体的なのに次の一手が「宣言を直して再実行」のままだと、何をどう直すのかが
292
+ * 伝わらない。一番必要な瞬間——機械が「作れませんでした」と言った瞬間——に解決法を知らせない
293
+ * のは、ADR 0130が禁じたものそのものである。
294
+ */
295
+ const SCAFFOLD_CATALOG = Object.freeze({
296
+ anchor_outside_owned: {
297
+ message: 'concern_anchorのwithinが、自分の所有pathを指していない。所有していない資源の内側に担当を主張できない。',
298
+ next_action: 'align_anchor_with_owns',
299
+ },
300
+ draft_invalid: {
301
+ message: '下書きがlattice.todo_witness_draft契約を満たしていない。',
302
+ next_action: 'fix_draft_schema_then_retry',
303
+ },
304
+ });
305
+
306
+ /**
307
+ * 理由の集合から、最も行動を要する案内を1つ選ぶ。
308
+ *
309
+ * 並べると読み手はどれから手を付けるか決められない。「形が壊れている」を最優先にし、
310
+ * 以下、宣言の直し方が具体的なものほど上へ置く。
311
+ */
312
+ export function selectWitnessScaffoldGuidance(reasons = []) {
313
+ const codes = reasons.map((reason) => String(reason).split(':')[0]);
314
+ const order = [
315
+ 'draft_invalid', 'anchor_outside_owned',
316
+ ];
317
+ const code = order.find((candidate) => codes.includes(candidate));
318
+ if (code === undefined) {
319
+ return { code: 'witness_scaffold_incomplete', message: '下書きから宣言を組めなかった。', next_action: 'resolve_declaration_then_retry' };
320
+ }
321
+ return { code, ...SCAFFOLD_CATALOG[code] };
322
+ }