@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,123 +1,123 @@
1
- /**
2
- * Todo Gantt scope projection.
3
- *
4
- * The store carries every completed ToDo forward across plan revisions on
5
- * purpose — an accepted artifact is the predecessor of the work that follows
6
- * it. That makes the dependency diagram grow monotonically: once a campaign
7
- * finishes, its whole tree stays on screen forever and every later revision
8
- * stacks on top of it. The journal is the source of truth, so nothing is
9
- * removed from the store; the diagram is a projection, and this module is
10
- * where that projection narrows.
11
- *
12
- * The rule is "drop the dead branches": a completed ToDo that no longer leads
13
- * to any live work is history, and history leaves the diagram entirely.
14
- * Completed ToDos that are still the direct premise of live work stay visible,
15
- * because they are the context for what is dispatchable right now.
16
- *
17
- * Summarising history into placeholder nodes was tried and is worse than
18
- * drawing nothing: a summary box still occupies a column, so the diagram stays
19
- * as wide as the finished plans ever made it while saying nothing a reader
20
- * acts on. The count belongs in the legend and the ToDos themselves belong in
21
- * the task index; neither needs floor space in the graph.
22
- *
23
- * This module is pure graph math over the layout's internal node/edge shape.
24
- * It must run AFTER dependency waves, the longest dependency chain and the
25
- * ready frontier have been computed on the FULL graph — those numbers describe
26
- * the real plan, and measuring them on a narrowed graph would make them lie.
27
- */
28
-
29
- import { isAuditPendingPhaseStatus } from './todo-audit-pending.mjs';
30
-
31
- export const TODO_GANTT_SCOPES = Object.freeze(['live', 'all']);
32
-
33
- /**
34
- * Hops of completed predecessors kept in front of live work. 1 = keep the
35
- * direct premises of live ToDos, drop everything upstream of them.
36
- */
37
- export const DEFAULT_FOLD_DISTANCE = 1;
38
-
39
- function compareText(left, right) {
40
- return left < right ? -1 : left > right ? 1 : 0;
41
- }
42
-
43
- /**
44
- * ADR 0147: phase無しplan(v1/v2/v3)は終端の暗黙Phase(terminal-audit)がacceptedになるまで
45
- * 「閉じた」ことにならない。全taskがdoneでも監査未了なら、そのplanのToDoは生きた作業と同じ
46
- * 扱い(distance 0)にして畳ませない——畳んでしまうと監査待ちであることが図から消え、
47
- * ADR 0147が塞ごうとした「一度も重監査を通らず完走した」事故と外形が同じになる。
48
- *
49
- * v4/v5(phaseを宣言したplan)はこの判定の対象外にする——既存のPhase gateが重監査を担って
50
- * おり、ここで同じ規律を足すとPhase単位の既存fold挙動を変えてしまう(非目標)。
51
- * `phase_status`はレイアウト層(todo-gantt-layout.mjs)がplanの世代を問わず埋める。
52
- * phase無しplanのtaskにはphase_idフィールド自体が無いため、そこでは暗黙Phaseの状態を
53
- * 埋める。フィールドが無い/nullの入力(既存test・素のnode)は従来どおり対象外(false)になる。
54
- *
55
- * 監査待ちの状態集合そのものは`todo-audit-pending.mjs`が持つ(gate_ready・reviewing・rejected)。
56
- * `active`(一部taskがまだpending)が入っていないことにここも依存している——ここまで
57
- * 広げると、完走していない枝の通常foldまで止めてしまい既存挙動を変える。
58
- *
59
- * 下のv4/v5除外は共有moduleへ移さない。あれは「監査待ちとは何か」の定義ではなく、
60
- * ganttがどこまでfoldを止めるかという本moduleの方針である。移すと、宣言Phase planの
61
- * fold挙動を他の消費者の都合で変えてしまう。
62
- */
63
- function auditPending(node) {
64
- if (!isAuditPendingPhaseStatus(node.phase_status)) return false;
65
- const schema = node.plan_schema ?? null;
66
- return !['lattice.todo_plan.v4', 'lattice.todo_plan.v5'].includes(schema);
67
- }
68
-
69
- /**
70
- * Forward distance from each node to the nearest live (non-done) node, over the
71
- * dependency DAG. A live node is at distance 0; a node with no live descendant
72
- * is at Infinity. A done node whose plan's terminal audit (ADR 0147) has not
73
- * been accepted is also pinned at distance 0 — it must not fold away silently.
74
- *
75
- * Edges always increase the wave (`assignWaves` is a longest-path layering), so
76
- * visiting nodes in descending wave order guarantees every successor is settled
77
- * before the node that depends on it.
78
- */
79
- function distanceToLive(nodes, edges, wave) {
80
- const outgoing = new Map(nodes.map(({ key }) => [key, []]));
81
- for (const edge of edges) outgoing.get(edge.from).push(edge.to);
82
- const distance = new Map();
83
- const ordered = [...nodes].sort((left, right) => wave.get(right.key) - wave.get(left.key)
84
- || compareText(right.key, left.key));
85
- for (const node of ordered) {
86
- if (node.status !== 'done' || auditPending(node)) {
87
- distance.set(node.key, 0);
88
- continue;
89
- }
90
- let best = Infinity;
91
- for (const successor of outgoing.get(node.key)) {
92
- const settled = distance.get(successor);
93
- if (settled !== undefined && settled + 1 < best) best = settled + 1;
94
- }
95
- distance.set(node.key, best);
96
- }
97
- return distance;
98
- }
99
-
100
- /**
101
- * Project the full dependency graph onto the `live` scope.
102
- *
103
- * Removing nodes from a DAG cannot create a cycle, so the narrowed graph needs
104
- * no verification: what is left is a subgraph of what was already acyclic.
105
- *
106
- * @returns {{nodes: Array, edges: Array, foldedKeys: Set<string>}}
107
- */
108
- export function projectTodoGanttScope({
109
- nodes, edges, wave, foldDistance = DEFAULT_FOLD_DISTANCE,
110
- }) {
111
- const distance = distanceToLive(nodes, edges, wave);
112
- const foldedKeys = new Set(nodes
113
- .filter((node) => node.status === 'done' && distance.get(node.key) > foldDistance)
114
- .map(({ key }) => key));
115
- if (foldedKeys.size === 0) return { nodes, edges, foldedKeys };
116
- return {
117
- nodes: nodes.filter((node) => !foldedKeys.has(node.key)),
118
- // An edge with a dropped endpoint goes with it. The kept node keeps its
119
- // real premises in the right pane, which reads the full graph.
120
- edges: edges.filter((edge) => !foldedKeys.has(edge.from) && !foldedKeys.has(edge.to)),
121
- foldedKeys,
122
- };
123
- }
1
+ /**
2
+ * Todo Gantt scope projection.
3
+ *
4
+ * The store carries every completed ToDo forward across plan revisions on
5
+ * purpose — an accepted artifact is the predecessor of the work that follows
6
+ * it. That makes the dependency diagram grow monotonically: once a campaign
7
+ * finishes, its whole tree stays on screen forever and every later revision
8
+ * stacks on top of it. The journal is the source of truth, so nothing is
9
+ * removed from the store; the diagram is a projection, and this module is
10
+ * where that projection narrows.
11
+ *
12
+ * The rule is "drop the dead branches": a completed ToDo that no longer leads
13
+ * to any live work is history, and history leaves the diagram entirely.
14
+ * Completed ToDos that are still the direct premise of live work stay visible,
15
+ * because they are the context for what is dispatchable right now.
16
+ *
17
+ * Summarising history into placeholder nodes was tried and is worse than
18
+ * drawing nothing: a summary box still occupies a column, so the diagram stays
19
+ * as wide as the finished plans ever made it while saying nothing a reader
20
+ * acts on. The count belongs in the legend and the ToDos themselves belong in
21
+ * the task index; neither needs floor space in the graph.
22
+ *
23
+ * This module is pure graph math over the layout's internal node/edge shape.
24
+ * It must run AFTER dependency waves, the longest dependency chain and the
25
+ * ready frontier have been computed on the FULL graph — those numbers describe
26
+ * the real plan, and measuring them on a narrowed graph would make them lie.
27
+ */
28
+
29
+ import { isAuditPendingPhaseStatus } from './todo-audit-pending.mjs';
30
+
31
+ export const TODO_GANTT_SCOPES = Object.freeze(['live', 'all']);
32
+
33
+ /**
34
+ * Hops of completed predecessors kept in front of live work. 1 = keep the
35
+ * direct premises of live ToDos, drop everything upstream of them.
36
+ */
37
+ export const DEFAULT_FOLD_DISTANCE = 1;
38
+
39
+ function compareText(left, right) {
40
+ return left < right ? -1 : left > right ? 1 : 0;
41
+ }
42
+
43
+ /**
44
+ * ADR 0147: phase無しplan(v1/v2/v3)は終端の暗黙Phase(terminal-audit)がacceptedになるまで
45
+ * 「閉じた」ことにならない。全taskがdoneでも監査未了なら、そのplanのToDoは生きた作業と同じ
46
+ * 扱い(distance 0)にして畳ませない——畳んでしまうと監査待ちであることが図から消え、
47
+ * ADR 0147が塞ごうとした「一度も重監査を通らず完走した」事故と外形が同じになる。
48
+ *
49
+ * v4/v5(phaseを宣言したplan)はこの判定の対象外にする——既存のPhase gateが重監査を担って
50
+ * おり、ここで同じ規律を足すとPhase単位の既存fold挙動を変えてしまう(非目標)。
51
+ * `phase_status`はレイアウト層(todo-gantt-layout.mjs)がplanの世代を問わず埋める。
52
+ * phase無しplanのtaskにはphase_idフィールド自体が無いため、そこでは暗黙Phaseの状態を
53
+ * 埋める。フィールドが無い/nullの入力(既存test・素のnode)は従来どおり対象外(false)になる。
54
+ *
55
+ * 監査待ちの状態集合そのものは`todo-audit-pending.mjs`が持つ(gate_ready・reviewing・rejected)。
56
+ * `active`(一部taskがまだpending)が入っていないことにここも依存している——ここまで
57
+ * 広げると、完走していない枝の通常foldまで止めてしまい既存挙動を変える。
58
+ *
59
+ * 下のv4/v5除外は共有moduleへ移さない。あれは「監査待ちとは何か」の定義ではなく、
60
+ * ganttがどこまでfoldを止めるかという本moduleの方針である。移すと、宣言Phase planの
61
+ * fold挙動を他の消費者の都合で変えてしまう。
62
+ */
63
+ function auditPending(node) {
64
+ if (!isAuditPendingPhaseStatus(node.phase_status)) return false;
65
+ const schema = node.plan_schema ?? null;
66
+ return !['lattice.todo_plan.v4', 'lattice.todo_plan.v5'].includes(schema);
67
+ }
68
+
69
+ /**
70
+ * Forward distance from each node to the nearest live (non-done) node, over the
71
+ * dependency DAG. A live node is at distance 0; a node with no live descendant
72
+ * is at Infinity. A done node whose plan's terminal audit (ADR 0147) has not
73
+ * been accepted is also pinned at distance 0 — it must not fold away silently.
74
+ *
75
+ * Edges always increase the wave (`assignWaves` is a longest-path layering), so
76
+ * visiting nodes in descending wave order guarantees every successor is settled
77
+ * before the node that depends on it.
78
+ */
79
+ function distanceToLive(nodes, edges, wave) {
80
+ const outgoing = new Map(nodes.map(({ key }) => [key, []]));
81
+ for (const edge of edges) outgoing.get(edge.from).push(edge.to);
82
+ const distance = new Map();
83
+ const ordered = [...nodes].sort((left, right) => wave.get(right.key) - wave.get(left.key)
84
+ || compareText(right.key, left.key));
85
+ for (const node of ordered) {
86
+ if (node.status !== 'done' || auditPending(node)) {
87
+ distance.set(node.key, 0);
88
+ continue;
89
+ }
90
+ let best = Infinity;
91
+ for (const successor of outgoing.get(node.key)) {
92
+ const settled = distance.get(successor);
93
+ if (settled !== undefined && settled + 1 < best) best = settled + 1;
94
+ }
95
+ distance.set(node.key, best);
96
+ }
97
+ return distance;
98
+ }
99
+
100
+ /**
101
+ * Project the full dependency graph onto the `live` scope.
102
+ *
103
+ * Removing nodes from a DAG cannot create a cycle, so the narrowed graph needs
104
+ * no verification: what is left is a subgraph of what was already acyclic.
105
+ *
106
+ * @returns {{nodes: Array, edges: Array, foldedKeys: Set<string>}}
107
+ */
108
+ export function projectTodoGanttScope({
109
+ nodes, edges, wave, foldDistance = DEFAULT_FOLD_DISTANCE,
110
+ }) {
111
+ const distance = distanceToLive(nodes, edges, wave);
112
+ const foldedKeys = new Set(nodes
113
+ .filter((node) => node.status === 'done' && distance.get(node.key) > foldDistance)
114
+ .map(({ key }) => key));
115
+ if (foldedKeys.size === 0) return { nodes, edges, foldedKeys };
116
+ return {
117
+ nodes: nodes.filter((node) => !foldedKeys.has(node.key)),
118
+ // An edge with a dropped endpoint goes with it. The kept node keeps its
119
+ // real premises in the right pane, which reads the full graph.
120
+ edges: edges.filter((edge) => !foldedKeys.has(edge.from) && !foldedKeys.has(edge.to)),
121
+ foldedKeys,
122
+ };
123
+ }