@quolu/lattice 0.60.4 → 0.60.6

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 (41) hide show
  1. package/LICENSE +1 -1
  2. package/README.ja.md +1 -1
  3. package/README.md +1 -1
  4. package/bin/lattice-dashboard.mjs +7 -3
  5. package/bin/lattice.mjs +2 -4
  6. package/docs/schemas/lattice.executor_packet.v1.schema.json +1 -1
  7. package/docs/schemas/lattice.executor_receipt.v1.schema.json +1 -1
  8. package/docs/schemas/lattice.phase_todo_revision.v3.schema.json +1 -1
  9. package/docs/schemas/lattice.plan_create_input.v1.schema.json +1 -1
  10. package/docs/schemas/lattice.plan_create_input.v2.schema.json +1 -1
  11. package/docs/schemas/lattice.plan_create_input.v3.schema.json +1 -1
  12. package/docs/schemas/lattice.plan_create_input.v4.schema.json +1 -1
  13. package/docs/schemas/lattice.plan_scope_review.v1.schema.json +1 -1
  14. package/docs/schemas/lattice.run_request.v1.schema.json +1 -1
  15. package/docs/schemas/lattice.runtime_adapter_capabilities.v2.schema.json +1 -1
  16. package/docs/schemas/lattice.runtime_adapter_registration_input.v1.schema.json +1 -1
  17. package/docs/schemas/lattice.runtime_adapter_registration_input.v2.schema.json +1 -1
  18. package/docs/schemas/lattice.todo_extraction.v2.schema.json +1 -1
  19. package/docs/schemas/lattice.todo_extraction.v3.schema.json +1 -1
  20. package/docs/schemas/lattice.todo_extraction.v4.schema.json +1 -1
  21. package/docs/schemas/lattice.todo_revision.v2.schema.json +1 -1
  22. package/docs/schemas/lattice.todo_revision_set.v3.schema.json +1 -1
  23. package/docs/schemas/lattice.todo_structure_binding.v1.schema.json +1 -1
  24. package/docs/schemas/lattice.todo_structure_realization.v1.schema.json +1 -1
  25. package/docs/schemas/lattice.todo_structure_set.v1.schema.json +1 -1
  26. package/package.json +5 -4
  27. package/sensor/dist/bin/node-version-check.js +1 -1
  28. package/sensor/dist/bin/node-version-check.js.map +1 -1
  29. package/sensor/dist/sync/worktree.js +1 -1
  30. package/sensor/dist/sync/worktree.js.map +1 -1
  31. package/sensor/package.json +1 -1
  32. package/src/bridge-daemon.mjs +11 -3
  33. package/src/bridge-hub-landing.mjs +1 -1
  34. package/src/runtime-cli.mjs +7 -0
  35. package/src/runtime-managed-supervisor.mjs +9 -1
  36. package/src/sensor-node-runtime.mjs +3 -3
  37. package/src/todo-cli.mjs +5 -4
  38. package/src/todo-dashboard-registry.mjs +34 -13
  39. package/src/todo-gantt-live.mjs +18 -4
  40. package/src/todo-store-git-transaction.mjs +1 -1
  41. package/src/todo-store.mjs +128 -20
package/LICENSE CHANGED
@@ -132,7 +132,7 @@ of your licenses.
132
132
 
133
133
  ---
134
134
 
135
- Required Notice: Copyright 2026 quolu (kitepon-rgb) — https://github.com/kitepon-rgb/Lattice
135
+ Required Notice: Copyright 2026 quolu (kitepon-rgb) — https://github.com/kitepon/Lattice
136
136
 
137
137
  Patent notice: the design implemented by this software is the subject of Japanese
138
138
  patent application 特願2026-178950 (filed 2026-07-27).
package/README.ja.md CHANGED
@@ -9,7 +9,7 @@
9
9
  [kitepon.dev](https://kitepon.dev/)を運営する[クオ(@QLyun35332)](https://x.com/QLyun35332)が開発・メンテナンスしています。
10
10
 
11
11
  [![npm](https://img.shields.io/npm/v/@quolu/lattice?color=cb3837&logo=npm)](https://www.npmjs.com/package/@quolu/lattice)
12
- [![CI](https://github.com/kitepon-rgb/Lattice/actions/workflows/ci.yml/badge.svg)](https://github.com/kitepon-rgb/Lattice/actions/workflows/ci.yml)
12
+ [![CI](https://github.com/kitepon/Lattice/actions/workflows/ci.yml/badge.svg)](https://github.com/kitepon/Lattice/actions/workflows/ci.yml)
13
13
  [![license](https://img.shields.io/badge/license-PolyForm%20Noncommercial%201.0.0-blue)](LICENSE)
14
14
  [![node](https://img.shields.io/node/v/@quolu/lattice?color=339933&logo=node.js&logoColor=white)](https://nodejs.org/)
15
15
  [![patent](https://img.shields.io/badge/patent-pending%20JP%202026--178950-6366f1)](#特許)
package/README.md CHANGED
@@ -7,7 +7,7 @@
7
7
  # Lattice
8
8
 
9
9
  [![npm](https://img.shields.io/npm/v/@quolu/lattice?color=cb3837&logo=npm)](https://www.npmjs.com/package/@quolu/lattice)
10
- [![CI](https://github.com/kitepon-rgb/Lattice/actions/workflows/ci.yml/badge.svg)](https://github.com/kitepon-rgb/Lattice/actions/workflows/ci.yml)
10
+ [![CI](https://github.com/kitepon/Lattice/actions/workflows/ci.yml/badge.svg)](https://github.com/kitepon/Lattice/actions/workflows/ci.yml)
11
11
  [![license](https://img.shields.io/badge/license-PolyForm%20Noncommercial%201.0.0-blue)](LICENSE)
12
12
  [![node](https://img.shields.io/node/v/@quolu/lattice?color=339933&logo=node.js&logoColor=white)](https://nodejs.org/)
13
13
  [![patent](https://img.shields.io/badge/patent-pending%20JP%202026--178950-6366f1)](#patent)
@@ -8,7 +8,7 @@ import {
8
8
  forgetTodoDashboardDaemonRecord,
9
9
  readVisibleTodoDashboardProjects,
10
10
  todoDashboardMemberNeedsVisibility,
11
- writeTodoDashboardDaemonDescriptor,
11
+ writeTodoDashboardDaemonRecord,
12
12
  } from '../src/todo-dashboard-registry.mjs';
13
13
  import {
14
14
  createTodoGanttProjectRegistry,
@@ -117,8 +117,12 @@ function exit(code) {
117
117
  }
118
118
 
119
119
  await synchronize();
120
- const dashboard = await startTodoGanttDashboardServer({ registry, port });
121
- await writeTodoDashboardDaemonDescriptor({ port: dashboard.port, env });
120
+ const dashboard = await startTodoGanttDashboardServer({
121
+ registry,
122
+ port,
123
+ onShutdown: process.platform === 'win32' ? () => exit(0) : null,
124
+ });
125
+ await writeTodoDashboardDaemonRecord({ port: dashboard.port, env });
122
126
  const timer = setInterval(() => synchronize().then((count) => {
123
127
  if (count === 0) exit(0);
124
128
  }).catch(() => exit(1)), 1_000);
package/bin/lattice.mjs CHANGED
@@ -2,13 +2,13 @@
2
2
 
3
3
  import { installPipeCloseGuard } from '../src/cli-stdio.mjs';
4
4
  import { renderCliHelp } from '../src/cli-help.mjs';
5
- import { installNode22SqliteWarningFilter } from '../src/sensor-node-runtime.mjs';
5
+ import { installSqliteExperimentalWarningFilter } from '../src/sensor-node-runtime.mjs';
6
6
  import packageJson from '../package.json' with { type: 'json' };
7
7
 
8
8
  installPipeCloseGuard();
9
9
 
10
10
  const args = process.argv.slice(2);
11
- const restoreWarningHandler = installNode22SqliteWarningFilter();
11
+ installSqliteExperimentalWarningFilter();
12
12
  const help = renderCliHelp(args);
13
13
 
14
14
  if (help !== null) {
@@ -145,8 +145,6 @@ if (help !== null) {
145
145
  }
146
146
  }
147
147
 
148
- restoreWarningHandler();
149
-
150
148
  async function runRuntimeErrorsCli(rest) {
151
149
  const runtimeErrors = await import('../src/runtime-errors.mjs');
152
150
  const usage = () => {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "$schema": "https://json-schema.org/draft/2020-12/schema",
3
- "$id": "https://github.com/kitepon-rgb/Lattice/blob/main/docs/schemas/lattice.executor_packet.v1.schema.json",
3
+ "$id": "https://github.com/kitepon/Lattice/blob/main/docs/schemas/lattice.executor_packet.v1.schema.json",
4
4
  "title": "lattice.executor_packet.v1",
5
5
  "description": "The single context packet issued at dispatch. It is the machine record of the context an executor received.",
6
6
  "$comment": "Runtime validation additionally enforces canonical JSON bytes, the self-digest rule for `packet_digest`, and that `context_content_digest` equals the SHA-256 of the canonical JSON projection of exactly {todo_id, task_ref, scope, base_sha, verifier_refs, forbidden_operations} — plan attribution fields are excluded so that an epoch rebind is provably content-preserving.",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "$schema": "https://json-schema.org/draft/2020-12/schema",
3
- "$id": "https://github.com/kitepon-rgb/Lattice/blob/main/docs/schemas/lattice.executor_receipt.v1.schema.json",
3
+ "$id": "https://github.com/kitepon/Lattice/blob/main/docs/schemas/lattice.executor_receipt.v1.schema.json",
4
4
  "title": "lattice.executor_receipt.v1",
5
5
  "description": "The receipt an executor returns for one dispatched packet. `packet_digest` is what attributes it to the packet it received.",
6
6
  "$comment": "Runtime validation additionally enforces canonical JSON bytes and the self-digest rule for `receipt_digest`. Acceptance is decided by event order, not by the receipt's own claims: the binding is checked against the `executor_dispatched` / `epoch_rebound` event that recorded the handle, worktree and packet digest for that TODO.",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "$schema": "https://json-schema.org/draft/2020-12/schema",
3
- "$id": "https://github.com/kitepon-rgb/Lattice/blob/main/docs/schemas/lattice.phase_todo_revision.v3.schema.json",
3
+ "$id": "https://github.com/kitepon/Lattice/blob/main/docs/schemas/lattice.phase_todo_revision.v3.schema.json",
4
4
  "title": "lattice.phase_todo_revision.v3",
5
5
  "description": "`lattice todo revise-phase`が受理する最新契約。v1/v2のphase-audited desired state・task/phase migrationへ、runtime task migration・source inventory・per-ToDo source cutover batchを加えた12 keyの完全契約。",
6
6
  "$comment": "Runtime validation (src/todo-revision.mjs validatePhaseTodoRevision) additionally enforces: canonical JSON+LF bytes; strict sort order on task_migration (by from_task_id), runtime_task_migration.entries (by predecessor_task_id), runtime_task_migration.entries[].successor_task_ids/evidence_digests, and source_inventory.excluded_tombstones/source_cutover_batch.operations (by source_ref); uniqueness constraints across all of those; desired_plan.plan_version === phaseTodoRevisionPlanVersion(...); runtime_task_migration projects onto task_migration exactly (validRuntimeTodoProjection) and its successor_task_ids cover exactly desired_plan.tasks; phase_migration's target phase set equals desired_plan.phases exactly; every reconciliation.*_digest field against its own recomputation; and revision_digest as the canonical self-digest over every other top-level field.",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "$schema": "https://json-schema.org/draft/2020-12/schema",
3
- "$id": "https://github.com/kitepon-rgb/Lattice/blob/main/docs/schemas/lattice.plan_create_input.v1.schema.json",
3
+ "$id": "https://github.com/kitepon/Lattice/blob/main/docs/schemas/lattice.plan_create_input.v1.schema.json",
4
4
  "title": "lattice.plan_create_input.v1",
5
5
  "description": "Canonical initial authoring input for lattice plan create.",
6
6
  "$comment": "Runtime validation additionally enforces canonical JSON+LF bytes, strict ordering, unique task ids, graph validity, byte bounds, and input_digest as the canonical self-digest.",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "$schema": "https://json-schema.org/draft/2020-12/schema",
3
- "$id": "https://github.com/kitepon-rgb/Lattice/blob/main/docs/schemas/lattice.plan_create_input.v2.schema.json",
3
+ "$id": "https://github.com/kitepon/Lattice/blob/main/docs/schemas/lattice.plan_create_input.v2.schema.json",
4
4
  "title": "lattice.plan_create_input.v2",
5
5
  "description": "Canonical initial authoring input for a phase-controlled Lattice plan.",
6
6
  "$comment": "Runtime validation additionally enforces canonical JSON+LF bytes, sorted identifiers, unique ids, acyclic task/phase graphs, byte bounds, and input_digest.",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "$schema": "https://json-schema.org/draft/2020-12/schema",
3
- "$id": "https://github.com/kitepon-rgb/Lattice/blob/main/docs/schemas/lattice.plan_create_input.v3.schema.json",
3
+ "$id": "https://github.com/kitepon/Lattice/blob/main/docs/schemas/lattice.plan_create_input.v3.schema.json",
4
4
  "title": "lattice.plan_create_input.v3",
5
5
  "description": "Canonical initial authoring input for a phase-audited plan whose task scheduling is controlled by the task DAG.",
6
6
  "$comment": "Runtime validation additionally enforces canonical JSON+LF bytes, sorted identifiers and edges, unique ids, combined task/phase acyclicity, byte bounds, and input_digest.",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "$schema": "https://json-schema.org/draft/2020-12/schema",
3
- "$id": "https://github.com/kitepon-rgb/Lattice/blob/main/docs/schemas/lattice.plan_create_input.v4.schema.json",
3
+ "$id": "https://github.com/kitepon/Lattice/blob/main/docs/schemas/lattice.plan_create_input.v4.schema.json",
4
4
  "title": "lattice.plan_create_input.v4",
5
5
  "description": "Canonical initial authoring input for a phase-audited plan. Every task requires a design memo. After the complete plan is written, run `lattice plan scope-review` and confirm that every task preserves an owner-supplied work specification without adding a product purpose. あなたがこのToDoに対して、何も考えていないならば、設計メモに `NO_PLAN` と書いてください",
6
6
  "$comment": "Runtime validation additionally enforces canonical JSON+LF bytes, sorted identifiers and edges, unique ids, combined task/phase acyclicity, byte bounds, non-empty design_memo, and input_digest.",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "$schema": "https://json-schema.org/draft/2020-12/schema",
3
- "$id": "https://github.com/kitepon-rgb/Lattice/blob/main/docs/schemas/lattice.plan_scope_review.v1.schema.json",
3
+ "$id": "https://github.com/kitepon/Lattice/blob/main/docs/schemas/lattice.plan_scope_review.v1.schema.json",
4
4
  "title": "lattice.plan_scope_review.v1",
5
5
  "description": "AI-authored review performed after the complete plan is written and before it is registered. Every planned task is judged against the owner-supplied work specifications; task splitting and parallelization are allowed, but a new product purpose is not.",
6
6
  "$comment": "Lattice validates complete task coverage, known work-spec references, verdict consistency, and digest binding. The operating AI owns the semantic judgment.",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "$schema": "https://json-schema.org/draft/2020-12/schema",
3
- "$id": "https://github.com/kitepon-rgb/Lattice/blob/main/docs/schemas/lattice.run_request.v1.schema.json",
3
+ "$id": "https://github.com/kitepon/Lattice/blob/main/docs/schemas/lattice.run_request.v1.schema.json",
4
4
  "title": "lattice.run_request.v1",
5
5
  "description": "Canonical run request accepted by `lattice plan compile --request` and `lattice run start --request`.",
6
6
  "$comment": "Runtime validation additionally enforces canonical JSON bytes and the self-digest rule: `request_digest` is the SHA-256 of the canonical JSON of the request with `request_digest` removed. Rejections are reported as `lattice.cli_error.v2` with `detail.reason` and `detail.path`.",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "$schema": "https://json-schema.org/draft/2020-12/schema",
3
- "$id": "https://github.com/kitepon-rgb/Lattice/blob/main/docs/schemas/lattice.runtime_adapter_capabilities.v2.schema.json",
3
+ "$id": "https://github.com/kitepon/Lattice/blob/main/docs/schemas/lattice.runtime_adapter_capabilities.v2.schema.json",
4
4
  "title": "lattice.runtime_adapter_capabilities.v2",
5
5
  "description": "controller handshakeとlaunch descriptorを束縛するruntime adapter能力契約。host_driven_epochはhostによるmanaged epoch駆動の受入宣言である。",
6
6
  "type": "object",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "$schema": "https://json-schema.org/draft/2020-12/schema",
3
- "$id": "https://github.com/kitepon-rgb/Lattice/blob/main/docs/schemas/lattice.runtime_adapter_registration_input.v1.schema.json",
3
+ "$id": "https://github.com/kitepon/Lattice/blob/main/docs/schemas/lattice.runtime_adapter_registration_input.v1.schema.json",
4
4
  "title": "lattice.runtime_adapter_registration_input.v1",
5
5
  "description": "lattice run adapter register の入力契約。digestと固定capabilitiesはCLIが導出する。",
6
6
  "oneOf": [
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "$schema": "https://json-schema.org/draft/2020-12/schema",
3
- "$id": "https://github.com/kitepon-rgb/Lattice/blob/main/docs/schemas/lattice.runtime_adapter_registration_input.v2.schema.json",
3
+ "$id": "https://github.com/kitepon/Lattice/blob/main/docs/schemas/lattice.runtime_adapter_registration_input.v2.schema.json",
4
4
  "title": "lattice.runtime_adapter_registration_input.v2",
5
5
  "description": "lattice run adapter register の入力契約。host_driven_epochでhostによるmanaged epoch駆動への同意を宣言し、digestと固定capabilitiesはCLIが導出する。",
6
6
  "oneOf": [
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "$schema": "https://json-schema.org/draft/2020-12/schema",
3
- "$id": "https://github.com/kitepon-rgb/Lattice/blob/main/docs/schemas/lattice.todo_extraction.v2.schema.json",
3
+ "$id": "https://github.com/kitepon/Lattice/blob/main/docs/schemas/lattice.todo_extraction.v2.schema.json",
4
4
  "title": "lattice.todo_extraction.v2",
5
5
  "description": "G4 migration-wave-only intermediate artifact authored by AI. Version 2 adds fail-closed historical in-progress registration without changing v1.",
6
6
  "$comment": "Runtime validation additionally enforces UTF-8 byte bounds, strict sort order, unique and resolvable task ids, and extraction_digest as the canonical self-digest. Registration separately rejects an extraction with no register_* task.",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "$schema": "https://json-schema.org/draft/2020-12/schema",
3
- "$id": "https://github.com/kitepon-rgb/Lattice/blob/main/docs/schemas/lattice.todo_extraction.v3.schema.json",
3
+ "$id": "https://github.com/kitepon/Lattice/blob/main/docs/schemas/lattice.todo_extraction.v3.schema.json",
4
4
  "title": "lattice.todo_extraction.v3",
5
5
  "description": "AI-authored ToDo migration artifact. Every task carries a non-blank Markdown design_memo. After the complete plan is written, run `lattice plan scope-review` and confirm that every registered task preserves an owner-supplied work specification without adding a product purpose. If no plan exists, write NO_PLAN explicitly.",
6
6
  "$comment": "あなたがこのToDoに対して、何も考えていないならば、設計メモに `NO_PLAN` と書いてください。Runtime validation also enforces UTF-8 byte bounds, exact keys, strict sort order, referential integrity, disposition consistency, and the canonical self-digest.",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "$schema": "https://json-schema.org/draft/2020-12/schema",
3
- "$id": "https://github.com/kitepon-rgb/Lattice/blob/main/docs/schemas/lattice.todo_extraction.v4.schema.json",
3
+ "$id": "https://github.com/kitepon/Lattice/blob/main/docs/schemas/lattice.todo_extraction.v4.schema.json",
4
4
  "title": "lattice.todo_extraction.v4",
5
5
  "description": "AI-authored ToDo migration artifact. Every task carries a non-blank Markdown design_memo. After the complete plan is written, run `lattice plan scope-review` and confirm that every registered task preserves an owner-supplied work specification without adding a product purpose. If no plan exists, write NO_PLAN explicitly. Runtime validation accepts at most one explicit, reasoned cross-plan dependency; an empty tasks array is reserved for that one dependency between existing tasks, with the top-level plan identity naming the source plan and both endpoint topology digests.",
6
6
  "$comment": "あなたがこのToDoに対して、何も考えていないならば、設計メモに `NO_PLAN` と書いてください。Runtime validation also enforces UTF-8 byte bounds, exact keys, strict sort order, referential integrity, disposition consistency, the cross-plan dependency limit, and the canonical self-digest.",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "$schema": "https://json-schema.org/draft/2020-12/schema",
3
- "$id": "https://github.com/kitepon-rgb/Lattice/blob/main/docs/schemas/lattice.todo_revision.v2.schema.json",
3
+ "$id": "https://github.com/kitepon/Lattice/blob/main/docs/schemas/lattice.todo_revision.v2.schema.json",
4
4
  "title": "lattice.todo_revision.v2",
5
5
  "description": "`lattice todo revise`が受理する最新契約。v1のdesired state・source inventory・reconciliationへ、per-ToDoのsource cutover batch(Markdown置換の証跡)を加える。",
6
6
  "$comment": "Runtime validation (src/todo-revision.mjs validateTodoRevision) additionally enforces: canonical JSON+LF bytes; strict sort order on task_migration (by from_task_id) and source_inventory.excluded_tombstones/source_cutover_batch.operations (by source_ref); uniqueness of from_task_id/to_task_id/source_ref across those collections; desired_plan.plan_version === todoRevisionPlanVersion(...); reconciliation.reconciliation_digest === todoReconciliationDigest(...); revision_digest as the canonical self-digest over every other top-level field; and that every task_migration.to_task_id (other than 'removed') resolves to a desired_plan task.",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "$schema": "https://json-schema.org/draft/2020-12/schema",
3
- "$id": "https://github.com/kitepon-rgb/Lattice/blob/main/docs/schemas/lattice.todo_revision_set.v3.schema.json",
3
+ "$id": "https://github.com/kitepon/Lattice/blob/main/docs/schemas/lattice.todo_revision_set.v3.schema.json",
4
4
  "title": "lattice.todo_revision_set.v3",
5
5
  "description": "`lattice todo revise-set`が受理する最新契約。複数planへ跨がるrevisionを1つのatomic書込みへ束ねる。v3のmemberは`lattice.todo_revision.v1/v2`(非phase plan)と`lattice.phase_todo_revision.v1/v2`(phase-audited plan)のいずれかであり、`lattice.phase_todo_revision.v3`はmemberとして受理されない——v3固有のruntime_task_migration/source_cutover_batchはrevise-setの外で単独revise-phaseとして扱う契約になっている。",
6
6
  "$comment": "Runtime validation (src/todo-revision.mjs validateTodoRevisionSet) additionally enforces: canonical JSON+LF bytes; revisions.length in [2, 64]; each member's own digest chain (validateTodoRevision / validatePhaseTodoRevision); every member.project_id === project_id; v3はrevisions内に最低1つphase_todo_revision memberを含む必要がある; revisionsが厳密にplan_key昇順でソートされている(重複plan_key禁止); revision_set_digestは他の全top-level fieldへの正準self-digest。",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "$schema": "https://json-schema.org/draft/2020-12/schema",
3
- "$id": "https://github.com/kitepon-rgb/Lattice/blob/main/docs/schemas/lattice.todo_structure_binding.v1.schema.json",
3
+ "$id": "https://github.com/kitepon/Lattice/blob/main/docs/schemas/lattice.todo_structure_binding.v1.schema.json",
4
4
  "title": "lattice.todo_structure_binding.v1",
5
5
  "description": "Immutable activation binding emitted by a successful authoritative planned compile.",
6
6
  "$comment": "Runtime validation additionally enforces exact keys, strict timestamps, actor identity, and the canonical self-digest.",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "$schema": "https://json-schema.org/draft/2020-12/schema",
3
- "$id": "https://github.com/kitepon-rgb/Lattice/blob/main/docs/schemas/lattice.todo_structure_realization.v1.schema.json",
3
+ "$id": "https://github.com/kitepon/Lattice/blob/main/docs/schemas/lattice.todo_structure_realization.v1.schema.json",
4
4
  "title": "lattice.todo_structure_realization.v1",
5
5
  "description": "Append-only realized logical dataflow for one applicable task.",
6
6
  "$comment": "Runtime validation additionally enforces canonical ordering, chain continuity, supersedes membership, structure-set identity, planned digest, UTF-8 byte bounds, and the canonical self-digest.",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "$schema": "https://json-schema.org/draft/2020-12/schema",
3
- "$id": "https://github.com/kitepon-rgb/Lattice/blob/main/docs/schemas/lattice.todo_structure_set.v1.schema.json",
3
+ "$id": "https://github.com/kitepon/Lattice/blob/main/docs/schemas/lattice.todo_structure_set.v1.schema.json",
4
4
  "title": "lattice.todo_structure_set.v1",
5
5
  "description": "AI-authored logical dataflow overlay for one registered Lattice plan version.",
6
6
  "$comment": "Runtime validation additionally enforces UTF-8 byte bounds, strict canonical ordering, referential integrity, exact plan task coverage when supplied, unique data IDs, constant canonical-byte limits, the canonical self-digest, and plan-wide totals of at most 512 anchors, 4096 ports, 2048 operations, and 4096 sinks.",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@quolu/lattice",
3
- "version": "0.60.4",
3
+ "version": "0.60.6",
4
4
  "description": "Schedulability compiler for multi-agent development: observe real code boundaries, refactor the conflicting seam, recompile the plan for parallel execution",
5
5
  "author": {
6
6
  "name": "Quo / クオ at kitepon.dev",
@@ -10,7 +10,7 @@
10
10
  "license": "PolyForm-Noncommercial-1.0.0",
11
11
  "repository": {
12
12
  "type": "git",
13
- "url": "git+https://github.com/kitepon-rgb/Lattice.git"
13
+ "url": "git+https://github.com/kitepon/Lattice.git"
14
14
  },
15
15
  "engines": {
16
16
  "node": ">=22.13 <25 || >=26"
@@ -76,6 +76,7 @@
76
76
  "verify:release-commit": "node scripts/verify-release-commit.mjs",
77
77
  "prepublishOnly": "npm run verify:release-commit",
78
78
  "test": "node scripts/run-product-tests.mjs",
79
+ "test:profile": "node scripts/run-product-tests.mjs",
79
80
  "test:sensor": "npm --prefix sensor test",
80
81
  "check": "node scripts/check-syntax.mjs",
81
82
  "ci": "npm run test && npm run test:sensor && npm run check && npm run check:cli-surface && npm run check:open-questions && npm run check:reachability && npm run verify:todo-store",
@@ -86,9 +87,9 @@
86
87
  "upstream:check": "node scripts/upstream-check.mjs",
87
88
  "upstream:sync": "node scripts/upstream-sync.mjs"
88
89
  },
89
- "homepage": "https://github.com/kitepon-rgb/Lattice#readme",
90
+ "homepage": "https://github.com/kitepon/Lattice#readme",
90
91
  "bugs": {
91
- "url": "https://github.com/kitepon-rgb/Lattice/issues"
92
+ "url": "https://github.com/kitepon/Lattice/issues"
92
93
  },
93
94
  "keywords": [
94
95
  "ai",
@@ -31,7 +31,7 @@ function buildNode25BlockBanner(nodeVersion) {
31
31
  'Node.js 25.x has a V8 WASM JIT (turboshaft) Zone allocator bug that',
32
32
  'crashes with `Fatal process out of memory: Zone` when LatticeSensor',
33
33
  'compiles tree-sitter grammars. LatticeSensor WILL crash on this Node',
34
- 'version mid-indexing. See https://github.com/kitepon-rgb/Lattice/issues/81',
34
+ 'version mid-indexing. See https://github.com/kitepon/Lattice/issues/81',
35
35
  '',
36
36
  'Fix: install Node.js 22 LTS:',
37
37
  ' nvm install 22 && nvm use 22 # nvm',
@@ -1 +1 @@
1
- {"version":3,"file":"node-version-check.js","sourceRoot":"","sources":["../../src/bin/node-version-check.ts"],"names":[],"mappings":";AAAA;;;;;;;;GAQG;;;AAWH,wDAmBC;AAGD,4CAEC;AAmBD,sDAkBC;AAtED;;;;;;;;GAQG;AACH,SAAgB,sBAAsB,CAAC,WAAmB;IACxD,MAAM,GAAG,GAAG,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;IAC3B,OAAO;QACL,GAAG;QACH,gDAAgD,WAAW,EAAE;QAC7D,GAAG;QACH,qEAAqE;QACrE,qEAAqE;QACrE,sEAAsE;QACtE,4EAA4E;QAC5E,EAAE;QACF,8BAA8B;QAC9B,+DAA+D;QAC/D,6EAA6E;QAC7E,EAAE;QACF,sDAAsD;QACtD,yDAAyD;QACzD,GAAG;KACJ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACf,CAAC;AAED,wDAAwD;AACxD,SAAgB,gBAAgB,CAAC,SAAiB;IAChD,OAAO,SAAS,KAAK,EAAE,CAAC;AAC1B,CAAC;AAED;;;;;;GAMG;AACU,QAAA,cAAc,GAAG,EAAE,CAAC;AAEjC;;;;;;;GAOG;AACH,SAAgB,qBAAqB,CAAC,WAAmB;IACvD,MAAM,GAAG,GAAG,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;IAC3B,OAAO;QACL,GAAG;QACH,gDAAgD,WAAW,EAAE;QAC7D,GAAG;QACH,kCAAkC,sBAAc,gCAAgC;QAChF,yEAAyE;QACzE,sBAAsB;QACtB,EAAE;QACF,8BAA8B;QAC9B,+DAA+D;QAC/D,6EAA6E;QAC7E,EAAE;QACF,8CAA8C;QAC9C,yDAAyD;QACzD,GAAG;KACJ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACf,CAAC"}
1
+ {"version":3,"file":"node-version-check.js","sourceRoot":"","sources":["../../src/bin/node-version-check.ts"],"names":[],"mappings":";AAAA;;;;;;;;GAQG;;;AAWH,wDAmBC;AAGD,4CAEC;AAmBD,sDAkBC;AAtED;;;;;;;;GAQG;AACH,SAAgB,sBAAsB,CAAC,WAAmB;IACxD,MAAM,GAAG,GAAG,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;IAC3B,OAAO;QACL,GAAG;QACH,gDAAgD,WAAW,EAAE;QAC7D,GAAG;QACH,qEAAqE;QACrE,qEAAqE;QACrE,sEAAsE;QACtE,wEAAwE;QACxE,EAAE;QACF,8BAA8B;QAC9B,+DAA+D;QAC/D,6EAA6E;QAC7E,EAAE;QACF,sDAAsD;QACtD,yDAAyD;QACzD,GAAG;KACJ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACf,CAAC;AAED,wDAAwD;AACxD,SAAgB,gBAAgB,CAAC,SAAiB;IAChD,OAAO,SAAS,KAAK,EAAE,CAAC;AAC1B,CAAC;AAED;;;;;;GAMG;AACU,QAAA,cAAc,GAAG,EAAE,CAAC;AAEjC;;;;;;;GAOG;AACH,SAAgB,qBAAqB,CAAC,WAAmB;IACvD,MAAM,GAAG,GAAG,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;IAC3B,OAAO;QACL,GAAG;QACH,gDAAgD,WAAW,EAAE;QAC7D,GAAG;QACH,kCAAkC,sBAAc,gCAAgC;QAChF,yEAAyE;QACzE,sBAAsB;QACtB,EAAE;QACF,8BAA8B;QAC9B,+DAA+D;QAC/D,6EAA6E;QAC7E,EAAE;QACF,8CAA8C;QAC9C,yDAAyD;QACzD,GAAG;KACJ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACf,CAAC"}
@@ -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":"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"}
1
+ {"version":3,"file":"worktree.js","sourceRoot":"","sources":["../../src/sync/worktree.ts"],"names":[],"mappings":"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"}
@@ -5,7 +5,7 @@
5
5
  "description": "Lattice-owned local structural sensor.",
6
6
  "repository": {
7
7
  "type": "git",
8
- "url": "git+https://github.com/kitepon-rgb/Lattice.git"
8
+ "url": "git+https://github.com/kitepon/Lattice.git"
9
9
  },
10
10
  "main": "dist/index.js",
11
11
  "types": "dist/index.d.ts",
@@ -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
  }
@@ -20,7 +20,8 @@ export const TODO_DASHBOARD_CODE_VERSION = packageJson.version;
20
20
  const REGISTRY_SCHEMA = 'lattice.todo_dashboard_registry.v1';
21
21
  const DAEMON_SCHEMA = 'lattice.todo_dashboard_daemon.v1';
22
22
  const DEFAULT_PORT = 0;
23
- export const TODO_DASHBOARD_STALE_MS = 2 * 60 * 60 * 1_000;
23
+ // 登録簿の鮮度窓=dashboard配信寿命。heartbeatの露出判定へ流用しない(ADR 0165)。
24
+ export const TODO_DASHBOARD_STALE_MS = 7 * 24 * 60 * 60 * 1_000;
24
25
  const LOCK_ATTEMPTS = 240;
25
26
  const LOCK_WAIT_MS = 25;
26
27
  const LOCK_STALE_MS = 30_000;
@@ -377,6 +378,17 @@ function signalIfPresent(signalProcess, pid, signal) {
377
378
  }
378
379
  }
379
380
 
381
+ async function requestDaemonShutdown(descriptor) {
382
+ try {
383
+ const response = await fetch(`http://127.0.0.1:${descriptor.port}/__lattice/shutdown`, {
384
+ method: 'POST', signal: AbortSignal.timeout(500),
385
+ });
386
+ return response.status === 202;
387
+ } catch {
388
+ return false;
389
+ }
390
+ }
391
+
380
392
  async function stopAttestedLegacyDaemon(descriptor, {
381
393
  signalProcess = process.kill, isProcessAlive = processIsAlive, timeoutMs = 3_000,
382
394
  } = {}) {
@@ -387,7 +399,7 @@ async function stopAttestedLegacyDaemon(descriptor, {
387
399
  error.code = 'DASHBOARD_LEGACY_ATTESTATION_LOST';
388
400
  throw error;
389
401
  }
390
- signalProcess(descriptor.pid, 'SIGTERM');
402
+ if (!await requestDaemonShutdown(descriptor)) signalProcess(descriptor.pid, 'SIGTERM');
391
403
  if (await awaitDaemonStopped(descriptor, { isProcessAlive, deadline: Date.now() + timeoutMs })) return;
392
404
  const error = new Error('legacy dashboard daemon did not stop');
393
405
  error.code = 'DASHBOARD_LEGACY_STOP_FAILED';
@@ -406,7 +418,7 @@ async function stopStrayDaemon(descriptor, {
406
418
  signalProcess = process.kill, isProcessAlive = processIsAlive, timeoutMs = 3_000,
407
419
  } = {}) {
408
420
  if (await daemonAttestation(descriptor) === null) return false;
409
- signalIfPresent(signalProcess, descriptor.pid, 'SIGTERM');
421
+ if (!await requestDaemonShutdown(descriptor)) signalIfPresent(signalProcess, descriptor.pid, 'SIGTERM');
410
422
  const half = Math.ceil(timeoutMs / 2);
411
423
  if (await awaitDaemonStopped(descriptor, { isProcessAlive, deadline: Date.now() + half })) return true;
412
424
  signalIfPresent(signalProcess, descriptor.pid, 'SIGKILL');
@@ -450,7 +462,7 @@ async function stopSpawnedReplacement(child, descriptor, {
450
462
  || !await isProcessAlive(child.pid);
451
463
  return exited && await daemonAttestation(descriptor) === null;
452
464
  };
453
- child.kill('SIGTERM');
465
+ if (!await requestDaemonShutdown(descriptor)) child.kill('SIGTERM');
454
466
  let deadline = Date.now() + Math.ceil(timeoutMs / 2);
455
467
  while (Date.now() < deadline) {
456
468
  if (await stopped()) return;
@@ -467,15 +479,24 @@ async function stopSpawnedReplacement(child, descriptor, {
467
479
  throw error;
468
480
  }
469
481
 
470
- export async function writeTodoDashboardDaemonDescriptor({ port, env = process.env }) {
482
+ function currentDaemonRecord(port) {
483
+ return { schema: DAEMON_SCHEMA, pid: process.pid, port,
484
+ started_at: new Date().toISOString() };
485
+ }
486
+
487
+ export async function writeTodoDashboardDaemonRecord({ port, env = process.env }) {
471
488
  if (!Number.isSafeInteger(port) || port <= 0 || port > 65_535) throw new TypeError('daemon port invalid');
472
489
  const refs = paths(env);
473
490
  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」が生まれる。
491
+ const record = currentDaemonRecord(port);
478
492
  await atomicJson(daemonRecordRef(refs, process.pid), record);
493
+ return record;
494
+ }
495
+
496
+ export async function writeTodoDashboardDaemonDescriptor({ port, env = process.env }) {
497
+ const refs = paths(env);
498
+ // 直接descriptorを公開する呼出面でも、孤児を再発見できるdaemon固有記録を先に置く。
499
+ const record = await writeTodoDashboardDaemonRecord({ port, env });
479
500
  await atomicJson(refs.descriptor, record);
480
501
  }
481
502
 
@@ -545,7 +566,8 @@ export async function ensureTodoDashboardDaemon({ env = process.env, spawnDaemon
545
566
  const deadline = Date.now() + startupTimeoutMs;
546
567
  while (Date.now() < deadline) {
547
568
  await new Promise((resolve) => setTimeout(resolve, 50));
548
- const descriptor = await readJson(refs.descriptor, null);
569
+ const descriptor = typeof child.pid === 'number'
570
+ ? await readJson(daemonRecordRef(refs, child.pid), null) : null;
549
571
  const healthy = await daemonHealthy(descriptor);
550
572
  if (!healthy && typeof child.pid === 'number'
551
573
  && !await replacementIsProcessAlive(child.pid)) break;
@@ -557,17 +579,16 @@ export async function ensureTodoDashboardDaemon({ env = process.env, spawnDaemon
557
579
  });
558
580
  } catch (error) {
559
581
  await stopSpawnedReplacement(child, descriptor, { isProcessAlive: replacementIsProcessAlive });
560
- await atomicJson(refs.descriptor, legacy);
561
582
  throw error;
562
583
  }
563
584
  }
585
+ await atomicJson(refs.descriptor, descriptor);
564
586
  await reap(descriptor.pid);
565
587
  return descriptor;
566
588
  }
567
589
  }
568
590
  child.kill?.('SIGTERM');
569
- if (legacy !== null) await atomicJson(refs.descriptor, legacy);
570
- else await rm(refs.descriptor, { force: true });
591
+ if (legacy === null) await rm(refs.descriptor, { force: true });
571
592
  const error = new Error('dashboard daemon did not become ready');
572
593
  error.code = 'DASHBOARD_DAEMON_UNAVAILABLE';
573
594
  throw error;
@@ -87,7 +87,7 @@ function dashboardHtml(projects) {
87
87
  }).join('');
88
88
  const content = rows.length === 0 ? '<p>アクティブなプロジェクトはありません。</p>'
89
89
  : `<ul>${rows}</ul>`;
90
- return `<!doctype html><html lang="ja"><head><meta http-equiv="Content-Security-Policy" content="default-src 'none'; style-src 'unsafe-inline'"><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><meta name="description" content="Latticeが管理している公開中の工程と現在地を確認できます。"><meta property="og:title" content="公開中の工程表 — Lattice"><meta property="og:description" content="Latticeが管理している公開中の工程と現在地を確認できます。"><meta name="theme-color" content="#f7f3ea"><title>公開中の工程表 — Lattice</title><style>:root{color-scheme:light;--paper:#f7f3ea;--panel:#fffdf8;--ink:#201d19;--soft:#6c655d;--line:#d8d0c5;--cobalt:#315cbe;--orange:#e85f2a}*{box-sizing:border-box}body{min-height:100vh;margin:0;color:var(--ink);background:var(--paper);font:16px/1.7 system-ui,-apple-system,sans-serif}.shell{max-width:880px;margin:0 auto;padding:28px 22px 40px}.brand{display:flex;align-items:center;gap:9px;padding-bottom:24px;border-bottom:1px solid var(--line);font-size:.88rem}.brand a,.footer a{color:var(--ink);font-weight:800;text-decoration:none}.brand a:hover,.footer a:hover{color:var(--cobalt)}.brand span{color:var(--soft)}main{padding:64px 0 72px}.eyebrow{margin:0 0 8px;color:var(--orange);font-size:.76rem;font-weight:800;letter-spacing:.14em}.lead{max-width:620px;margin:0 0 34px;color:var(--soft)}h1{margin:0 0 14px;font-size:clamp(2rem,6vw,3.4rem);line-height:1.12;letter-spacing:-.04em}ul{display:grid;gap:12px;margin:0;padding:0;list-style:none}li a{display:grid;grid-template-columns:minmax(0,1fr) auto auto;align-items:center;gap:20px;padding:18px 20px;border:1px solid var(--line);border-radius:12px;color:inherit;background:var(--panel);text-decoration:none;box-shadow:0 8px 28px rgba(48,39,27,.04)}li a:hover{border-color:var(--cobalt);transform:translateY(-1px)}li strong{font-size:1.04rem}li code{color:var(--soft);font-size:.78rem}li span{color:var(--cobalt);font-weight:800}.note{margin:28px 0 0;padding:16px 18px;border-left:3px solid var(--orange);color:var(--soft);background:rgba(255,253,248,.72);font-size:.88rem}.footer{display:flex;flex-wrap:wrap;justify-content:space-between;gap:16px;padding-top:20px;border-top:1px solid var(--line);color:var(--soft);font-size:.82rem}.footer nav{display:flex;gap:18px}@media(max-width:560px){.shell{padding:20px 16px 32px}main{padding:44px 0 56px}li a{grid-template-columns:minmax(0,1fr) auto;padding:16px}li code{grid-column:1/-1;grid-row:2}.footer{display:block}.footer nav{margin-top:10px}}</style></head><body><div class="shell"><header class="brand"><a href="https://kitepon.dev/">kitepon.dev</a><span aria-hidden="true">/</span><strong>Lattice</strong></header><main><p class="eyebrow">LIVE DEVELOPMENT</p><h1>公開中の工程表</h1><p class="lead">Latticeが管理しているプロジェクトの工程と、いまどこまで進んでいるかを公開データから確認できます。</p>${content}<p class="note">表示内容はLatticeの記録から自動生成されます。製品の紹介や使い方はGitHubをご覧ください。</p></main><footer class="footer"><span>kitepon.dev の開発工程を、Latticeで可視化しています。</span><nav aria-label="関連リンク"><a href="https://kitepon.dev/">kitepon.dev</a><a href="https://github.com/kitepon-rgb/Lattice">GitHub</a></nav></footer></div></body></html>`;
90
+ return `<!doctype html><html lang="ja"><head><meta http-equiv="Content-Security-Policy" content="default-src 'none'; style-src 'unsafe-inline'"><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><meta name="description" content="Latticeが管理している公開中の工程と現在地を確認できます。"><meta property="og:title" content="公開中の工程表 — Lattice"><meta property="og:description" content="Latticeが管理している公開中の工程と現在地を確認できます。"><meta name="theme-color" content="#f7f3ea"><title>公開中の工程表 — Lattice</title><style>:root{color-scheme:light;--paper:#f7f3ea;--panel:#fffdf8;--ink:#201d19;--soft:#6c655d;--line:#d8d0c5;--cobalt:#315cbe;--orange:#e85f2a}*{box-sizing:border-box}body{min-height:100vh;margin:0;color:var(--ink);background:var(--paper);font:16px/1.7 system-ui,-apple-system,sans-serif}.shell{max-width:880px;margin:0 auto;padding:28px 22px 40px}.brand{display:flex;align-items:center;gap:9px;padding-bottom:24px;border-bottom:1px solid var(--line);font-size:.88rem}.brand a,.footer a{color:var(--ink);font-weight:800;text-decoration:none}.brand a:hover,.footer a:hover{color:var(--cobalt)}.brand span{color:var(--soft)}main{padding:64px 0 72px}.eyebrow{margin:0 0 8px;color:var(--orange);font-size:.76rem;font-weight:800;letter-spacing:.14em}.lead{max-width:620px;margin:0 0 34px;color:var(--soft)}h1{margin:0 0 14px;font-size:clamp(2rem,6vw,3.4rem);line-height:1.12;letter-spacing:-.04em}ul{display:grid;gap:12px;margin:0;padding:0;list-style:none}li a{display:grid;grid-template-columns:minmax(0,1fr) auto auto;align-items:center;gap:20px;padding:18px 20px;border:1px solid var(--line);border-radius:12px;color:inherit;background:var(--panel);text-decoration:none;box-shadow:0 8px 28px rgba(48,39,27,.04)}li a:hover{border-color:var(--cobalt);transform:translateY(-1px)}li strong{font-size:1.04rem}li code{color:var(--soft);font-size:.78rem}li span{color:var(--cobalt);font-weight:800}.note{margin:28px 0 0;padding:16px 18px;border-left:3px solid var(--orange);color:var(--soft);background:rgba(255,253,248,.72);font-size:.88rem}.footer{display:flex;flex-wrap:wrap;justify-content:space-between;gap:16px;padding-top:20px;border-top:1px solid var(--line);color:var(--soft);font-size:.82rem}.footer nav{display:flex;gap:18px}@media(max-width:560px){.shell{padding:20px 16px 32px}main{padding:44px 0 56px}li a{grid-template-columns:minmax(0,1fr) auto;padding:16px}li code{grid-column:1/-1;grid-row:2}.footer{display:block}.footer nav{margin-top:10px}}</style></head><body><div class="shell"><header class="brand"><a href="https://kitepon.dev/">kitepon.dev</a><span aria-hidden="true">/</span><strong>Lattice</strong></header><main><p class="eyebrow">LIVE DEVELOPMENT</p><h1>公開中の工程表</h1><p class="lead">Latticeが管理しているプロジェクトの工程と、いまどこまで進んでいるかを公開データから確認できます。</p>${content}<p class="note">表示内容はLatticeの記録から自動生成されます。製品の紹介や使い方はGitHubをご覧ください。</p></main><footer class="footer"><span>kitepon.dev の開発工程を、Latticeで可視化しています。</span><nav aria-label="関連リンク"><a href="https://kitepon.dev/">kitepon.dev</a><a href="https://github.com/kitepon/Lattice">GitHub</a></nav></footer></div></body></html>`;
91
91
  }
92
92
 
93
93
  function notFoundHtml(code, path) {
@@ -181,10 +181,13 @@ function finishRequestFailure(response, code, path) {
181
181
  } catch { response.destroy(); }
182
182
  }
183
183
 
184
- export async function startTodoGanttDashboardServer({ registry, port = 0, redirectSingleProject = false }) {
184
+ export async function startTodoGanttDashboardServer({
185
+ registry, port = 0, redirectSingleProject = false, onShutdown = null,
186
+ }) {
185
187
  if (!Number.isSafeInteger(port) || port < 0 || port > 65_535) throw new TypeError('port must be 0..65535');
186
188
  if (registry === null || typeof registry !== 'object' || typeof registry.get !== 'function'
187
189
  || typeof registry.list !== 'function') throw new TypeError('registry required');
190
+ if (onShutdown !== null && typeof onShutdown !== 'function') throw new TypeError('onShutdown must be a function');
188
191
  const clientsByProject = new Map();
189
192
  const lastHeads = new Map();
190
193
  const lastHeartbeats = new Map();
@@ -208,6 +211,13 @@ export async function startTodoGanttDashboardServer({ registry, port = 0, redire
208
211
  try {
209
212
  const url = new URL(requestPath, `http://${LOOPBACK}`);
210
213
  requestPath = url.pathname;
214
+ if (url.pathname === '/__lattice/shutdown' && request.method === 'POST' && onShutdown !== null) {
215
+ response.writeHead(202, { 'content-type': 'application/json; charset=utf-8',
216
+ 'cache-control': 'no-store', 'x-content-type-options': 'nosniff' });
217
+ response.end(`${JSON.stringify({ schema: 'lattice.todo_dashboard_shutdown.v1', accepted: true })}\n`);
218
+ setImmediate(() => Promise.resolve().then(onShutdown).catch(() => {}));
219
+ return;
220
+ }
211
221
  if (request.method !== 'GET') {
212
222
  response.setHeader('allow', 'GET');
213
223
  sendHttpError(response, 405, 'METHOD_NOT_ALLOWED', requestPath);
@@ -341,9 +351,13 @@ export async function startTodoGanttDashboardServer({ registry, port = 0, redire
341
351
  });
342
352
  }
343
353
 
344
- export async function startTodoGanttLiveServer({ projectId, displayName = projectId, port = 0, render, readHead }) {
354
+ export async function startTodoGanttLiveServer({
355
+ projectId, displayName = projectId, port = 0, render, readHead, onShutdown = null,
356
+ }) {
345
357
  const registry = createTodoGanttProjectRegistry([{ projectId, displayName, render, readHead }]);
346
- const dashboard = await startTodoGanttDashboardServer({ registry, port, redirectSingleProject: true });
358
+ const dashboard = await startTodoGanttDashboardServer({
359
+ registry, port, redirectSingleProject: true, onShutdown,
360
+ });
347
361
  const path = projectPath(projectId);
348
362
  return Object.freeze({
349
363
  projectId,
@@ -117,7 +117,7 @@ async function actualIndexPath(repoRoot, env) {
117
117
  }
118
118
 
119
119
  async function syncPath(target) {
120
- const handle = await open(target, fsConstants.O_RDONLY);
120
+ const handle = await open(target, fsConstants.O_RDWR);
121
121
  try { await handle.sync(); } finally { await handle.close(); }
122
122
  }
123
123
 
@@ -1177,6 +1177,102 @@ function pinnedSourceLine(repoRoot, source, cache = null) {
1177
1177
  throw new Error('line outside blob');
1178
1178
  }
1179
1179
 
1180
+ // 検証対象objectの一括prefetch。event payloadに現れるevidence記述子とimport sourceを
1181
+ // 収集し、repoごとに1回の`cat-file --batch`でcacheへseedする。object 1個=spawn 1回の
1182
+ // 雪崩はWindowsで起動単価66〜100msになり、evidenceが育ったstoreのstatusを桁で遅くする。
1183
+ // 成功した読みだけをcacheへ入れ、missing・型不一致・batchごと失敗(repo不在等の境界)は
1184
+ // 何もcacheしない——正誤の裁定は従来どおりper-objectの検証経路が同じ結果を出す。
1185
+ function prefetchVerificationObjects(manifest, repoRoot, events, pinnedSourceCache) {
1186
+ const candidates = [];
1187
+ for (const event of events) {
1188
+ // 改版genesisはevent直下のstate_migrationでtask状態(evidence込み)を搬送する。
1189
+ // replayがcarry系だけを復元・検証するので、収集も同じpolicyへ揃える。
1190
+ if (Array.isArray(event.state_migration)) {
1191
+ for (const migration of event.state_migration) {
1192
+ if (['carry', 'carry_reconciled_metadata', 'acquire_phase'].includes(migration.state_policy)
1193
+ && migration.state !== null && typeof migration.state === 'object'
1194
+ && migration.state.evidence !== null && typeof migration.state.evidence === 'object') {
1195
+ candidates.push(migration.state.evidence);
1196
+ }
1197
+ }
1198
+ }
1199
+ const payload = event.payload;
1200
+ if (payload === null || typeof payload !== 'object') continue;
1201
+ if (payload.evidence !== null && typeof payload.evidence === 'object') candidates.push(payload.evidence);
1202
+ if (payload.decision_evidence !== null && typeof payload.decision_evidence === 'object') {
1203
+ candidates.push(payload.decision_evidence);
1204
+ }
1205
+ if (Array.isArray(payload.evidence_slots)) {
1206
+ for (const slot of payload.evidence_slots) {
1207
+ if (slot !== null && typeof slot === 'object'
1208
+ && slot.evidence !== null && typeof slot.evidence === 'object') candidates.push(slot.evidence);
1209
+ }
1210
+ }
1211
+ }
1212
+ const repositories = new Map(manifest.repositories.map((repo) => [repo.repo_id, repo.path]));
1213
+ const oidsByRepo = new Map();
1214
+ const pinnedCommits = new Set();
1215
+ const pinnedSpecs = new Set();
1216
+ for (const candidate of candidates) {
1217
+ if (validateEvidenceDescriptor(candidate)) {
1218
+ const repoRef = repositories.get(candidate.repo_id);
1219
+ if (repoRef === undefined) continue;
1220
+ const absoluteRepo = path.resolve(repoRoot, repoRef);
1221
+ if (evidenceBlobCache.has(`${absoluteRepo}\0${candidate.git_blob_oid}`)) continue;
1222
+ let oids = oidsByRepo.get(absoluteRepo);
1223
+ if (oids === undefined) oidsByRepo.set(absoluteRepo, oids = new Set());
1224
+ oids.add(candidate.git_blob_oid);
1225
+ } else if (validateTodoImportSource(candidate) && pinnedSourceCache !== null) {
1226
+ if (!pinnedSourceCache.commits.has(candidate.source_commit)) pinnedCommits.add(candidate.source_commit);
1227
+ const spec = `${candidate.source_commit}:${candidate.origin_plan_ref}`;
1228
+ if (!pinnedSourceCache.blobs.has(spec)) pinnedSpecs.add(spec);
1229
+ }
1230
+ }
1231
+ for (const [absoluteRepo, oids] of oidsByRepo) {
1232
+ let entries;
1233
+ try {
1234
+ entries = gitCatFileBatch([...oids], {
1235
+ cwd: absoluteRepo, maxBodyBytes: TODO_LIMITS.narrativeSectionBytes + 1,
1236
+ });
1237
+ } catch { continue; }
1238
+ let index = 0;
1239
+ for (const oid of oids) {
1240
+ const entry = entries[index];
1241
+ index += 1;
1242
+ // seedしないだけで正誤は変えない: 上限超過bodyは単体読みのmaxBufferでは読めない
1243
+ // (=従来は未検証)ので、batch同乗の予算合算で検証済みへ昇格させない。cacheが
1244
+ // 満杯なら追い出さずseedを止める——seeding自身の追い出しは自分のseedを連鎖破壊し、
1245
+ // 513件以上で全件個別spawnへ戻る。溢れた分はper-item経路が従来どおり扱う。
1246
+ if (entry.missing || entry.type !== 'blob'
1247
+ || entry.bytes.length > TODO_LIMITS.narrativeSectionBytes
1248
+ || evidenceBlobCache.size >= EVIDENCE_BLOB_CACHE_LIMIT) continue;
1249
+ evidenceBlobCache.set(`${absoluteRepo}\0${oid}`, entry.bytes);
1250
+ }
1251
+ }
1252
+ const pinnedNames = [...pinnedCommits, ...pinnedSpecs];
1253
+ if (pinnedNames.length === 0) return;
1254
+ let entries;
1255
+ try {
1256
+ entries = gitCatFileBatch(pinnedNames, {
1257
+ cwd: repoRoot, maxBodyBytes: TODO_LIMITS.narrativeSectionBytes + 1,
1258
+ });
1259
+ } catch { return; }
1260
+ let index = 0;
1261
+ for (const commit of pinnedCommits) {
1262
+ const entry = entries[index];
1263
+ index += 1;
1264
+ if (!entry.missing && entry.type === 'commit') pinnedSourceCache.commits.add(commit);
1265
+ }
1266
+ for (const spec of pinnedSpecs) {
1267
+ const entry = entries[index];
1268
+ index += 1;
1269
+ if (!entry.missing && entry.type === 'blob'
1270
+ && entry.bytes.length <= TODO_LIMITS.narrativeSectionBytes) {
1271
+ pinnedSourceCache.blobs.set(spec, entry.bytes);
1272
+ }
1273
+ }
1274
+ }
1275
+
1180
1276
  function markdownCheckboxState(lineBytes) {
1181
1277
  if (lineBytes.length >= 3 && lineBytes[0] === 0xef && lineBytes[1] === 0xbb && lineBytes[2] === 0xbf) return null;
1182
1278
  let line;
@@ -1221,7 +1317,7 @@ function narrativeAnchorSource(value) {
1221
1317
  });
1222
1318
  }
1223
1319
 
1224
- function materializeImportedNarrativeAnchors(repoRoot, planInput, sources) {
1320
+ function materializeImportedNarrativeAnchors(repoRoot, planInput, sources, cache = null) {
1225
1321
  if (sources === undefined) return planInput;
1226
1322
  if (!['lattice.todo_plan.v2', 'lattice.todo_plan.v6'].includes(planInput?.schema)
1227
1323
  || !Array.isArray(sources)
@@ -1241,7 +1337,7 @@ function materializeImportedNarrativeAnchors(repoRoot, planInput, sources) {
1241
1337
  if (source !== undefined && task.narrative_ref === source.origin_plan_ref
1242
1338
  && ['checked', 'unchecked'].includes(source.checkbox_state)) {
1243
1339
  try {
1244
- const line = pinnedSourceLine(repoRoot, source);
1340
+ const line = pinnedSourceLine(repoRoot, source, cache);
1245
1341
  if (markdownCheckboxState(line) === source.checkbox_state) {
1246
1342
  narrativeAnchor = {
1247
1343
  origin_plan_ref: source.origin_plan_ref,
@@ -1259,7 +1355,7 @@ function materializeImportedNarrativeAnchors(repoRoot, planInput, sources) {
1259
1355
  };
1260
1356
  }
1261
1357
 
1262
- function verifyPlanNarrativeAnchors(repoRoot, plan, trustedPlan = null) {
1358
+ function verifyPlanNarrativeAnchors(repoRoot, plan, trustedPlan = null, cache = null) {
1263
1359
  if (!['lattice.todo_plan.v2', 'lattice.todo_plan.v3', 'lattice.todo_plan.v4',
1264
1360
  'lattice.todo_plan.v5', 'lattice.todo_plan.v6', 'lattice.todo_plan.v7'].includes(plan.schema)) return;
1265
1361
  const trusted = new Map((['lattice.todo_plan.v2', 'lattice.todo_plan.v3', 'lattice.todo_plan.v4',
@@ -1273,7 +1369,7 @@ function verifyPlanNarrativeAnchors(repoRoot, plan, trustedPlan = null) {
1273
1369
  if (previous !== undefined
1274
1370
  && canonicalizeTodoArtifact(previous) === canonicalizeTodoArtifact(anchor)) continue;
1275
1371
  try {
1276
- const line = pinnedSourceLine(repoRoot, anchor);
1372
+ const line = pinnedSourceLine(repoRoot, anchor, cache);
1277
1373
  if (sha256Bytes(line) !== anchor.source_line_digest || markdownCheckboxState(line) === null) {
1278
1374
  throw new Error('anchor mismatch');
1279
1375
  }
@@ -1374,6 +1470,7 @@ export async function readTodoStore(options = {}) {
1374
1470
  && descriptor.active_revision_digest !== plan.plan_digest) {
1375
1471
  fail('STORE_INCONSISTENT', 'manifest_revision_binding_mismatch');
1376
1472
  }
1473
+ prefetchVerificationObjects(manifest, repoRoot, journal.events, pinnedSourceCache);
1377
1474
  const verifyEvidence = evidenceVerifier(manifest, repoRoot, options.forWrite === true);
1378
1475
  const verifyImportSource = importSourceVerifier(repoRoot, options.forWrite === true, pinnedSourceCache);
1379
1476
  const tasks = replay(plan, journal.events, {
@@ -1471,7 +1568,13 @@ async function atomicWrite(absolute, bytes) {
1471
1568
  let handle;
1472
1569
  try {
1473
1570
  handle = await open(temporary, 'wx', 0o600);
1474
- await handle.writeFile(bytes); await handle.sync(); await handle.close(); handle = null;
1571
+ await handle.writeFile(bytes);
1572
+ // WSL2でのNTFSパス(/mnt/以下)ではfsyncがEIOを返すことがある。データ自体は書き込まれている。
1573
+ try { await handle.sync(); } catch (syncError) {
1574
+ if (!(process.platform === 'linux' && /^\/mnt\//.test(absolute) && syncError?.code === 'EIO')) throw syncError;
1575
+ process.stderr.write(`lattice: fsync skipped (WSL2/NTFS EIO) ${absolute}\n`);
1576
+ }
1577
+ await handle.close(); handle = null;
1475
1578
  await rename(temporary, absolute);
1476
1579
  const directory = await open(path.dirname(absolute), 'r');
1477
1580
  // Windowsはdirectory handleのfsyncを許さず常にEPERM/EINVALを返す(Node仕様)。
@@ -1493,7 +1596,11 @@ async function atomicWriteMode(absolute, bytes, mode) {
1493
1596
  try {
1494
1597
  handle = await open(temporary, 'wx', mode);
1495
1598
  await handle.writeFile(bytes); await handle.chmod(mode);
1496
- await handle.sync(); await handle.close(); handle = null;
1599
+ try { await handle.sync(); } catch (syncError) {
1600
+ if (!(process.platform === 'linux' && /^\/mnt\//.test(absolute) && syncError?.code === 'EIO')) throw syncError;
1601
+ process.stderr.write(`lattice: fsync skipped (WSL2/NTFS EIO) ${absolute}\n`);
1602
+ }
1603
+ await handle.close(); handle = null;
1497
1604
  await rename(temporary, absolute);
1498
1605
  await fsyncDirectory(path.dirname(absolute));
1499
1606
  } finally {
@@ -1549,23 +1656,20 @@ function processIsDead(pid) {
1549
1656
 
1550
1657
  async function acquireCrossPlanRecoveryClaim(recoveryClaimsRef) {
1551
1658
  await mkdir(recoveryClaimsRef, { recursive: true, mode: 0o700 });
1552
- const claimName = `${process.pid}-${randomBytes(8).toString('hex')}`;
1659
+ const claimOrder = process.hrtime.bigint().toString().padStart(20, '0');
1660
+ const claimName = `${claimOrder}-${process.pid}-${randomBytes(8).toString('hex')}`;
1553
1661
  const claimRef = path.join(recoveryClaimsRef, claimName);
1554
1662
  await mkdir(claimRef, { mode: 0o700 });
1555
1663
  for (const entry of await readdir(recoveryClaimsRef, { withFileTypes: true })) {
1556
1664
  if (!entry.isDirectory() || entry.name === claimName) continue;
1557
- const pid = Number(entry.name.split('-', 1)[0]);
1665
+ const parts = entry.name.split('-', 3);
1666
+ const pid = Number(parts.length === 2 ? parts[0] : parts[1]);
1558
1667
  if (Number.isSafeInteger(pid) && pid > 0 && processIsDead(pid)) {
1559
1668
  await rm(path.join(recoveryClaimsRef, entry.name), { recursive: true, force: true });
1560
1669
  }
1561
1670
  }
1562
- const candidates = await Promise.all((await readdir(recoveryClaimsRef)).map(async (name) => ({
1563
- name,
1564
- mtimeNs: (await lstat(path.join(recoveryClaimsRef, name), { bigint: true })).mtimeNs,
1565
- })));
1566
- candidates.sort((left, right) => left.mtimeNs < right.mtimeNs ? -1
1567
- : left.mtimeNs > right.mtimeNs ? 1 : left.name < right.name ? -1 : 1);
1568
- if (candidates[0]?.name === claimName) return claimRef;
1671
+ const candidates = (await readdir(recoveryClaimsRef)).sort();
1672
+ if (candidates[0] === claimName) return claimRef;
1569
1673
  await rmdir(claimRef);
1570
1674
  return null;
1571
1675
  }
@@ -1857,10 +1961,13 @@ export async function appendTodoEvent(options = {}) {
1857
1961
  }
1858
1962
  // Validate the prospective history, including hard evidence and transitions, before any write.
1859
1963
  validateMergedTransition(store, member, event);
1964
+ const appendSourceCache = { commits: new Set(), blobs: new Map() };
1965
+ prefetchVerificationObjects(store.manifest, repoRoot,
1966
+ [...member.journal.events, event], appendSourceCache);
1860
1967
  replay(member.plan, [...member.journal.events, event], {
1861
1968
  now: options.now ? new Date(options.now) : new Date(),
1862
1969
  verifyEvidence: evidenceVerifier(store.manifest, repoRoot, true),
1863
- verifyImportSource: importSourceVerifier(repoRoot, true),
1970
+ verifyImportSource: importSourceVerifier(repoRoot, true, appendSourceCache),
1864
1971
  });
1865
1972
  const eventBytes = canonicalLine(event);
1866
1973
  const activeRef = member.descriptor.journal_ref;
@@ -1879,7 +1986,7 @@ export async function appendTodoEvent(options = {}) {
1879
1986
  const tasks = replay(member.plan, [...member.journal.events, event], {
1880
1987
  now: options.now ? new Date(options.now) : new Date(),
1881
1988
  verifyEvidence: evidenceVerifier(store.manifest, repoRoot, false),
1882
- verifyImportSource: importSourceVerifier(repoRoot, false),
1989
+ verifyImportSource: importSourceVerifier(repoRoot, false, appendSourceCache),
1883
1990
  });
1884
1991
  const snapshot = snapshotFor(member.plan, [...member.journal.events, event], tasks);
1885
1992
  await atomicWrite(path.resolve(repoRoot, member.descriptor.snapshot_ref), canonicalLine(snapshot));
@@ -2059,14 +2166,15 @@ function historicalImportInputs(value, timestampKey) {
2059
2166
  }
2060
2167
 
2061
2168
  function prepareImportedArtifacts(repoRoot, options, projectId, memberPlans) {
2169
+ const importSourceCache = { commits: new Set(), blobs: new Map() };
2062
2170
  const planInput = materializeImportedNarrativeAnchors(
2063
- repoRoot, options.plan, options.narrativeAnchorSources,
2171
+ repoRoot, options.plan, options.narrativeAnchorSources, importSourceCache,
2064
2172
  );
2065
2173
  const plan = buildTodoPlan(planInput);
2066
2174
  if (plan.project_id !== projectId || plan.predecessor_plan_digest !== null) {
2067
2175
  throw new TypeError('imported plan must be a project-local genesis plan');
2068
2176
  }
2069
- verifyPlanNarrativeAnchors(repoRoot, plan);
2177
+ verifyPlanNarrativeAnchors(repoRoot, plan, null, importSourceCache);
2070
2178
  const genesis = buildPlanGenesis(plan, { ...options.genesis, historical_import: true });
2071
2179
  const events = [genesis];
2072
2180
  const inProgressTasks = options.inProgressTasks ?? [];
@@ -2084,7 +2192,7 @@ function prepareImportedArtifacts(repoRoot, options, projectId, memberPlans) {
2084
2192
  }
2085
2193
  for (const input of inProgressTasks) events.push(buildHistoricalStart(plan, events.at(-1), input, genesis));
2086
2194
  for (const input of completedTasks) events.push(buildHistoricalDone(plan, events.at(-1), input, genesis));
2087
- const verifyImportSource = importSourceVerifier(repoRoot, true);
2195
+ const verifyImportSource = importSourceVerifier(repoRoot, true, importSourceCache);
2088
2196
  const tasks = replay(plan, events, {
2089
2197
  now: options.now ? new Date(options.now) : new Date(), verifyImportSource,
2090
2198
  });