@quolu/lattice 0.64.4 → 0.66.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 (63) hide show
  1. package/LICENSE +147 -147
  2. package/README.ja.md +383 -378
  3. package/README.md +308 -303
  4. package/bin/lattice-mcp.mjs +0 -0
  5. package/bin/lattice-scripted-adapter.mjs +0 -0
  6. package/bin/lattice-scripted-worker.mjs +0 -0
  7. package/bin/lattice-work-order-adapter.mjs +0 -0
  8. package/bin/lattice.mjs +0 -0
  9. package/docs/bridge-setup.md +248 -248
  10. package/docs/schemas/lattice.executor_packet.v1.schema.json +57 -57
  11. package/docs/schemas/lattice.executor_receipt.v1.schema.json +66 -66
  12. package/docs/schemas/lattice.phase_todo_revision.v3.schema.json +360 -360
  13. package/docs/schemas/lattice.plan_create_input.v1.schema.json +56 -56
  14. package/docs/schemas/lattice.plan_create_input.v2.schema.json +72 -72
  15. package/docs/schemas/lattice.plan_create_input.v3.schema.json +81 -81
  16. package/docs/schemas/lattice.plan_create_input.v4.schema.json +357 -357
  17. package/docs/schemas/lattice.plan_scope_review.v1.schema.json +55 -55
  18. package/docs/schemas/lattice.run_request.v1.schema.json +238 -238
  19. package/docs/schemas/lattice.runtime_adapter_capabilities.v2.schema.json +55 -55
  20. package/docs/schemas/lattice.runtime_adapter_registration_input.v1.schema.json +78 -78
  21. package/docs/schemas/lattice.runtime_adapter_registration_input.v2.schema.json +86 -86
  22. package/docs/schemas/lattice.todo_extraction.v2.schema.json +298 -298
  23. package/docs/schemas/lattice.todo_extraction.v3.schema.json +150 -150
  24. package/docs/schemas/lattice.todo_extraction.v4.schema.json +161 -161
  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/docs/schemas/lattice.todo_structure_binding.v1.schema.json +47 -47
  28. package/docs/schemas/lattice.todo_structure_realization.v1.schema.json +55 -55
  29. package/docs/schemas/lattice.todo_structure_set.v1.schema.json +264 -264
  30. package/package.json +109 -109
  31. package/sensor/LICENSE +21 -21
  32. package/sensor/NOTICE +19 -19
  33. package/sensor/dist/bin/lattice-sensor.js +9 -9
  34. package/sensor/dist/db/index.js +24 -24
  35. package/sensor/dist/db/migrations.js +41 -41
  36. package/sensor/dist/db/queries.js +164 -164
  37. package/sensor/dist/db/schema.sql +205 -205
  38. package/sensor/dist/directory.js +5 -5
  39. package/sensor/dist/extraction/wasm/tree-sitter-c_sharp.wasm +0 -0
  40. package/sensor/dist/extraction/wasm/tree-sitter-cfml.wasm +0 -0
  41. package/sensor/dist/extraction/wasm/tree-sitter-cfquery.wasm +0 -0
  42. package/sensor/dist/extraction/wasm/tree-sitter-cfscript.wasm +0 -0
  43. package/sensor/dist/extraction/wasm/tree-sitter-cobol.wasm +0 -0
  44. package/sensor/dist/extraction/wasm/tree-sitter-erlang.wasm +0 -0
  45. package/sensor/dist/extraction/wasm/tree-sitter-go.wasm +0 -0
  46. package/sensor/dist/extraction/wasm/tree-sitter-java.wasm +0 -0
  47. package/sensor/dist/extraction/wasm/tree-sitter-javascript.wasm +0 -0
  48. package/sensor/dist/extraction/wasm/tree-sitter-nix.wasm +0 -0
  49. package/sensor/dist/extraction/wasm/tree-sitter-pascal.wasm +0 -0
  50. package/sensor/dist/extraction/wasm/tree-sitter-python.wasm +0 -0
  51. package/sensor/dist/extraction/wasm/tree-sitter-tsx.wasm +0 -0
  52. package/sensor/dist/extraction/wasm/tree-sitter-typescript.wasm +0 -0
  53. package/sensor/dist/extraction/wasm/tree-sitter-vbnet.wasm +0 -0
  54. package/sensor/dist/mcp/liveness-watchdog.js +53 -53
  55. package/sensor/dist/mcp/server-instructions.js +95 -95
  56. package/sensor/package.json +56 -56
  57. package/src/cli-help.mjs +3 -1
  58. package/src/todo-cli.mjs +15 -4
  59. package/src/todo-contracts.mjs +9 -3
  60. package/src/todo-gantt-layout.mjs +6 -1
  61. package/src/todo-status.mjs +1 -1
  62. package/src/todo-store.mjs +18 -2
  63. package/src/todo-structure-overlay.mjs +1 -1
@@ -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
@@ -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/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
+ }
package/src/cli-help.mjs CHANGED
@@ -102,6 +102,7 @@ Write commands:
102
102
  # --serial-confirmedは互換のため受理する。いずれも門ではない
103
103
  block --plan <key> --task <id> --reason <text>
104
104
  unblock --plan <key> --task <id>
105
+ retire --plan <key> --task <id> --reason <text>
105
106
  done --plan <key> --task <id> (--evidence <file>|--message <text>)
106
107
  # taskを閉じる。--evidenceはdescriptor JSONでも証拠本文でもよい。repo内なら絶対path可。
107
108
  # 監査と構造finalizationは残作業であり、doneの門ではない
@@ -109,7 +110,7 @@ Write commands:
109
110
  evidence promote --plan <key> --task <id> --evidence <file>
110
111
  # done状態と完了時刻を維持し、最新doneへ追記eventで証拠を再束縛する
111
112
  dependency connect --from-plan <key> --from-task <id> --to-plan <key> --to-task <id> --reason <text>
112
- # 開発中に発見したplan跨ぎ依存を明示接続する。依存の自動推定は行わない
113
+ # 開発中に発見した依存を明示接続する(同一plan内・plan跨ぎとも)。依存の自動推定は行わない
113
114
  independence compile --plan <key> --input <file> # witness setとsensorから並列可否を記録する
114
115
  independence witness migrate --plan <key> # revision後の宣言をtask migrationで写す
115
116
  independence witness scaffold --plan <key> --input <draft> # 下書きとfresh観測から宣言を書き出す
@@ -273,6 +274,7 @@ const SUBCOMMAND_USAGE = Object.freeze({
273
274
  'todo retract': 'todo retract --plan <key> --task <id> --reason <text>',
274
275
  'todo block': 'todo block --plan <key> --task <id> --reason <text>',
275
276
  'todo unblock': 'todo unblock --plan <key> --task <id>',
277
+ 'todo retire': 'todo retire --plan <key> --task <id> --reason <text>',
276
278
  'todo done': 'todo done --plan <key> --task <id> (--evidence <file>|--message <text>) [--test-result <markdown-file>]',
277
279
  'todo reopen': 'todo reopen --plan <key> --task <id> --reason <text> [--override-reason <text>]',
278
280
  'todo evidence': 'todo evidence promote --plan <key> --task <id> --evidence <file>',
package/src/todo-cli.mjs CHANGED
@@ -256,7 +256,7 @@ function internalFailure(stderr, error) {
256
256
  const TODO_COMMAND_NAMES = Object.freeze([
257
257
  'status', 'show', 'note', 'bindings', 'independence', 'structure', 'seam-profile', 'seam-proposal',
258
258
  'verify', 'repair-eol', 'snapshot', 'gantt', 'dashboard', 'phase', 'migrate', 'start', 'block',
259
- 'unblock', 'done', 'reopen', 'evidence', 'split', 'revise', 'revise-phase', 'revise-set',
259
+ 'unblock', 'retire', 'done', 'reopen', 'evidence', 'split', 'revise', 'revise-phase', 'revise-set',
260
260
  ]);
261
261
 
262
262
  function typedArgumentFailure(stderr, code, message, detail) {
@@ -275,7 +275,7 @@ function supportsAtomicStoreCommit(argv) {
275
275
  if (command === 'snapshot') return argv[1] === '--rebuild';
276
276
  if (command === 'migrate') return !argv.includes('--dry-run') && !argv.includes('--schema');
277
277
  if (['revise', 'split', 'revise-set', 'revise-phase', 'start', 'retract', 'block',
278
- 'unblock', 'done', 'reopen'].includes(command)) return true;
278
+ 'unblock', 'retire', 'done', 'reopen'].includes(command)) return true;
279
279
  if (command === 'evidence') return argv[1] === 'promote';
280
280
  if (command === 'phase') return argv[1] !== 'status';
281
281
  return false;
@@ -996,7 +996,7 @@ async function independenceMode({ repoRoot, env, planKey, mode, reason }) {
996
996
  return result;
997
997
  }
998
998
 
999
- /** 開発中に発見したplan跨ぎ依存を、consumer planのplan-scoped chainへ接続する。 */
999
+ /** 開発中に発見した依存(同一plan内・plan跨ぎとも)を、consumer planのplan-scoped chainへ接続する。 */
1000
1000
  async function dependencyConnect({
1001
1001
  repoRoot, env, fromPlanKey, fromTaskId, toPlanKey, toTaskId, reason,
1002
1002
  }) {
@@ -3436,7 +3436,7 @@ function writesTodoStore(argv) {
3436
3436
  case 'evidence': return second === 'promote';
3437
3437
  case 'dependency': return second === 'connect';
3438
3438
  case 'phase': return second !== 'status';
3439
- case 'start': case 'retract': case 'block': case 'unblock': case 'done':
3439
+ case 'start': case 'retract': case 'block': case 'unblock': case 'retire': case 'done':
3440
3440
  case 'reopen': case 'split': case 'revise': case 'revise-phase': case 'revise-set':
3441
3441
  return true;
3442
3442
  default: return false;
@@ -3961,6 +3961,17 @@ export async function runTodoCli({ argv, cwd, stdout, stderr, env = process.env
3961
3961
  kind: 'block', payload: { reason: flags.reason }, evidenceRef: null,
3962
3962
  });
3963
3963
  }
3964
+ } else if (argv[0] === 'retire') {
3965
+ // 恒久除去(2026-08-29オーナー裁定)。planの形は変えず、状態遷移として工程を閉じる。
3966
+ const flags = matchFlagCommand(argv, ['retire'], {
3967
+ known: ['plan', 'task', 'reason'], required: ['plan', 'task', 'reason'],
3968
+ });
3969
+ if (flags !== null && isTodoIdentifier(flags.plan) && isTodoIdentifier(flags.task)) {
3970
+ action = (repoRoot) => mutate({
3971
+ repoRoot, env, planKey: flags.plan, taskId: flags.task,
3972
+ kind: 'retire', payload: { reason: flags.reason }, evidenceRef: null,
3973
+ });
3974
+ }
3964
3975
  } else if (argv[0] === 'unblock') {
3965
3976
  const flags = matchFlagCommand(argv, ['unblock'], {
3966
3977
  known: ['plan', 'task'], required: ['plan', 'task'],
@@ -2,7 +2,7 @@ import { createHash } from 'node:crypto';
2
2
  import { isCanonicalUtcTimestamp } from './timestamp-contract.mjs';
3
3
 
4
4
  export const TODO_EVENT_KINDS = Object.freeze([
5
- 'plan_genesis', 'start', 'start_retracted', 'block', 'unblock', 'done', 'reopen',
5
+ 'plan_genesis', 'start', 'start_retracted', 'block', 'unblock', 'retire', 'done', 'reopen',
6
6
  'phase_review', 'phase_accept', 'phase_reject', 'phase_reopen',
7
7
  // ADR 0148: 監査していない歴史を「監査なしで閉じた」として明示的に閉じるための専用kind。
8
8
  // phase_review/accept/reject/reopenと同じv3 tail event shape(phase_id持ち)に収め、
@@ -530,6 +530,9 @@ function validPayload(event) {
530
530
  && isTodoDigest(payload.target_start_digest);
531
531
  if (event.kind === 'block') return exactRecord(payload, ['reason']) && nullableText(payload.reason) && payload.reason !== null;
532
532
  if (event.kind === 'unblock') return exactRecord(payload, []);
533
+ // retire: 消費計画が消えた工程の恒久除去(ADR 0147系の状態遷移に足す軽量kind・2026-08-29オーナー裁定)。
534
+ // 理由必須。blocked_reasonのfieldを理由の置き場として再利用し、state recordの形を変えない。
535
+ if (event.kind === 'retire') return exactRecord(payload, ['reason']) && nullableText(payload.reason) && payload.reason !== null;
533
536
  if (event.kind === 'done' && payload?.done_mode === 'authored') {
534
537
  return (exactRecord(payload, ['done_mode', 'imported', 'evidence'])
535
538
  || exactRecord(payload, ['done_mode', 'imported', 'evidence', 'test_result']))
@@ -582,7 +585,7 @@ function validCarriedState(value) {
582
585
  const resultAware = exactRecord(value, [
583
586
  'status', 'started_at', 'done_at', 'blocked_reason', 'evidence', 'imported', 'test_result',
584
587
  ]);
585
- if ((!legacy && !resultAware) || !['pending', 'in-progress', 'blocked', 'done'].includes(value.status)
588
+ if ((!legacy && !resultAware) || !['pending', 'in-progress', 'blocked', 'retired', 'done'].includes(value.status)
586
589
  || (value.started_at !== null && !isStrictTodoTimestamp(value.started_at))
587
590
  || (value.done_at !== null && !isStrictTodoTimestamp(value.done_at))
588
591
  || (value.blocked_reason !== null && !nullableText(value.blocked_reason))
@@ -599,6 +602,9 @@ function validCarriedState(value) {
599
602
  && activeEvidenceValid && testResult === null;
600
603
  if (value.status === 'blocked') return value.done_at === null && value.blocked_reason !== null
601
604
  && activeEvidenceValid && testResult === null;
605
+ // retired: 実行されないまま恒久に閉じた工程。理由はblocked_reasonが持ち、証拠・試験結果は持たない。
606
+ if (value.status === 'retired') return value.done_at === null && value.blocked_reason !== null
607
+ && value.evidence === null && testResult === null;
602
608
  return value.blocked_reason === null && value.evidence !== null
603
609
  // importedは完了状態の来歴を表す。evidence_promotion後もtrueを維持するため、
604
610
  // imported doneの現在証拠はimport sourceまたは通常evidence descriptorのどちらも有効。
@@ -737,7 +743,7 @@ export function validateTodoSnapshot(value) {
737
743
  && value.tasks.every((entry) => exactRecord(entry, [
738
744
  'task_id', 'status', 'started_at', 'done_at', 'blocked_reason', 'evidence', 'evidence_unverified', 'imported',
739
745
  ...(resultAware ? ['test_result'] : []),
740
- ]) && isTodoIdentifier(entry.task_id) && ['pending', 'in-progress', 'blocked', 'done'].includes(entry.status)
746
+ ]) && isTodoIdentifier(entry.task_id) && ['pending', 'in-progress', 'blocked', 'retired', 'done'].includes(entry.status)
741
747
  && (entry.started_at === null || isStrictTodoTimestamp(entry.started_at))
742
748
  && (entry.done_at === null || isStrictTodoTimestamp(entry.done_at)) && nullableText(entry.blocked_reason)
743
749
  && (entry.evidence === null || evidence(entry.evidence) || validateTodoImportSource(entry.evidence))
@@ -140,6 +140,7 @@ function normalizeInput(readModel, chainProjection) {
140
140
  }
141
141
 
142
142
  const nodesByKey = new Map();
143
+ const retiredKeys = new Set(); // 撤去済みtask。端点に持つ辺は図から一緒に消す
143
144
  const members = [...readModel.members].sort((left, right) => {
144
145
  const leftPlan = left?.plan;
145
146
  const rightPlan = right?.plan;
@@ -164,9 +165,12 @@ function normalizeInput(readModel, chainProjection) {
164
165
  fail('TODO_LAYOUT_INVALID_INPUT', 'plan task has an invalid shape');
165
166
  }
166
167
  const state = statusByTask.get(task.task_id);
167
- if (!plain(state) || !['pending', 'in-progress', 'blocked', 'done'].includes(state.status)) {
168
+ if (!plain(state) || !['pending', 'in-progress', 'blocked', 'retired', 'done'].includes(state.status)) {
168
169
  fail('TODO_LAYOUT_INVALID_INPUT', `missing task state for ${task.task_id}`);
169
170
  }
171
+ // retiredは「実行されないまま恒久に閉じた」工程。工程図は今とこれからの仕事を表す面なので
172
+ // 描画から除く(理由と履歴はsnapshot/journalが持つ。2026-08-29オーナー裁定「消せ」)。
173
+ if (state.status === 'retired') { retiredKeys.add(refKey({ project_id: plan.project_id, plan_key: plan.plan_key, task_id: task.task_id })); continue; }
170
174
  const ref = { project_id: plan.project_id, plan_key: plan.plan_key, task_id: task.task_id };
171
175
  const key = refKey(ref);
172
176
  if (nodesByKey.has(key)) fail('TODO_LAYOUT_INVALID_INPUT', `duplicate task: ${key}`);
@@ -203,6 +207,7 @@ function normalizeInput(readModel, chainProjection) {
203
207
  const toRef = refOf(toValue, 'edge.to');
204
208
  const from = refKey(fromRef);
205
209
  const to = refKey(toRef);
210
+ if (retiredKeys.has(from) || retiredKeys.has(to)) return; // 撤去済み端点の辺は描かない
206
211
  if (!nodesByKey.has(from) || !nodesByKey.has(to) || from === to) {
207
212
  fail('TODO_LAYOUT_INVALID_INPUT', 'edge must connect two distinct read-model tasks');
208
213
  }
@@ -441,7 +441,7 @@ function buildTodoGraph(readModel) {
441
441
  }
442
442
  for (const task of member.plan.tasks) {
443
443
  const state = states.get(task.task_id);
444
- if (!plain(state) || !['pending', 'in-progress', 'blocked', 'done'].includes(state.status)) {
444
+ if (!plain(state) || !['pending', 'in-progress', 'blocked', 'retired', 'done'].includes(state.status)) {
445
445
  fail('TODO_STATUS_INVALID_INPUT', 'todo_status_task_state_missing', {
446
446
  plan_key: member.plan.plan_key,
447
447
  task_id: task.task_id,
@@ -439,7 +439,8 @@ function derivedPhaseStatus(plan, taskStates, phaseStates, phaseId) {
439
439
  // 存在しない)ため、所属taskをフィルタで絞らずplan全taskとして扱う。
440
440
  const tasks = phaseId === TERMINAL_AUDIT_PHASE_ID && isPhaselessTodoPlanSchema(plan.schema)
441
441
  ? plan.tasks : plan.tasks.filter((entry) => entry.phase_id === phaseId);
442
- return tasks.every((entry) => taskStates.get(entry.task_id)?.status === 'done') ? 'gate_ready' : 'active';
442
+ // retiredは「実行されないまま閉じた」工程であり、Phase完了の妨げにしない(全taskがretiredのPhaseも閉じられる)
443
+ return tasks.every((entry) => ['done', 'retired'].includes(taskStates.get(entry.task_id)?.status)) ? 'gate_ready' : 'active';
443
444
  }
444
445
 
445
446
  function projectPhaseStates(plan, events, taskStates) {
@@ -726,6 +727,10 @@ function replay(plan, events, { now = new Date(), verifyEvidence, verifyImportSo
726
727
  } else if (event.kind === 'unblock') {
727
728
  if (state.status !== 'blocked') fail('STORE_INCONSISTENT', 'invalid_unblock_transition');
728
729
  state.status = 'in-progress'; state.blocked_reason = null;
730
+ } else if (event.kind === 'retire') {
731
+ // 恒久除去はpending/blockedからだけ。in-progressは保持者がretractしてから、doneは撤去対象でない。
732
+ if (!['pending', 'blocked'].includes(state.status)) fail('STORE_INCONSISTENT', 'invalid_retire_transition');
733
+ state.status = 'retired'; state.blocked_reason = event.payload.reason;
729
734
  } else if (event.kind === 'done') {
730
735
  if (event.payload.done_mode === 'authored') {
731
736
  if (state.status !== 'in-progress' || !dependenciesDone) fail('STORE_INCONSISTENT', 'invalid_done_transition');
@@ -973,10 +978,21 @@ function validateCrossPlanDependencyTransition(store, owner, input) {
973
978
  if (from.project_id !== store.project_id || to.project_id !== store.project_id) {
974
979
  fail('DEPENDENCY_INVALID', 'dependency_project_mismatch');
975
980
  }
976
- if (from.plan_key === to.plan_key) fail('DEPENDENCY_INVALID', 'dependency_must_cross_plans');
981
+ // 同一plan内の後追い接続も受ける(2026-08-25 オーナー裁定)。「工程の線を誤ったので足す」という
982
+ // 当たり前の修理を、source cutover込みのplan改訂トランザクションへ格上げする門は、能力を守らない
983
+ // 防御でしかなかった。ready計算・gantt描画・start/doneゲートはmergedTaskKeyでplan非依存に動くため、
984
+ // 跨ぎと同じ台帳・同じ検査(束縛digest・重複・自己辺・循環)でそのまま成立する。
985
+ if (mergedTaskKey(from) === mergedTaskKey(to)) {
986
+ fail('DEPENDENCY_INVALID', 'dependency_self_reference');
987
+ }
977
988
  if (to.plan_key !== owner.plan.plan_key || to.project_id !== owner.plan.project_id) {
978
989
  fail('DEPENDENCY_INVALID', 'dependency_owner_mismatch');
979
990
  }
991
+ // 同一planでは、plan本体が既に持つhard依存と同じ辺を二重に記録しない
992
+ if (from.plan_key === to.plan_key && owner.plan.hard_dependencies.some((edge) => (
993
+ edge.from.task_id === from.task_id && edge.to.task_id === to.task_id))) {
994
+ fail('DEPENDENCY_EXISTS', 'dependency_already_in_plan', { from, to });
995
+ }
980
996
  const source = crossPlanDependencyTask(store, from);
981
997
  const target = crossPlanDependencyTask(store, to);
982
998
  // A completed source already satisfies the prerequisite. Keep the existing
@@ -61,7 +61,7 @@ function normalizeTaskStates(structureSet, taskStates) {
61
61
  for (const entry of taskStates) {
62
62
  if (!isPlain(entry) || Object.keys(entry).length !== 2
63
63
  || typeof entry.task_id !== 'string'
64
- || !['pending', 'in-progress', 'blocked', 'done'].includes(entry.status)
64
+ || !['pending', 'in-progress', 'blocked', 'retired', 'done'].includes(entry.status)
65
65
  || states.has(entry.task_id)) {
66
66
  fail('STRUCTURE_OVERLAY_INPUT_INVALID', 'task_state_entry_invalid');
67
67
  }