@felixdotgo/aidlc-kit 0.1.0 → 0.2.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 (150) hide show
  1. package/DEVELOPMENT.md +14 -0
  2. package/README.md +14 -6
  3. package/dist/assets/.agents/aidlc/docker/Dockerfile.claude +7 -0
  4. package/dist/assets/.agents/aidlc/docker/Dockerfile.codex +9 -0
  5. package/dist/assets/.agents/aidlc/docker/Dockerfile.opencode +8 -0
  6. package/dist/assets/.agents/aidlc/orchestrator.md +1 -1
  7. package/dist/assets/.agents/aidlc/phase-clarify.md +1 -1
  8. package/dist/assets/.agents/aidlc/phase-plan.md +2 -2
  9. package/dist/assets/.agents/aidlc/schemas/config.schema.json +2 -1
  10. package/dist/assets/.agents/aidlc/schemas/manifest.schema.json +1 -1
  11. package/dist/assets/.agents/aidlc/scripts/lib/context-runtime.mjs +5 -0
  12. package/dist/assets/.agents/aidlc/scripts/lib/loop-authority.mjs +245 -0
  13. package/dist/assets/.agents/aidlc/scripts/lib/loop-commit-message.mjs +45 -0
  14. package/dist/assets/.agents/aidlc/scripts/lib/loop-daemon.mjs +232 -0
  15. package/dist/assets/.agents/aidlc/scripts/lib/loop-drivers.mjs +21 -4
  16. package/dist/assets/.agents/aidlc/scripts/lib/loop-fleet-cli.mjs +86 -28
  17. package/dist/assets/.agents/aidlc/scripts/lib/loop-fleet-remote.mjs +7 -0
  18. package/dist/assets/.agents/aidlc/scripts/lib/loop-fleet-supervisor.mjs +22 -6
  19. package/dist/assets/.agents/aidlc/scripts/lib/loop-fleet.mjs +46 -15
  20. package/dist/assets/.agents/aidlc/scripts/lib/loop-git.mjs +22 -9
  21. package/dist/assets/.agents/aidlc/scripts/lib/loop-intake.mjs +23 -5
  22. package/dist/assets/.agents/aidlc/scripts/lib/loop-local-workers.mjs +43 -15
  23. package/dist/assets/.agents/aidlc/scripts/lib/loop-runtime.mjs +82 -6
  24. package/dist/assets/.agents/aidlc/scripts/lib/loop-stream.mjs +60 -0
  25. package/dist/assets/.agents/aidlc/scripts/lib/loop-supervisor-run-local.mjs +142 -5
  26. package/dist/assets/.agents/aidlc/scripts/lib/loop-supervisor.mjs +9 -1
  27. package/dist/assets/.agents/aidlc/scripts/lib/loop-worker-docker.mjs +31 -5
  28. package/dist/assets/.agents/aidlc/scripts/lib/loop-worker-image.mjs +64 -0
  29. package/dist/assets/.agents/aidlc/scripts/lib/loop-worker-snapshot.mjs +3 -2
  30. package/dist/assets/.agents/aidlc/scripts/lib/store.mjs +59 -4
  31. package/dist/assets/.agents/aidlc/scripts/loop-entry.mjs +85 -73
  32. package/dist/assets/.agents/aidlc/scripts/loop-gate.mjs +19 -7
  33. package/dist/assets/.agents/aidlc/scripts/loop.mjs +2 -1
  34. package/dist/assets/.agents/aidlc/scripts/remote-state.mjs +2 -0
  35. package/dist/assets/.agents/aidlc/scripts/state.mjs +4 -0
  36. package/dist/assets/.agents/aidlc/templates/design.md +9 -0
  37. package/dist/assets/.agents/aidlc/templates/intent.md +4 -0
  38. package/dist/assets/.agents/aidlc/templates/workplan.md +1 -1
  39. package/dist/assets/.agents/skills/aidlc-plan/SKILL.md +1 -1
  40. package/dist/assets/ui/bulma.min.css +3 -0
  41. package/dist/assets/ui/dashboard.css +414 -0
  42. package/dist/services/aidlc-coordination/Dockerfile +11 -3
  43. package/dist/services/aidlc-coordination/compose.yaml +12 -0
  44. package/dist/services/aidlc-coordination/package-lock.json +3 -1
  45. package/dist/services/aidlc-coordination/package.json +9 -4
  46. package/dist/services/aidlc-coordination/src/lifecycle-core.mjs +59 -4
  47. package/dist/services/aidlc-coordination/src/repository.mjs +66 -0
  48. package/dist/services/aidlc-coordination/src/server.mjs +9 -3
  49. package/dist/services/aidlc-coordination/test/fleet.test.mjs +21 -0
  50. package/dist/services/aidlc-coordination/test/server-health-auth.test.mjs +67 -0
  51. package/dist/src/adapters.d.ts +0 -3
  52. package/dist/src/adapters.js +11 -6
  53. package/dist/src/cli/argv.d.ts +12 -0
  54. package/dist/src/cli/argv.js +62 -0
  55. package/dist/src/cli/commands.d.ts +8 -0
  56. package/dist/src/cli/commands.js +129 -0
  57. package/dist/src/cli/options.d.ts +25 -0
  58. package/dist/src/cli/options.js +32 -0
  59. package/dist/src/cli/prompt.d.ts +9 -0
  60. package/dist/src/cli/prompt.js +66 -0
  61. package/dist/src/cli.js +67 -217
  62. package/dist/src/context.js +2 -0
  63. package/dist/src/gate.d.ts +1 -0
  64. package/dist/src/gate.js +51 -37
  65. package/dist/src/installer.d.ts +19 -7
  66. package/dist/src/installer.js +111 -178
  67. package/dist/src/legacy.js +1 -0
  68. package/dist/src/managed-content.d.ts +7 -0
  69. package/dist/src/managed-content.js +15 -4
  70. package/dist/src/managed-planning.d.ts +65 -0
  71. package/dist/src/managed-planning.js +219 -0
  72. package/dist/src/mcp-setup.js +3 -1
  73. package/dist/src/model/config-contracts.d.ts +65 -0
  74. package/dist/src/model/config-contracts.js +1 -0
  75. package/dist/src/model/install-contracts.d.ts +29 -0
  76. package/dist/src/model/install-contracts.js +1 -0
  77. package/dist/src/model/lifecycle-enums.d.ts +13 -0
  78. package/dist/src/model/lifecycle-enums.js +1 -0
  79. package/dist/src/model/loop.d.ts +20 -0
  80. package/dist/src/model/loop.js +1 -0
  81. package/dist/src/model/manifest.d.ts +16 -0
  82. package/dist/src/model/manifest.js +1 -0
  83. package/dist/src/model/records.d.ts +138 -0
  84. package/dist/src/model/records.js +1 -0
  85. package/dist/src/model.d.ts +11 -264
  86. package/dist/src/model.js +1 -1
  87. package/dist/src/profiles.d.ts +8 -0
  88. package/dist/src/profiles.js +63 -8
  89. package/dist/src/project-migration.d.ts +1 -0
  90. package/dist/src/project-migration.js +7 -0
  91. package/dist/src/state/archive-record.d.ts +9 -0
  92. package/dist/src/state/archive-record.js +26 -0
  93. package/dist/src/state/commands.d.ts +18 -0
  94. package/dist/src/state/commands.js +27 -0
  95. package/dist/src/state/domain.d.ts +9 -0
  96. package/dist/src/state/domain.js +9 -0
  97. package/dist/src/state/guards.d.ts +7 -0
  98. package/dist/src/state/guards.js +17 -0
  99. package/dist/src/state/io.d.ts +10 -0
  100. package/dist/src/state/io.js +35 -0
  101. package/dist/src/state/items.d.ts +24 -0
  102. package/dist/src/state/items.js +147 -0
  103. package/dist/src/state/lessons.d.ts +7 -0
  104. package/dist/src/state/lessons.js +74 -0
  105. package/dist/src/state/lifecycle.d.ts +5 -0
  106. package/dist/src/state/lifecycle.js +78 -0
  107. package/dist/src/state/lock.d.ts +7 -0
  108. package/dist/src/state/lock.js +125 -0
  109. package/dist/src/state/memory.d.ts +8 -0
  110. package/dist/src/state/memory.js +100 -0
  111. package/dist/src/state/migrate.d.ts +17 -0
  112. package/dist/src/state/migrate.js +60 -0
  113. package/dist/src/state/partition.d.ts +15 -0
  114. package/dist/src/state/partition.js +90 -0
  115. package/dist/src/state/paths.d.ts +11 -0
  116. package/dist/src/state/paths.js +12 -0
  117. package/dist/src/state/render.d.ts +26 -0
  118. package/dist/src/state/render.js +32 -0
  119. package/dist/src/state/store.d.ts +4 -0
  120. package/dist/src/state/store.js +75 -0
  121. package/dist/src/state/transitions.d.ts +21 -0
  122. package/dist/src/state/transitions.js +95 -0
  123. package/dist/src/state/validate.d.ts +4 -0
  124. package/dist/src/state/validate.js +143 -0
  125. package/dist/src/state.d.ts +20 -117
  126. package/dist/src/state.js +20 -815
  127. package/dist/src/task-ui.d.ts +54 -0
  128. package/dist/src/task-ui.js +1570 -0
  129. package/dist/src/upgrade-board.d.ts +5 -0
  130. package/dist/src/upgrade-board.js +89 -0
  131. package/dist/src/upgrade.d.ts +3 -5
  132. package/dist/src/upgrade.js +174 -255
  133. package/dist/src/workflow.js +4 -4
  134. package/docs/README.md +6 -2
  135. package/docs/architecture.md +12 -8
  136. package/docs/command-reference.md +83 -19
  137. package/docs/configuration.md +30 -7
  138. package/docs/coordination-service.md +20 -5
  139. package/docs/design-decisions.md +83 -0
  140. package/docs/development.md +41 -2
  141. package/docs/loop-local-run.md +495 -0
  142. package/docs/loop-runbook.md +235 -0
  143. package/docs/operating-workflow.md +5 -2
  144. package/docs/testing-and-release.md +11 -7
  145. package/docs/troubleshooting.md +125 -0
  146. package/package.json +5 -2
  147. package/dist/src/ledger.d.ts +0 -22
  148. package/dist/src/ledger.js +0 -119
  149. package/dist/src/tracker.d.ts +0 -29
  150. package/dist/src/tracker.js +0 -41
@@ -15,12 +15,23 @@ npm install
15
15
 
16
16
  Codex, Claude Code, or OpenCode are optional for manually exercising real evaluator drivers. Ordinary build, lint, and unit-test work does not need external model credentials. OpenCode driver runs also need `AIDLC_OPENCODE_MODEL` set to a `provider/model[#variant]` value.
17
17
 
18
+ ## Platform support
19
+
20
+ Node.js 20.17 or newer is required on every platform. Linux and macOS are fully supported and tested. Windows is best-effort: win32 path separators are handled in `src/state/guards.ts`, `src/profiles.ts`, and the installed `assets/aidlc/scripts/lib/store.mjs`/`context-runtime.mjs`, and the state lock uses `process.kill(pid, 0)` for liveness; the lock and path handling are not exercised on Windows in CI. Loop and fleet features require Docker and a POSIX shell (`loop-local-workers.mjs` rejects non-Linux hosts).
21
+
22
+ | Platform | Status | Notes |
23
+ | --- | --- | --- |
24
+ | Linux | Supported | Full CI coverage; Docker worker isolation available. |
25
+ | macOS | Supported | Full test coverage; POSIX shell recipes apply. |
26
+ | Windows | Best-effort | Use WSL or Git Bash for the local build recipe; lock/path code handles win32 separators but is untested. |
27
+ | Loop / fleet | Linux only | Requires Docker + POSIX shell; `AIDLC_TEST_WORKER_IMAGE`/`AIDLC_WORKER_DOCKER_IMAGE` for e2e. |
28
+
18
29
  ## Repository layout
19
30
 
20
31
  ```text
21
32
  assets/aidlc/ Source workflow contracts, schemas, and installed scripts
22
33
  skills/ Source thin phase adapters
23
- src/ TypeScript package CLI, installer, lifecycle, profiles, and migrations
34
+ src/ TypeScript package CLI (src/cli/), installer + shared planning core, lifecycle domain (src/model/, src/state/), profiles, and migrations
24
35
  dev/evaluator/ Development-only evaluator harness and runners
25
36
  scripts/build-assets.mjs Bundles workflow assets for package distribution
26
37
  test/unit/ Unit and installed-runtime parity tests
@@ -35,6 +46,7 @@ The asset build copies `assets/aidlc/` and `skills/` into `dist/assets/.agents/`
35
46
  | Command | Purpose |
36
47
  | --- | --- |
37
48
  | `npm run build` | Rebuild bundled workflow assets and compile TypeScript into `dist/`. |
49
+ | `npm run sync-core` | Copy `assets/aidlc/scripts/lib/store.mjs` to `services/aidlc-coordination/src/lifecycle-core.mjs` byte-identically. |
38
50
  | `npm run lint` | Type-check without emitting files. |
39
51
  | `npm test` | Build, then run every compiled unit test with Node's test runner. |
40
52
  | `node --test dist/test/unit/<file>.test.js` | Run a focused compiled test after building. |
@@ -74,7 +86,7 @@ To test the package before release, create its `.tgz` archive and run the local
74
86
 
75
87
  ```sh
76
88
  npm pack
77
- npx --package felixdotgo-aidlc-kit-0.0.1.tgz -- aidlc-workflow init .
89
+ npx --package felixdotgo-aidlc-kit-0.2.0.tgz -- aidlc-kit init .
78
90
  ```
79
91
 
80
92
  `npx` runs the archive without publishing it. Run the command from the directory containing the archive. Use a disposable project instead of `.` if you do not want the command to write workflow files into the current directory.
@@ -95,6 +107,22 @@ The TypeScript implementation powers the package CLI. Installed projects use a d
95
107
 
96
108
  Likewise, a public configuration or schema change must update its validator, JSON schema, installed assets, package inventory, documentation, and tests together.
97
109
 
110
+ The dual-core sync rule: after editing `assets/aidlc/scripts/lib/store.mjs`, run `npm run sync-core` so `services/aidlc-coordination/src/lifecycle-core.mjs` stays byte-identical.
111
+
112
+ ## Code style and conventions
113
+
114
+ Adopted as the project baseline (approved decision `D1-convention-standard` on task `refactor-source-readability`):
115
+
116
+ - **Strict TypeScript, no new dependencies for tooling.** Keep `strict` mode; enforce via `npm run lint` (`tsc --noEmit`). No ESLint/Prettier for now.
117
+ - **File size targets.** Aim for ≤250 lines per source file and ≤60 lines per function; split by domain responsibility when exceeded.
118
+ - **Module boundaries.** One responsibility per module; a facade module may re-export a public API for compatibility (for example `src/state.ts`).
119
+ - **Naming.** Descriptive camelCase for functions/variables, `PascalCase` for types/interfaces, `SCREAMING_SNAKE_CASE` for true constants; file names match their primary export.
120
+ - **Error handling.** Fail with typed, one-line errors; never swallow exceptions silently; validate inputs before I/O.
121
+ - **Dependencies.** Pass dependencies (fetch, clock, environment) explicitly as parameters; avoid module-level mutable state.
122
+ - **Public API.** Document exported symbols with JSDoc when the contract is non-obvious.
123
+ - **Behavior preservation.** Refactors must not change CLI output, state schema, file layout, or installed assets; run parity tests.
124
+ - **Verification.** Run the narrowest relevant test while iterating; `npm test` + `npm run lint` before handing off.
125
+
98
126
  ## Safe implementation rules
99
127
 
100
128
  - Resolve all project-relative paths through the shared path helpers; reject traversal and symlink escape.
@@ -104,3 +132,14 @@ Likewise, a public configuration or schema change must update its validator, JSO
104
132
  - Treat human-gated lifecycle transitions as atomic, auditable state operations.
105
133
 
106
134
  Read [Architecture](./architecture.md) before changing a module boundary, migration, installed script, or bundle layout.
135
+
136
+ ## Contributing to prompt assets
137
+
138
+ Prompt assets are the installed workflow contracts under `assets/aidlc/` (notably `orchestrator.md` and `phase-*.md`). They feed the phase packets generated by `context.mjs`.
139
+
140
+ - Keep them compact: the default phase-packet budget is 16,000 characters (`config.context.maxChars`). Optional rules and diagnostics are omitted first; the next-action contract, full phase contract, approved decisions, safety/spec anchors, task state, and applicable latest evidence are mandatory and are never truncated — if they do not fit, packet generation fails instead.
141
+ - Heading invariants are enforced by `gate-check.mjs`:
142
+ - Intent (`G0_confirm`): `## 📋 Problem`, `## 🗺️ Affected areas`, `## 💭 Assumptions`, `## ❓ Open questions`, `## 🎯 Scope`.
143
+ - Design (`G1_review`): `## 🧩 Solution per affected area`, `## 📌 Spec traceability`, `## 🔗 Cross-service contracts`, `## ⚠️ Risks / edge cases`.
144
+ - When changing contracts, run `npm run release:check` (`--adapters opencode`) and the evaluator suite; update docs and tests in the same change.
145
+ - Never duplicate canonical agent instructions in overview docs; point to the installed assets instead. See [Design decisions](./design-decisions.md) (D6) for the compact-style rationale.
@@ -0,0 +1,495 @@
1
+ # Run the AI-DLC Loop (local) for the aidlc-kit repo
2
+
3
+ End-to-end guide for running the loop on the `aidlc-kit` repo itself (dogfooding),
4
+ single machine (`local` mode). All lead/workers use **OpenCode**.
5
+
6
+ > **Local loop mode is usable** today; **fleet mode is still under active development**. The loop is **experimental** (pre-1.0): flags, `loop.*` fields, environment variables, and diagnostics can change incompatibly before `1.0.0`.
7
+
8
+ > **npx vs. source.** The run commands below show **both** forms: `npx --package @felixdotgo/aidlc-kit@0.2.0 aidlc-kit …` (the published package — primary for end users) and `node dist/src/cli.js …` (dogfooding the `aidlc-kit` repo from a source checkout, after `npm run build`). Both drive the same installed `loop-entry.mjs`. Gate approval (§5.4) always stays on the trusted terminal via the installed `loop-gate.mjs` script and is never an `npx` command.
9
+
10
+ ---
11
+
12
+ ## 1. What the loop is (summary)
13
+
14
+ The loop runs an AI-DLC task from start to finish:
15
+
16
+ - The **lead** agent runs the `clarify`, `plan`, and `wrap` phases (on the host).
17
+ - **Workers** only run build items (in Docker, isolated).
18
+ - Each loop iteration reads `state.mjs task next` → gets `nextAction` → passes context to
19
+ the agent → runs one turn → re-reads canonical state → updates checkpoint/fingerprint.
20
+ - The loop **stops at every gate** and **never self-approves** — approval happens only
21
+ through the trusted terminal.
22
+ - The loop also stops on its own when: the per-run `maxTurns` budget is exhausted (it
23
+ bounds one supervised run, not the task's lifetime — a resumed run gets a fresh budget),
24
+ `maxStallTurns` consecutive turns show no change, or the state is
25
+ `blocked`/`terminal`/`complete`. Both limits come from `loop.limits` (see
26
+ [configuration](./configuration.md#optional-loop-configuration)).
27
+
28
+ Setting the config alone **does not run anything** — the loop only runs when you call
29
+ `loop start`.
30
+
31
+ ---
32
+
33
+ ## 2. Current configuration (already set up)
34
+
35
+ `.agents/config.json`:
36
+
37
+ ```json
38
+ {
39
+ "loop": {
40
+ "mode": "local",
41
+ "agents": {
42
+ "lead": "opencode",
43
+ "workers": { "worker-a": "opencode", "worker-b": "opencode" }
44
+ }
45
+ }
46
+ }
47
+ ```
48
+
49
+ Quick check:
50
+
51
+ ```sh
52
+ node -e 'const c=JSON.parse(require("fs").readFileSync(".agents/config.json","utf8")); console.log(JSON.stringify(c.loop,null,2))'
53
+ ```
54
+
55
+ ---
56
+
57
+ ## 3. Prerequisites
58
+
59
+ | Item | Requirement | Notes |
60
+ | --- | --- | --- |
61
+ | Linux host | required | Loop/fleet needs Docker + POSIX shell. |
62
+ | Node | ≥ 20.17 | The repo is currently on v24. |
63
+ | Docker engine | for build/worker | `docker info` must pass. |
64
+ | OpenCode CLI | `opencode` on PATH | `opencode --version` (currently v2.2.5). |
65
+ | Valid model | `AIDLC_OPENCODE_MODEL` | **Must be present in `opencode models`.** |
66
+ | Worker image | for build phase | Image containing the real `opencode` CLI (see §7). |
67
+ | Credentials | per model | `OPENAI_API_KEY` / `ANTHROPIC_API_KEY` for workers. |
68
+
69
+ ---
70
+
71
+ ## 4. Check configuration & sync
72
+
73
+ ### 4.1 Check for drift (installed `.agents/` vs source `assets/`)
74
+
75
+ ```sh
76
+ diff -rq assets/aidlc/scripts .agents/aidlc/scripts
77
+ ```
78
+
79
+ If it produces output → the installed copy is stale. A typical error when drifted:
80
+
81
+ ```
82
+ loop: Isolated local worker dispatcher is not integrated; loop execution is unavailable
83
+ ```
84
+
85
+ ### 4.2 Re-sync (human-only — requires a TTY)
86
+
87
+ ```sh
88
+ npm run build
89
+ node dist/src/cli.js upgrade . --dry-run # preview the plan
90
+ node dist/src/cli.js upgrade . # apply, type "0.2.0" when prompted
91
+ ```
92
+
93
+ Then re-run `diff -rq ...` (should be empty) and a quick test:
94
+
95
+ ```sh
96
+ node dist/src/cli.js loop status --root . --task-id <task-id>
97
+ ```
98
+
99
+ ---
100
+
101
+ ## 5. Run the loop end-to-end (local)
102
+
103
+ ### 5.1 Set environment variables
104
+
105
+ ```sh
106
+ # REQUIRED — use a model that is actually in the list:
107
+ export AIDLC_OPENCODE_MODEL=opencode-go/deepseek-v4-pro
108
+
109
+ # OPTIONAL — required ONLY for the web dashboard live log when you start the
110
+ # daemon manually. Must point at the exact file the UI reads:
111
+ export AIDLC_LOOP_EVENT_LOG="$PWD/.agents/data/loop/ui-events.jsonl"
112
+
113
+ # No need to set AIDLC_WORKER_DOCKER_IMAGE — the loop builds the image itself (see §7).
114
+ # Only the worker credential is needed when it reaches the build phase:
115
+ # export OPENAI_API_KEY=... or export ANTHROPIC_API_KEY=... (per model)
116
+ ```
117
+
118
+ List available models:
119
+
120
+ ```sh
121
+ opencode models
122
+ ```
123
+
124
+ > [!IMPORTANT]
125
+ > Both variables must be exported **in the same shell that starts the daemon** — the
126
+ > daemon and every lead/worker turn it spawns inherit only that process's environment.
127
+ > A daemon started *before* you exported `AIDLC_OPENCODE_MODEL` will keep failing with
128
+ > `opencode provider requires AIDLC_OPENCODE_MODEL` until it is restarted.
129
+
130
+ ### 5.2 Start the daemon, then add tasks
131
+
132
+ In **local** mode `loop start` launches the persistent daemon (one per project — a second
133
+ `loop start` fails with `Loop daemon already running (pid N)`). It does **not** read a
134
+ requirement:
135
+
136
+ ```sh
137
+ # via npx (published package):
138
+ npx --package @felixdotgo/aidlc-kit@0.2.0 aidlc-kit loop start --root .
139
+
140
+ # or from a source checkout (after `npm run build`):
141
+ node dist/src/cli.js loop start --root .
142
+ ```
143
+
144
+ The daemon only runs **loop-owned** tasks — tasks created through loop intake (they have
145
+ `.agents/data/tasks/<id>/loop-intake.json`) or that already have a loop run record. A task
146
+ you create interactively (Claude Code/Codex/OpenCode via `state.mjs task create`) is never
147
+ picked up, so the daemon cannot race an interactive lead on the same task.
148
+
149
+ Add a loop-owned task in either way:
150
+
151
+ - **Web dashboard** → **New task** (§5.7): creates the task and starts the daemon if none is
152
+ running.
153
+ - **Terminal** — the requirement is read from stdin (never an argument), max 8192 bytes; the
154
+ task id/title are derived from its first line (override with `--task-id`/`--title`):
155
+
156
+ ```sh
157
+ echo "Add PDF report export" | npx --package @felixdotgo/aidlc-kit@0.2.0 aidlc-kit loop intake --root .
158
+ echo "Add PDF report export" | node dist/src/cli.js loop intake --root .
159
+ # or the installed entry point directly:
160
+ echo "Add PDF report export" | node .agents/aidlc/scripts/loop-entry.mjs intake "" .
161
+ ```
162
+
163
+ Intake refuses (and creates nothing) while an **interactive** task is actionable; finish or
164
+ pause it first. Other actionable loop-owned tasks are fine — the daemon runs them in turn.
165
+
166
+ Stopping the daemon (`Ctrl-C` or `SIGTERM`) is two-stage: the first signal stops after the
167
+ current turn, the second cancels the in-flight agent turn, the third exits immediately. A
168
+ stopped run is recorded as `stopped` (the task is **not** paused) and resumes on the next
169
+ `loop start`. Note that a terminal `Ctrl-C` is also delivered to the agent child process, so
170
+ the in-flight turn usually ends right away even on the first press; it is still recorded as
171
+ `stopped`, not as a transport failure. `aidlc-kit loop start` stays in the foreground for
172
+ the whole shutdown (it forwards `SIGTERM` to the daemon and lets the terminal deliver
173
+ `Ctrl-C`), so the second and third presses still reach the daemon.
174
+
175
+ ### 5.3 Watch status
176
+
177
+ ```sh
178
+ # via npx:
179
+ npx --package @felixdotgo/aidlc-kit@0.2.0 aidlc-kit loop status --task-id <task-id> --root .
180
+
181
+ # or from source:
182
+ node dist/src/cli.js loop status --task-id <task-id> --root .
183
+ ```
184
+
185
+ Prints `authority`, `record` (run record), and `nextAction`
186
+ (`classification` = `run_phase` / `await_user` / `blocked` / `complete`...).
187
+
188
+ ### 5.4 Approve a gate (trusted terminal or dashboard — never the agent)
189
+
190
+ ```sh
191
+ node .agents/aidlc/scripts/loop-gate.mjs <task-id>
192
+ # type: approve
193
+ ```
194
+
195
+ Never approve from the lead/worker, and never pipe input. The signed-in dashboard (§5.7)
196
+ offers the same approval on the task detail page.
197
+
198
+ ### 5.5 Resume after stopping at a gate / pause
199
+
200
+ There is **no `loop resume`** (in either mode) — the daemon is the only runner,
201
+ and it auto-resumes every task whose derived `nextAction` is `run_phase` (an *actionable*
202
+ task). What you do depends on why the task stopped:
203
+
204
+ - **After approving a gate** — nothing to do. Approval moves the task back to `active`, the
205
+ daemon's next poll sees it as actionable, and it runs the next lead turn automatically.
206
+ - **After a pause** (e.g. an agent transport failure or the per-run turn limit) — the loop
207
+ marks the task `paused` (`nextAction` becomes `blocked`), which is **not** actionable, so
208
+ the daemon skips it. Fix the root cause, then set the task back to `active` (or press
209
+ **resume** on the dashboard, which does exactly this):
210
+
211
+ ```sh
212
+ node .agents/aidlc/scripts/state.mjs task status <task-id> --status active
213
+ ```
214
+
215
+ The daemon picks it up on the next poll (default ~5s).
216
+
217
+ > `aidlc-kit loop resume`/`loop-entry.mjs resume` fails closed with
218
+ > `loop resume is removed; the daemon auto-resumes actionable tasks — run 'loop start', and re-activate a paused task from the dashboard (resume) or with the task status command`.
219
+ > Use `state.mjs task status --status active` (or `loop-entry.mjs resume-task <task-id> .`)
220
+ > instead of trying to "resume the loop".
221
+
222
+ ### 5.6 Streaming log — see what the lead is doing
223
+
224
+ The loop prints progress to **stderr** (stdout remains the final JSON), so you can see
225
+ what the lead is doing during a long turn:
226
+
227
+ ```
228
+ ━━ turn 1 · phase clarify · agent opencode ━━
229
+ › tool: grep
230
+ › tool: read
231
+ › command completed (exit 0)
232
+ <stream text from the lead>
233
+ ━━ turn done ━━
234
+ ```
235
+
236
+ When the lead reaches a gate, the daemon immediately prints the **gate view** (approval
237
+ instructions) and moves on to the next actionable task; after you approve in a second
238
+ terminal via `loop-gate.mjs <id>` (or on the dashboard), the next poll resumes that task.
239
+ The loop never self-approves.
240
+
241
+ Streamed events: `turn_start`/`turn_end` (turn boundary + phase + agent), `agent_text`
242
+ (lead text, capped at 4096 chars/chunk), `agent_tool` (tool being used such as
243
+ `grep`/`read`/`glob`/`shell`), `lifecycle_command` (shell result + exit code), and `gate`
244
+ (gate view), plus daemon events `daemon_start`, `daemon_poll`, `daemon_poll_error` (the task
245
+ list could not be read; the daemon keeps polling), `daemon_turn`, `daemon_turn_error`, and
246
+ `loop_stopped`. A task whose run throws, or ends `blocked`/`reconcile`, is retried with
247
+ exponential backoff (poll interval doubled per failure, capped at 10 minutes). In the build
248
+ phase, `build_deferred` names a ready item held back for a later batch (`reason`: overlaps
249
+ another item, a busy path held by an active assignment, an unresolved assignment, or
250
+ `capacity` — at most one item per configured worker per batch). Every event of a run
251
+ carries its `taskId`.
252
+
253
+ ### 5.7 Web dashboard (`aidlc-kit ui`)
254
+
255
+ ```sh
256
+ # via npx:
257
+ npx --package @felixdotgo/aidlc-kit@0.2.0 aidlc-kit ui .
258
+
259
+ # or from source:
260
+ node dist/src/cli.js ui .
261
+ ```
262
+
263
+ The dashboard is branded **Loop**. It binds `127.0.0.1:4173` by default and prints a **login
264
+ URL** (`http://127.0.0.1:4173/#token=…`). Open it (or pass `--open` to launch the browser):
265
+ the page stores the token in `localStorage` and strips it from the address bar, so mutating
266
+ actions (new task, resume, gate approve) work without pasting anything. The token is new on
267
+ every start; use `--token-file ~/.aidlc-ui-token` (outside the project, mode `600`, created
268
+ if missing) to keep the browser signed in across restarts. **⚙ Settings** still
269
+ accepts a pasted token. Requests from another origin or with a foreign `Host` header are
270
+ rejected `403` (see [Command reference](./command-reference.md#dashboard-authentication)).
271
+ The header shows the authority (`authority · local`) and whether the daemon is running. The page follows the system light/dark preference
272
+ automatically (Bulma plus the flat `assets/ui/dashboard.css` theme layer) and uses the full
273
+ window width; results of actions appear as toasts in the bottom-right corner. Shortcuts:
274
+ <kbd>N</kbd> new task, <kbd>/</kbd> search, <kbd>Esc</kbd> close the side panel or a dialog.
275
+
276
+ Where things live (this is the #1 source of confusion):
277
+
278
+ - **Gate approval button is only on the full task detail page** (`/tasks/<task-id>`), **not
279
+ on the dashboard**. Tasks waiting at a gate are listed under **Needs your review** at the
280
+ top of the dashboard; **Open review →** jumps to their detail page. Otherwise click a task to
281
+ open it in the **side panel** (it slides in from the right and adds `?task=<task-id>` to the
282
+ URL, so refresh or a shared link reopens it and Back closes it) and press **Open full page ↗**,
283
+ or ctrl/cmd-click / middle-click the task link. Both the side panel and the detail page show a
284
+ horizontal **progress** stepper (clarify → plan → build → wrap → done) above the artifacts. On the detail page the **Approve · `<review step>`** panel (approval note + green
285
+ **✓ Approve** button) appears only while the task is `blocked_on_user`. If the gate's
286
+ structural checks no longer pass, the page lists the failing checks instead of the gate view.
287
+ - **New task → Create task** runs loop intake (§5.2) and starts the daemon only if none is
288
+ running (its stderr goes to `.agents/data/loop/daemon.log`). A toast shows the new task id,
289
+ or the real intake error (for example an actionable interactive task).
290
+ - **Stat cards, search, and the status/risk filters** narrow the Kanban and List views in the
291
+ browser only; they never call the API or change state.
292
+ - **Task buttons follow the status:** a `paused` task shows **resume** (sets it back to
293
+ `active`); a `blocked_on_user` task shows **approve**, which opens the detail page.
294
+ - **The live log (bottom of the detail page)** shows only that task's events (on connect, the
295
+ last 200 of them found in the most recent 256 KB of the file, then live). It reads the fixed file `.agents/data/loop/ui-events.jsonl`, which
296
+ rotates to `.jsonl.1` at 5 MB. The daemon only writes there when `AIDLC_LOOP_EVENT_LOG`
297
+ points at that exact path: a daemon started from the dashboard sets it automatically; a
298
+ **manually started** daemon needs the export from §5.1.
299
+
300
+ Gate approval stays equally available on the trusted terminal (`loop-gate.mjs`, §5.4), which
301
+ needs no token.
302
+
303
+ ---
304
+
305
+ ## 6. Lifecycle (phases / gates)
306
+
307
+ | Phase | Who runs it | Stops at gate |
308
+ | --- | --- | --- |
309
+ | clarify | lead | `G0_confirm` |
310
+ | plan | lead | `G1_review` |
311
+ | build | workers (Docker) | `G2_codereview` |
312
+ | wrap | lead | → `complete` |
313
+
314
+ `clarify`, `plan`, `wrap` run on the host, **no Docker needed**.
315
+ Only `build` needs the worker image + credentials (§7).
316
+
317
+ ---
318
+
319
+ ## 7. Worker Docker image (build phase only)
320
+
321
+ The build phase fans workers out into isolated Docker containers and needs an image
322
+ containing the worker provider CLI (`opencode`/`codex`/`claude`). **You no longer need to
323
+ build it by hand following a guide**:
324
+
325
+ - The official worker image is **shipped with the package**
326
+ (`assets/aidlc/docker/Dockerfile.<provider>`).
327
+ - Run a single command to build it (default provider `opencode`):
328
+
329
+ ```sh
330
+ npx @felixdotgo/aidlc-kit loop worker-image --provider opencode
331
+ # --provider codex | --provider claude ; default tag aidlc-worker-<provider>:local
332
+ ```
333
+
334
+ - The build command **records the image name into `.agents/config.json`**
335
+ (`loop.workerImage`); the supervisor reads it automatically, so you **don't need to
336
+ `export AIDLC_WORKER_DOCKER_IMAGE`** manually.
337
+ - When the loop reaches the build phase and an image is missing, the supervisor
338
+ **auto-builds** the image for the right provider before preflight, so you usually don't
339
+ need to run the command above proactively.
340
+
341
+ The `aidlc-worker-*-test:local` images on your machine **only contain a fake CLI for
342
+ tests**; the real image is built from the shipped Dockerfile (base `node:24-bookworm-slim`,
343
+ CLI installed via `npm install -g`).
344
+
345
+ Notes:
346
+ - Debian/glibc base because the `opencode` binary is a dynamically linked ELF; the driver
347
+ runs non-root + `--read-only`, so the CLI must be on the system `PATH` (npm global →
348
+ `/usr/local/bin`).
349
+ - `opencode` uses `@opencode/cli` (OpenCode 2), **not** `opencode-ai` (OpenCode 1).
350
+ - Worker credentials still need to be exported (per model): `OPENAI_API_KEY` /
351
+ `ANTHROPIC_API_KEY` / `OPENCODE_API_KEY`.
352
+ - Preflight runs `docker run ... --entrypoint <cli> --version`; a missing or invalid
353
+ binary fails before any worker starts.
354
+
355
+ ---
356
+
357
+ ## 8. Quick lead-only run (no Docker, no workers)
358
+
359
+ Run the loop right away with a single lead, no Docker/workers needed:
360
+
361
+ ```sh
362
+ export AIDLC_OPENCODE_MODEL=opencode-go/deepseek-v4-pro
363
+
364
+ node .agents/aidlc/scripts/loop.mjs run <task-id> --agent opencode --max-turns 12 --root .
365
+ node .agents/aidlc/scripts/loop.mjs status <task-id> --root .
366
+ node .agents/aidlc/scripts/loop.mjs resume <task-id> --root .
367
+ ```
368
+
369
+ JSON output returns `status` ∈ `gate | paused | blocked | complete` with `stopReason` and
370
+ `nextAction`.
371
+
372
+ ---
373
+
374
+ ## 9. Troubleshooting
375
+
376
+ ### 9.1 Loop stops immediately / no approve prompt
377
+
378
+ First check the evidence to see why the loop stopped:
379
+
380
+ ```sh
381
+ node .agents/aidlc/scripts/state.mjs task show <task-id>
382
+ ```
383
+
384
+ In the streaming build (after sync), the error appears directly in `stopReason` instead of
385
+ `opencode exited 1:` (empty stderr). Example — model unavailable:
386
+
387
+ ```
388
+ "agent transport failure: opencode reported an error: Model unavailable: opencode-go/flint-40b-free"
389
+ ```
390
+
391
+ → Wrong/unavailable model. Check `opencode models`, set `AIDLC_OPENCODE_MODEL` again, restart
392
+ the daemon, then re-activate the task (§5.5).
393
+
394
+ Raw OpenCode log (full stacktrace) if needed:
395
+
396
+ ```sh
397
+ grep -n "ModelUnavailableError\|Failed to drain Session" ~/.local/share/opencode/log/opencode.log | tail -20
398
+ ```
399
+
400
+ > Note: if the lead runs for a long time without output, it is **not hanging** — a
401
+ > clarify/plan turn with a free model can take a few minutes. Streaming (§5.6) now lets
402
+ > you see each tool/text the lead is running.
403
+
404
+ ### 9.2 `Isolated local worker dispatcher is not integrated`
405
+
406
+ The installed `.agents/` is stale (missing `createIsolatedLocalSupervisor`). Re-sync per
407
+ §4.2.
408
+
409
+ ### 9.3 `Loop requires a configured lead and worker providers`
410
+
411
+ `loop.mode` is enabled but `loop.agents.lead` or `loop.agents.workers` is missing. Check §2.
412
+
413
+ ### 9.4 `Requested mode does not match configured authority`
414
+
415
+ The `--mode` passed in differs from `loop.mode` in config. Drop `--mode` to use the config
416
+ value.
417
+
418
+ ### 9.5 `opencode provider requires AIDLC_OPENCODE_MODEL`
419
+
420
+ `AIDLC_OPENCODE_MODEL` is unset or in the wrong `provider/model[#variant]` format. See §5.1.
421
+ Remember the daemon must be (re)started *after* exporting the variable — a daemon started
422
+ without it keeps failing until restarted.
423
+
424
+ ### 9.6 Web dashboard live log is empty / stuck on "connecting…"
425
+
426
+ - The live log reads a **fixed** file `.agents/data/loop/ui-events.jsonl`; the daemon only
427
+ writes there when `AIDLC_LOOP_EVENT_LOG` points at that exact path. A manually started
428
+ daemon without that export produces no events — see §5.1. Restart the daemon after setting
429
+ it.
430
+ - The live log is only on the task **detail** page (`/tasks/<id>`), not the dashboard.
431
+ - A stopped daemon produces no events. The dashboard header shows whether it is running;
432
+ check `.agents/data/loop/daemon.log` for start-up errors.
433
+ - The detail-page log is filtered to that task; daemon-wide events (`daemon_poll`) appear
434
+ only in the raw file.
435
+ - Check `tail -f .agents/data/loop/ui-events.jsonl` directly: if the file grows with
436
+ `daemon_poll` lines, the daemon is healthy and only the browser side is stale (hard refresh).
437
+
438
+ ### 9.7 Task pauses immediately after I approve a gate; daemon polls `actionable: []`
439
+
440
+ Approval moved the task to `active`, the daemon picked it up and tried a lead turn, but the
441
+ turn failed — so the loop re-paused the task (`status: paused`, `nextAction: blocked`), and a
442
+ paused task is not actionable. Diagnose from the evidence:
443
+
444
+ ```sh
445
+ node .agents/aidlc/scripts/state.mjs task show <task-id>
446
+ ```
447
+
448
+ Look for a `diagnostic` with `Paused while active: …`. For `agent transport failure: …` the
449
+ cause is almost always the missing/invalid `AIDLC_OPENCODE_MODEL` (or an unavailable model).
450
+ For `turn limit reached` the run used its whole per-run `maxTurns` budget without reaching a
451
+ gate; raise `loop.limits.maxTurns` if the phase legitimately needs more turns. Then:
452
+
453
+ 1. Fix the environment (`opencode models`, export `AIDLC_OPENCODE_MODEL`).
454
+ 2. Kill and restart the daemon with the corrected environment (§5.1).
455
+ 3. Set the task back to actionable:
456
+
457
+ ```sh
458
+ node .agents/aidlc/scripts/state.mjs task status <task-id> --status active
459
+ ```
460
+
461
+ ### 9.8 Clicking "approve" (or "resume") on the web UI does nothing
462
+
463
+ - The gate **approve** form exists only on the full detail page `/tasks/<id>`; the
464
+ dashboard's **approve** button on a `blocked_on_user` card just opens that page.
465
+ - Mutating actions need the session token; without it they return `401` (shown inline under
466
+ the button). Open the login URL printed by `aidlc-kit ui` (the token changes on every
467
+ restart unless you use `--token-file`), or paste the token into **Settings**.
468
+ - A `403` means the request was refused before the token check: the page was opened under a
469
+ different host name than the one the server is bound to (use the printed URL; any loopback
470
+ name works when bound to loopback), or the request came from another origin.
471
+ - **resume** only applies to a `paused` task; on any other status it returns `409` with the
472
+ reason (a gate wait must be approved instead). A resumed task runs only if it is
473
+ loop-owned and a daemon is running (§5.2).
474
+ - If approve does *literally nothing* (no inline result, no error, no reload) and you are on
475
+ an old build: open the browser DevTools console and look for
476
+ `SyntaxError: Identifier 'post' has already been declared`. That was a UI bug (the detail
477
+ page declared `const post` twice, silently disabling the approve handler) fixed in source —
478
+ rebuild/repack from a checkout that includes the fix, restart `ui`, and hard-refresh
479
+ (Ctrl+Shift+R) to drop the cached page.
480
+
481
+ ### 9.9 Build items wait (`build_deferred`) or the task pauses with `build dispatch failure`
482
+
483
+ `build_deferred` is normal: ready items whose owned paths overlap, or that exceed the number
484
+ of workers, run in a later batch. A pause with `build dispatch failure: Unresolved assignment …`
485
+ means an earlier assignment still holds the paths every ready item needs; the loop never
486
+ releases it on its own. See [Troubleshooting](./troubleshooting.md#build-items-deferred-or-build-dispatch-failure-unresolved-assignment).
487
+
488
+ ---
489
+
490
+ ## 10. References
491
+
492
+ - [Loop setup runbook](./loop-runbook.md) — the official runbook.
493
+ - [Configuration](./configuration.md#optional-loop-configuration) — `loop.*` fields.
494
+ - [Command reference](./command-reference.md#loop) — full flags & env.
495
+ - [Coordination service](./coordination-service.md) — fleet (multi-machine).