@quolu/lattice 0.52.3 → 0.53.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 (188) hide show
  1. package/LICENSE +147 -147
  2. package/README.ja.md +355 -355
  3. package/README.md +258 -258
  4. package/bin/lattice-bridge.mjs +25 -0
  5. package/bin/lattice-hub.mjs +67 -0
  6. package/bin/lattice-mcp.mjs +0 -0
  7. package/bin/lattice-scripted-adapter.mjs +0 -0
  8. package/bin/lattice-scripted-worker.mjs +0 -0
  9. package/bin/lattice-work-order-adapter.mjs +0 -0
  10. package/bin/lattice.mjs +0 -0
  11. package/docs/bridge-setup.md +132 -132
  12. package/docs/schemas/lattice.executor_packet.v1.schema.json +57 -57
  13. package/docs/schemas/lattice.executor_receipt.v1.schema.json +66 -66
  14. package/docs/schemas/lattice.phase_todo_revision.v3.schema.json +360 -360
  15. package/docs/schemas/lattice.plan_create_input.v1.schema.json +56 -56
  16. package/docs/schemas/lattice.plan_create_input.v2.schema.json +72 -72
  17. package/docs/schemas/lattice.plan_create_input.v3.schema.json +81 -81
  18. package/docs/schemas/lattice.plan_create_input.v4.schema.json +85 -85
  19. package/docs/schemas/lattice.run_request.v1.schema.json +238 -238
  20. package/docs/schemas/lattice.runtime_adapter_capabilities.v2.schema.json +55 -55
  21. package/docs/schemas/lattice.runtime_adapter_registration_input.v1.schema.json +78 -78
  22. package/docs/schemas/lattice.runtime_adapter_registration_input.v2.schema.json +86 -86
  23. package/docs/schemas/lattice.todo_extraction.v2.schema.json +298 -298
  24. package/docs/schemas/lattice.todo_extraction.v3.schema.json +146 -146
  25. package/docs/schemas/lattice.todo_revision.v2.schema.json +260 -260
  26. package/docs/schemas/lattice.todo_revision_set.v3.schema.json +363 -363
  27. package/package.json +103 -103
  28. package/sensor/LICENSE +21 -21
  29. package/sensor/NOTICE +19 -19
  30. package/sensor/dist/bin/lattice-sensor.js +9 -9
  31. package/sensor/dist/db/index.js +24 -24
  32. package/sensor/dist/db/migrations.js +41 -41
  33. package/sensor/dist/db/queries.js +164 -164
  34. package/sensor/dist/db/schema.sql +205 -205
  35. package/sensor/dist/directory.js +5 -5
  36. package/sensor/dist/extraction/wasm/tree-sitter-c_sharp.wasm +0 -0
  37. package/sensor/dist/extraction/wasm/tree-sitter-cfml.wasm +0 -0
  38. package/sensor/dist/extraction/wasm/tree-sitter-cfquery.wasm +0 -0
  39. package/sensor/dist/extraction/wasm/tree-sitter-cfscript.wasm +0 -0
  40. package/sensor/dist/extraction/wasm/tree-sitter-cobol.wasm +0 -0
  41. package/sensor/dist/extraction/wasm/tree-sitter-erlang.wasm +0 -0
  42. package/sensor/dist/extraction/wasm/tree-sitter-go.wasm +0 -0
  43. package/sensor/dist/extraction/wasm/tree-sitter-java.wasm +0 -0
  44. package/sensor/dist/extraction/wasm/tree-sitter-javascript.wasm +0 -0
  45. package/sensor/dist/extraction/wasm/tree-sitter-nix.wasm +0 -0
  46. package/sensor/dist/extraction/wasm/tree-sitter-pascal.wasm +0 -0
  47. package/sensor/dist/extraction/wasm/tree-sitter-python.wasm +0 -0
  48. package/sensor/dist/extraction/wasm/tree-sitter-tsx.wasm +0 -0
  49. package/sensor/dist/extraction/wasm/tree-sitter-typescript.wasm +0 -0
  50. package/sensor/dist/extraction/wasm/tree-sitter-vbnet.wasm +0 -0
  51. package/sensor/dist/mcp/liveness-watchdog.js +53 -53
  52. package/sensor/dist/mcp/server-instructions.js +95 -95
  53. package/sensor/package.json +56 -56
  54. package/src/artifact-contracts-v2.mjs +325 -325
  55. package/src/artifact-contracts.mjs +895 -895
  56. package/src/boundary-compiler.mjs +712 -712
  57. package/src/boundary-observation-compiler-v2.mjs +344 -344
  58. package/src/bounded-seam.mjs +230 -230
  59. package/src/bridge-address.mjs +107 -107
  60. package/src/bridge-cli.mjs +303 -297
  61. package/src/bridge-config.mjs +382 -346
  62. package/src/bridge-daemon.mjs +378 -378
  63. package/src/bridge-hub-heartbeat.mjs +170 -0
  64. package/src/bridge-hub-protocol.mjs +198 -0
  65. package/src/bridge-hub-server.mjs +544 -0
  66. package/src/bridge-launch-agent.mjs +323 -323
  67. package/src/bridge-registrar.mjs +102 -102
  68. package/src/bridge-server.mjs +376 -376
  69. package/src/cli-help.mjs +308 -308
  70. package/src/cli-stdio.mjs +40 -40
  71. package/src/control-compiler.mjs +532 -532
  72. package/src/dag-chain.mjs +261 -261
  73. package/src/factory-diagnostics.mjs +188 -188
  74. package/src/git-process.mjs +72 -0
  75. package/src/hash-chain.mjs +76 -76
  76. package/src/hooks-cli.mjs +1057 -1057
  77. package/src/isolation-runner.mjs +409 -409
  78. package/src/node-version-guard.mjs +44 -44
  79. package/src/project-cli.mjs +3 -3
  80. package/src/project-identity.mjs +130 -130
  81. package/src/rc1-black-box-oracle.mjs +906 -906
  82. package/src/rc1-comparison.mjs +154 -154
  83. package/src/rc1-evidence-bundle.mjs +456 -456
  84. package/src/rc1-v4-campaign.mjs +708 -708
  85. package/src/rc1-v4-transform.mjs +467 -467
  86. package/src/rc1-v5-artifact-set.mjs +855 -855
  87. package/src/rc1-v5-behavior-evidence.mjs +594 -594
  88. package/src/rc1-v5-campaign.mjs +797 -797
  89. package/src/rc1-v5-transform.mjs +421 -421
  90. package/src/rc1-v6-artifact-set.mjs +807 -807
  91. package/src/rc1-v6-behavior-evidence.mjs +273 -273
  92. package/src/rc1-v6-campaign.mjs +623 -623
  93. package/src/rc1-v6-causal-binding.mjs +473 -473
  94. package/src/rc1-v6-measurement.mjs +313 -313
  95. package/src/rc2-artifact-set.mjs +1584 -1584
  96. package/src/rc2-campaign.mjs +1506 -1506
  97. package/src/rc2-delivery-policy-front-end.mjs +1079 -1079
  98. package/src/rc2-delivery-policy-oracle.mjs +134 -134
  99. package/src/rc2-delivery-policy-transform.mjs +1127 -1127
  100. package/src/rc2-rc1-transfer-front-end.mjs +511 -511
  101. package/src/rc3-actual-dogfood.mjs +652 -652
  102. package/src/rc3-dogfood-scaffold.mjs +315 -315
  103. package/src/rc3-scripted-campaign.mjs +1383 -1383
  104. package/src/rc4-stage1-dogfood.mjs +673 -673
  105. package/src/runtime-adapter-registry.mjs +524 -524
  106. package/src/runtime-cli.mjs +4588 -4588
  107. package/src/runtime-contracts.mjs +824 -824
  108. package/src/runtime-control-store.mjs +604 -604
  109. package/src/runtime-controller-protocol.mjs +587 -587
  110. package/src/runtime-decision-verifier.mjs +701 -701
  111. package/src/runtime-diff-observer.mjs +361 -361
  112. package/src/runtime-direct-os-observer.mjs +301 -301
  113. package/src/runtime-driver-state.mjs +166 -166
  114. package/src/runtime-engine.mjs +779 -779
  115. package/src/runtime-errors.mjs +356 -356
  116. package/src/runtime-event-store.mjs +189 -189
  117. package/src/runtime-front-end.mjs +925 -925
  118. package/src/runtime-gate-store.mjs +481 -481
  119. package/src/runtime-hold-recompile.mjs +916 -916
  120. package/src/runtime-io-sentinel.mjs +391 -391
  121. package/src/runtime-lifecycle-lock.mjs +294 -294
  122. package/src/runtime-managed-supervisor.mjs +1490 -1490
  123. package/src/runtime-multi-epoch-store.mjs +838 -838
  124. package/src/runtime-projection.mjs +269 -269
  125. package/src/runtime-pull-intake.mjs +1192 -1192
  126. package/src/runtime-scripted-adapter-controller.mjs +1160 -1160
  127. package/src/runtime-scripted-executor.mjs +163 -163
  128. package/src/runtime-scripted-worktree.mjs +104 -104
  129. package/src/runtime-seam-resolve.mjs +428 -428
  130. package/src/runtime-seam-treatment.mjs +173 -173
  131. package/src/runtime-socket-owner.mjs +125 -125
  132. package/src/runtime-work-order-contracts.mjs +91 -91
  133. package/src/runtime-work-order-controller.mjs +1171 -1171
  134. package/src/runtime-worktree-executor.mjs +199 -199
  135. package/src/schedulability-compiler-v2.mjs +303 -303
  136. package/src/schedulability-verifier-v2.mjs +317 -317
  137. package/src/seam-apply.mjs +549 -549
  138. package/src/seam-commit-shared.mjs +22 -22
  139. package/src/seam-commit-transform.mjs +81 -81
  140. package/src/seam-commit.mjs +18 -18
  141. package/src/seam-cost.mjs +322 -322
  142. package/src/seam-derivation.mjs +188 -188
  143. package/src/seam-gate.mjs +146 -146
  144. package/src/seam-proposal-contracts.mjs +446 -446
  145. package/src/seam-proposal-queries.mjs +521 -521
  146. package/src/seam-proposal.mjs +2011 -2011
  147. package/src/seam-ref.mjs +33 -33
  148. package/src/seam-rewrite.mjs +286 -286
  149. package/src/seam-transform.mjs +554 -554
  150. package/src/seam-verification.mjs +260 -260
  151. package/src/sensor-adapter.mjs +432 -432
  152. package/src/sensor-cli.mjs +139 -139
  153. package/src/sensor-diff.mjs +661 -661
  154. package/src/sensor-node-runtime.mjs +53 -53
  155. package/src/sensor-runtime.mjs +52 -52
  156. package/src/timestamp-contract.mjs +8 -8
  157. package/src/todo-audit-pending.mjs +91 -91
  158. package/src/todo-chain.mjs +178 -178
  159. package/src/todo-cli.mjs +8 -8
  160. package/src/todo-contracts.mjs +728 -728
  161. package/src/todo-dashboard-registry.mjs +573 -573
  162. package/src/todo-dispatch-shape.mjs +190 -190
  163. package/src/todo-gantt-html-independence.mjs +239 -239
  164. package/src/todo-gantt-html-shared.mjs +226 -226
  165. package/src/todo-gantt-html-style.mjs +131 -131
  166. package/src/todo-gantt-html.mjs +248 -248
  167. package/src/todo-gantt-layout.mjs +974 -974
  168. package/src/todo-gantt-live.mjs +361 -361
  169. package/src/todo-gantt-nested.mjs +263 -263
  170. package/src/todo-gantt-presentation.mjs +217 -217
  171. package/src/todo-gantt-scope.mjs +123 -123
  172. package/src/todo-gantt-svg.mjs +353 -353
  173. package/src/todo-independence-contracts.mjs +595 -595
  174. package/src/todo-independence-guidance.mjs +322 -322
  175. package/src/todo-independence.mjs +640 -640
  176. package/src/todo-markdown-renderer.mjs +260 -260
  177. package/src/todo-migration.mjs +448 -448
  178. package/src/todo-narrative-anchor.mjs +130 -130
  179. package/src/todo-note-store.mjs +629 -629
  180. package/src/todo-parallel-candidates.mjs +114 -114
  181. package/src/todo-revision.mjs +995 -995
  182. package/src/todo-split.mjs +472 -472
  183. package/src/todo-status.mjs +690 -690
  184. package/src/todo-store-git-transaction.mjs +418 -418
  185. package/src/todo-store.mjs +42 -21
  186. package/src/treatment-compiler.mjs +728 -728
  187. package/src/treatment-runner.mjs +656 -656
  188. 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
+ }