@felixdotgo/aidlc-kit 0.2.0 → 0.3.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 (109) hide show
  1. package/DEVELOPMENT.md +13 -26
  2. package/README.md +35 -39
  3. package/dist/assets/.agents/aidlc/orchestrator.md +1 -1
  4. package/dist/assets/.agents/aidlc/schemas/state.schema.json +2 -0
  5. package/dist/assets/.agents/aidlc/scripts/lib/assignment-store.mjs +94 -48
  6. package/dist/assets/.agents/aidlc/scripts/lib/context-runtime.mjs +5 -4
  7. package/dist/assets/.agents/aidlc/scripts/lib/loop-authority.mjs +21 -5
  8. package/dist/assets/.agents/aidlc/scripts/lib/loop-daemon.mjs +52 -78
  9. package/dist/assets/.agents/aidlc/scripts/lib/loop-drivers.mjs +88 -14
  10. package/dist/assets/.agents/aidlc/scripts/lib/loop-env.mjs +279 -0
  11. package/dist/assets/.agents/aidlc/scripts/lib/loop-fleet-cli.mjs +20 -5
  12. package/dist/assets/.agents/aidlc/scripts/lib/loop-fleet-remote.mjs +10 -1
  13. package/dist/assets/.agents/aidlc/scripts/lib/loop-fleet-supervisor.mjs +2 -1
  14. package/dist/assets/.agents/aidlc/scripts/lib/loop-fleet.mjs +3 -2
  15. package/dist/assets/.agents/aidlc/scripts/lib/loop-git.mjs +9 -3
  16. package/dist/assets/.agents/aidlc/scripts/lib/loop-holder.mjs +376 -0
  17. package/dist/assets/.agents/aidlc/scripts/lib/loop-intake.mjs +11 -3
  18. package/dist/assets/.agents/aidlc/scripts/lib/loop-lease.mjs +67 -61
  19. package/dist/assets/.agents/aidlc/scripts/lib/loop-leases.mjs +268 -0
  20. package/dist/assets/.agents/aidlc/scripts/lib/loop-local-workers.mjs +78 -17
  21. package/dist/assets/.agents/aidlc/scripts/lib/loop-runtime.mjs +42 -10
  22. package/dist/assets/.agents/aidlc/scripts/lib/loop-stream.mjs +66 -9
  23. package/dist/assets/.agents/aidlc/scripts/lib/loop-supervisor-local.mjs +16 -56
  24. package/dist/assets/.agents/aidlc/scripts/lib/loop-supervisor-run-local.mjs +12 -6
  25. package/dist/assets/.agents/aidlc/scripts/lib/loop-worker-docker.mjs +14 -2
  26. package/dist/assets/.agents/aidlc/scripts/lib/loop-worker-log.mjs +158 -0
  27. package/dist/assets/.agents/aidlc/scripts/lib/repo-head.mjs +22 -0
  28. package/dist/assets/.agents/aidlc/scripts/lib/store.mjs +11 -0
  29. package/dist/assets/.agents/aidlc/scripts/lib/task-runtime-migrate.mjs +75 -0
  30. package/dist/assets/.agents/aidlc/scripts/lib/task-runtime.mjs +97 -0
  31. package/dist/assets/.agents/aidlc/scripts/loop-entry.mjs +87 -4
  32. package/dist/assets/.agents/aidlc/scripts/loop.cmd +101 -0
  33. package/dist/assets/.agents/aidlc/scripts/loop.sh +71 -0
  34. package/dist/assets/.agents/aidlc/scripts/remote-state.mjs +10 -4
  35. package/dist/assets/.agents/aidlc/scripts/state.mjs +16 -8
  36. package/dist/assets/ui/changes.js +83 -0
  37. package/dist/assets/ui/common.js +664 -0
  38. package/dist/assets/ui/dashboard.css +281 -59
  39. package/dist/assets/ui/dashboard.js +77 -0
  40. package/dist/assets/ui/detail.js +159 -0
  41. package/dist/assets/ui/live-log.js +332 -0
  42. package/dist/services/aidlc-coordination/package.json +1 -1
  43. package/dist/services/aidlc-coordination/src/lifecycle-core.mjs +11 -0
  44. package/dist/src/cli/argv.js +10 -4
  45. package/dist/src/cli/commands.js +52 -22
  46. package/dist/src/cli/loop-launcher.d.ts +47 -0
  47. package/dist/src/cli/loop-launcher.js +322 -0
  48. package/dist/src/cli.js +3 -0
  49. package/dist/src/installer.js +4 -2
  50. package/dist/src/model/records.d.ts +4 -0
  51. package/dist/src/state/validate.js +5 -0
  52. package/dist/src/task-ui.d.ts +31 -52
  53. package/dist/src/task-ui.js +25 -1562
  54. package/dist/src/ui/assets.d.ts +14 -0
  55. package/dist/src/ui/assets.js +45 -0
  56. package/dist/src/ui/changes.d.ts +83 -0
  57. package/dist/src/ui/changes.js +498 -0
  58. package/dist/src/ui/containers.d.ts +71 -0
  59. package/dist/src/ui/containers.js +214 -0
  60. package/dist/src/ui/daemon.d.ts +75 -0
  61. package/dist/src/ui/daemon.js +232 -0
  62. package/dist/src/ui/data.d.ts +42 -0
  63. package/dist/src/ui/data.js +89 -0
  64. package/dist/src/ui/docs.d.ts +13 -0
  65. package/dist/src/ui/docs.js +39 -0
  66. package/dist/src/ui/files.d.ts +3 -0
  67. package/dist/src/ui/files.js +20 -0
  68. package/dist/src/ui/http.d.ts +14 -0
  69. package/dist/src/ui/http.js +80 -0
  70. package/dist/src/ui/live.d.ts +5 -0
  71. package/dist/src/ui/live.js +43 -0
  72. package/dist/src/ui/markdown.d.ts +2 -0
  73. package/dist/src/ui/markdown.js +127 -0
  74. package/dist/src/ui/options.d.ts +48 -0
  75. package/dist/src/ui/options.js +81 -0
  76. package/dist/src/ui/security.d.ts +34 -0
  77. package/dist/src/ui/security.js +38 -0
  78. package/dist/src/ui/server.d.ts +39 -0
  79. package/dist/src/ui/server.js +347 -0
  80. package/dist/src/ui/sse.d.ts +14 -0
  81. package/dist/src/ui/sse.js +281 -0
  82. package/dist/src/ui/views/board.d.ts +7 -0
  83. package/dist/src/ui/views/board.js +131 -0
  84. package/dist/src/ui/views/changes.d.ts +19 -0
  85. package/dist/src/ui/views/changes.js +154 -0
  86. package/dist/src/ui/views/components.d.ts +56 -0
  87. package/dist/src/ui/views/components.js +113 -0
  88. package/dist/src/ui/views/docs.d.ts +15 -0
  89. package/dist/src/ui/views/docs.js +64 -0
  90. package/dist/src/ui/views/layout.d.ts +44 -0
  91. package/dist/src/ui/views/layout.js +233 -0
  92. package/dist/src/ui/views/task.d.ts +25 -0
  93. package/dist/src/ui/views/task.js +320 -0
  94. package/dist/src/upgrade.js +5 -2
  95. package/dist/src/workflow.d.ts +4 -0
  96. package/dist/src/workflow.js +33 -1
  97. package/docs/README.md +48 -33
  98. package/docs/architecture.md +101 -38
  99. package/docs/command-reference.md +269 -93
  100. package/docs/configuration.md +62 -42
  101. package/docs/coordination-service.md +85 -36
  102. package/docs/design-decisions.md +35 -31
  103. package/docs/development.md +111 -61
  104. package/docs/loop-local-run.md +135 -402
  105. package/docs/loop-runbook.md +84 -105
  106. package/docs/operating-workflow.md +55 -42
  107. package/docs/testing-and-release.md +70 -51
  108. package/docs/troubleshooting.md +165 -69
  109. package/package.json +1 -1
@@ -1,6 +1,6 @@
1
1
  # Configuration
2
2
 
3
- Each installed project owns its workflow configuration at `.agents/config.json`. It selects topology profiles, specification roots, safe verification commands, and local rules without changing the package-managed kernel. The package supports Codex, Claude Code, and OpenCode.
3
+ Each installed project owns `.agents/config.json`: topology profiles, specification roots, verification commands, local rules, and the optional `mcp` and `loop.*` settings. CLI flags and environment variables: [Command reference](./command-reference.md); loop setup: [Loop setup runbook](./loop-runbook.md).
4
4
 
5
5
  ## Minimal configuration
6
6
 
@@ -20,44 +20,26 @@ Each installed project owns its workflow configuration at `.agents/config.json`.
20
20
  }
21
21
  ```
22
22
 
23
- Commands are stored as executable and argument arrays. Do not replace them with shell strings: this keeps execution inspectable and avoids accidental shell interpretation.
23
+ - Commands are executable and argument arrays, never shell strings.
24
+ - G0, G1, and G2 always require explicit human approval; configuration cannot auto-pass a gate.
24
25
 
25
26
  ## Optional `$schema` key
26
27
 
27
- The generated `.agents/config.json` includes an optional top-level `$schema` key pointing at the relative path `./aidlc/schemas/config.schema.json` (the schema installed under `.agents/aidlc/schemas/`). Editors use it for autocompletion and validation of the configuration file, and it has no runtime effect. New installs receive it automatically through `init`; existing installs can add it manually:
28
-
29
- ```json
30
- {
31
- "$schema": "./aidlc/schemas/config.schema.json"
32
- }
33
- ```
34
-
35
- The value must be a string of at most 512 characters. The path is relative so validation works offline without a network dependency.
36
-
37
- G0, G1, and G2 always require explicit human approval. Configuration cannot auto-pass a gate.
28
+ - `init` writes a top-level `$schema` of `./aidlc/schemas/config.schema.json` (installed under `.agents/aidlc/schemas/`); existing installs can add it by hand.
29
+ - Editors use it for autocompletion and validation; it has no runtime effect.
30
+ - Must be a string of at most 512 characters; the relative path keeps validation offline.
38
31
 
39
32
  ## Configuration layers
40
33
 
41
- Values resolve in this order, with the later applicable layer taking precedence:
34
+ Later applicable layers take precedence:
42
35
 
43
36
  ```text
44
37
  kernel → built-in topology → local profile parents/child → project config/rules → approved task decisions
45
38
  ```
46
39
 
47
- Built-in topology profiles are:
48
-
49
- - `topology/generic`
50
- - `topology/single`
51
- - `topology/workspace`
52
- - `topology/git-submodules`
53
-
54
- Arrays are stable-deduplicated. Command keys are last-wins, so a project can replace a profile command intentionally. Validate the resolved profile chain with:
55
-
56
- ```sh
57
- npx --package @felixdotgo/aidlc-kit@0.2.0 aidlc-kit profile validate .
58
- ```
59
-
60
- This command uses the npm package at version `0.2.0`. To run from a source checkout instead, see the [development guide](./development.md).
40
+ - Built-in topology profiles: `topology/generic`, `topology/single`, `topology/workspace`, `topology/git-submodules`.
41
+ - Arrays are stable-deduplicated; command keys are last-wins, so a project can replace a profile command.
42
+ - Validate the resolved chain with `npx --package @felixdotgo/aidlc-kit@0.3.0 aidlc-kit profile validate .` (source checkout: [development guide](./development.md)).
61
43
 
62
44
  ## Rules, project data, and managed assets
63
45
 
@@ -67,30 +49,37 @@ This command uses the npm package at version `0.2.0`. To run from a source check
67
49
  | `.agents/data/` | Project owned | Lifecycle state, task artifacts, indexes, memory, and evidence. |
68
50
  | `.agents/project/` | Project owned | Profiles, rules, and project-specific evaluation material. |
69
51
 
70
- Keep business/domain rules in `.agents/project/rules/`. Do not edit managed kernel files to customise a project; a later installation or human-run upgrade may report conflicts for modified managed content.
52
+ Keep business/domain rules in `.agents/project/rules/`. Never edit managed kernel files; a later install or upgrade may report conflicts for modified managed content.
71
53
 
72
54
  ## Agent permissions
73
55
 
74
- The Codex adapter installs `.codex/config.toml` with `approval_policy = "on-request"` and `sandbox_mode = "workspace-write"`, plus `.codex/rules/aidlc.rules`. The execpolicy allowlists only the six lifecycle entry points under `node .agents/aidlc/scripts/`, allowing AI-DLC state transitions without granting general command approval. Each allowed lifecycle command must be run as its own exec invocation: joining commands with `&`, `&&`, `;`, or `|` creates shell command segments that are not covered by a single prefix rule. Codex loads this project config only after the user trusts the project; network access stays disabled unless the project separately enables it. Claude Code installs `.claude/settings.local.json` and phase skills that allow only `node .agents/aidlc/scripts/*`, so lifecycle state updates do not require repeated confirmation while general Bash commands remain unapproved.
56
+ Each adapter grants only what the lifecycle needs; general commands stay unapproved.
75
57
 
76
- The OpenCode adapter installs only an owner-scoped managed block in `AGENTS.md` and reads `.opencode/` solely to detect the adapter. It writes no OpenCode configuration, MCP, or permission file, so user and project OpenCode settings are not overridden. Codex and OpenCode blocks are independently owned and merge in the same `AGENTS.md` when both adapters are installed.
58
+ | Adapter | Installed files | What is allowed | Notes |
59
+ | --- | --- | --- | --- |
60
+ | Codex | A managed block in `AGENTS.md`, `.codex/config.toml` (`approval_policy = "on-request"`, `sandbox_mode = "workspace-write"`), and `.codex/rules/aidlc.rules` | The execpolicy allowlists only the seven lifecycle entry points under `node .agents/aidlc/scripts/` (`state`, `task-next`, `context`, `gate-check`, `gate-view`, `render`, `assignment`). | Loaded only after the user trusts the project; network access stays disabled unless the project enables it. |
61
+ | Claude Code | A managed block in `CLAUDE.md`, `.claude/settings.local.json`, and phase skills | Only `node .agents/aidlc/scripts/*`. | Other Bash commands stay unapproved. |
62
+ | OpenCode | An owner-scoped managed block in `AGENTS.md` only | Nothing: no OpenCode configuration, MCP, or permission file is written. | Reads `.opencode/` only to detect the adapter; OpenCode settings are never overridden. |
77
63
 
78
- ## Context and risk
64
+ - Codex allowlist rule: run each lifecycle command as its own exec invocation. Commands joined with `&`, `&&`, `;`, or `|` create shell segments that a single prefix rule does not cover.
65
+ - Codex and OpenCode blocks are independently owned and merge in the same `AGENTS.md` when both are installed.
66
+ - `init --force --yes` is only for an intentional initial install that replaces an unmanaged conflicting file, never an upgrade ([Command reference](./command-reference.md#init)).
79
67
 
80
- `context.maxChars` bounds a phase packet. The runtime preserves lifecycle instructions, approved decisions, and mandatory invariants; it fails clearly rather than silently truncating required contract content.
68
+ ## Context and risk
81
69
 
82
- Set the default risk level deliberately. Planning, migrations, cross-service contracts, and security-sensitive changes deserve a stronger reviewer or an explicit human decision even when the configuration default is normal.
70
+ - `context.maxChars` bounds a phase packet. The runtime preserves lifecycle instructions, approved decisions, and mandatory invariants, and fails clearly rather than silently truncating them.
71
+ - Set `risk.default` deliberately: planning, migrations, cross-service contracts, and security-sensitive changes deserve a stronger reviewer or an explicit human decision even at `normal`.
83
72
 
84
73
  ## Optional AI-DLC Coordination
85
74
 
86
- `mcp` is absent (or `{ "enabled": false }`) by default. An enabled configuration requires an HTTP(S) `endpoint` and non-empty `workspace`; it may also name a token environment variable, polling interval from 1,000 to 3,600,000 ms, and the enabled Jira/Trello/GitHub Issues preflight providers. See [AI-DLC Coordination service](./coordination-service.md) for the remote-authority and deployment contract. The coordination service (and fleet mode) is **experimental and still under active development** (known bugs included), and it can change incompatibly in any release before `1.0.0`.
75
+ `mcp` is absent (or `{ "enabled": false }`) by default. Enabled, it requires an HTTP(S) `endpoint` and non-empty `workspace`, and may name a token environment variable, a polling interval from 1,000 to 3,600,000 ms, and the enabled Jira/Trello/GitHub Issues preflight providers. Experimental ([Status and stability](./loop-runbook.md#status-and-stability)); contract in [AI-DLC Coordination service](./coordination-service.md).
87
76
 
88
77
  ## Optional loop configuration
89
78
 
90
79
  > [!WARNING]
91
- > The loop is **experimental** and pre-1.0 (`0.x`): the `loop.*` fields, provider mappings, and environment variables can change incompatibly in any release before `1.0.0`. **Local mode is usable** today; **fleet mode is still under active development**.
80
+ > Experimental, pre-1.0. See [Status and stability](./loop-runbook.md#status-and-stability).
92
81
 
93
- `loop.mode` can be `off`, `local`, or `fleet`; an absent `loop` field means `off`. Both enabled modes run the loop as the same persistent multi-task daemon (one per project, enforced by a lock): `loop start` launches it, and it polls the configured authority — canonical local state, or the coordination service in `fleet` mode — auto-picking every actionable **loop-owned** task without prompting for a requirement. A task is loop-owned when it was created through loop intake (the web dashboard **New task**, `aidlc-kit loop intake`, or `loop-entry.mjs intake`) or, locally, already has a loop run record; fleet ownership is recorded by the intake file `.agents/data/tasks/<id>/loop-intake.json` with `authority: "fleet"`. Tasks created by an interactive lead are never picked up. The optional `loop.agents` maps one lead and named workers to `codex`, `claude`, or `opencode`, independently of `mcp.providers`:
82
+ The `loop` object selects the loop mode, agents, and limits, set through `init`, the human-run upgrade preview, or a deliberate edit. It installs and starts nothing, authorizes no upgrade, grants no gate approval, and holds no provider credentials.
94
83
 
95
84
  ```json
96
85
  {
@@ -103,12 +92,43 @@ Set the default risk level deliberately. Planning, migrations, cross-service con
103
92
  }
104
93
  ```
105
94
 
106
- The optional `loop.pollMs` (integer, `1000`–`300000`, default `5000`) sets the daemon's poll interval. The optional `loop.limits` bounds each supervised lead run: `maxTurns` (`1`–`1000`, default `12`) is the turn budget of **one run** — a resumed run starts a fresh budget, so a long multi-gate task is not permanently re-paused — `turnTimeoutMs` (`1`–`3600000`, default `600000`) caps a single agent turn, and `maxStallTurns` (`1`–`20`, default `2`) pauses after that many consecutive turns without observable progress. `loop.pollMs` and `loop.limits` apply to `fleet` mode as well: `loop start` passes them to the fleet supervisor as `AIDLC_FLEET_MAX_TURNS`, `AIDLC_FLEET_TURN_TIMEOUT_MS`, `AIDLC_FLEET_MAX_STALL_TURNS`, and `AIDLC_FLEET_POLL_MS`, and an explicitly exported value of any of those variables overrides the config.
95
+ ### Mode and agents
96
+
97
+ - `loop.mode` is `off`, `local`, or `fleet`; an absent `loop` means `off`. Selecting a mode starts nothing: run `./.agents/aidlc/scripts/loop.sh up` (or `aidlc-kit loop start`).
98
+ - Both enabled modes run one lock-enforced multi-task daemon per project. It polls its authority — canonical local state, or the coordination service in `fleet` mode — and auto-picks every actionable **loop-owned** task without prompting for a requirement.
99
+ - A task is loop-owned when created through loop intake (dashboard **New task**, `aidlc-kit loop intake`, `loop-entry.mjs intake`) or, locally, when it has a loop run record; fleet ownership is `.agents/data/tasks/<id>/runtime/intake.json` with `authority: "fleet"`. Interactive-lead tasks are never picked up.
100
+ - `loop.agents` maps one lead and named workers to `codex`, `claude`, or `opencode`, independently of `mcp.providers`.
101
+
102
+ ### Polling and limits
103
+
104
+ | Field | Range | Default | Meaning |
105
+ | --- | --- | --- | --- |
106
+ | `loop.pollMs` | integer `1000`–`300000` | `5000` | Daemon poll interval. |
107
+ | `loop.limits.maxTurns` | `1`–`1000` | `12` | Turn budget of **one supervised lead run**; a resumed run starts a fresh budget. |
108
+ | `loop.limits.turnTimeoutMs` | `1`–`3600000` | `600000` | Caps a single agent turn. |
109
+ | `loop.limits.maxStallTurns` | `1`–`20` | `2` | Pauses after that many consecutive turns without observable progress. |
110
+
111
+ In `fleet` mode these become `AIDLC_FLEET_MAX_TURNS`, `AIDLC_FLEET_TURN_TIMEOUT_MS`, `AIDLC_FLEET_MAX_STALL_TURNS`, and `AIDLC_FLEET_POLL_MS`; an exported value overrides the config.
112
+
113
+ ### Worker image
114
+
115
+ `loop.workerImage` maps a provider to a worker Docker image tag (for example `{ "opencode": "aidlc-worker-opencode:local" }`), recorded by `aidlc-kit loop worker-image`. Local dispatch and fleet workers fall back to `aidlc-worker-<provider>:local`, auto-built from the packaged Dockerfile when missing; `AIDLC_WORKER_DOCKER_IMAGE` overrides it.
116
+
117
+ ### Provider credentials and OpenCode
118
+
119
+ Provider credentials go in the git-ignored `.agents/loop.env` (or the shell), never in this file; env-file and auto-detection rules are in [Command reference](./command-reference.md#loop).
120
+
121
+ - Any `opencode` provider needs `AIDLC_OPENCODE_MODEL` as `provider/model[#variant]` (for example `opencode-go/gpt-6-luna`) in `.agents/loop.env`, the shell, or `"model"` in `opencode.json` (detected locally). Without a valid value the provider turn fails closed and `loop start`/`loop up` stop before the daemon starts.
122
+ - **Isolated lead.** Rejects OpenCode with `OpenCode workers require file-based credentials and are not available in isolated containers yet`; no host fallback.
123
+ - **Docker workers.** Local worker batches and fleet worker containers accept OpenCode, authenticating from the key matching the model prefix (`openai/` → `OPENAI_API_KEY`, `anthropic/` → `ANTHROPIC_API_KEY`, otherwise `OPENCODE_API_KEY`) and redirecting OpenCode's XDG data/config roots into the worker's ephemeral HOME.
107
124
 
108
- The optional `loop.workerImage` maps a provider to a worker Docker image tag (for example `{ "opencode": "aidlc-worker-opencode:local" }`). `aidlc-kit loop worker-image` records it; local build dispatch and fleet worker machines use it, falling back to `aidlc-worker-<provider>:local`, which is auto-built from the packaged Dockerfile when missing. `AIDLC_WORKER_DOCKER_IMAGE` overrides it.
125
+ ### Parallel work partition
109
126
 
110
- With `loop.mode` set to `local` or `fleet`, the Approve plan gate check enforces a parallel work partition: every open build item needs owned paths (`PARTITION_SCOPE`), and items that can run concurrently must not own overlapping paths (`PARTITION_OVERLAP`). With the loop `off` the same findings are warnings and the gate still passes. See [Troubleshooting](./troubleshooting.md#parallel-work-partition-partition_overlap--partition_scope).
127
+ With `loop.mode` `local` or `fleet`, the Approve plan gate check requires owned paths on every open build item (`PARTITION_SCOPE`) and no overlapping paths between items that can run concurrently (`PARTITION_OVERLAP`). With the loop `off` both are warnings and the gate passes; see [Troubleshooting](./troubleshooting.md#parallel-work-partition-partition_overlap--partition_scope).
111
128
 
112
- When any selected provider is `opencode`, set `AIDLC_OPENCODE_MODEL` to a `provider/model[#variant]` value such as `opencode-go/gpt-6-luna`. The provider turn fails closed without a valid value. The container-isolated lead rejects OpenCode with `OpenCode workers require file-based credentials and are not available in isolated containers yet`; it does not fall back to the host. Local Docker worker batches and fleet worker containers accept OpenCode: they authenticate from the environment key matching the model prefix (`openai/` → `OPENAI_API_KEY`, `anthropic/` → `ANTHROPIC_API_KEY`, otherwise `OPENCODE_API_KEY`) and redirect OpenCode's XDG data/config roots into the worker's ephemeral HOME.
129
+ ## Related
113
130
 
114
- Configure this field through the init or human-run upgrade preview, or edit the project-owned config deliberately. Provider credentials do not belong in this object. The configuration is an opt-in selection; it does not install or start a service, authorize an upgrade, or grant gate approval. Selecting a mode alone does not start the loop — run `aidlc-kit loop start` to launch the daemon. Existing fleet operations and storage requirements are documented in [Coordination service](./coordination-service.md).
131
+ - [Loop setup runbook](./loop-runbook.md) — loop setup.
132
+ - [Command reference](./command-reference.md#loop) — loop actions and environment.
133
+ - [AI-DLC Coordination service](./coordination-service.md) — fleet operations.
134
+ - [Operating the workflow](./operating-workflow.md) — lifecycle and gates.
@@ -1,52 +1,72 @@
1
1
  # AI-DLC Coordination service
2
2
 
3
- > [!WARNING]
4
- > The coordination service and the **fleet** end-to-end loop are **experimental and still under active development**: they contain known bugs and rough edges. The package is pre-1.0 (`0.x`), so setup flags, `mcp.*`/`loop.*` configuration, endpoints, environment variables, and storage behaviour can change incompatibly in any release before `1.0.0`. Do not treat it as a stable dependency yet.
3
+ The optional, Docker-deployable authority for a workflow workspace: remote canonical state for a team, or the fleet loop across machines ([runbook](./loop-runbook.md#fleet-multi-machine)). Disabled by default: without an enabled `mcp` block, installed lifecycle scripts stay local and make no network calls.
5
4
 
6
- The optional AI-DLC Coordination service is a separate Docker-deployable authority for a workflow workspace. It is disabled by default: without an enabled `mcp` block, installed lifecycle scripts remain local and make no network calls.
5
+ > [!WARNING]
6
+ > Experimental, pre-1.0. See [Status and stability](./loop-runbook.md#status-and-stability).
7
7
 
8
8
  ## Safe setup
9
9
 
10
- Preview a configuration first. Outside an interactive terminal, provide all four selection flags.
10
+ Preview first; outside an interactive terminal, provide all four selection flags:
11
11
 
12
12
  ```sh
13
- npx --package @felixdotgo/aidlc-kit@0.2.0 aidlc-kit mcp setup . --dry-run \
13
+ npx --package @felixdotgo/aidlc-kit@0.3.0 aidlc-kit mcp setup . --dry-run \
14
14
  --deployment docker --storage sqlite --bind localhost --workspace engineering
15
15
  ```
16
16
 
17
- This command uses the npm package at version `0.2.0`. To run from a source checkout instead, see the [development guide](./development.md).
18
-
19
- Remove `--dry-run` and confirm to create `.agents/aidlc-coordination/` and update `.agents/config.json`. Setup does not start Docker. Copy `.env.example` to a secret-managed `.env`, review it, then start explicitly:
17
+ Remove `--dry-run` and confirm to create `.agents/aidlc-coordination/` and update `.agents/config.json`. Setup does not start Docker. Copy `.env.example` to a secret-managed `.env`, review it, then start:
20
18
 
21
19
  ```sh
22
20
  docker compose --env-file .agents/aidlc-coordination/.env -f .agents/aidlc-coordination/compose.yaml up -d
23
21
  ```
24
22
 
25
- Use SQLite only on the local Docker volume. Use PostgreSQL for multiple hosts. A network bind requires `MCP_STATE_AUTH_TOKEN` and TLS/reverse-proxy termination; never commit either the `.env` file or provider credentials.
23
+ - SQLite only on the local Docker volume; PostgreSQL for multiple hosts.
24
+ - A network bind requires `MCP_STATE_AUTH_TOKEN` and TLS/reverse-proxy termination. Never commit `.env` or provider credentials.
25
+ - Source checkout: [development guide](./development.md).
26
26
 
27
27
  ## State and synchronization contract
28
28
 
29
- When enabled, the remote workspace is canonical. Import/export is an explicit recovery or migration action; there is no background dual-write or local fallback during an outage. Clients pull a revision or poll `state_events_since`; the service provides neither realtime subscriptions nor push notifications.
29
+ - When enabled, the remote workspace is canonical. Import/export is an explicit recovery or migration action; there is no background dual-write or local fallback during an outage.
30
+ - Clients pull a revision or poll `state_events_since`; there are no realtime subscriptions or push notifications.
30
31
 
31
32
  ## Multi-machine loop deployment
32
33
 
33
- The fleet service supports both approved storage backends: SQLite through one coordination service on one host with the database on a local volume (set `MCP_STATE_DATABASE=sqlite`, the default, and `MCP_STATE_SQLITE_PATH`), and PostgreSQL for deployments spanning multiple hosts or service instances. Each deployment keeps its chosen backend; there is no implicit fallback or migration between them. SQLite limits availability and write concurrency to that single host. Do not switch an existing authority automatically; follow the supported deployment below.
34
+ Storage backends, with no implicit fallback or migration between them (never switch an existing authority automatically):
34
35
 
35
- Fleet mode is separate from the default local service profile. The source checkout keeps its deployment template in `services/aidlc-coordination/`; human-run `mcp setup` copies it into the target project's `.agents/aidlc-coordination/`. Copy that directory's `.env.fleet.example` to a private `.env`, set the database choice and three distinct credential classes (`MCP_FLEET_LEAD_TOKEN`, `MCP_FLEET_REVIEWER_TOKEN`, and the worker-ID-to-token JSON map `MCP_FLEET_WORKER_TOKENS`), then start the fleet profile. From a source checkout:
36
+ - **SQLite**: one service on one host, database on a local volume (`MCP_STATE_DATABASE=sqlite`, the default, plus `MCP_STATE_SQLITE_PATH`). Availability and write concurrency are limited to that host.
37
+ - **PostgreSQL**: multiple hosts or service instances.
38
+
39
+ Fleet mode is a separate compose profile. The source checkout keeps the template in `services/aidlc-coordination/`; human-run `mcp setup` copies it to `.agents/aidlc-coordination/`. Copy `.env.fleet.example` to a private `.env`, set the database and three distinct credential classes (`MCP_FLEET_LEAD_TOKEN`, `MCP_FLEET_REVIEWER_TOKEN`, and the worker-ID-to-token JSON map `MCP_FLEET_WORKER_TOKENS`), then start:
36
40
 
37
41
  ```sh
38
42
  docker compose --env-file .env --profile fleet -f services/aidlc-coordination/compose.yaml up -d fleet-coordination
43
+ # installed project: -f .agents/aidlc-coordination/compose.yaml
39
44
  ```
40
45
 
41
- For an installed project, use `-f .agents/aidlc-coordination/compose.yaml` instead.
46
+ - Default binding `127.0.0.1:8788`; for workers on other machines, expose `POST /mcp` through a TLS reverse proxy and set the binding deliberately.
47
+ - Requires the chosen database backend and a shared Git remote. Each worker gets an isolated worktree, a scoped branch, and its own credential.
48
+ - A process supervisor must restart the supervisor, lead, and workers; after restart they reacquire fenced leases and reconcile reported or uncertain Git results before retrying.
49
+
50
+ **Vocabulary.** Three kinds of fenced ownership, each with its own epoch counter:
51
+
52
+ - **supervisor lease** (`supervisor_*`): one `loop start` supervising a task;
53
+ - **lead lease** (`run_claim`): the lead epoch every lifecycle mutation must present;
54
+ - **job claims** (`job_*`): a worker's ownership of one item, with a job epoch.
42
55
 
43
- The default host binding is `127.0.0.1:8788`. For workers on other machines, expose `POST /mcp` through a TLS reverse proxy and set the binding deliberately. Fleet mode requires the chosen database backend and a shared Git remote. Give each worker an isolated worktree, a scoped branch, and its own worker credential. A process supervisor must restart the supervisor, lead, and workers; after restart they reacquire fenced leases and reconcile reported or uncertain Git results before retrying.
56
+ `aidlc-kit loop leases` shows the supervisor epoch only when it differs from the lead epoch. A `FleetConflict` (for example `stale or expired lead claim`) is reported as `Fleet conflict: <explanation>. Inspect: node .agents/aidlc/scripts/loop-entry.mjs leases (<original message>)`. Local leases: [Troubleshooting](./troubleshooting.md#loop-leases-locks-and-claims).
44
57
 
45
- The lead credential can advance lifecycle state only while holding the current task epoch. Worker credentials can claim, checkpoint, and report assigned jobs but cannot mutate lifecycle state or approve gates. The reviewer credential can read pending gate snapshots and record a later explicit human approval against the snapshot and current revision. When the database, Git remote, or reviewer is unavailable, the run waits or fails closed; it does not fall back to local state.
58
+ **Roles.**
46
59
 
47
- The installed remote lifecycle client needs `AIDLC_FLEET_URL` (the full HTTPS `/mcp` URL), `AIDLC_FLEET_WORKSPACE`, and `AIDLC_FLEET_PROJECT_ROOT` (absolute). Lead mutations also need `AIDLC_FLEET_TOKEN` and the current `AIDLC_FLEET_LEAD_EPOCH` from its claim.
60
+ - Lead credential: advances lifecycle state only while holding the current task epoch.
61
+ - Worker credentials: claim, checkpoint, and report assigned jobs; never mutate lifecycle state or approve gates.
62
+ - Reviewer credential: reads pending gate snapshots and records a later explicit human approval against the snapshot and current revision.
63
+ - When the database, Git remote, or reviewer is unavailable, the run waits or fails closed; never a local fallback.
48
64
 
49
- Create the task records before the remote feature manifest. For each task, claim a lead epoch, export the returned `epoch`, then create it with the feature ID; repeat for sibling tasks. Once both records exist, create and link the manifest:
65
+ The remote lifecycle client needs `AIDLC_FLEET_URL` (full HTTPS `/mcp` URL), `AIDLC_FLEET_WORKSPACE`, and `AIDLC_FLEET_PROJECT_ROOT` (absolute); lead mutations also `AIDLC_FLEET_TOKEN` and the current `AIDLC_FLEET_LEAD_EPOCH` from its claim.
66
+
67
+ ### Create tasks and a feature manifest
68
+
69
+ Create task records before the manifest: claim a lead epoch, export it, create the task with the feature ID, release; repeat per task; then create and link the manifest:
50
70
 
51
71
  ```sh
52
72
  node .agents/aidlc/scripts/remote-state.mjs run claim checkout-api
@@ -59,52 +79,81 @@ node .agents/aidlc/scripts/remote-state.mjs feature link checkout checkout-integ
59
79
  node .agents/aidlc/scripts/remote-state.mjs feature ready checkout
60
80
  ```
61
81
 
62
- The service checks that every manifest member has a canonical task record with the matching feature ID. A partially created task stays fail-closed until the manifest is completed. The lead epoch is task-specific and must be refreshed after release or expiry.
82
+ Every manifest member must have a canonical task record with the matching feature ID; a partially created task stays fail-closed until the manifest is complete. The lead epoch is task-specific; refresh it after release or expiry.
83
+
84
+ ### Approve gates as the reviewer
63
85
 
64
- In a connected reviewer conversation, configure `AIDLC_FLEET_REVIEWER_TOKEN`, inspect the pending packet, and only after the human replies approving that packet run:
86
+ In a connected reviewer conversation with `AIDLC_FLEET_REVIEWER_TOKEN`, inspect the pending packet; only after the human approves it, run:
65
87
 
66
88
  ```sh
67
89
  node .agents/aidlc/scripts/remote-state.mjs gate inbox
68
90
  node .agents/aidlc/scripts/remote-state.mjs gate approve <task-id> --snapshot <snapshot-id> --source "<explicit user approval>"
69
91
  ```
70
92
 
71
- The second command checks that the snapshot remains current and uses the reviewer-only service operation. At G2 it also verifies that the snapshot's integration commit still matches the live remote branch. Protect the integration branch against writes outside the lead so it cannot change between verification and approval. The review message is the source provenance; the token authenticates the reviewer client, while the client operator remains responsible for using the user's actual later reply.
93
+ - `gate approve` checks the snapshot is still current and uses the reviewer-only operation; at G2 it also verifies the snapshot's integration commit matches the live remote branch (protect that branch against writes outside the lead).
94
+ - `--source` is the review message as provenance; the token authenticates the client, and the operator is responsible for using the user's actual reply.
72
95
 
73
- Run the packaged fleet process on a lead machine and worker machines under a process supervisor. Point every process to the same `AIDLC_FLEET_URL` and workspace, but give each its own role token. On the lead, set `AIDLC_FLEET_WORKER_IDS` (comma-separated), `AIDLC_FLEET_INTEGRATION_BRANCH`, and optionally `AIDLC_FLEET_AGENT` (`codex`, `claude`, or `opencode`); `AIDLC_FLEET_WORKER_PROVIDERS` can override the provider per worker. The `job_schedule` tool accepts the same three agent values, and worker containers run all of them, including `opencode` (authenticated from the environment key matching the `AIDLC_OPENCODE_MODEL` prefix: `openai/` → `OPENAI_API_KEY`, `anthropic/` → `ANTHROPIC_API_KEY`, otherwise `OPENCODE_API_KEY`). On each worker, set its matching `AIDLC_FLEET_WORKER_ID`, `AIDLC_FLEET_WORKTREES_ROOT`, and `AIDLC_FLEET_PROJECT_ROOT`. The worker's provider comes from `AIDLC_FLEET_WORKER_AGENT` (or `AIDLC_FLEET_AGENT`, or its entry in `AIDLC_FLEET_WORKER_PROVIDERS`); a worker with a provider rejects jobs for another provider. `AIDLC_WORKER_DOCKER_IMAGE` is optional when the provider is known: the image is then `loop.workerImage[provider]` from the project config or the default `aidlc-worker-<provider>:local`, built from the packaged Dockerfile when missing. Either way it is resolved to its immutable `sha256:` image id before any job runs:
96
+ ### Run the fleet processes
97
+
98
+ Run the packaged fleet process on the lead and each worker machine under a process supervisor, same `AIDLC_FLEET_URL` and workspace, own role token each. Environment variables: runbook [lead](./loop-runbook.md#step-2--set-lead-machine-environment) and [worker](./loop-runbook.md#step-4--run-a-worker-on-each-worker-machine) tables; `AIDLC_FLEET_AGENT` selects the lead provider for the direct runner (default `codex`; `aidlc-kit loop` sets it from `loop.agents.lead`) and is the per-worker fallback; `AIDLC_FLEET_WORKER_PROVIDERS` overrides it per worker. `job_schedule` accepts `codex`, `claude`, or `opencode`; worker containers run all three, only the isolated lead container does not support OpenCode ([credentials](./configuration.md#provider-credentials-and-opencode)). The worker image is resolved to its immutable `sha256:` id before any job runs.
74
99
 
75
100
  ```sh
76
101
  node dist/services/aidlc-coordination/src/fleet-run.mjs feature <feature-id>
77
102
  node dist/services/aidlc-coordination/src/fleet-run.mjs worker
103
+ # installed project: node .agents/aidlc-coordination/src/fleet-run.mjs …
78
104
  ```
79
105
 
80
- `coordinator <task-id>` runs one task. `--once` processes a bounded polling pass for supervision or diagnostics. Defaults are four worker jobs per feature, five-minute leases with one-minute heartbeat, 12 turns per phase or assignment, 10 minutes per turn, and two consecutive stalls. Set `AIDLC_FLEET_MAX_WORKERS`, `AIDLC_FLEET_LEASE_MS`, `AIDLC_FLEET_HEARTBEAT_MS`, `AIDLC_FLEET_MAX_TURNS`, `AIDLC_FLEET_TURN_TIMEOUT_MS`, and `AIDLC_FLEET_MAX_STALL_TURNS` to change them. Feature mode dispatches dependency-ready tasks in order from one integration checkout; independent items within the active task can use several machines concurrently. The coordinator never dispatches two items whose owned paths overlap by path segment (`src` overlaps `src/a.ts`, not `srcx`) at the same time; the later item waits for a following pass. With `MCP_FLEET_ENABLED=1`, the service also refuses to approve the Approve plan gate for a plan with such overlaps or with unscoped open items (`PARTITION_OVERLAP`, `PARTITION_SCOPE` are errors). After a lost worker report, the lead checks remote Git state and stops for reconciliation if the outcome is ambiguous. Set `AIDLC_FLEET_CODE_ONLY_RETRY=1` to permit an automatic retry only after the prior turn's timeout window has elapsed and Git confirms that no branch was published.
106
+ - `coordinator <task-id>` runs one task; `--once` runs a bounded polling pass.
107
+ - Defaults: four worker jobs per feature, five-minute leases with one-minute heartbeat, 12 turns per phase or assignment, 10 minutes per turn, two consecutive stalls. Override: `AIDLC_FLEET_MAX_WORKERS`, `AIDLC_FLEET_LEASE_MS`, `AIDLC_FLEET_HEARTBEAT_MS`, `AIDLC_FLEET_MAX_TURNS`, `AIDLC_FLEET_TURN_TIMEOUT_MS`, `AIDLC_FLEET_MAX_STALL_TURNS`.
108
+ - Feature mode dispatches dependency-ready tasks in order from one integration checkout; independent items of the active task can run on several machines.
109
+ - The coordinator never dispatches two items whose owned paths overlap by path segment (`src` overlaps `src/a.ts`, not `srcx`); the later item waits. With `MCP_FLEET_ENABLED=1` the service also refuses to approve the Approve plan gate for such overlaps or unscoped open items (`PARTITION_OVERLAP`, `PARTITION_SCOPE`).
110
+ - After a lost worker report the lead checks remote Git state and stops for reconciliation if ambiguous. `AIDLC_FLEET_CODE_ONLY_RETRY=1` permits an automatic retry only after the prior turn's timeout window elapsed and Git confirms no branch was published.
111
+ - The coordinator and feature commands return at a human gate (feature mode dispatches no further tasks); restart after the reviewer acts.
112
+ - Command events and failure diagnostics report status and exit code, never command text or output. Worker progress observes file content changes as well as paths.
81
113
 
82
- The coordinator and feature commands return when a task reaches a human gate; feature mode stops before dispatching further tasks. After the reviewer acts, restart the command to continue.
114
+ ### Unattended operation with the loop daemon
83
115
 
84
- For unattended operation on the lead machine, use the shared loop daemon instead of a hand-run supervisor: `aidlc-kit loop start --mode fleet` preflights the service and the remote integration branch, then runs the supervisor for each fleet loop-owned task (created with `aidlc-kit loop intake --mode fleet` or the dashboard **New task**) until its next gate, releases the supervisor lease, and re-claims the task on a later poll once the reviewer has approved. The daemon takes `loop.limits` and `loop.pollMs` from `.agents/config.json` as defaults for the `AIDLC_FLEET_*` limits, and never passes `AIDLC_FLEET_REVIEWER_TOKEN` to a lead or worker process. See the [loop runbook](./loop-runbook.md#fleet-multi-machine). Command events and command-failure diagnostics report status and exit code without the command text or its output. Worker progress observes changes to file content as well as paths.
116
+ `aidlc-kit loop start --mode fleet` on the lead machine replaces a hand-run supervisor: it runs each fleet loop-owned task until its next gate, releases the supervisor lease, and re-claims after reviewer approval. It takes `loop.limits` and `loop.pollMs` as defaults for the `AIDLC_FLEET_*` limits and never passes `AIDLC_FLEET_REVIEWER_TOKEN` to a lead or worker process. Steps: [loop runbook](./loop-runbook.md#fleet-multi-machine). On the lead machine the task's event history, run record, and intake files live under `.agents/data/tasks/<task-id>/runtime/` exactly as in local mode; worker machines write only `runtime/workers/` there. Claims are service-side, so no `assignments.json` exists in fleet mode.
85
117
 
86
118
  ## Shared coordination board
87
119
 
88
- The opt-in service also provides a small workspace-scoped Kanban board that is separate from lifecycle state. Board tasks may link to an AI-DLC task ID or artifact, but board writes, comments, and claims cannot mutate lifecycle state, approve a gate, or rewrite project files.
120
+ A small workspace-scoped Kanban board, separate from lifecycle state. Board tasks may link to an AI-DLC task ID or artifact, but board writes, comments, and claims cannot mutate lifecycle state, approve a gate, or rewrite project files.
89
121
 
90
- `board_get`, `board_apply`, `task_thread_get`, `task_comment_add`, `task_claim`, and `coordination_events_since` are bounded MCP tools. Tasks use `backlog`, `ready`, `in_progress`, `blocked`, `done`, or `cancelled`, plus priority, labels, assignees, and renewable one-minute-to-one-hour claims. Mutations require an expected board revision, idempotency key, actor ID, and role. A conflicting live claim is rejected.
122
+ - Bounded MCP tools: `board_get`, `board_apply`, `task_thread_get`, `task_comment_add`, `task_claim`, `coordination_events_since`.
123
+ - Status `backlog`, `ready`, `in_progress`, `blocked`, `done`, or `cancelled`, plus priority, labels, assignees, and renewable one-minute-to-one-hour claims.
124
+ - Mutations require an expected board revision, idempotency key, actor ID, and role; a conflicting live claim is rejected.
125
+ - Threads: short append-only comments with author type, optional mentions, and `question`, `status`, `handoff`, or `decision` intent; never credentials, hidden reasoning, chat, or gate approvals.
126
+ - Clients poll compact cursor events, coalesce them, and read details only for assignment, mention, watched-task, explicit-user, or claim-expiry activity, always after a fresh revision check.
91
127
 
92
- Threads are short append-only comments with author type, optional mentions, and `question`, `status`, `handoff`, or `decision` intent. They never carry credentials, hidden reasoning, general chat, or gate approvals. Clients poll compact cursor events, coalesce and debounce them, then read details only for assignment, mention, watched-task, explicit-user, or claim-expiry activity. A notice always requires a fresh revision check before action.
128
+ ### `state_apply` contract
93
129
 
94
- Every `state_apply` write needs an `expectedRevision`, an `idempotencyKey`, and an absolute `clientProjectRoot`. The root is used only to format portable `nextActions` with absolute script paths and an explicit `--root`; it does not grant the server filesystem access or authority over that path. A stale revision returns the current projection for pull/rebase, and a retry with the same idempotency key returns the original committed result. Successful responses include `nextActions` for touched tasks so remote callers follow the same item loop and stop classifications as the local CLI.
130
+ - Every write needs an `expectedRevision`, an `idempotencyKey`, and an absolute `clientProjectRoot`. The root only formats portable `nextActions` (absolute script paths, explicit `--root`); it grants no filesystem access or authority.
131
+ - A stale revision returns the current projection for pull/rebase; a retry with the same idempotency key returns the original committed result.
132
+ - Responses include `nextActions` for touched tasks, so remote callers follow the same item loop and stop classifications as the local CLI.
133
+ - SQLite uses an immediate transaction and busy timeout; PostgreSQL locks the workspace row in a transaction.
95
134
 
96
- The service checks lifecycle invariants across the whole before/after snapshot, not only touched task IDs. Existing evidence is append-only: the prior evidence array must remain an exact structural prefix (compared field-wise, so re-serialization key order does not matter, while record order still does). A validated `blocked_on_user` wait cannot be deleted, and while a task waits its readiness-bearing state (items, decisions, artifacts, and every other non-evidence field) is frozen; only evidence may be appended. Leaving a wait requires, in the same mutation, one of: a passing approval for that exact gate, gate-bound cancellation diagnostic evidence with a non-empty source, or a sourced closure/handoff record (`closed`/`superseded` with closure metadata, or `paused` with handoff metadata). The one compatibility recovery mirrors the local CLI: an invalid wait at the gateless wrap phase may return to `active` with no appended evidence; it is not a human approval. A snapshot that requests or holds `blocked_on_user` while shared lifecycle predicates still require planning, items, verification, review, or an unexhausted repair bound is rejected. The `legacyG2Wait` repair-bound exemption marker is migration-owned: the service stamps it only when upgrading a stored pre-v4 row, and rejects any client mutation that creates or changes it. SQLite uses an immediate transaction and busy timeout; PostgreSQL locks the workspace row in a transaction.
135
+ Lifecycle invariants are checked across the whole before/after snapshot, not only touched task IDs:
97
136
 
98
- In default shared-token mode, these guards keep remote history tamper-evident but do not authenticate approvals: `source` remains provenance supplied by a client. Fleet mode binds separate lead, worker, and reviewer credentials to server-side roles, and only a reviewer credential can call the atomic gate approval tool. The service cannot inspect client project artifacts, so the lead must run local artifact checks before publishing a review snapshot; the service enforces structural gate readiness and snapshot revision at approval.
137
+ - Evidence is append-only: the prior evidence array must remain an exact structural prefix (field-wise; key order is free, record order is not).
138
+ - A validated `blocked_on_user` wait cannot be deleted; while a task waits, every non-evidence field (items, decisions, artifacts, …) is frozen.
139
+ - Leaving a wait requires, in the same mutation, one of: a passing approval for that exact gate; gate-bound cancellation evidence with a non-empty source; or a sourced closure/handoff record (`closed`/`superseded` with closure metadata, `paused` with handoff metadata). One compatibility recovery mirrors the local CLI: an invalid wait at the gateless wrap phase may return to `active` with no appended evidence; it is not a human approval.
140
+ - A snapshot that requests or holds `blocked_on_user` while shared predicates still require planning, items, verification, review, or an unexhausted repair bound is rejected.
141
+ - The `legacyG2Wait` repair-bound exemption marker is migration-owned: stamped only when upgrading a stored pre-v4 row; client mutations creating or changing it are rejected.
99
142
 
100
- `work_item_request` is intentionally only a credential-free preflight for Jira, Trello, or GitHub Issues. It reports the required environment variable names and never returns a reusable token, Authorization header, or token-bearing URL. External updates remain opt-in and must be made outside the lifecycle transaction.
143
+ In default shared-token mode these guards keep history tamper-evident but do not authenticate approvals: `source` is client-supplied provenance. Fleet mode binds lead, worker, and reviewer credentials to server-side roles; only a reviewer credential can call the atomic gate approval tool. The service cannot inspect client artifacts: the lead runs local artifact checks before publishing a review snapshot; the service enforces structural gate readiness and snapshot revision at approval.
101
144
 
102
- ## Service health, logging, and image
145
+ `work_item_request` is only a credential-free preflight for Jira, Trello, or GitHub Issues: it reports the required environment variable names and never returns a reusable token, Authorization header, or token-bearing URL. External updates are opt-in, outside the lifecycle transaction.
103
146
 
104
- The service exposes an unauthenticated, static `GET /healthz` that returns `200 {"ok":true}` without reading state, version, or workspace data. It is registered before the auth middleware and is used by the container `HEALTHCHECK` and compose healthchecks.
147
+ ## Service health, logging, and image
105
148
 
106
- Every request emits one compact JSON line to stderr on response finish, of the form `{"ts":...,"method":...,"path":...,"status":...,"principal":...,"ms":...}` (principal is the fleet principal id when present, otherwise omitted). Logging never includes headers, request bodies, Authorization headers, or tokens.
149
+ - `GET /healthz`: unauthenticated, static `200 {"ok":true}`, reads no state, version, or workspace data; registered before the auth middleware; used by the container `HEALTHCHECK` and compose healthchecks.
150
+ - Each request logs one JSON line to stderr: `{"ts":...,"method":...,"path":...,"status":...,"principal":...,"ms":...}` (`principal` omitted when absent). Never headers, bodies, or tokens.
151
+ - McpServer version: the `version` field of the `package.json` next to the service root (`new URL("../package.json", import.meta.url)`), fallback `"0.0.0-dev"` (source checkouts); the bundled image gets it by build-time injection.
152
+ - Image: two stages, both `node:24-bookworm-slim` (matching `better-sqlite3` ABI), non-root `node` user, `HEALTHCHECK` against `/healthz`; the compose `aidlc-coordination` and `fleet-coordination` services define matching healthchecks.
107
153
 
108
- The McpServer version is read from the `version` field of the `package.json` next to the service root (`new URL("../package.json", import.meta.url)`), falling back to `"0.0.0-dev"` when missing or unreadable. In source checkouts the service package has no version field, so the fallback applies; the bundled image receives the package version through build-time injection.
154
+ ## Related
109
155
 
110
- The container image is now built in two stages (both `node:24-bookworm-slim`, so the `better-sqlite3` ABI matches), installs dependencies in the build stage, and runs as the non-root `node` user with a `HEALTHCHECK` against `/healthz`. The compose `aidlc-coordination` and `fleet-coordination` services also define matching healthchecks.
156
+ - [Loop setup runbook](./loop-runbook.md#fleet-multi-machine) — step-by-step fleet loop setup.
157
+ - [Configuration](./configuration.md) — the `mcp` and `loop` config blocks.
158
+ - [Command reference](./command-reference.md) — `mcp setup`, `loop`, and environment variables.
159
+ - [Troubleshooting](./troubleshooting.md#loop-leases-locks-and-claims) — leases, claims, and fleet conflicts.
@@ -1,72 +1,72 @@
1
1
  # Design decisions (ADR-lite)
2
2
 
3
- Numbered decisions adopted by the project. Each entry records context, decision, and consequences. Cross-reference [Architecture](./architecture.md) for module boundaries and source-to-consumer flow.
3
+ Numbered decisions that constrain future changes across modules or releases (runtime boundaries, state formats, safety policies). Local implementation choices belong in code comments or the originating task.
4
4
 
5
5
  ## D1. Dual lifecycle cores with conformance instead of codegen
6
6
 
7
- - **Context:** Installed projects must run lifecycle scripts with no dependencies; the package CLI itself is TypeScript and needs types.
8
- - **Decision:** Keep two cores — TypeScript in `src/state.ts` and a dependency-free ESM mirror in `assets/aidlc/scripts/lib/store.mjs` plus a committed byte-identical copy in `services/aidlc-coordination/src/lifecycle-core.mjs` — enforced by `lifecycle-core-conformance` (byte-identity + property/fuzz) rather than codegen.
9
- - **Consequences:** Parity is checked, not generated; a change to one core requires `npm run sync-core` and conformance tests. Cheaper than a build-time codegen step in a no-dep runtime.
7
+ - **Context:** Installed projects need dependency-free lifecycle scripts; the package CLI is TypeScript.
8
+ - **Decision:** Two cores: `src/state.ts` (TypeScript) and `assets/aidlc/scripts/lib/store.mjs` (dependency-free ESM), plus a committed byte-identical copy in `services/aidlc-coordination/src/lifecycle-core.mjs`.
9
+ - **Consequences:** Parity is checked by `lifecycle-core-conformance` (byte identity + property/fuzz), not generated; a core change requires `npm run sync-core` and those tests.
10
10
 
11
11
  ## D2. Single JSON state with file lock and archive catalog
12
12
 
13
- - **Context:** The workflow is local-first with no registry or database dependency in consumer projects.
14
- - **Decision:** Canonical state is one JSON file (`.agents/data/state/aidlc-state.json`) serialized by a file lock (`.aidlc-state.lock` + guard), with terminal records archived under `.agents/data/state/backups/` and digest-verified.
15
- - **Consequences:** Simple backup/restore and git diffing; contention is explicit and bounded by the 5s/30s lock defaults; not suited to high-concurrency multi-writer workloads beyond the lock.
13
+ - **Context:** Local-first; no registry or database in consumer projects.
14
+ - **Decision:** One JSON file (`.agents/data/state/aidlc-state.json`) serialized by a file lock (`.aidlc-state.lock` + guard); terminal records archived under `.agents/data/state/backups/`, digest-verified.
15
+ - **Consequences:** Simple backup, restore, and git diffing; contention is bounded by the 5s/30s lock defaults; unsuited to high-concurrency multi-writer workloads.
16
16
 
17
17
  ## D3. Append-only evidence with append-order boundaries
18
18
 
19
- - **Context:** Verification/review outcomes must be auditable and not rewritten by later runs.
20
- - **Decision:** Evidence is append-only; current-build boundaries are defined by append order, not wall-clock timestamps.
21
- - **Consequences:** Re-running a check appends a new record; "latest" means latest in append order. Tampering requires rewriting the whole state file, which the lock and digest checks make detectable.
19
+ - **Context:** Verification and review outcomes must stay auditable and never be rewritten.
20
+ - **Decision:** Evidence is append-only; current-build boundaries follow append order, not wall-clock timestamps.
21
+ - **Consequences:** Re-running a check appends; "latest" means latest appended. Tampering requires rewriting the whole state file, detectable via lock and digest checks.
22
22
 
23
23
  ## D4. Human-only upgrades
24
24
 
25
- - **Context:** Workflow upgrades touch managed assets and state; silent or automated upgrades risk data loss.
26
- - **Decision:** `upgrade` rejects `--yes`/`--force`, requires an interactive TTY, and asks the human to type the target version. No background checks or postinstall.
25
+ - **Context:** Upgrades touch managed assets and state; automated upgrades risk data loss.
26
+ - **Decision:** `upgrade` rejects `--yes`/`--force`, requires an interactive TTY, and asks the human to type the target version; no background checks or postinstall.
27
27
  - **Consequences:** Upgrades are auditable and intentional; CI cannot auto-upgrade; rollback uses the upgrade journal.
28
28
 
29
29
  ## D5. Local-first / no-registry policy
30
30
 
31
- - **Context:** Consumer projects must operate offline; status/doctor must not phone home.
32
- - **Decision:** `remoteUpdates: false` by default; status and doctor never consult a registry. Fleet is opt-in and separately deployed.
31
+ - **Context:** Consumer projects must work offline; status/doctor must not phone home.
32
+ - **Decision:** `remoteUpdates: false` by default; status and doctor never consult a registry; fleet is opt-in and separately deployed.
33
33
  - **Consequences:** No surprise network calls; version drift is detected locally; fleet authority is explicit via `AIDLC_FLEET_URL`.
34
34
 
35
35
  ## D6. Compact code style in the installed kernel
36
36
 
37
- - **Context:** Phase packets have a 16,000-character budget; installed scripts must fit.
38
- - **Decision:** The installed kernel uses compact style (phase-packet budget 16,000 chars); package and service code do not follow it.
39
- - **Consequences:** Two style regimes; contributors must not "pretty-print" installed scripts in ways that bloat packets; mandatory sections are never truncated.
37
+ - **Context:** Installed scripts must fit the 16,000-character phase-packet budget.
38
+ - **Decision:** The installed kernel uses compact style; package and service code do not.
39
+ - **Consequences:** Two style regimes; never "pretty-print" installed scripts in ways that bloat packets; mandatory sections are never truncated.
40
40
 
41
41
  ## D7. Machine-enforced repair bounds
42
42
 
43
- - **Context:** Repair loops can run indefinitely without a hard stop.
43
+ - **Context:** Repair loops need a hard stop.
44
44
  - **Decision:** Three failed verify records per affected area, or two failed review records, in the current build flip `nextAction` to `blocked` and add a `REPAIR_BOUND` error to the Review implementation gate check.
45
- - **Consequences:** The model cannot self-approve past the bound; a durable handoff is required. Legacy G2 waits migrated by schema v4 are exempt once.
45
+ - **Consequences:** No self-approval past the bound; a durable handoff is required. Legacy G2 waits migrated by schema v4 are exempt once.
46
46
 
47
47
  ## D8. Assignment records are operational metadata
48
48
 
49
49
  - **Context:** Worker assignment state must not become a second lifecycle authority.
50
- - **Decision:** Assignment records (`.agents/data/assignments/`) are operational metadata only; canonical task state remains the authority.
51
- - **Consequences:** Assignment corruption or loss does not invalidate the task; reconciliation (`assignment.mjs reconcile`) is required before dispatch.
50
+ - **Decision:** Assignment records (`.agents/data/tasks/<id>/runtime/assignments.json`) are operational metadata; canonical task state is the authority.
51
+ - **Consequences:** Assignment corruption or loss does not invalidate the task; `assignment.mjs reconcile` is required before dispatch.
52
52
 
53
53
  ## D9. Configured commands as executable + args arrays
54
54
 
55
55
  - **Context:** Shell strings are unsafe and non-portable.
56
56
  - **Decision:** Project-configured commands are stored and executed as executable + args arrays; shell strings are rejected.
57
- - **Consequences:** No shell injection; commands must be invoked explicitly; cross-platform behaviour is predictable.
57
+ - **Consequences:** No shell injection; explicit invocation; predictable cross-platform behaviour.
58
58
 
59
59
  ## D10. Task language `vi` / `en`
60
60
 
61
- - **Context:** Task artifacts and prompts need a declared language; broader i18n is not yet justified.
62
- - **Decision:** Task language is restricted to `vi` or `en`; further extensibility is deferred.
63
- - **Consequences:** New languages require a schema and conformance change; existing tasks keep their declared language.
61
+ - **Context:** Artifacts and prompts need a declared language; broader i18n is not justified yet.
62
+ - **Decision:** Task language is `vi` or `en`; further extensibility is deferred.
63
+ - **Consequences:** New languages need a schema and conformance change; existing tasks keep their declared language.
64
64
 
65
65
  ## D11. Optional `$schema` reference in generated config
66
66
 
67
67
  - **Context:** Offline-first tooling needs schema validation without network.
68
- - **Decision:** Generated config may include a relative `$schema` reference pointing at the local schema; it is optional.
69
- - **Consequences:** Editors get validation offline; missing reference degrades gracefully.
68
+ - **Decision:** Generated config may include an optional relative `$schema` reference to the local schema.
69
+ - **Consequences:** Editors validate offline; a missing reference degrades gracefully.
70
70
 
71
71
  ## D12. Timing-safe comparison for all coordination auth
72
72
 
@@ -76,8 +76,12 @@ Numbered decisions adopted by the project. Each entry records context, decision,
76
76
 
77
77
  ## How to add a decision
78
78
 
79
- 1. Record the decision in canonical state via `decision set` so it is tracked with the task that introduced it.
80
- 2. Cite the source (file, PR, or task id) and present it at Approve plan (G1) for review.
81
- 3. Append a numbered entry here following the Context / Decision / Consequences shape, and link from [Architecture](./architecture.md) when it changes a boundary.
79
+ 1. Record it in canonical state via `decision set`, tracked with its task.
80
+ 2. Cite the source (file, PR, or task id) and present it at Approve plan (G1).
81
+ 3. Append a numbered Context / Decision / Consequences entry here; link from [Architecture](./architecture.md) when it changes a boundary.
82
82
 
83
- See [Architecture](./architecture.md) for the module and ownership boundaries these decisions protect.
83
+ ## Related
84
+
85
+ - [Architecture](./architecture.md)
86
+ - [Development guide](./development.md)
87
+ - [Testing and release](./testing-and-release.md)