@quolu/lattice 0.60.3 → 0.60.5

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 (71) hide show
  1. package/LICENSE +147 -147
  2. package/README.ja.md +387 -387
  3. package/README.md +291 -291
  4. package/bin/lattice-dashboard.mjs +7 -3
  5. package/bin/lattice-mcp.mjs +0 -0
  6. package/bin/lattice-scripted-adapter.mjs +0 -0
  7. package/bin/lattice-scripted-worker.mjs +0 -0
  8. package/bin/lattice-work-order-adapter.mjs +0 -0
  9. package/bin/lattice.mjs +2 -4
  10. package/docs/bridge-setup.md +239 -239
  11. package/docs/schemas/lattice.executor_packet.v1.schema.json +57 -57
  12. package/docs/schemas/lattice.executor_receipt.v1.schema.json +66 -66
  13. package/docs/schemas/lattice.phase_todo_revision.v3.schema.json +360 -360
  14. package/docs/schemas/lattice.plan_create_input.v1.schema.json +56 -56
  15. package/docs/schemas/lattice.plan_create_input.v2.schema.json +72 -72
  16. package/docs/schemas/lattice.plan_create_input.v3.schema.json +81 -81
  17. package/docs/schemas/lattice.plan_create_input.v4.schema.json +85 -85
  18. package/docs/schemas/lattice.plan_scope_review.v1.schema.json +55 -55
  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 +150 -150
  25. package/docs/schemas/lattice.todo_extraction.v4.schema.json +161 -161
  26. package/docs/schemas/lattice.todo_revision.v2.schema.json +260 -260
  27. package/docs/schemas/lattice.todo_revision_set.v3.schema.json +363 -363
  28. package/docs/schemas/lattice.todo_structure_binding.v1.schema.json +47 -47
  29. package/docs/schemas/lattice.todo_structure_realization.v1.schema.json +55 -55
  30. package/docs/schemas/lattice.todo_structure_set.v1.schema.json +264 -264
  31. package/package.json +109 -108
  32. package/sensor/LICENSE +21 -21
  33. package/sensor/NOTICE +19 -19
  34. package/sensor/dist/bin/lattice-sensor.js +9 -9
  35. package/sensor/dist/bin/node-version-check.js +1 -1
  36. package/sensor/dist/bin/node-version-check.js.map +1 -1
  37. package/sensor/dist/db/index.js +24 -24
  38. package/sensor/dist/db/migrations.js +41 -41
  39. package/sensor/dist/db/queries.js +164 -164
  40. package/sensor/dist/db/schema.sql +205 -205
  41. package/sensor/dist/directory.js +5 -5
  42. package/sensor/dist/extraction/wasm/tree-sitter-c_sharp.wasm +0 -0
  43. package/sensor/dist/extraction/wasm/tree-sitter-cfml.wasm +0 -0
  44. package/sensor/dist/extraction/wasm/tree-sitter-cfquery.wasm +0 -0
  45. package/sensor/dist/extraction/wasm/tree-sitter-cfscript.wasm +0 -0
  46. package/sensor/dist/extraction/wasm/tree-sitter-cobol.wasm +0 -0
  47. package/sensor/dist/extraction/wasm/tree-sitter-erlang.wasm +0 -0
  48. package/sensor/dist/extraction/wasm/tree-sitter-go.wasm +0 -0
  49. package/sensor/dist/extraction/wasm/tree-sitter-java.wasm +0 -0
  50. package/sensor/dist/extraction/wasm/tree-sitter-javascript.wasm +0 -0
  51. package/sensor/dist/extraction/wasm/tree-sitter-nix.wasm +0 -0
  52. package/sensor/dist/extraction/wasm/tree-sitter-pascal.wasm +0 -0
  53. package/sensor/dist/extraction/wasm/tree-sitter-python.wasm +0 -0
  54. package/sensor/dist/extraction/wasm/tree-sitter-tsx.wasm +0 -0
  55. package/sensor/dist/extraction/wasm/tree-sitter-typescript.wasm +0 -0
  56. package/sensor/dist/extraction/wasm/tree-sitter-vbnet.wasm +0 -0
  57. package/sensor/dist/mcp/liveness-watchdog.js +53 -53
  58. package/sensor/dist/mcp/server-instructions.js +95 -95
  59. package/sensor/dist/sync/worktree.js +1 -1
  60. package/sensor/dist/sync/worktree.js.map +1 -1
  61. package/sensor/package.json +56 -56
  62. package/src/bridge-daemon.mjs +11 -3
  63. package/src/bridge-hub-landing.mjs +1 -1
  64. package/src/runtime-cli.mjs +7 -0
  65. package/src/runtime-managed-supervisor.mjs +9 -1
  66. package/src/sensor-node-runtime.mjs +3 -3
  67. package/src/todo-cli.mjs +5 -4
  68. package/src/todo-dashboard-registry.mjs +32 -12
  69. package/src/todo-gantt-live.mjs +18 -4
  70. package/src/todo-store-git-transaction.mjs +1 -1
  71. package/src/todo-store.mjs +128 -20
@@ -18,79 +18,79 @@ exports.SERVER_INSTRUCTIONS_NO_ROOT_INDEX = exports.SERVER_INSTRUCTIONS = void 0
18
18
  * burn tokens. ADR 0049 / 0059 require the eight compatibility tools to remain
19
19
  * visible by default; provider identity is Lattice regardless of their names.
20
20
  */
21
- exports.SERVER_INSTRUCTIONS = `# Lattice sensor — code intelligence over an indexed knowledge graph
22
-
23
- Provider identity: lattice. The lattice_sensor_* names below are compatibility tool names served by Lattice's own bundled sensor; they do not call or require the independent Lattice sensor product.
24
-
25
- Lattice sensor is a SQLite knowledge graph of every symbol, edge, and file in
26
- the workspace — pre-computed structure you would otherwise re-derive by
27
- reading files (cached intelligence: thousands of parse/trace decisions you
28
- don't pay to re-reason each run). Reads are sub-millisecond; the index lags
29
- writes by ~1s through the file watcher. Reach for it BEFORE *and* while
30
- writing or editing code — not just for questions: one call returns the
31
- verbatim source PLUS who calls it and what it affects, so you edit with the
32
- blast radius in view. More accurate context, in far fewer tokens and
33
- round-trips than reading files yourself.
34
-
35
- ## One tool: lattice_sensor_explore — use it instead of reading files
36
-
37
- There is a single tool, \`lattice_sensor_explore\`, and it is Read-equivalent. It
38
- takes either a natural-language question or a bag of symbol/file names and
39
- returns the **verbatim, line-numbered source** of the relevant symbols
40
- grouped by file — the same \`<n>\\t<line>\` shape \`Read\` gives you, safe to
41
- \`Edit\` from — PLUS the call path among them (including dynamic-dispatch hops
42
- like callbacks, React re-render, and JSX children that grep can't follow) and
43
- a blast-radius summary of what depends on them.
44
-
45
- Whether you're answering "how does X work" or implementing a change (fixing a
46
- bug, adding a feature), call \`lattice_sensor_explore\` before you Read. ONE call
47
- usually answers the whole question. Lattice sensor IS the pre-built search index —
48
- so running your own grep + read loop, or delegating the lookup to a separate
49
- file-reading sub-task/agent, repeats work lattice sensor already did and costs more
50
- for the same answer. A direct lattice sensor answer is typically one to a few
51
- calls; a grep/read exploration is dozens.
52
-
53
- ## How to query
54
-
55
- - **Almost any question — "how does X work", architecture, a bug, "what/where is X", or surveying an area** → \`lattice_sensor_explore\` with a natural-language question or the relevant names. ONE capped call returns the verbatim source grouped by file; most often the ONLY call you need.
56
- - **"How does X reach/become Y? / the flow / the path from X to Y"** → \`lattice_sensor_explore\`, naming the symbols that span the flow (e.g. \`mutateElement renderScene\`) — it surfaces the call path among them, riding dynamic-dispatch hops, and returns their source.
57
- - **Reading or editing a file/symbol you can name** → put its name or file path in the \`lattice_sensor_explore\` query — it returns that current line-numbered source (safe to \`Edit\` from) with the call path and blast radius attached, so you don't Read it separately. For an overloaded name it returns every matching definition's body in one call.
58
- - **Need more?** Call \`lattice_sensor_explore\` again with more specific names — treat the source it returns as already Read.
59
-
60
- ## Anti-patterns
61
-
62
- - **Trust lattice sensor's results — don't re-verify them with grep.** They come from a full AST parse; re-checking with grep is slower, less accurate, and wastes context.
63
- - **Don't grep or Read first** to find or understand indexed code — ONE \`lattice_sensor_explore\` returns the relevant symbols' source together in a single round-trip. Reach for raw \`Read\`/\`Grep\` only to confirm a specific detail lattice sensor didn't cover, or for what lattice sensor doesn't index (configs, docs).
64
- - **Don't reconstruct a flow by hand** — name the endpoints in one \`lattice_sensor_explore\` and it surfaces the path between them, dynamic-dispatch hops included.
65
- - **After editing, check the staleness banner.** When a tool response starts with "⚠️ Some files referenced below were edited since the last index sync…", the listed files are pending re-index — Read those specific files for accurate content. Every file NOT in that banner is fresh, so still trust lattice sensor. A different, rarer banner — "⚠️ LatticeSensor auto-sync is DISABLED…" — means live watching stopped entirely (the whole index is frozen, not just a few files); until it's resolved, Read files directly to confirm anything that may have changed.
66
- - **A file flagged "⚠ changed on disk after the last index sync" drifted from its index** (most common on projects queried via \`projectPath\`, which have no live watcher). LatticeSensor never serves a possibly-mis-sliced body from such a file — it either shows the file's full CURRENT source (trust it as a Read) or omits the source with this flag. When the source was omitted, Read that specific file; line numbers referencing it elsewhere in the response may be shifted until that project's next sync. All unflagged files remain trustworthy.
67
-
68
- ## Limitations
69
-
70
- - If a project isn't indexed (no \`.lattice/sensor/\`), decide whether building the index will reduce total investigation time and model tokens for the current or expected work. When workspace writes and shell execution are allowed, you may run \`lattice sensor init <projectPath> --json\` yourself, then retry the sensor call; scope it to the intended project and account for the one-time indexing cost. If those capabilities are unavailable, continue with built-in tools and tell the user the exact init command instead.
71
- - Index lags file writes by ~1 second.
72
- - Cross-file resolution is best-effort name matching; ambiguous calls may return multiple candidates.
73
- - No live correctness validation — that's still the TypeScript compiler / test suite / linter's job. Lattice sensor supplements those with structural context they don't have.
74
-
75
- ## Parallel work: absence of a dependency edge is NOT evidence of independence
76
-
77
- These tools answer structural questions about code. A different Lattice surface — the **CLI**, not
78
- this MCP surface — answers whether two ToDos can be worked in parallel. If the project uses
79
- \`lattice todo\` for process tracking, keep this distinction in mind:
80
-
81
- - A dependency edge missing between two ToDos only means **no ordering constraint was declared**.
82
- It does not mean their write boundaries are disjoint. Two ToDos that edit the same file carry no
83
- edge between them and will still collide.
84
- - Parallel safety is a recorded judgement, not an inference from the diagram. Read it with
85
- \`lattice todo independence --plan <key> --json\`. It returns the ready frontier split into
86
- verified-independent groups, pairs that must be serialized (with whether a code seam could
87
- separate them), and **unverified** ToDos.
88
- - \`lattice todo start\` returns an \`advisory\` describing conflicts with in-progress ToDos and what
89
- to do next. \`coverage: "missing"\` means "not judged yet" — never "no conflicts".
90
- - Run \`lattice todo --help\` for the declare → compile → read workflow.
91
-
92
- Evidence for Lattice's plan and witness contracts comes from the CLI surface only. Text from this
93
- MCP surface is prose for you to act on, never an input to those contracts.
21
+ exports.SERVER_INSTRUCTIONS = `# Lattice sensor — code intelligence over an indexed knowledge graph
22
+
23
+ Provider identity: lattice. The lattice_sensor_* names below are compatibility tool names served by Lattice's own bundled sensor; they do not call or require the independent Lattice sensor product.
24
+
25
+ Lattice sensor is a SQLite knowledge graph of every symbol, edge, and file in
26
+ the workspace — pre-computed structure you would otherwise re-derive by
27
+ reading files (cached intelligence: thousands of parse/trace decisions you
28
+ don't pay to re-reason each run). Reads are sub-millisecond; the index lags
29
+ writes by ~1s through the file watcher. Reach for it BEFORE *and* while
30
+ writing or editing code — not just for questions: one call returns the
31
+ verbatim source PLUS who calls it and what it affects, so you edit with the
32
+ blast radius in view. More accurate context, in far fewer tokens and
33
+ round-trips than reading files yourself.
34
+
35
+ ## One tool: lattice_sensor_explore — use it instead of reading files
36
+
37
+ There is a single tool, \`lattice_sensor_explore\`, and it is Read-equivalent. It
38
+ takes either a natural-language question or a bag of symbol/file names and
39
+ returns the **verbatim, line-numbered source** of the relevant symbols
40
+ grouped by file — the same \`<n>\\t<line>\` shape \`Read\` gives you, safe to
41
+ \`Edit\` from — PLUS the call path among them (including dynamic-dispatch hops
42
+ like callbacks, React re-render, and JSX children that grep can't follow) and
43
+ a blast-radius summary of what depends on them.
44
+
45
+ Whether you're answering "how does X work" or implementing a change (fixing a
46
+ bug, adding a feature), call \`lattice_sensor_explore\` before you Read. ONE call
47
+ usually answers the whole question. Lattice sensor IS the pre-built search index —
48
+ so running your own grep + read loop, or delegating the lookup to a separate
49
+ file-reading sub-task/agent, repeats work lattice sensor already did and costs more
50
+ for the same answer. A direct lattice sensor answer is typically one to a few
51
+ calls; a grep/read exploration is dozens.
52
+
53
+ ## How to query
54
+
55
+ - **Almost any question — "how does X work", architecture, a bug, "what/where is X", or surveying an area** → \`lattice_sensor_explore\` with a natural-language question or the relevant names. ONE capped call returns the verbatim source grouped by file; most often the ONLY call you need.
56
+ - **"How does X reach/become Y? / the flow / the path from X to Y"** → \`lattice_sensor_explore\`, naming the symbols that span the flow (e.g. \`mutateElement renderScene\`) — it surfaces the call path among them, riding dynamic-dispatch hops, and returns their source.
57
+ - **Reading or editing a file/symbol you can name** → put its name or file path in the \`lattice_sensor_explore\` query — it returns that current line-numbered source (safe to \`Edit\` from) with the call path and blast radius attached, so you don't Read it separately. For an overloaded name it returns every matching definition's body in one call.
58
+ - **Need more?** Call \`lattice_sensor_explore\` again with more specific names — treat the source it returns as already Read.
59
+
60
+ ## Anti-patterns
61
+
62
+ - **Trust lattice sensor's results — don't re-verify them with grep.** They come from a full AST parse; re-checking with grep is slower, less accurate, and wastes context.
63
+ - **Don't grep or Read first** to find or understand indexed code — ONE \`lattice_sensor_explore\` returns the relevant symbols' source together in a single round-trip. Reach for raw \`Read\`/\`Grep\` only to confirm a specific detail lattice sensor didn't cover, or for what lattice sensor doesn't index (configs, docs).
64
+ - **Don't reconstruct a flow by hand** — name the endpoints in one \`lattice_sensor_explore\` and it surfaces the path between them, dynamic-dispatch hops included.
65
+ - **After editing, check the staleness banner.** When a tool response starts with "⚠️ Some files referenced below were edited since the last index sync…", the listed files are pending re-index — Read those specific files for accurate content. Every file NOT in that banner is fresh, so still trust lattice sensor. A different, rarer banner — "⚠️ LatticeSensor auto-sync is DISABLED…" — means live watching stopped entirely (the whole index is frozen, not just a few files); until it's resolved, Read files directly to confirm anything that may have changed.
66
+ - **A file flagged "⚠ changed on disk after the last index sync" drifted from its index** (most common on projects queried via \`projectPath\`, which have no live watcher). LatticeSensor never serves a possibly-mis-sliced body from such a file — it either shows the file's full CURRENT source (trust it as a Read) or omits the source with this flag. When the source was omitted, Read that specific file; line numbers referencing it elsewhere in the response may be shifted until that project's next sync. All unflagged files remain trustworthy.
67
+
68
+ ## Limitations
69
+
70
+ - If a project isn't indexed (no \`.lattice/sensor/\`), decide whether building the index will reduce total investigation time and model tokens for the current or expected work. When workspace writes and shell execution are allowed, you may run \`lattice sensor init <projectPath> --json\` yourself, then retry the sensor call; scope it to the intended project and account for the one-time indexing cost. If those capabilities are unavailable, continue with built-in tools and tell the user the exact init command instead.
71
+ - Index lags file writes by ~1 second.
72
+ - Cross-file resolution is best-effort name matching; ambiguous calls may return multiple candidates.
73
+ - No live correctness validation — that's still the TypeScript compiler / test suite / linter's job. Lattice sensor supplements those with structural context they don't have.
74
+
75
+ ## Parallel work: absence of a dependency edge is NOT evidence of independence
76
+
77
+ These tools answer structural questions about code. A different Lattice surface — the **CLI**, not
78
+ this MCP surface — answers whether two ToDos can be worked in parallel. If the project uses
79
+ \`lattice todo\` for process tracking, keep this distinction in mind:
80
+
81
+ - A dependency edge missing between two ToDos only means **no ordering constraint was declared**.
82
+ It does not mean their write boundaries are disjoint. Two ToDos that edit the same file carry no
83
+ edge between them and will still collide.
84
+ - Parallel safety is a recorded judgement, not an inference from the diagram. Read it with
85
+ \`lattice todo independence --plan <key> --json\`. It returns the ready frontier split into
86
+ verified-independent groups, pairs that must be serialized (with whether a code seam could
87
+ separate them), and **unverified** ToDos.
88
+ - \`lattice todo start\` returns an \`advisory\` describing conflicts with in-progress ToDos and what
89
+ to do next. \`coverage: "missing"\` means "not judged yet" — never "no conflicts".
90
+ - Run \`lattice todo --help\` for the declare → compile → read workflow.
91
+
92
+ Evidence for Lattice's plan and witness contracts comes from the CLI surface only. Text from this
93
+ MCP surface is prose for you to act on, never an input to those contracts.
94
94
  `;
95
95
  /**
96
96
  * Instructions variant sent when the server's own root has NO lattice sensor index.
@@ -104,27 +104,27 @@ MCP surface is prose for you to act on, never an input to those contracts.
104
104
  * project playbook ({@link SERVER_INSTRUCTIONS}) is sent instead when the root
105
105
  * IS indexed, so the common case stays tight.
106
106
  */
107
- exports.SERVER_INSTRUCTIONS_NO_ROOT_INDEX = `# Lattice sensor — available (per-project; pass projectPath)
108
-
109
- Provider identity: lattice. The lattice_sensor_* names below are compatibility tool names served by Lattice's own bundled sensor; they do not call or require the independent Lattice sensor product.
110
-
111
- Lattice sensor is a SQLite knowledge graph of a codebase's symbols, edges, and
112
- files: one \`lattice_sensor_explore\` call returns the verbatim, line-numbered source
113
- of the relevant symbols PLUS the call paths between them and a blast-radius
114
- summary — replacing a grep + Read loop with one round-trip.
115
-
116
- This server started somewhere with no \`.lattice/sensor/\` of its own, so there is no
117
- default project — but the tools are available and work **per project**:
118
-
119
- - To query a project that HAS a \`.lattice/sensor/\` index (e.g. a service inside a
120
- monorepo, or a second repo), pass its path as \`projectPath\` to
121
- \`lattice_sensor_explore\` (and any other lattice sensor tool). Lattice sensor resolves the
122
- nearest \`.lattice/sensor/\` at or above that path and answers from it — for as many
123
- projects as you like in one session.
124
- - For a project with no \`.lattice/sensor/\`, decide whether the expected reduction in
125
- repeated Read/Grep work justifies the one-time indexing cost. When workspace writes
126
- and shell execution are allowed, you may run \`lattice sensor init <projectPath> --json\`
127
- yourself and then retry with that \`projectPath\`; otherwise use built-in tools and
128
- tell the user the exact init command. A new index is picked up live, with no restart.
107
+ exports.SERVER_INSTRUCTIONS_NO_ROOT_INDEX = `# Lattice sensor — available (per-project; pass projectPath)
108
+
109
+ Provider identity: lattice. The lattice_sensor_* names below are compatibility tool names served by Lattice's own bundled sensor; they do not call or require the independent Lattice sensor product.
110
+
111
+ Lattice sensor is a SQLite knowledge graph of a codebase's symbols, edges, and
112
+ files: one \`lattice_sensor_explore\` call returns the verbatim, line-numbered source
113
+ of the relevant symbols PLUS the call paths between them and a blast-radius
114
+ summary — replacing a grep + Read loop with one round-trip.
115
+
116
+ This server started somewhere with no \`.lattice/sensor/\` of its own, so there is no
117
+ default project — but the tools are available and work **per project**:
118
+
119
+ - To query a project that HAS a \`.lattice/sensor/\` index (e.g. a service inside a
120
+ monorepo, or a second repo), pass its path as \`projectPath\` to
121
+ \`lattice_sensor_explore\` (and any other lattice sensor tool). Lattice sensor resolves the
122
+ nearest \`.lattice/sensor/\` at or above that path and answers from it — for as many
123
+ projects as you like in one session.
124
+ - For a project with no \`.lattice/sensor/\`, decide whether the expected reduction in
125
+ repeated Read/Grep work justifies the one-time indexing cost. When workspace writes
126
+ and shell execution are allowed, you may run \`lattice sensor init <projectPath> --json\`
127
+ yourself and then retry with that \`projectPath\`; otherwise use built-in tools and
128
+ tell the user the exact init command. A new index is picked up live, with no restart.
129
129
  `;
130
130
  //# sourceMappingURL=server-instructions.js.map
@@ -173,7 +173,7 @@ function worktreeMismatchNotice(m) {
173
173
  /** Resolve symlinks where possible so tmp/realpath quirks don't break equality. */
174
174
  function realpath(p) {
175
175
  try {
176
- return fs.realpathSync(path.resolve(p));
176
+ return fs.realpathSync.native(path.resolve(p));
177
177
  }
178
178
  catch {
179
179
  return path.resolve(p);
@@ -1 +1 @@
1
- {"version":3,"file":"worktree.js","sourceRoot":"","sources":["../../src/sync/worktree.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;GAiBG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAcH,0CAgBC;AAUD,oCAeC;AAoBD,kEA8BC;AAGD,0DASC;AAOD,wDAOC;AAjID,uCAAyB;AACzB,2CAA6B;AAC7B,iDAA6C;AAE7C;;;;;;;GAOG;AACH,SAAgB,eAAe,CAAC,GAAW;IACzC,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,IAAA,4BAAY,EAAC,KAAK,EAAE,CAAC,WAAW,EAAE,iBAAiB,CAAC,EAAE;YAChE,GAAG,EAAE,GAAG;YACR,QAAQ,EAAE,MAAM;YAChB,KAAK,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,CAAC;YACnC,WAAW,EAAE,IAAI;YACjB,sEAAsE;YACtE,uEAAuE;YACvE,8DAA8D;YAC9D,OAAO,EAAE,IAAI;SACd,CAAC,CAAC,IAAI,EAAE,CAAC;QACV,OAAO,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IACpC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED;;;;;;;GAOG;AACH,SAAgB,YAAY,CAAC,GAAW;IACtC,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,IAAA,4BAAY,EAAC,KAAK,EAAE,CAAC,WAAW,EAAE,kBAAkB,CAAC,EAAE;YACjE,GAAG,EAAE,GAAG;YACR,QAAQ,EAAE,MAAM;YAChB,KAAK,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,CAAC;YACnC,WAAW,EAAE,IAAI;YACjB,OAAO,EAAE,IAAI,EAAE,oCAAoC;SACpD,CAAC,CAAC,IAAI,EAAE,CAAC;QACV,IAAI,CAAC,GAAG;YAAE,OAAO,IAAI,CAAC;QACtB,iEAAiE;QACjE,OAAO,QAAQ,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC;IACvE,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AASD;;;;;;;;;;GAUG;AACH,SAAgB,2BAA2B,CACzC,SAAiB,EACjB,SAAiB;IAEjB,MAAM,YAAY,GAAG,eAAe,CAAC,SAAS,CAAC,CAAC;IAChD,IAAI,CAAC,YAAY;QAAE,OAAO,IAAI,CAAC;IAE/B,MAAM,iBAAiB,GAAG,QAAQ,CAAC,SAAS,CAAC,CAAC;IAC9C,IAAI,YAAY,KAAK,iBAAiB;QAAE,OAAO,IAAI,CAAC;IAEpD,4EAA4E;IAC5E,0EAA0E;IAC1E,sEAAsE;IACtE,IAAI,eAAe,CAAC,iBAAiB,CAAC,KAAK,iBAAiB;QAAE,OAAO,IAAI,CAAC;IAE1E,2EAA2E;IAC3E,2EAA2E;IAC3E,0EAA0E;IAC1E,0EAA0E;IAC1E,2EAA2E;IAC3E,iFAAiF;IACjF,4EAA4E;IAC5E,wEAAwE;IACxE,6EAA6E;IAC7E,qEAAqE;IACrE,MAAM,cAAc,GAAG,YAAY,CAAC,YAAY,CAAC,CAAC;IAClD,MAAM,WAAW,GAAG,YAAY,CAAC,iBAAiB,CAAC,CAAC;IACpD,IAAI,cAAc,IAAI,WAAW,IAAI,cAAc,KAAK,WAAW;QAAE,OAAO,IAAI,CAAC;IAEjF,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,iBAAiB,EAAE,CAAC;AACxD,CAAC;AAED,gEAAgE;AAChE,SAAgB,uBAAuB,CAAC,CAAwB;IAC9D,OAAO,CACL,qEAAqE;QACrE,iBAAiB,CAAC,CAAC,YAAY,IAAI;QACnC,iBAAiB,CAAC,CAAC,SAAS,IAAI;QAChC,mFAAmF;QACnF,uFAAuF;QACvF,6BAA6B,CAC9B,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,SAAgB,sBAAsB,CAAC,CAAwB;IAC7D,OAAO,CACL,qEAAqE,CAAC,CAAC,SAAS,KAAK;QACrF,6BAA6B,CAAC,CAAC,YAAY,uCAAuC;QAClF,qFAAqF;QACrF,uBAAuB,CACxB,CAAC;AACJ,CAAC;AAED,mFAAmF;AACnF,SAAS,QAAQ,CAAC,CAAS;IACzB,IAAI,CAAC;QACH,OAAO,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;IAC1C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;IACzB,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"worktree.js","sourceRoot":"","sources":["../../src/sync/worktree.ts"],"names":[],"mappings":"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"}
@@ -1,56 +1,56 @@
1
- {
2
- "name": "@quolu/lattice-sensor",
3
- "version": "0.7.3-lattice.1",
4
- "private": true,
5
- "description": "Lattice-owned local structural sensor.",
6
- "repository": {
7
- "type": "git",
8
- "url": "git+https://github.com/kitepon-rgb/Lattice.git"
9
- },
10
- "main": "dist/index.js",
11
- "types": "dist/index.d.ts",
12
- "files": [
13
- "dist",
14
- "README.md"
15
- ],
16
- "scripts": {
17
- "build": "npm run clean && tsc && npm run copy-assets",
18
- "copy-assets": "node -e \"const fs=require('fs');fs.mkdirSync('dist/db',{recursive:true});fs.copyFileSync('src/db/schema.sql','dist/db/schema.sql');fs.mkdirSync('dist/extraction/wasm',{recursive:true});fs.readdirSync('src/extraction/wasm').filter(f=>f.endsWith('.wasm')).forEach(f=>fs.copyFileSync('src/extraction/wasm/'+f,'dist/extraction/wasm/'+f))\"",
19
- "dev": "tsc --watch",
20
- "build:kernel": "bash scripts/build-kernel.sh",
21
- "test": "vitest run --maxWorkers=4 --minWorkers=1",
22
- "test:watch": "vitest",
23
- "test:eval": "vitest run __tests__/evaluation/",
24
- "eval": "npm run build && npx tsx __tests__/evaluation/runner.ts",
25
- "clean": "node -e \"const fs=require('fs');fs.rmSync('dist',{recursive:true,force:true})\""
26
- },
27
- "keywords": [
28
- "code-intelligence",
29
- "knowledge-graph",
30
- "static-analysis"
31
- ],
32
- "author": "",
33
- "license": "MIT",
34
- "dependencies": {
35
- "@clack/prompts": "^1.3.0",
36
- "commander": "^14.0.2",
37
- "fast-string-width": "^3.0.2",
38
- "fast-wrap-ansi": "^0.2.0",
39
- "ignore": "^7.0.5",
40
- "jsonc-parser": "^3.3.1",
41
- "picomatch": "^4.0.4",
42
- "sisteransi": "^1.0.5",
43
- "tree-sitter-wasms": "^0.1.11",
44
- "web-tree-sitter": "^0.25.3"
45
- },
46
- "devDependencies": {
47
- "@types/better-sqlite3": "^7.6.0",
48
- "@types/node": "^20.19.30",
49
- "@types/picomatch": "^4.0.2",
50
- "typescript": "^5.0.0",
51
- "vitest": "^2.1.9"
52
- },
53
- "engines": {
54
- "node": ">=20.0.0 <25.0.0 || >=26.0.0"
55
- }
56
- }
1
+ {
2
+ "name": "@quolu/lattice-sensor",
3
+ "version": "0.7.3-lattice.1",
4
+ "private": true,
5
+ "description": "Lattice-owned local structural sensor.",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/kitepon/Lattice.git"
9
+ },
10
+ "main": "dist/index.js",
11
+ "types": "dist/index.d.ts",
12
+ "files": [
13
+ "dist",
14
+ "README.md"
15
+ ],
16
+ "scripts": {
17
+ "build": "npm run clean && tsc && npm run copy-assets",
18
+ "copy-assets": "node -e \"const fs=require('fs');fs.mkdirSync('dist/db',{recursive:true});fs.copyFileSync('src/db/schema.sql','dist/db/schema.sql');fs.mkdirSync('dist/extraction/wasm',{recursive:true});fs.readdirSync('src/extraction/wasm').filter(f=>f.endsWith('.wasm')).forEach(f=>fs.copyFileSync('src/extraction/wasm/'+f,'dist/extraction/wasm/'+f))\"",
19
+ "dev": "tsc --watch",
20
+ "build:kernel": "bash scripts/build-kernel.sh",
21
+ "test": "vitest run --maxWorkers=4 --minWorkers=1",
22
+ "test:watch": "vitest",
23
+ "test:eval": "vitest run __tests__/evaluation/",
24
+ "eval": "npm run build && npx tsx __tests__/evaluation/runner.ts",
25
+ "clean": "node -e \"const fs=require('fs');fs.rmSync('dist',{recursive:true,force:true})\""
26
+ },
27
+ "keywords": [
28
+ "code-intelligence",
29
+ "knowledge-graph",
30
+ "static-analysis"
31
+ ],
32
+ "author": "",
33
+ "license": "MIT",
34
+ "dependencies": {
35
+ "@clack/prompts": "^1.3.0",
36
+ "commander": "^14.0.2",
37
+ "fast-string-width": "^3.0.2",
38
+ "fast-wrap-ansi": "^0.2.0",
39
+ "ignore": "^7.0.5",
40
+ "jsonc-parser": "^3.3.1",
41
+ "picomatch": "^4.0.4",
42
+ "sisteransi": "^1.0.5",
43
+ "tree-sitter-wasms": "^0.1.11",
44
+ "web-tree-sitter": "^0.25.3"
45
+ },
46
+ "devDependencies": {
47
+ "@types/better-sqlite3": "^7.6.0",
48
+ "@types/node": "^20.19.30",
49
+ "@types/picomatch": "^4.0.2",
50
+ "typescript": "^5.0.0",
51
+ "vitest": "^2.1.9"
52
+ },
53
+ "engines": {
54
+ "node": ">=20.0.0 <25.0.0 || >=26.0.0"
55
+ }
56
+ }
@@ -19,8 +19,9 @@ const STOP_RECEIPT_SCHEMA = 'lattice.bridge_stop_receipt.v1';
19
19
  const ACTIVE_MARKER_SCHEMA = 'lattice.bridge_daemon_active.v1';
20
20
  const CONTROL_MAX_BYTES = 65_536;
21
21
  /** control fileの差し替え競合だけを吸収する再読の上限と間隔(`readStrictJson`が唯一の使用者)。 */
22
- const CONTROL_READ_RETRY_LIMIT = 5;
23
- const CONTROL_READ_RETRY_DELAY_MS = 20;
22
+ const CONTROL_READ_RETRY_LIMIT = 50;
23
+ const CONTROL_READ_RETRY_DELAY_MS = 10;
24
+ const sleep = (milliseconds) => new Promise((resolve) => setTimeout(resolve, milliseconds));
24
25
 
25
26
  export function bridgeDaemonDescriptorPath(env = process.env) {
26
27
  return path.join(bridgeConfigPaths(env).root, 'bridge-daemon.json');
@@ -48,7 +49,14 @@ async function atomicDescriptor(ref, value) {
48
49
  const temporary = `${ref}.${process.pid}.${randomBytes(8).toString('hex')}.tmp`;
49
50
  try {
50
51
  await writeFile(temporary, `${JSON.stringify(value)}\n`, { encoding: 'utf8', mode: 0o600, flag: 'wx' });
51
- await rename(temporary, ref);
52
+ for (let attempt = 0;; attempt += 1) {
53
+ try { await rename(temporary, ref); break; }
54
+ catch (error) {
55
+ if (process.platform !== 'win32' || !['EPERM', 'EACCES'].includes(error?.code)
56
+ || attempt >= CONTROL_READ_RETRY_LIMIT - 1) throw error;
57
+ await sleep(CONTROL_READ_RETRY_DELAY_MS);
58
+ }
59
+ }
52
60
  await chmod(ref, 0o600);
53
61
  } finally { await rm(temporary, { force: true }); }
54
62
  }
@@ -80,7 +80,7 @@ const LANDING_COPY = Object.freeze({
80
80
  }),
81
81
  });
82
82
 
83
- const GITHUB_URL = 'https://github.com/kitepon-rgb/Lattice';
83
+ const GITHUB_URL = 'https://github.com/kitepon/Lattice';
84
84
 
85
85
  export function escapeHtml(value) {
86
86
  return String(value).replaceAll('&', '&amp;').replaceAll('<', '&lt;')
@@ -4445,6 +4445,13 @@ export async function runRuntimeCli({ argv, cwd, stdout, stderr }) {
4445
4445
  && argv[0] === 'run' && argv[1] === 'activate'
4446
4446
  && argv[2] === '--run' && typeof argv[3] === 'string' && argv[3].length > 0) {
4447
4447
  action = async () => {
4448
+ if (process.platform === 'win32') {
4449
+ return typedFailure(
4450
+ stderr,
4451
+ 'PLATFORM_UNSUPPORTED',
4452
+ 'Lattice managed runtime v1 is POSIX-only; use WSL2 on Windows',
4453
+ );
4454
+ }
4448
4455
  const { repoRoot, runDir, runRef } = await resolveRunStore(cwd, argv[3]);
4449
4456
  await requireLegacyRunMode(runDir, 'activate');
4450
4457
  return runActivate({ runDir, runRef, repoRoot, stdout, requestId: requestIdOverride });
@@ -54,9 +54,17 @@ function sha256Bytes(bytes) { return createHash('sha256').update(bytes).digest('
54
54
  const execFileAsync = promisify(execFile);
55
55
 
56
56
  export async function observeManagedProcessStartIdentity(pid) {
57
+ if (!Number.isSafeInteger(pid) || pid < 1) {
58
+ fail('ADAPTER_CONTROLLER_UNAVAILABLE', `process start identity観測失敗: ${pid}`);
59
+ }
57
60
  let startedIdentity;
58
61
  try {
59
- const { stdout } = await execFileAsync('/bin/ps', ['-o', 'lstart=', '-p', String(pid)], { encoding: 'utf8' });
62
+ const executable = process.platform === 'win32' ? 'powershell.exe' : '/bin/ps';
63
+ const args = process.platform === 'win32'
64
+ ? ['-NoProfile', '-NonInteractive', '-Command',
65
+ `[System.Diagnostics.Process]::GetProcessById(${pid}).StartTime.ToUniversalTime().Ticks`]
66
+ : ['-o', 'lstart=', '-p', String(pid)];
67
+ const { stdout } = await execFileAsync(executable, args, { encoding: 'utf8' });
60
68
  startedIdentity = stdout.trim();
61
69
  } catch { fail('ADAPTER_CONTROLLER_UNAVAILABLE', `process start identity観測失敗: ${pid}`); }
62
70
  if (!startedIdentity) fail('ADAPTER_CONTROLLER_UNAVAILABLE', `process不在: ${pid}`);
@@ -4,15 +4,15 @@ export const SQLITE_EXPERIMENTAL_WARNING_MESSAGE =
4
4
 
5
5
  export function sensorNodeRuntimeFlags(nodeVersion = process.versions.node) {
6
6
  const major = Number(nodeVersion.split('.')[0]);
7
- return major === 22 ? [SQLITE_EXPERIMENTAL_WARNING_FLAG] : [];
7
+ return major === 22 || major === 24 ? [SQLITE_EXPERIMENTAL_WARNING_FLAG] : [];
8
8
  }
9
9
 
10
10
  /**
11
- * Node 22 prints node:sqlite's ExperimentalWarning to stderr. The CLI has a
11
+ * Node 22/24 print node:sqlite's ExperimentalWarning to stderr. The CLI has a
12
12
  * JSON-only output contract, so suppress that exact warning while node:sqlite loads.
13
13
  * Every other process warning keeps Node's original behavior.
14
14
  */
15
- export function installNode22SqliteWarningFilter({
15
+ export function installSqliteExperimentalWarningFilter({
16
16
  nodeVersion = process.versions.node,
17
17
  processObject = process,
18
18
  } = {}) {
package/src/todo-cli.mjs CHANGED
@@ -3428,6 +3428,8 @@ export async function renderTodoGanttForProject({
3428
3428
  async function serveGantt({ repoRoot, port, stdout, env, scope = DEFAULT_GANTT_SCOPE }) {
3429
3429
  const initialStore = await readTodoStoreStable({ repoRoot });
3430
3430
  const identity = await resolveProjectIdentity({ repoRoot, projectId: initialStore.project_id, env });
3431
+ let stop;
3432
+ const stopped = new Promise((resolve) => { stop = resolve; });
3431
3433
  const live = await startTodoGanttLiveServer({
3432
3434
  projectId: initialStore.project_id,
3433
3435
  displayName: identity.displayName,
@@ -3444,6 +3446,7 @@ async function serveGantt({ repoRoot, port, stdout, env, scope = DEFAULT_GANTT_S
3444
3446
  readHead: async () => ganttLiveHeadDigest({
3445
3447
  repoRoot, store: await readTodoStoreStable({ repoRoot }),
3446
3448
  }),
3449
+ onShutdown: process.platform === 'win32' ? stop : null,
3447
3450
  });
3448
3451
  const result = { schema: 'lattice.todo_gantt_live_result.v3', project_id: live.projectId,
3449
3452
  resource_scope: 'project', selection_scope: scope,
@@ -3452,10 +3455,8 @@ async function serveGantt({ repoRoot, port, stdout, env, scope = DEFAULT_GANTT_S
3452
3455
  host: live.host, port: live.port, project_path: live.projectPath,
3453
3456
  url: live.url, events_url: live.eventsUrl };
3454
3457
  stdout.write(`${JSON.stringify(result)}\n`);
3455
- await new Promise((resolve) => {
3456
- const stop = () => resolve();
3457
- process.once('SIGINT', stop); process.once('SIGTERM', stop);
3458
- });
3458
+ process.once('SIGINT', stop); process.once('SIGTERM', stop);
3459
+ await stopped;
3459
3460
  await live.close();
3460
3461
  return null;
3461
3462
  }
@@ -377,6 +377,17 @@ function signalIfPresent(signalProcess, pid, signal) {
377
377
  }
378
378
  }
379
379
 
380
+ async function requestDaemonShutdown(descriptor) {
381
+ try {
382
+ const response = await fetch(`http://127.0.0.1:${descriptor.port}/__lattice/shutdown`, {
383
+ method: 'POST', signal: AbortSignal.timeout(500),
384
+ });
385
+ return response.status === 202;
386
+ } catch {
387
+ return false;
388
+ }
389
+ }
390
+
380
391
  async function stopAttestedLegacyDaemon(descriptor, {
381
392
  signalProcess = process.kill, isProcessAlive = processIsAlive, timeoutMs = 3_000,
382
393
  } = {}) {
@@ -387,7 +398,7 @@ async function stopAttestedLegacyDaemon(descriptor, {
387
398
  error.code = 'DASHBOARD_LEGACY_ATTESTATION_LOST';
388
399
  throw error;
389
400
  }
390
- signalProcess(descriptor.pid, 'SIGTERM');
401
+ if (!await requestDaemonShutdown(descriptor)) signalProcess(descriptor.pid, 'SIGTERM');
391
402
  if (await awaitDaemonStopped(descriptor, { isProcessAlive, deadline: Date.now() + timeoutMs })) return;
392
403
  const error = new Error('legacy dashboard daemon did not stop');
393
404
  error.code = 'DASHBOARD_LEGACY_STOP_FAILED';
@@ -406,7 +417,7 @@ async function stopStrayDaemon(descriptor, {
406
417
  signalProcess = process.kill, isProcessAlive = processIsAlive, timeoutMs = 3_000,
407
418
  } = {}) {
408
419
  if (await daemonAttestation(descriptor) === null) return false;
409
- signalIfPresent(signalProcess, descriptor.pid, 'SIGTERM');
420
+ if (!await requestDaemonShutdown(descriptor)) signalIfPresent(signalProcess, descriptor.pid, 'SIGTERM');
410
421
  const half = Math.ceil(timeoutMs / 2);
411
422
  if (await awaitDaemonStopped(descriptor, { isProcessAlive, deadline: Date.now() + half })) return true;
412
423
  signalIfPresent(signalProcess, descriptor.pid, 'SIGKILL');
@@ -450,7 +461,7 @@ async function stopSpawnedReplacement(child, descriptor, {
450
461
  || !await isProcessAlive(child.pid);
451
462
  return exited && await daemonAttestation(descriptor) === null;
452
463
  };
453
- child.kill('SIGTERM');
464
+ if (!await requestDaemonShutdown(descriptor)) child.kill('SIGTERM');
454
465
  let deadline = Date.now() + Math.ceil(timeoutMs / 2);
455
466
  while (Date.now() < deadline) {
456
467
  if (await stopped()) return;
@@ -467,15 +478,24 @@ async function stopSpawnedReplacement(child, descriptor, {
467
478
  throw error;
468
479
  }
469
480
 
470
- export async function writeTodoDashboardDaemonDescriptor({ port, env = process.env }) {
481
+ function currentDaemonRecord(port) {
482
+ return { schema: DAEMON_SCHEMA, pid: process.pid, port,
483
+ started_at: new Date().toISOString() };
484
+ }
485
+
486
+ export async function writeTodoDashboardDaemonRecord({ port, env = process.env }) {
471
487
  if (!Number.isSafeInteger(port) || port <= 0 || port > 65_535) throw new TypeError('daemon port invalid');
472
488
  const refs = paths(env);
473
489
  await mkdir(refs.daemons, { recursive: true, mode: 0o700 });
474
- const record = { schema: DAEMON_SCHEMA, pid: process.pid, port,
475
- started_at: new Date().toISOString() };
476
- // 記録を先に置く。descriptorだけが在って記録が無い瞬間を作ると、その隙に起動側が
477
- // 死んだ時に、まさに直そうとしている「誰からも観測されないdaemon」が生まれる。
490
+ const record = currentDaemonRecord(port);
478
491
  await atomicJson(daemonRecordRef(refs, process.pid), record);
492
+ return record;
493
+ }
494
+
495
+ export async function writeTodoDashboardDaemonDescriptor({ port, env = process.env }) {
496
+ const refs = paths(env);
497
+ // 直接descriptorを公開する呼出面でも、孤児を再発見できるdaemon固有記録を先に置く。
498
+ const record = await writeTodoDashboardDaemonRecord({ port, env });
479
499
  await atomicJson(refs.descriptor, record);
480
500
  }
481
501
 
@@ -545,7 +565,8 @@ export async function ensureTodoDashboardDaemon({ env = process.env, spawnDaemon
545
565
  const deadline = Date.now() + startupTimeoutMs;
546
566
  while (Date.now() < deadline) {
547
567
  await new Promise((resolve) => setTimeout(resolve, 50));
548
- const descriptor = await readJson(refs.descriptor, null);
568
+ const descriptor = typeof child.pid === 'number'
569
+ ? await readJson(daemonRecordRef(refs, child.pid), null) : null;
549
570
  const healthy = await daemonHealthy(descriptor);
550
571
  if (!healthy && typeof child.pid === 'number'
551
572
  && !await replacementIsProcessAlive(child.pid)) break;
@@ -557,17 +578,16 @@ export async function ensureTodoDashboardDaemon({ env = process.env, spawnDaemon
557
578
  });
558
579
  } catch (error) {
559
580
  await stopSpawnedReplacement(child, descriptor, { isProcessAlive: replacementIsProcessAlive });
560
- await atomicJson(refs.descriptor, legacy);
561
581
  throw error;
562
582
  }
563
583
  }
584
+ await atomicJson(refs.descriptor, descriptor);
564
585
  await reap(descriptor.pid);
565
586
  return descriptor;
566
587
  }
567
588
  }
568
589
  child.kill?.('SIGTERM');
569
- if (legacy !== null) await atomicJson(refs.descriptor, legacy);
570
- else await rm(refs.descriptor, { force: true });
590
+ if (legacy === null) await rm(refs.descriptor, { force: true });
571
591
  const error = new Error('dashboard daemon did not become ready');
572
592
  error.code = 'DASHBOARD_DAEMON_UNAVAILABLE';
573
593
  throw error;