@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.
- package/DEVELOPMENT.md +14 -0
- package/README.md +14 -6
- package/dist/assets/.agents/aidlc/docker/Dockerfile.claude +7 -0
- package/dist/assets/.agents/aidlc/docker/Dockerfile.codex +9 -0
- package/dist/assets/.agents/aidlc/docker/Dockerfile.opencode +8 -0
- package/dist/assets/.agents/aidlc/orchestrator.md +1 -1
- package/dist/assets/.agents/aidlc/phase-clarify.md +1 -1
- package/dist/assets/.agents/aidlc/phase-plan.md +2 -2
- package/dist/assets/.agents/aidlc/schemas/config.schema.json +2 -1
- package/dist/assets/.agents/aidlc/schemas/manifest.schema.json +1 -1
- package/dist/assets/.agents/aidlc/scripts/lib/context-runtime.mjs +5 -0
- package/dist/assets/.agents/aidlc/scripts/lib/loop-authority.mjs +245 -0
- package/dist/assets/.agents/aidlc/scripts/lib/loop-commit-message.mjs +45 -0
- package/dist/assets/.agents/aidlc/scripts/lib/loop-daemon.mjs +232 -0
- package/dist/assets/.agents/aidlc/scripts/lib/loop-drivers.mjs +21 -4
- package/dist/assets/.agents/aidlc/scripts/lib/loop-fleet-cli.mjs +86 -28
- package/dist/assets/.agents/aidlc/scripts/lib/loop-fleet-remote.mjs +7 -0
- package/dist/assets/.agents/aidlc/scripts/lib/loop-fleet-supervisor.mjs +22 -6
- package/dist/assets/.agents/aidlc/scripts/lib/loop-fleet.mjs +46 -15
- package/dist/assets/.agents/aidlc/scripts/lib/loop-git.mjs +22 -9
- package/dist/assets/.agents/aidlc/scripts/lib/loop-intake.mjs +23 -5
- package/dist/assets/.agents/aidlc/scripts/lib/loop-local-workers.mjs +43 -15
- package/dist/assets/.agents/aidlc/scripts/lib/loop-runtime.mjs +82 -6
- package/dist/assets/.agents/aidlc/scripts/lib/loop-stream.mjs +60 -0
- package/dist/assets/.agents/aidlc/scripts/lib/loop-supervisor-run-local.mjs +142 -5
- package/dist/assets/.agents/aidlc/scripts/lib/loop-supervisor.mjs +9 -1
- package/dist/assets/.agents/aidlc/scripts/lib/loop-worker-docker.mjs +31 -5
- package/dist/assets/.agents/aidlc/scripts/lib/loop-worker-image.mjs +64 -0
- package/dist/assets/.agents/aidlc/scripts/lib/loop-worker-snapshot.mjs +3 -2
- package/dist/assets/.agents/aidlc/scripts/lib/store.mjs +59 -4
- package/dist/assets/.agents/aidlc/scripts/loop-entry.mjs +85 -73
- package/dist/assets/.agents/aidlc/scripts/loop-gate.mjs +19 -7
- package/dist/assets/.agents/aidlc/scripts/loop.mjs +2 -1
- package/dist/assets/.agents/aidlc/scripts/remote-state.mjs +2 -0
- package/dist/assets/.agents/aidlc/scripts/state.mjs +4 -0
- package/dist/assets/.agents/aidlc/templates/design.md +9 -0
- package/dist/assets/.agents/aidlc/templates/intent.md +4 -0
- package/dist/assets/.agents/aidlc/templates/workplan.md +1 -1
- package/dist/assets/.agents/skills/aidlc-plan/SKILL.md +1 -1
- package/dist/assets/ui/bulma.min.css +3 -0
- package/dist/assets/ui/dashboard.css +414 -0
- package/dist/services/aidlc-coordination/Dockerfile +11 -3
- package/dist/services/aidlc-coordination/compose.yaml +12 -0
- package/dist/services/aidlc-coordination/package-lock.json +3 -1
- package/dist/services/aidlc-coordination/package.json +9 -4
- package/dist/services/aidlc-coordination/src/lifecycle-core.mjs +59 -4
- package/dist/services/aidlc-coordination/src/repository.mjs +66 -0
- package/dist/services/aidlc-coordination/src/server.mjs +9 -3
- package/dist/services/aidlc-coordination/test/fleet.test.mjs +21 -0
- package/dist/services/aidlc-coordination/test/server-health-auth.test.mjs +67 -0
- package/dist/src/adapters.d.ts +0 -3
- package/dist/src/adapters.js +11 -6
- package/dist/src/cli/argv.d.ts +12 -0
- package/dist/src/cli/argv.js +62 -0
- package/dist/src/cli/commands.d.ts +8 -0
- package/dist/src/cli/commands.js +129 -0
- package/dist/src/cli/options.d.ts +25 -0
- package/dist/src/cli/options.js +32 -0
- package/dist/src/cli/prompt.d.ts +9 -0
- package/dist/src/cli/prompt.js +66 -0
- package/dist/src/cli.js +67 -217
- package/dist/src/context.js +2 -0
- package/dist/src/gate.d.ts +1 -0
- package/dist/src/gate.js +51 -37
- package/dist/src/installer.d.ts +19 -7
- package/dist/src/installer.js +111 -178
- package/dist/src/legacy.js +1 -0
- package/dist/src/managed-content.d.ts +7 -0
- package/dist/src/managed-content.js +15 -4
- package/dist/src/managed-planning.d.ts +65 -0
- package/dist/src/managed-planning.js +219 -0
- package/dist/src/mcp-setup.js +3 -1
- package/dist/src/model/config-contracts.d.ts +65 -0
- package/dist/src/model/config-contracts.js +1 -0
- package/dist/src/model/install-contracts.d.ts +29 -0
- package/dist/src/model/install-contracts.js +1 -0
- package/dist/src/model/lifecycle-enums.d.ts +13 -0
- package/dist/src/model/lifecycle-enums.js +1 -0
- package/dist/src/model/loop.d.ts +20 -0
- package/dist/src/model/loop.js +1 -0
- package/dist/src/model/manifest.d.ts +16 -0
- package/dist/src/model/manifest.js +1 -0
- package/dist/src/model/records.d.ts +138 -0
- package/dist/src/model/records.js +1 -0
- package/dist/src/model.d.ts +11 -264
- package/dist/src/model.js +1 -1
- package/dist/src/profiles.d.ts +8 -0
- package/dist/src/profiles.js +63 -8
- package/dist/src/project-migration.d.ts +1 -0
- package/dist/src/project-migration.js +7 -0
- package/dist/src/state/archive-record.d.ts +9 -0
- package/dist/src/state/archive-record.js +26 -0
- package/dist/src/state/commands.d.ts +18 -0
- package/dist/src/state/commands.js +27 -0
- package/dist/src/state/domain.d.ts +9 -0
- package/dist/src/state/domain.js +9 -0
- package/dist/src/state/guards.d.ts +7 -0
- package/dist/src/state/guards.js +17 -0
- package/dist/src/state/io.d.ts +10 -0
- package/dist/src/state/io.js +35 -0
- package/dist/src/state/items.d.ts +24 -0
- package/dist/src/state/items.js +147 -0
- package/dist/src/state/lessons.d.ts +7 -0
- package/dist/src/state/lessons.js +74 -0
- package/dist/src/state/lifecycle.d.ts +5 -0
- package/dist/src/state/lifecycle.js +78 -0
- package/dist/src/state/lock.d.ts +7 -0
- package/dist/src/state/lock.js +125 -0
- package/dist/src/state/memory.d.ts +8 -0
- package/dist/src/state/memory.js +100 -0
- package/dist/src/state/migrate.d.ts +17 -0
- package/dist/src/state/migrate.js +60 -0
- package/dist/src/state/partition.d.ts +15 -0
- package/dist/src/state/partition.js +90 -0
- package/dist/src/state/paths.d.ts +11 -0
- package/dist/src/state/paths.js +12 -0
- package/dist/src/state/render.d.ts +26 -0
- package/dist/src/state/render.js +32 -0
- package/dist/src/state/store.d.ts +4 -0
- package/dist/src/state/store.js +75 -0
- package/dist/src/state/transitions.d.ts +21 -0
- package/dist/src/state/transitions.js +95 -0
- package/dist/src/state/validate.d.ts +4 -0
- package/dist/src/state/validate.js +143 -0
- package/dist/src/state.d.ts +20 -117
- package/dist/src/state.js +20 -815
- package/dist/src/task-ui.d.ts +54 -0
- package/dist/src/task-ui.js +1570 -0
- package/dist/src/upgrade-board.d.ts +5 -0
- package/dist/src/upgrade-board.js +89 -0
- package/dist/src/upgrade.d.ts +3 -5
- package/dist/src/upgrade.js +174 -255
- package/dist/src/workflow.js +4 -4
- package/docs/README.md +6 -2
- package/docs/architecture.md +12 -8
- package/docs/command-reference.md +83 -19
- package/docs/configuration.md +30 -7
- package/docs/coordination-service.md +20 -5
- package/docs/design-decisions.md +83 -0
- package/docs/development.md +41 -2
- package/docs/loop-local-run.md +495 -0
- package/docs/loop-runbook.md +235 -0
- package/docs/operating-workflow.md +5 -2
- package/docs/testing-and-release.md +11 -7
- package/docs/troubleshooting.md +125 -0
- package/package.json +5 -2
- package/dist/src/ledger.d.ts +0 -22
- package/dist/src/ledger.js +0 -119
- package/dist/src/tracker.d.ts +0 -29
- package/dist/src/tracker.js +0 -41
package/docs/development.md
CHANGED
|
@@ -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.
|
|
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).
|