@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/dist/src/workflow.js
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import { createHash } from "node:crypto";
|
|
2
1
|
import { readdirSync, readFileSync } from "node:fs";
|
|
3
2
|
import { join } from "node:path";
|
|
4
3
|
import { fileURLToPath } from "node:url";
|
|
5
4
|
import { defaultConfig } from "./profiles.js";
|
|
6
|
-
import { createLessonIndex, emptyMemoryRegistry, emptyState, lessonStateDigest } from "./state.js";
|
|
5
|
+
import { createLessonIndex, digest, emptyMemoryRegistry, emptyState, lessonStateDigest } from "./state.js";
|
|
7
6
|
const owner = "aidlc-core";
|
|
8
7
|
const assetRoot = fileURLToPath(new URL("../assets/.agents", import.meta.url));
|
|
9
8
|
const packageJson = JSON.parse(readFileSync(fileURLToPath(new URL("../../package.json", import.meta.url)), "utf8"));
|
|
10
|
-
|
|
9
|
+
// node:crypto update() hashes Uint8Array bytes unchanged; the cast only satisfies digest's narrower parameter type.
|
|
10
|
+
export const contentHash = (content) => digest(content);
|
|
11
11
|
const collect = (directory, target) => readdirSync(directory, { withFileTypes: true }).flatMap((entry) => {
|
|
12
12
|
const source = join(directory, entry.name);
|
|
13
13
|
const destination = `${target}/${entry.name}`;
|
|
@@ -30,7 +30,7 @@ export const manifestFor = (files, adapters) => ({
|
|
|
30
30
|
workflow: "AI-DLC",
|
|
31
31
|
source: "local-package-assets",
|
|
32
32
|
remoteUpdates: false,
|
|
33
|
-
managedBy: "aidlc-
|
|
33
|
+
managedBy: "aidlc-kit",
|
|
34
34
|
adapters,
|
|
35
35
|
files: Object.fromEntries(files.filter((file) => file.ownershipClass === "managed" || !file.ownershipClass).map((file) => [manifestKey(file), {
|
|
36
36
|
hash: contentHash(file.strategy === "managed-block" ? file.content.trimEnd() : file.content),
|
package/docs/README.md
CHANGED
|
@@ -8,7 +8,8 @@ Use this page to choose the guide that matches your role and next task.
|
|
|
8
8
|
2. Read [Operating the workflow](./operating-workflow.md) before asking an agent to change a project.
|
|
9
9
|
3. Use [Configuration](./configuration.md) to configure project rules, profiles, and Codex, Claude Code, and OpenCode permissions.
|
|
10
10
|
4. Keep [Command reference](./command-reference.md) nearby for safe CLI usage.
|
|
11
|
-
5. Read [AI-DLC Coordination service](./coordination-service.md) before enabling remote canonical state.
|
|
11
|
+
5. Read [AI-DLC Coordination service](./coordination-service.md) before enabling remote canonical state — the coordination service and **fleet** mode are **experimental and still under active development**, with known bugs and possible breaking changes in every release before `1.0.0`.
|
|
12
|
+
6. Use the [Loop setup runbook](./loop-runbook.md) to run the loop end-to-end — enable from `off`, run in `local` mode, or spread across machines with `fleet`.
|
|
12
13
|
|
|
13
14
|
## For contributors and maintainers
|
|
14
15
|
|
|
@@ -26,6 +27,9 @@ Use this page to choose the guide that matches your role and next task.
|
|
|
26
27
|
| [Development](./development.md) | Contributors | Dependencies, source layout, local commands, and implementation rules. |
|
|
27
28
|
| [Testing and release](./testing-and-release.md) | Maintainers | Test strategy, evaluator, package smoke test, and release checks. |
|
|
28
29
|
| [Architecture](./architecture.md) | Contributors | Boundaries, dependency direction, and source-to-consumer flow. |
|
|
29
|
-
| [AI-DLC Coordination service](./coordination-service.md) | Operators | Opt-in service deployment, state authority, and credential safety. |
|
|
30
|
+
| [AI-DLC Coordination service](./coordination-service.md) | Operators | Opt-in service deployment, state authority, and credential safety. Experimental and under active development: known bugs, breaking changes possible before `1.0.0`. |
|
|
31
|
+
| [Loop setup runbook](./loop-runbook.md) | Operators | Step-by-step loop setup: enable from `off`, run in `local` mode (usable today), or spread across machines with `fleet` (in development). Experimental: breaking changes possible before `1.0.0`. |
|
|
32
|
+
| [Troubleshooting](./troubleshooting.md) | Operators and contributors | Symptom/error-code recovery guide for locks, corrupt state, continuation guard, gate-check errors, and fleet authority. |
|
|
33
|
+
| [Design decisions](./design-decisions.md) | Contributors | ADR-lite: dual core, single JSON state, compact style, human-only upgrade, append-only evidence, and how to add a decision. |
|
|
30
34
|
|
|
31
35
|
When a public command, configuration field, or installed layout changes, update the relevant guide and its validation in the same change.
|
package/docs/architecture.md
CHANGED
|
@@ -26,14 +26,15 @@ The distinction matters:
|
|
|
26
26
|
|
|
27
27
|
| Boundary | Modules | Responsibility |
|
|
28
28
|
| --- | --- | --- |
|
|
29
|
-
| CLI and installation | `src/cli.ts`, `src/installer.ts`, `src/workflow.ts`, `src/adapters.ts` | Parse commands, select adapters, plan deterministic writes, install managed assets, and expose local status/doctor checks. |
|
|
30
|
-
|
|
|
31
|
-
|
|
|
32
|
-
|
|
|
33
|
-
|
|
|
29
|
+
| CLI and installation | `src/cli.ts` + `src/cli/` (`argv`, `options`, `prompt`, `commands` registry), `src/installer.ts`, `src/workflow.ts`, `src/adapters.ts`, `src/mcp-setup.ts` | Parse commands, select adapters, plan deterministic writes, install managed assets (including the coordination-service template), and expose local status/doctor checks. |
|
|
30
|
+
| Shared planning core | `src/managed-planning.ts` | Single manifest reading, inventory-key handling, and the managed-file planning ladder (create/skip/update/conflict) with explicit init vs upgrade policies; consumed by installation and migration. |
|
|
31
|
+
| Lifecycle domain | `src/model/` (barrel `src/model.ts`), `src/state/` (barrel `src/state.ts`: `io`, `paths`, `domain`, `guards`, `archive-record`, `items`, `commands`, `transitions`, `validate`, `store`, `memory`, `lessons`, `lifecycle`, `render`, `migrate`, `lock`), `src/gate.ts`, `src/context.ts` | Validate canonical state, calculate transitions and gate readiness, classify next actions, and build bounded phase packets. |
|
|
32
|
+
| Profiles and project integration | `src/profiles.ts` | Resolve project configuration/profiles, validate config (incl. the config-only `tracker` section — no runtime tracker consumer ships today), and expand effective rules. |
|
|
33
|
+
| Migration | `src/upgrade.ts`, `src/project-migration.ts`, `src/legacy.ts`, `src/upgrade-board.ts` | Plan and apply safe legacy-data and version-layout migration (incl. legacy `BOARD.md` parsing) with audit backups. |
|
|
34
|
+
| Shared file semantics | `src/project-path.ts`, `src/managed-content.ts` | Keep all paths inside the project, reject unsafe symlinks, and own the managed-block marker format as its single definition. |
|
|
34
35
|
| Development evaluator | `dev/evaluator/` | Run repository-only evaluation; never expose evaluator assets through the published package CLI. |
|
|
35
36
|
|
|
36
|
-
Dependencies flow inward: CLI, installation, and migration depend on lifecycle and shared helpers; lifecycle does not depend on installation; evaluator may provision an installation but is not part of published runtime behaviour.
|
|
37
|
+
Dependencies flow inward: CLI, installation, and migration depend on the shared planning core, lifecycle, and shared helpers; the planning core depends only on lifecycle/config/shared modules (`model`, `workflow`, `managed-content`, `profiles`) — never on the CLI, installer, or upgrade. Migration does not import installation (`upgrade.ts` consumes `managed-planning.ts` directly); the remaining one-directional `installer.ts` → `project-migration.ts` edge supports init-time legacy-layout planning. Lifecycle does not depend on installation; evaluator may provision an installation but is not part of published runtime behaviour. `src/ledger.ts` and `src/tracker.ts` were removed as runtime-dead code; the `tracker` config section remains schema-only (`TrackerConfig`).
|
|
37
38
|
|
|
38
39
|
## Installed workflow runtime
|
|
39
40
|
|
|
@@ -45,7 +46,10 @@ The installed kernel consists of contracts, templates, schemas, profiles, and de
|
|
|
45
46
|
| `.agents/data/` | Project owned | Canonical state, task artifacts, derived indexes, memory, and evidence. |
|
|
46
47
|
| `.agents/project/` | Project owned | Local profiles, rules, and evaluation material. |
|
|
47
48
|
|
|
48
|
-
The state machine enforces the lifecycle; gates require explicit human approval; evidence is append-only. Installed scripts remain usable without an `aidlc-
|
|
49
|
+
The state machine enforces the lifecycle; gates require explicit human approval; evidence is append-only. Installed scripts remain usable without an `aidlc-kit` executable or a duplicate board file.
|
|
50
|
+
|
|
51
|
+
> [!WARNING]
|
|
52
|
+
> The **local/interactive loop is usable** today; the end-to-end **fleet** coordinator described below is **still under active development**. Both are **experimental** — because the package is still pre-1.0 (`0.x`), their command surface, configuration fields, and state layout can change incompatibly in any release before `1.0.0`.
|
|
49
53
|
|
|
50
54
|
The interactive loop runs inside the current Codex, Claude Code, or OpenCode conversation. Native workers use local assignment records under `.agents/data/loop/`; those records hold claims and checkpoints while `.agents/data/state/` remains lifecycle authority. The optional headless fleet runner uses the coordination service's PostgreSQL state, fenced lead and worker claims, and Git commit handoff. Its assignment records are operational metadata; task phase, evidence, and gates stay in the canonical lifecycle state. All hosts derive dispatch from task `nextAction` and dependency-ready work.
|
|
51
55
|
|
|
@@ -74,4 +78,4 @@ Installed entry points share a strict dependency-free argv parser. Parsing and c
|
|
|
74
78
|
Use this guide with [Development](./development.md) and [Testing and release](./testing-and-release.md) before changing a cross-cutting boundary.
|
|
75
79
|
|
|
76
80
|
|
|
77
|
-
The optional end-to-end loop coordinator is implemented for both authorities. Local mode runs a foreground supervisor over the canonical local state:
|
|
81
|
+
The optional end-to-end loop coordinator is implemented for both authorities. Local mode runs a foreground supervisor over the canonical local state: a host-executed lead, Docker-isolated workers, and gate approval only through the trusted host terminal. Fleet mode runs the same loop against a coordination service: the service owns supervisor leases, gate checkpoints, and reviewer approval, workers execute in Docker with their own credentials, and Git integration is fenced per backend (SQLite on one host with a local volume, or PostgreSQL across hosts). Selecting a mode configures the loop; only an explicit `loop start` runs it. Host-executed lead turns accept Codex, Claude Code, or OpenCode; container-isolated lead turns accept Codex and Claude Code only and reject OpenCode with an explicit diagnostic because its credentials are file-based. Local Docker worker batches accept Codex, Claude Code, or OpenCode (OpenCode workers authenticate from environment credentials with their XDG roots redirected into the ephemeral worker HOME); fleet worker containers reject OpenCode. See [Coordination service](./coordination-service.md) for deployment and recovery constraints.
|
|
@@ -1,39 +1,41 @@
|
|
|
1
1
|
# Command reference
|
|
2
2
|
|
|
3
|
-
All package commands operate on local files. The examples run version `0.0
|
|
3
|
+
All package commands operate on local files. The examples run version `0.2.0` through npm; `npx` may ask permission to fetch that package on first use. Replace `.` with the target project path when needed. For a source checkout, see the [development guide](./development.md).
|
|
4
|
+
|
|
5
|
+
Global flags available on every invocation: `--version` (or `-v`) prints the package version and exits; `--help` prints usage.
|
|
4
6
|
|
|
5
7
|
## `init`
|
|
6
8
|
|
|
7
9
|
```sh
|
|
8
|
-
npx --package @felixdotgo/aidlc-kit@0.0
|
|
10
|
+
npx --package @felixdotgo/aidlc-kit@0.2.0 aidlc-kit init [path] [--agent <name[,name]> | --all] [--loop-mode off|local|fleet] [--loop-lead codex|claude|opencode] [--loop-workers <id=provider,...>] [--yes] [--dry-run] [--force]
|
|
9
11
|
```
|
|
10
12
|
|
|
11
13
|
Plans the installation, prints a preview, and then writes the workflow only after confirmation (or `--yes`). `--dry-run` prints the same plan without writing. If exactly one installed agent is detected, it can be selected automatically. In an interactive terminal, choose multiple agents with the checkbox prompt: use arrow keys to move, Space to toggle, A to toggle all, and Enter to confirm. For scripts and non-interactive terminals, use `--agent <name[,name]>` or `--all`.
|
|
12
14
|
|
|
13
|
-
In interactive setup, choose a loop mode and, when enabled, the lead and worker providers. For non-interactive setup, omit loop options to keep the default off, or specify `--loop-mode local|fleet` together with `--loop-lead` and `--loop-workers` (for example, `--loop-workers a=codex,b=opencode`). The preview includes the project config change. These settings only configure the loop; run `aidlc-
|
|
15
|
+
In interactive setup, choose a loop mode and, when enabled, the lead and worker providers. For non-interactive setup, omit loop options to keep the default off, or specify `--loop-mode local|fleet` together with `--loop-lead` and `--loop-workers` (for example, `--loop-workers a=codex,b=opencode`). The preview includes the project config change. These settings only configure the loop; run `aidlc-kit loop start` to actually start it. Local loop mode is usable today; fleet mode is still under active development; the loop and the coordination service remain experimental: their flags and defaults may change incompatibly in any release before `1.0.0`.
|
|
14
16
|
|
|
15
17
|
`--force` is for an intentional initial replacement of an unmanaged conflicting file. It is not an upgrade mechanism. If the project already has another workflow package version, use the human-run upgrade flow instead.
|
|
16
18
|
|
|
17
19
|
## `status`
|
|
18
20
|
|
|
19
21
|
```sh
|
|
20
|
-
npx --package @felixdotgo/aidlc-kit@0.0
|
|
22
|
+
npx --package @felixdotgo/aidlc-kit@0.2.0 aidlc-kit status [path] [--json]
|
|
21
23
|
```
|
|
22
24
|
|
|
23
|
-
Reports the installed version and local installation status. It does not query a registry.
|
|
25
|
+
Reports the installed version and local installation status. It does not query a registry. Pass `--json` to print the same data as a JSON object (`workflow`, `installedVersion`, `schema`, `detectedAgents`).
|
|
24
26
|
|
|
25
27
|
## `doctor`
|
|
26
28
|
|
|
27
29
|
```sh
|
|
28
|
-
npx --package @felixdotgo/aidlc-kit@0.0
|
|
30
|
+
npx --package @felixdotgo/aidlc-kit@0.2.0 aidlc-kit doctor [path] [--strict] [--json]
|
|
29
31
|
```
|
|
30
32
|
|
|
31
|
-
Checks the local manifest, configuration, and canonical state. `--strict` turns compatibility warnings such as a legacy manifest into errors. It does not query a registry.
|
|
33
|
+
Checks the local manifest, configuration, and canonical state. `--strict` turns compatibility warnings such as a legacy manifest into errors. It does not query a registry. Pass `--json` to print `{level, message, strict}`; an `error` level sets a non-zero exit code.
|
|
32
34
|
|
|
33
35
|
## `profile validate`
|
|
34
36
|
|
|
35
37
|
```sh
|
|
36
|
-
npx --package @felixdotgo/aidlc-kit@0.0
|
|
38
|
+
npx --package @felixdotgo/aidlc-kit@0.2.0 aidlc-kit profile validate [path]
|
|
37
39
|
```
|
|
38
40
|
|
|
39
41
|
Loads `.agents/config.json`, resolves its profile chain, and prints the effective profile order. Use it after changing `extends` or local profiles.
|
|
@@ -41,7 +43,7 @@ Loads `.agents/config.json`, resolves its profile chain, and prints the effectiv
|
|
|
41
43
|
## `uninstall`
|
|
42
44
|
|
|
43
45
|
```sh
|
|
44
|
-
npx --package @felixdotgo/aidlc-kit@0.0
|
|
46
|
+
npx --package @felixdotgo/aidlc-kit@0.2.0 aidlc-kit uninstall [path] [--yes] [--dry-run]
|
|
45
47
|
```
|
|
46
48
|
|
|
47
49
|
Prints the removal plan before applying it. It removes eligible unchanged managed assets but preserves project configuration and state. Prefer `--dry-run` first when working in an existing project.
|
|
@@ -49,15 +51,17 @@ Prints the removal plan before applying it. It removes eligible unchanged manage
|
|
|
49
51
|
## `mcp setup`
|
|
50
52
|
|
|
51
53
|
```sh
|
|
52
|
-
npx --package @felixdotgo/aidlc-kit@0.0
|
|
54
|
+
npx --package @felixdotgo/aidlc-kit@0.2.0 aidlc-kit mcp setup [path] [--dry-run] [--yes] [--storage sqlite|postgres] [--deployment docker|remote] [--bind localhost|network] [--workspace <id>] [--poll-ms <n>] [--token-env <name>] [--providers <p,...>] [--enable]
|
|
53
55
|
```
|
|
54
56
|
|
|
55
57
|
Previews the optional AI-DLC Coordination service configuration, then writes only after confirmation. Non-interactive use requires `--deployment`, `--storage`, `--bind`, and `--workspace`. It never starts Docker; `--enable` is explicit and remote mode has no local fallback. See [AI-DLC Coordination service](./coordination-service.md) for deployment and credential requirements.
|
|
56
58
|
|
|
59
|
+
`--token-env <name>` overrides the default `AIDLC_MCP_TOKEN` environment variable name used in the generated compose file. `--providers <p,...>` selects project-management connectors non-interactively (`jira`, `trello`, `github-issues`).
|
|
60
|
+
|
|
57
61
|
## `upgrade`
|
|
58
62
|
|
|
59
63
|
```sh
|
|
60
|
-
npx --package @felixdotgo/aidlc-kit@0.0
|
|
64
|
+
npx --package @felixdotgo/aidlc-kit@0.2.0 aidlc-kit upgrade [path] [--loop-mode off|local|fleet] [--loop-lead codex|claude|opencode] [--loop-workers <id=provider,...>] [--dry-run]
|
|
61
65
|
```
|
|
62
66
|
|
|
63
67
|
Upgrade is deliberately human-only. The command rejects `--yes` and `--force`; applying it requires an interactive terminal and an exact typed confirmation of the package version. Interactive confirmations use the same styled terminal prompt as `init`. Agents must not run, preview, detect, or suggest package upgrades on their own.
|
|
@@ -69,16 +73,76 @@ The upgrade plan handles supported legacy `.aidlc/` project data transactionally
|
|
|
69
73
|
## `loop`
|
|
70
74
|
|
|
71
75
|
```sh
|
|
72
|
-
npx --package @felixdotgo/aidlc-kit@0.0
|
|
76
|
+
npx --package @felixdotgo/aidlc-kit@0.2.0 aidlc-kit loop start [--root PATH] [--mode local|fleet]
|
|
77
|
+
npx --package @felixdotgo/aidlc-kit@0.2.0 aidlc-kit loop intake [--task-id ID] [--title TITLE] [--root PATH] [--mode local|fleet]
|
|
78
|
+
npx --package @felixdotgo/aidlc-kit@0.2.0 aidlc-kit loop status --task-id ID [--root PATH] [--mode local|fleet]
|
|
79
|
+
npx --package @felixdotgo/aidlc-kit@0.2.0 aidlc-kit loop worker-image [--provider opencode|codex|claude] [--tag TAG] [--root PATH]
|
|
73
80
|
```
|
|
74
81
|
|
|
75
|
-
|
|
82
|
+
> [!WARNING]
|
|
83
|
+
> **Local loop mode is usable** today; **fleet mode is still under active development**. The loop commands remain **experimental** — because the package is still pre-1.0 (`0.x`), their options, environment variables, and diagnostics can change incompatibly in any release before `1.0.0`.
|
|
84
|
+
|
|
85
|
+
`--mode` defaults to the configured `loop.mode`; the command fails closed when it does not match (`Requested mode does not match configured authority`). Every action except `worker-image` requires an enabled `loop.mode` with a lead and worker mapping. Local and fleet mode share one daemon and the same actions:
|
|
86
|
+
|
|
87
|
+
| Action | Behaviour |
|
|
88
|
+
| --- | --- |
|
|
89
|
+
| `start` | Starts the singleton loop daemon for the project in the foreground (lock `.agents/data/loop/daemon.lock`; a second `start` fails with `Loop daemon already running (pid N)`). It reads **no** requirement. Fleet `start` first preflights the coordination service (`fleet_get`) and the remote integration branch and fails closed instead of falling back to local. |
|
|
90
|
+
| `intake` | Creates one loop-owned task without running it. The requirement is read from stdin (an interactive prompt on a TTY), at most 8192 bytes; the id and title are derived from its first line unless `--task-id`/`--title` are given. Local intake refuses while an interactive (non-loop) task is actionable. Fleet intake creates the remote task under a short lead claim. Both write `.agents/data/tasks/<id>/loop-intake.json` (`authority: "local"` or `"fleet"`). |
|
|
91
|
+
| `status` | Prints the canonical task and its derived next action; local mode adds the loop run record, fleet mode the durable gate checkpoint. |
|
|
92
|
+
| `worker-image` | Builds (or rebuilds) the shipped worker image without requiring loop configuration: `--provider` defaults to `opencode`, `--tag` to `aidlc-worker-<provider>:local`. It runs `docker build` against the packaged `assets/aidlc/docker/Dockerfile.<provider>` and records the tag in `config.loop.workerImage`. |
|
|
93
|
+
|
|
94
|
+
`loop resume` is removed in both modes; it fails with `loop resume is removed; the daemon auto-resumes actionable tasks — run 'loop start' …`. Re-activate a paused task with the dashboard **resume** button, `state.mjs task status <id> --status active` (local), or `node .agents/aidlc/scripts/loop-entry.mjs resume-task <id> .` (either mode).
|
|
95
|
+
|
|
96
|
+
The daemon only runs **loop-owned** tasks: locally, tasks with an intake record or a loop run record; in fleet mode, only tasks whose local intake record says `authority: "fleet"`. Tasks created by an interactive lead are never picked up. Every `loop.pollMs` (default `5000` ms) it lists actionable loop-owned tasks and runs one bounded supervised run per task in round-robin order (bounded by the per-run `loop.limits.maxTurns`). A run that throws, or ends `blocked` or `reconcile`, is retried with exponential backoff (poll interval doubled per failure, capped at 10 minutes). A gate wait or completion does not stop the daemon, and an unreachable authority (for example the fleet service) is reported as `daemon_poll_error` and retried on the next poll. In fleet mode a run returns at a gate (`gate`/`waiting`) and releases the supervisor lease; after the reviewer approves, the next poll re-claims the task and continues.
|
|
97
|
+
|
|
98
|
+
Shutdown is two-stage in both modes: the first `SIGINT`/`SIGTERM` stops after the current turn, the second cancels the in-flight agent turn (and any build worker batch), and the third exits immediately. A terminal `Ctrl-C` also reaches the agent child process, so the in-flight turn usually ends on the first press. Stopped runs are recorded as `stopped` (the task is not paused) and continue on the next `start`. Events go to stderr, carry their `taskId`, and are also appended to the JSONL file named by `AIDLC_LOOP_EVENT_LOG` when set. Daemon events include `daemon_start`, `daemon_poll`, `daemon_poll_error`, `daemon_turn`, `daemon_turn_error`, `build_deferred` (a ready build item waiting for a later batch), `gate`, and `loop_stopped`.
|
|
99
|
+
|
|
100
|
+
Local mode requires Docker plus the provider credential environment; the worker image is auto-built from the packaged Dockerfile on first dispatch and its tag is recorded automatically, so `AIDLC_WORKER_DOCKER_IMAGE` is only needed to override the default. Local approval happens through the trusted host terminal command `node .agents/aidlc/scripts/loop-gate.mjs <task-id>`, which refuses piped input, or the dashboard.
|
|
101
|
+
|
|
102
|
+
Fleet mode requires `AIDLC_FLEET_URL`, `AIDLC_FLEET_WORKSPACE`, the lead `AIDLC_FLEET_TOKEN`, and `AIDLC_FLEET_INTEGRATION_BRANCH` (optionally `AIDLC_FLEET_GIT_REMOTE`). The facade derives `AIDLC_FLEET_PROJECT_ROOT`, `AIDLC_FLEET_WORKER_IDS`, `AIDLC_FLEET_WORKER_PROVIDERS`, and the lead agent from the root and `loop.agents`; explicitly set values must match. `loop.limits` (`maxTurns`, `turnTimeoutMs`, `maxStallTurns`) and `loop.pollMs` become the fleet defaults, and an explicit `AIDLC_FLEET_MAX_TURNS`, `AIDLC_FLEET_TURN_TIMEOUT_MS`, `AIDLC_FLEET_MAX_STALL_TURNS`, or `AIDLC_FLEET_POLL_MS` still overrides them. Lead and worker processes never receive `AIDLC_FLEET_REVIEWER_TOKEN`. Fleet workers run separately through the fleet runner on each worker machine, and a reviewer approves gates with the reviewer credential as documented in [Coordination service](./coordination-service.md).
|
|
103
|
+
|
|
104
|
+
The installed `loop-entry.mjs` is the single entry point behind the CLI and the dashboard. Its positional form is `loop-entry.mjs <action> [task-id] [root] [mode] [provider] [tag] [title]`, for example `echo "<requirement>" | node .agents/aidlc/scripts/loop-entry.mjs intake "" .`. Besides the actions above it accepts `resume-task <id>` (re-activate a paused task, both modes) and the fleet-only dashboard JSON actions `tasks`, `task <id>`, `gate <id>` (current review snapshot with its checkpoint), and `approve <id>` (approval source on stdin; reviewer credential only from that process's `AIDLC_FLEET_REVIEWER_TOKEN`). A local dashboard reads local state in-process, so those read actions fail in local mode.
|
|
105
|
+
|
|
106
|
+
## `ui`
|
|
107
|
+
|
|
108
|
+
```sh
|
|
109
|
+
npx --package @felixdotgo/aidlc-kit@0.2.0 aidlc-kit ui [path] [--port 4173] [--host 127.0.0.1] [--open] [--token-file PATH] [--reviewer-token-file PATH]
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
Starts a web dashboard (built on `node:http`, no extra dependencies) for viewing task status and operating the loop without typing CLI commands repeatedly. The project is the positional `path` (default `.`); `ui` has no `--root` flag. The authority comes from `loop.mode`: the header shows `authority · local` or `authority · fleet`. Local dashboards read canonical local state in-process; fleet dashboards read tasks, task details, and gate snapshots (review text from the coordination service) through the `loop-entry.mjs` JSON actions.
|
|
113
|
+
|
|
114
|
+
| Flag | Effect |
|
|
115
|
+
| --- | --- |
|
|
116
|
+
| `--port`, `--host` | Bind address; defaults to `127.0.0.1:4173`. Pass `--host 0.0.0.0` only as an explicit opt-in. |
|
|
117
|
+
| `--open` | Launches the system browser with the login URL (best effort; the URL is printed anyway). |
|
|
118
|
+
| `--token-file PATH` | Uses a fixed session token from `PATH` so the browser stays signed in across restarts. The file must live outside the project, be a regular non-symlink file with mode `600`/`400`, and hold one 16–256 character token; a missing file is created with a random token. |
|
|
119
|
+
| `--reviewer-token-file PATH` | Fleet only: reads the reviewer credential from `PATH` (same file rules, never created). `AIDLC_FLEET_REVIEWER_TOKEN` in the `ui` environment works the same way. |
|
|
120
|
+
|
|
121
|
+
Routes:
|
|
122
|
+
|
|
123
|
+
- `GET /` renders a **kanban board by default** (columns grouped by `phase`: clarify / plan / build / wrap / done) with a list/table toggle, plus **New task** and **Settings** modals. The page uses the full window width and is branded **Loop**. Clicking a task opens a **side panel** that slides in from the right (`/?task=<id>` opens it directly; Back or <kbd>Esc</kbd> closes it); ctrl/cmd-click or middle-click opens the full detail page.
|
|
124
|
+
- `GET /tasks/:id` renders the full task detail page: a horizontal progress stepper, a structured gate view (in fleet mode the review snapshot text), rendered `intent`/`design`/`workplan` artifacts (Markdown), decisions, work items, evidence, details, and a live log stream. `GET /partial/tasks/:id` returns the same detail body as an HTML fragment for the side panel.
|
|
125
|
+
- `GET /api/tasks` returns task summaries as JSON (`?archive=1`, `?statuses=`, `?query=`, `?limit=`, `?cursor=`); `GET /api/tasks/:id` returns one task; `GET /api/tasks/:id/gate` returns the gate view or fleet snapshot (`409` when the task is not waiting); `GET /api/tasks/:id/artifact/:name` returns raw artifact content for programmatic clients.
|
|
126
|
+
- `GET /api/loop/status?task-id=` returns `loop status` as JSON. `POST /api/loop/start` (field `requirement`) runs loop intake and starts the daemon if none is running (`201` with `taskId`, `title`, `daemon`; `409` with the intake error). `POST /api/loop/resume` (field `taskId`) re-activates a `paused` task (`409` for any other status). `POST /api/gate/approve` (fields `taskId`, `source`) runs the atomic `state.mjs gate approve` locally, or the fleet reviewer approval of the current snapshot. All spawn the installed scripts asynchronously as thin facades; a malformed or non-object JSON body is a `400`.
|
|
127
|
+
- `GET /api/events[?task-id=]` streams the event-log sidecar as SSE: on connect the last 200 matching lines within the most recent 256 KB, then new complete lines; it follows rotation/truncation and resumes from `Last-Event-ID`.
|
|
128
|
+
|
|
129
|
+
### Dashboard authentication
|
|
130
|
+
|
|
131
|
+
At start the CLI prints a login URL such as `http://127.0.0.1:4173/#token=<token>`. Opening it stores the token in the browser's `localStorage` and strips the fragment from the address bar (a fragment is never sent to the server); **⚙ Settings** still accepts a pasted token. There is no unauthenticated mode. The rules:
|
|
132
|
+
|
|
133
|
+
- The mutating actions (new task, resume, gate approve) require the token in a header — `x-aidlc-token` or `Authorization: Bearer <token>` — never in a form or JSON body field. A missing or wrong token is `401`.
|
|
134
|
+
- Every request whose `Host` header does not name the bound address is rejected `403` (any loopback name — `127.0.0.1`, `localhost`, `[::1]` — is accepted when bound to loopback). A wildcard bind (`0.0.0.0`, `::`) cannot pin the Host and relies on the token and the Origin check.
|
|
135
|
+
- A non-`GET` request whose `Origin` is not the dashboard's own origin is rejected `403`.
|
|
136
|
+
- In fleet mode, `POST /api/gate/approve` returns `403` unless the UI holds a reviewer credential (`AIDLC_FLEET_REVIEWER_TOKEN` or `--reviewer-token-file`); the detail page then shows the `remote-state.mjs gate inbox` / `gate approve` reviewer commands instead of the approve form. The credential is passed only to that one approve child process.
|
|
137
|
+
- The daemon and every other child process the UI spawns never inherit the UI token or the reviewer credential.
|
|
76
138
|
|
|
77
|
-
`
|
|
139
|
+
These rules exist because a browser will send requests to `127.0.0.1` on behalf of any web page: a header-only token defeats cross-site form posts (CSRF), the Host check defeats DNS rebinding, and keeping tokens out of the daemon environment stops a host-executed lead from approving its own gate. Residual risk: a `--token-file` is readable by any process running as the same user, so prefer the per-session token unless you need restarts to keep the browser signed in.
|
|
78
140
|
|
|
79
|
-
|
|
141
|
+
Operational notes:
|
|
80
142
|
|
|
81
|
-
|
|
143
|
+
- The gate **approve** form renders only on the full task detail page (`/tasks/:id`) while the task is `blocked_on_user`; on the dashboard such a task shows an **approve** button that opens that page. The dashboard header shows whether the daemon is running.
|
|
144
|
+
- The **live log** on the detail page is an SSE stream of that task's events over the fixed file `.agents/data/loop/ui-events.jsonl` (rotated to `.jsonl.1` at 5 MB). A daemon started by **New task** gets `AIDLC_LOOP_EVENT_LOG` pointed at that file automatically and writes its stderr to `.agents/data/loop/daemon.log`; a **manually started** daemon must export the same variable, or the live log stays empty (see [Loop local run](./loop-local-run.md#51-set-environment-variables)).
|
|
145
|
+
- The dashboard **resume** button appears only on `paused` tasks and sets them back to `active` (locally equivalent to `node .agents/aidlc/scripts/state.mjs task status <id> --status active`; in fleet mode `loop-entry.mjs resume-task`); the daemon then picks the task up on its next poll if it is loop-owned.
|
|
82
146
|
|
|
83
147
|
## Installed lifecycle scripts
|
|
84
148
|
|
|
@@ -105,7 +169,7 @@ node .agents/aidlc/scripts/loop.mjs resume <task-id> [--max-turns N] [--turn-tim
|
|
|
105
169
|
node .agents/aidlc/scripts/loop.mjs status <task-id> [--root PATH]
|
|
106
170
|
```
|
|
107
171
|
|
|
108
|
-
`--agent opencode` requires `AIDLC_OPENCODE_MODEL` in `provider/model[#variant]` form; the turn fails closed without it.
|
|
172
|
+
`--agent opencode` requires `AIDLC_OPENCODE_MODEL` in `provider/model[#variant]` form; the turn fails closed without it. The container-isolated lead rejects OpenCode with `OpenCode workers require file-based credentials and are not available in isolated containers yet` and does not fall back to the host. Local Docker worker batches and fleet worker containers accept OpenCode via environment credentials — the key follows the `AIDLC_OPENCODE_MODEL` prefix: `openai/` → `OPENAI_API_KEY`, `anthropic/` → `ANTHROPIC_API_KEY`, otherwise `OPENCODE_API_KEY` — with OpenCode's XDG data/config roots redirected into the worker's ephemeral HOME. Use `loop.mjs run --agent opencode` for host-executed lead turns, or use Codex or Claude Code where an isolated lead is required.
|
|
109
173
|
|
|
110
174
|
The defaults are 12 turns and 10 minutes per turn; two completed turns without observable progress stop the run. Each run records its provider, and `resume` rejects a different agent. The runner never grants human approval. An interactive client can resume by reading `task next` and reconciling local assignment metadata; fleet execution uses fenced service leases and the remote canonical state.
|
|
111
175
|
|
|
@@ -119,7 +183,7 @@ node .agents/aidlc-coordination/src/fleet-run.mjs coordinator <task-id> --once
|
|
|
119
183
|
node .agents/aidlc-coordination/src/fleet-run.mjs worker --once
|
|
120
184
|
```
|
|
121
185
|
|
|
122
|
-
`supervisor` claims the task's supervisor lease, runs fenced lead turns through the coordinator, publishes each gate snapshot with its checkpoint in one service transaction, waits for the reviewer approval, and resumes automatically; it returns at a pause, blocked state, or completion, and never approves a gate. `coordinator --once` runs a single unattended pass without the supervisor lease. `worker --once` claims one ready assignment on a worker machine
|
|
186
|
+
`supervisor` claims the task's supervisor lease, runs fenced lead turns through the coordinator, publishes each gate snapshot with its checkpoint in one service transaction, waits for the reviewer approval, and resumes automatically; it returns at a pause, blocked state, or completion, and never approves a gate. `coordinator --once` runs a single unattended pass without the supervisor lease. `worker --once` claims one ready assignment on a worker machine and fails closed without a usable worker image or that machine's worker credential; see [Coordination service](./coordination-service.md#multi-machine-loop-deployment) for how the image is resolved. `aidlc-kit loop start --mode fleet` runs the same supervisor inside the shared loop daemon in a bounded mode that returns at each gate instead of waiting for the reviewer.
|
|
123
187
|
|
|
124
188
|
Fleet mode uses `remote-state.mjs` instead of local `state.mjs`. Set `AIDLC_FLEET_URL`, `AIDLC_FLEET_WORKSPACE`, and `AIDLC_FLEET_PROJECT_ROOT`; lead mutations additionally require `AIDLC_FLEET_TOKEN` and the current `AIDLC_FLEET_LEAD_EPOCH`. Claim an epoch before creating or changing a task:
|
|
125
189
|
|
|
@@ -155,7 +219,7 @@ node .agents/aidlc/scripts/state.mjs gate approve <task-id> --gate <gate> --sour
|
|
|
155
219
|
node .agents/aidlc/scripts/state.mjs task transition <task-id> --to <phase> --mode audited --reason "<reason>" --source "<source>"
|
|
156
220
|
```
|
|
157
221
|
|
|
158
|
-
Self-transitions are rejected, and `task create` rejects an id that collides case-insensitively with an existing task or archive record. `task create` also fails while another unrelated task is actionable; its `--switch-from <ids>` acknowledgement still requires explicit user direction. `--feature <feature-id>` permits sibling tasks in an existing feature without abandoning them. `feature link` checks membership and task dependencies; `task item ... --depends-on <item-id,...> --owns <path,...>` adds item edges and an optional Git path scope.
|
|
222
|
+
Self-transitions are rejected, and `task create` rejects an id that collides case-insensitively with an existing task or archive record. `task create` also fails while another unrelated task is actionable; its `--switch-from <ids>` acknowledgement still requires explicit user direction. `--feature <feature-id>` permits sibling tasks in an existing feature without abandoning them. `feature link` checks membership and task dependencies; `task item ... --depends-on <item-id,...> --owns <path,...>` adds item edges and an optional Git path scope. Loop dispatch (local and fleet) requires a nonempty path scope. When `loop.mode` is `local` or `fleet`, the Approve plan check also fails with `PARTITION_SCOPE` for an open item without `--owns`, and with `PARTITION_OVERLAP` when two items that can run concurrently (no direct or transitive `--depends-on` between them) own overlapping paths; with the loop off both are warnings. Overlap is per path segment: `src` overlaps `src/a.ts` but not `srcx`. The rendered workplan shows each item's scope as `owns: … · after: …`. Path scopes are normalized (trailing slashes stripped) and may name either a file or a directory; directory scopes cover every file beneath them. Missing and cyclic dependencies fail, and an item or feature task cannot finish before its prerequisites. After reopening the Approve plan step, all non-deferred execution items return to `todo`; prior Review implementation, verification, and review evidence cannot satisfy the new build boundary, and repair-bound counters reset with it.
|
|
159
223
|
|
|
160
224
|
State writes and generated workplans use symlink-checked atomic replacement. State-lock release is owner-token checked and never removes a replacement owner's lock. If a process stops after writing a terminal archive record but before committing the catalog, a retry replaces that orphan only when the persisted catalog still contains the same active task identity and does not reference an archive record; malformed, referenced, or identity-conflicting records fail closed.
|
|
161
225
|
|
package/docs/configuration.md
CHANGED
|
@@ -22,6 +22,18 @@ Each installed project owns its workflow configuration at `.agents/config.json`.
|
|
|
22
22
|
|
|
23
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.
|
|
24
24
|
|
|
25
|
+
## Optional `$schema` key
|
|
26
|
+
|
|
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
|
+
|
|
25
37
|
G0, G1, and G2 always require explicit human approval. Configuration cannot auto-pass a gate.
|
|
26
38
|
|
|
27
39
|
## Configuration layers
|
|
@@ -42,10 +54,10 @@ Built-in topology profiles are:
|
|
|
42
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:
|
|
43
55
|
|
|
44
56
|
```sh
|
|
45
|
-
npx --package @felixdotgo/aidlc-kit@0.0
|
|
57
|
+
npx --package @felixdotgo/aidlc-kit@0.2.0 aidlc-kit profile validate .
|
|
46
58
|
```
|
|
47
59
|
|
|
48
|
-
This command uses the npm package at version `0.0
|
|
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).
|
|
49
61
|
|
|
50
62
|
## Rules, project data, and managed assets
|
|
51
63
|
|
|
@@ -71,21 +83,32 @@ Set the default risk level deliberately. Planning, migrations, cross-service con
|
|
|
71
83
|
|
|
72
84
|
## Optional AI-DLC Coordination
|
|
73
85
|
|
|
74
|
-
`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.
|
|
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
87
|
|
|
76
88
|
## Optional loop configuration
|
|
77
89
|
|
|
78
|
-
|
|
90
|
+
> [!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**.
|
|
92
|
+
|
|
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`:
|
|
79
94
|
|
|
80
95
|
```json
|
|
81
96
|
{
|
|
82
97
|
"loop": {
|
|
83
98
|
"mode": "local",
|
|
84
|
-
"agents": { "lead": "codex", "workers": { "worker-a": "codex", "worker-b": "opencode" } }
|
|
99
|
+
"agents": { "lead": "codex", "workers": { "worker-a": "codex", "worker-b": "opencode" } },
|
|
100
|
+
"pollMs": 5000,
|
|
101
|
+
"limits": { "maxTurns": 12, "turnTimeoutMs": 600000, "maxStallTurns": 2 }
|
|
85
102
|
}
|
|
86
103
|
}
|
|
87
104
|
```
|
|
88
105
|
|
|
89
|
-
|
|
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.
|
|
107
|
+
|
|
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.
|
|
109
|
+
|
|
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).
|
|
111
|
+
|
|
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.
|
|
90
113
|
|
|
91
|
-
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-
|
|
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).
|
|
@@ -1,5 +1,8 @@
|
|
|
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.
|
|
5
|
+
|
|
3
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.
|
|
4
7
|
|
|
5
8
|
## Safe setup
|
|
@@ -7,11 +10,11 @@ The optional AI-DLC Coordination service is a separate Docker-deployable authori
|
|
|
7
10
|
Preview a configuration first. Outside an interactive terminal, provide all four selection flags.
|
|
8
11
|
|
|
9
12
|
```sh
|
|
10
|
-
npx --package @felixdotgo/aidlc-kit@0.0
|
|
13
|
+
npx --package @felixdotgo/aidlc-kit@0.2.0 aidlc-kit mcp setup . --dry-run \
|
|
11
14
|
--deployment docker --storage sqlite --bind localhost --workspace engineering
|
|
12
15
|
```
|
|
13
16
|
|
|
14
|
-
This command uses the npm package at version `0.0
|
|
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).
|
|
15
18
|
|
|
16
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
20
|
|
|
@@ -67,16 +70,18 @@ node .agents/aidlc/scripts/remote-state.mjs gate approve <task-id> --snapshot <s
|
|
|
67
70
|
|
|
68
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.
|
|
69
72
|
|
|
70
|
-
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,
|
|
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:
|
|
71
74
|
|
|
72
75
|
```sh
|
|
73
76
|
node dist/services/aidlc-coordination/src/fleet-run.mjs feature <feature-id>
|
|
74
77
|
node dist/services/aidlc-coordination/src/fleet-run.mjs worker
|
|
75
78
|
```
|
|
76
79
|
|
|
77
|
-
`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. 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.
|
|
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.
|
|
81
|
+
|
|
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.
|
|
78
83
|
|
|
79
|
-
|
|
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.
|
|
80
85
|
|
|
81
86
|
## Shared coordination board
|
|
82
87
|
|
|
@@ -93,3 +98,13 @@ The service checks lifecycle invariants across the whole before/after snapshot,
|
|
|
93
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.
|
|
94
99
|
|
|
95
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.
|
|
101
|
+
|
|
102
|
+
## Service health, logging, and image
|
|
103
|
+
|
|
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.
|
|
105
|
+
|
|
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.
|
|
107
|
+
|
|
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.
|
|
109
|
+
|
|
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.
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# Design decisions (ADR-lite)
|
|
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.
|
|
4
|
+
|
|
5
|
+
## D1. Dual lifecycle cores with conformance instead of codegen
|
|
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.
|
|
10
|
+
|
|
11
|
+
## D2. Single JSON state with file lock and archive catalog
|
|
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.
|
|
16
|
+
|
|
17
|
+
## D3. Append-only evidence with append-order boundaries
|
|
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.
|
|
22
|
+
|
|
23
|
+
## D4. Human-only upgrades
|
|
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.
|
|
27
|
+
- **Consequences:** Upgrades are auditable and intentional; CI cannot auto-upgrade; rollback uses the upgrade journal.
|
|
28
|
+
|
|
29
|
+
## D5. Local-first / no-registry policy
|
|
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.
|
|
33
|
+
- **Consequences:** No surprise network calls; version drift is detected locally; fleet authority is explicit via `AIDLC_FLEET_URL`.
|
|
34
|
+
|
|
35
|
+
## D6. Compact code style in the installed kernel
|
|
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.
|
|
40
|
+
|
|
41
|
+
## D7. Machine-enforced repair bounds
|
|
42
|
+
|
|
43
|
+
- **Context:** Repair loops can run indefinitely without a hard stop.
|
|
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.
|
|
46
|
+
|
|
47
|
+
## D8. Assignment records are operational metadata
|
|
48
|
+
|
|
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.
|
|
52
|
+
|
|
53
|
+
## D9. Configured commands as executable + args arrays
|
|
54
|
+
|
|
55
|
+
- **Context:** Shell strings are unsafe and non-portable.
|
|
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.
|
|
58
|
+
|
|
59
|
+
## D10. Task language `vi` / `en`
|
|
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.
|
|
64
|
+
|
|
65
|
+
## D11. Optional `$schema` reference in generated config
|
|
66
|
+
|
|
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.
|
|
70
|
+
|
|
71
|
+
## D12. Timing-safe comparison for all coordination auth
|
|
72
|
+
|
|
73
|
+
- **Context:** Non-fleet coordination endpoints must not leak tokens via timing.
|
|
74
|
+
- **Decision:** Non-fleet coordination auth uses the same timing-safe comparison as the fleet path.
|
|
75
|
+
- **Consequences:** One auth implementation; no fast-path bypass; consistent with the service's credential model.
|
|
76
|
+
|
|
77
|
+
## How to add a decision
|
|
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.
|
|
82
|
+
|
|
83
|
+
See [Architecture](./architecture.md) for the module and ownership boundaries these decisions protect.
|