@dyzsasd/dev-loop 0.22.0 → 0.23.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/README.md +30 -10
- package/dist/agentops.js +5 -68
- package/dist/cli.js +4 -0
- package/dist/db.js +0 -26
- package/dist/doctor.js +2 -2
- package/dist/install-claude-plugin.js +78 -0
- package/dist/mcp-merge.js +18 -19
- package/dist/mirrorstore.js +1 -1
- package/dist/plugin/.claude-plugin/marketplace.json +13 -0
- package/dist/plugin/.claude-plugin/plugin.json +11 -0
- package/dist/plugin/config/mcp.codex.toml.example +33 -0
- package/dist/plugin/config/mcp.example.json +15 -0
- package/dist/plugin/config/mcp.opencode.json.example +16 -0
- package/dist/plugin/config/projects.example.json +82 -0
- package/dist/plugin/hooks/hooks.json +16 -0
- package/dist/plugin/references/codex-integration.md +282 -0
- package/dist/plugin/references/config-schema.md +358 -0
- package/dist/plugin/references/conventions.md +2159 -0
- package/dist/plugin/skills/architect-agent/SKILL.md +231 -0
- package/dist/plugin/skills/communication-agent/SKILL.md +247 -0
- package/dist/plugin/skills/dev-agent/SKILL.md +373 -0
- package/dist/plugin/skills/init/SKILL.md +496 -0
- package/dist/plugin/skills/junior-dev-agent/SKILL.md +348 -0
- package/dist/plugin/skills/ops-agent/SKILL.md +219 -0
- package/dist/plugin/skills/pm-agent/SKILL.md +427 -0
- package/dist/plugin/skills/qa-agent/SKILL.md +299 -0
- package/dist/plugin/skills/reflect-agent/SKILL.md +271 -0
- package/dist/plugin/skills/senior-dev-agent/SKILL.md +353 -0
- package/dist/plugin/skills/sweep-agent/SKILL.md +180 -0
- package/dist/run-agents.js +373 -0
- package/dist/seed.js +4 -3
- package/dist/server.js +1 -1
- package/dist/shim.js +3 -4
- package/dist/tooldefs.js +3 -25
- package/package.json +5 -5
- package/dist/topicstore.js +0 -174
|
@@ -0,0 +1,358 @@
|
|
|
1
|
+
# dev-loop — Config schema
|
|
2
|
+
|
|
3
|
+
The dev-loop agents (PM / QA / Dev / Sweep / Reflect / Ops / Architect /
|
|
4
|
+
Communication) read
|
|
5
|
+
`${CLAUDE_PLUGIN_DATA}/projects.json`. It maps each product to its Linear project, its
|
|
6
|
+
repo, its test environment, and its ship/deploy settings. One file, many products.
|
|
7
|
+
`/dev-loop:init` gathers and writes this file with you (operator-present setup).
|
|
8
|
+
|
|
9
|
+
## Schema
|
|
10
|
+
|
|
11
|
+
```jsonc
|
|
12
|
+
{
|
|
13
|
+
"defaultProject": "monpick", // used when the user doesn't name one and >1 exist
|
|
14
|
+
"projects": {
|
|
15
|
+
"<key>": { // short slug you'll refer to (e.g. "monpick", "geo")
|
|
16
|
+
"linearTeam": "Citronetic", // Linear team name (required)
|
|
17
|
+
"linearProject": "MonPick", // Linear project name — must exist (required)
|
|
18
|
+
"repoPath": "/abs/path/to/repo", // where Dev works (required for dev-agent). SINGLE-repo (default). For multi-repo, add repos[] below.
|
|
19
|
+
"repos": [ // OPTIONAL — multi-repo only (conventions §19). Absent ⇒ single-repo: top-level repoPath/build/git/deploy are authoritative, 100% unchanged.
|
|
20
|
+
{
|
|
21
|
+
"name": "web", // repo target name → becomes a `repo:<name>` label on tickets (both backends)
|
|
22
|
+
"path": "/abs/path/to/web", // this repo's working copy (Dev commits here for repo:web tickets)
|
|
23
|
+
"role": "primary", // "docs" | "primary" | other. LOAD-BEARING: "docs" else "primary" else repos[0] is the DOC-HOME repo (roots strategyDoc)
|
|
24
|
+
"lang": "ts", // INFORMATIONAL contributor hint only — no logic reads it
|
|
25
|
+
"contributorSkill": null, // optional per-repo skill Dev reads before coding; absent ⇒ top-level contributorSkill, else read this repo's CLAUDE.md
|
|
26
|
+
"defaultBranch": "main", // per-repo override; absent ⇒ git.defaultBranch (autoCommit/autoPush/autoDeploy stay product-level in git)
|
|
27
|
+
"build": null, // per-repo override of the top-level build gates; absent ⇒ top-level build
|
|
28
|
+
"deploy": null // per-repo override; absent ⇒ top-level deploy. A repo resolving to NO deploy SKIPS deploy (never inherits another repo's)
|
|
29
|
+
}
|
|
30
|
+
],
|
|
31
|
+
"strategyDoc": "docs/strategy.md", // PM's north star (required for pm-agent). Either a
|
|
32
|
+
// repo file relative to repoPath (shown), OR a Linear
|
|
33
|
+
// document: { "linearDocument": "<id|slug|url>" } or a
|
|
34
|
+
// "https://linear.app/.../document/..." string. PM reads
|
|
35
|
+
// it (file | get_document) and maintains it (commit |
|
|
36
|
+
// save_document) — see pm-agent §0 + Job C.
|
|
37
|
+
"contributorSkill": null, // optional: a Claude skill carrying this repo's conventions (test cmds, architecture). Dev invokes it before coding; absent ⇒ Dev reads the repo's CLAUDE.md. Per-repo override lives in repos[].contributorSkill (§19).
|
|
38
|
+
"mode": "live", // "live" | "dry-run" (see conventions §12)
|
|
39
|
+
"autonomy": "ask", // "ask" (default) | "full" — who decides vs escalates (see conventions §12a)
|
|
40
|
+
"backend": "linear", // "linear" (default when absent) | "local" | "service" — coordination substrate (see conventions §18)
|
|
41
|
+
"devSplit": false, // two-tier Dev (§21a): the AUTHORITATIVE flag the agents read. true ⇒ senior-dev/junior-dev own the queue + the legacy `dev` agent defers (no-op); absent/false ⇒ legacy single-dev. MUST be set together with the launcher's DEV_SPLIT=1 (which spawns the two panes) — the two halves of one switch. Agents NEVER infer the dev model from history/tickets, only from this flag.
|
|
42
|
+
"localBoard": null, // local backend only: override board dir; null → ${CLAUDE_PLUGIN_DATA}/<key>/board/
|
|
43
|
+
"ticketPrefix": "DL", // local/service backend: ID prefix for tickets (e.g. "DL-1"); ignored for linear
|
|
44
|
+
"hub": { // service backend only (conventions §18; see docs/HUB-ARCHITECTURE.md). The local MCP system-of-record.
|
|
45
|
+
"db": null, // path to the hub SQLite file; null → ${DEVLOOP_HUB_DB:-~/.dev-loop/hub.db}. Registered as an MCP server (`dev-loop-hub`) via .mcp.json; identity per-pane via DEVLOOP_ACTOR (see docs/RUNNING.md). Machine-local, never committed.
|
|
46
|
+
"docs": false, // P4: false (default) ⇒ strategyDoc is a repo file (as P2/P3). true ⇒ the strategy + roadmap live as hub documents (versioned, attributable, optimistic-CAS, OPERATOR-PUBLISHED via doc.publish). Or pin one doc: strategyDoc: { "hubDoc": "strategy" }. §17: hub docs are PRODUCT docs only — never a SKILL/conventions/code file.
|
|
47
|
+
"transport": "stdio" // DL-43/P2: "stdio" (default) ⇒ each pane's MCP server opens hub.db DIRECTLY (today's behavior, zero new surface). "daemon" ⇒ OPT-IN: the agent op-API on the loopback daemon (`POST /api/op/<op>`, read fresh per request) is live, and the thin stdio shim (hub/src/shim.ts) proxies tool calls to it instead of opening the DB — identity rides env→the `X-Devloop-Actor` header (dodges the `claude -p` Authorization-drop). Every mutating endpoint passes the writeOriginOk CSRF/DNS-rebind guard first (§16, 127.0.0.1-only). See docs/HUB-ARCHITECTURE.md + docs/design/daemon-multicli-repositioning.md.
|
|
48
|
+
},
|
|
49
|
+
"models": { // optional: per-agent model, applied by the LAUNCHER at session start (--model). DEFAULT is opus for EVERY agent; tune an agent DOWN to economize.
|
|
50
|
+
"pm": "opus", "qa": "opus", "dev": "opus", "sweep": "opus", "reflect": "opus", "ops": "opus", "architect": "opus", "communication": "opus",
|
|
51
|
+
"senior-dev": "claude-opus-4-8", // two-tier Dev (launcher DEV_SPLIT=1): the design-and-delegate + escalation direct-code agent. Launcher effort = max. Absent ⇒ opus.
|
|
52
|
+
"junior-dev": "claude-sonnet-4-6" // two-tier Dev (DEV_SPLIT=1): implements pre-designed tickets against the linked design. Launcher effort = high. Absent ⇒ sonnet. `dev` stays the LEGACY single-dev default (kept active).
|
|
53
|
+
},
|
|
54
|
+
|
|
55
|
+
"testEnv": { // where QA + verification run
|
|
56
|
+
"baseUrl": "https://monpick.vercel.app",
|
|
57
|
+
"setup": "python3 -m venv .venv && .venv/bin/pip install -q playwright && .venv/bin/playwright install chromium", // one-time harness bootstrap; QA runs it if the tooling is missing (optional)
|
|
58
|
+
"testCommand": ".venv/bin/python3 tests/{suite}", // {suite} filled per run; omit if N/A
|
|
59
|
+
"notes": "Personas: demo-creator@…/password123 (creator), demo-brand@… (brand)"
|
|
60
|
+
},
|
|
61
|
+
|
|
62
|
+
"build": { // gates Dev runs before shipping; all optional
|
|
63
|
+
"typecheck": "npx tsc --noEmit",
|
|
64
|
+
"build": "pnpm build",
|
|
65
|
+
"test": "pnpm exec tsx tests/*.test.ts"
|
|
66
|
+
},
|
|
67
|
+
|
|
68
|
+
"git": { // how Dev lands code (autonomy choices live here)
|
|
69
|
+
"defaultBranch": "main",
|
|
70
|
+
"autoCommit": true,
|
|
71
|
+
"autoPush": true, // false → leave commits local
|
|
72
|
+
"autoDeploy": true // false → skip deploy even if deploy.command set
|
|
73
|
+
},
|
|
74
|
+
|
|
75
|
+
"deploy": {
|
|
76
|
+
"command": "vercel --prod --yes", // run after a successful push when autoDeploy
|
|
77
|
+
"healthCheck": null // optional: a URL that must return 2xx, OR a command
|
|
78
|
+
// that must exit 0, run by Dev Step 6.5 after deploy.
|
|
79
|
+
// null → Dev hits testEnv.baseUrl root (non-5xx).
|
|
80
|
+
},
|
|
81
|
+
"ops": { // OPTIONAL — ops-agent only (conventions §21). Absent ⇒ Ops polls only the resolved deploy.healthCheck + testEnv.baseUrl root.
|
|
82
|
+
"checks": [], // optional: extra synthetic probes — each a URL (must return 2xx) or a command (must exit 0)
|
|
83
|
+
"criticalRoutes": [], // optional: core user-flow paths/URLs that must be up — string path/URL or { "url": "...", "expectStatus": 200 }
|
|
84
|
+
"logsCommand": null // optional: a READ-ONLY logs/metrics command for an error-rate/5xx signal (never mutating)
|
|
85
|
+
},
|
|
86
|
+
"communication": { // OPTIONAL — communication-agent only (conventions §21). Absent ⇒ no scheduled article drafts unless directly invoked by the operator.
|
|
87
|
+
"cadence": "daily", // daily is the intended loop cadence; the agent dedupes by YYYY-MM-DD output path
|
|
88
|
+
"language": "en", // article language, e.g. "en", "zh-CN", "fr"
|
|
89
|
+
"audience": "current and prospective users",
|
|
90
|
+
"tone": "clear, concrete, human, and restrained",
|
|
91
|
+
"maxWords": 900,
|
|
92
|
+
"sourceWindowDays": 7, // how far back to look for Done tickets/events/changelog facts
|
|
93
|
+
"output": "data", // "data" (default, machine-local drafts) | "repo" (write draft markdown under the doc-home repo)
|
|
94
|
+
"outputDir": "communications", // data output: ${CLAUDE_PLUGIN_DATA}/<key>/<outputDir>/YYYY-MM-DD.md
|
|
95
|
+
"repoOutputDir": "docs/communications", // repo output: <doc-home repo>/<repoOutputDir>/YYYY-MM-DD.md
|
|
96
|
+
"includeUnreleased": false // false ⇒ only published/verified/shipped facts; true ⇒ may mention roadmap items as upcoming, clearly labelled
|
|
97
|
+
},
|
|
98
|
+
"mirror": { // OPTIONAL — the P7 ONE-WAY Linear mirror (conventions §18/§23). Absent ⇒ no mirror (today's behavior). REQUIRES backend:"service" (the hub is the SoR being mirrored). Sweep Job 5 pushes it.
|
|
99
|
+
"teamId": "<linear-team-id>", // the Linear team the mirrored issues live in
|
|
100
|
+
"projectId": null, // optional Linear project id to file them under
|
|
101
|
+
"tokenEnv": "DEVLOOP_LINEAR_TOKEN", // ENV-VAR NAME of the Linear API key (§16 secret; read SERVER-SIDE; NEVER the literal)
|
|
102
|
+
"stateMap": { // optional hub State → Linear state id; a missing state ⇒ no stateId (state stays in the body; the push never fails)
|
|
103
|
+
"Todo": "<id>", "In Progress": "<id>", "In Review": "<id>", "Done": "<id>", "Canceled": "<id>"
|
|
104
|
+
},
|
|
105
|
+
"enabled": true
|
|
106
|
+
},
|
|
107
|
+
"codex": { // OPTIONAL — Codex companion (conventions §24). Absent OR enabled:false OR codex CLI not on PATH ⇒ never invoked (today's behavior).
|
|
108
|
+
"enabled": true, // master switch (false/absent ⇒ off)
|
|
109
|
+
"review": true, // Dev Step 5.5 + Architect may run an INDEPENDENT codex review (advisory; Critical/High block like Dev's own)
|
|
110
|
+
"rescue": false, // Dev may delegate ONE rescue pass to codex before a fix-exhausted block (still gated on Dev's own gates)
|
|
111
|
+
"imageGen": true, // PM mockups + Dev production assets via codex's native image_generation tool
|
|
112
|
+
"assetsDir": "public/generated", // repo-relative dir Dev commits generated assets into (multi-repo: the ticket's repo:<name> tree)
|
|
113
|
+
"model": null, // optional: pin a codex model (e.g. "gpt-5.4-mini"); null ⇒ codex's own default / its config.toml
|
|
114
|
+
"effort": null // optional: none|minimal|low|medium|high|xhigh; null ⇒ codex default
|
|
115
|
+
},
|
|
116
|
+
"notify": { // OPTIONAL — PM only (conventions §9). Absent ⇒ NO-OP (no out-of-band ping; full back-compat).
|
|
117
|
+
"type": "lark", // "slack" | "lark" — picks the webhook payload shape
|
|
118
|
+
"webhookEnv": "DEVLOOP_NOTIFY_WEBHOOK", // PREFERRED: name of an env var holding the webhook URL (a §16 secret). Or inline "webhook": "..." (machine-local only; never commit/echo).
|
|
119
|
+
"secretEnv": null, // lark only, optional: env-var name for the bot signing secret (if signature verification is on). Or inline "secret"; §16-class.
|
|
120
|
+
"events": ["human-parked"] // default; the only event today — a ticket left blocked+needs-pm with Bail-shape: external-prereq
|
|
121
|
+
},
|
|
122
|
+
|
|
123
|
+
"blockedStateName": null // set to a real Linear state name if you add a "Blocked" column; else null → use the `blocked` label
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
## Notes
|
|
130
|
+
- **Required per project**: `linearTeam`, `linearProject`. `repoPath` is required
|
|
131
|
+
for Dev; `strategyDoc` for PM; `testEnv` for QA. A skill prompts for any
|
|
132
|
+
required field it's missing rather than guessing.
|
|
133
|
+
- **`testEnv.setup`** (optional): a one-time command to bootstrap the test harness
|
|
134
|
+
(install the browser driver, create a venv, etc.). QA runs it when the tooling
|
|
135
|
+
named in `testCommand` is missing, so a fresh machine or a scheduled run isn't
|
|
136
|
+
blocked by an absent harness. Keep it idempotent.
|
|
137
|
+
- **Two orthogonal kinds of autonomy** (conventions §12a):
|
|
138
|
+
- *How code lands* — the `git` + `deploy` flags. Fully hands-off shipping is
|
|
139
|
+
`autoCommit/autoPush/autoDeploy: true` with a `deploy.command`; to put a human
|
|
140
|
+
in the loop on landing, set `autoPush`/`autoDeploy: false`.
|
|
141
|
+
- *How much the agents decide vs escalate* — the top-level `autonomy` field.
|
|
142
|
+
`"ask"` (default) keeps the conservative posture (escalate genuinely human-only
|
|
143
|
+
calls to the user, surface open product-direction decisions). `"full"` grants
|
|
144
|
+
standing authority to **decide and act, not ask**: resolve scoping/product-
|
|
145
|
+
direction calls from the `strategyDoc`, do irreversible prod ops attended
|
|
146
|
+
(pre/post-verify + records-only form), and stop only for genuine **external
|
|
147
|
+
prerequisites** (real credentials, money, legal) — never an interactive prompt.
|
|
148
|
+
Caution stays the method, not a reason to defer.
|
|
149
|
+
- **Safety**: there is no MonPick Linear project yet. Either create a dedicated
|
|
150
|
+
project (recommended) or point `linearProject` at one you own. The `dev-loop`
|
|
151
|
+
label (conventions §2) is what actually protects the human backlog, but a
|
|
152
|
+
dedicated project keeps the board clean.
|
|
153
|
+
- Secrets (passwords, tokens) are **not** stored here — reference how to obtain
|
|
154
|
+
them (`.env.local`, a vault, "ask user") in `testEnv.notes`. See the security
|
|
155
|
+
doctrine (conventions §16).
|
|
156
|
+
- **`lessons.md`** (optional) lives **per-project** at
|
|
157
|
+
`${CLAUDE_PLUGIN_DATA}/<project-key>/lessons.md` (the same per-project home as `reports/`,
|
|
158
|
+
conventions §14; the legacy root file next to `projects.json` remains only as a back-compat
|
|
159
|
+
**fallback** for un-migrated single-project installs). It holds per-operator
|
|
160
|
+
behavioral corrections, sectioned per agent (`Shared`/`PM`/`QA`/`Dev`/`Sweep`/`Reflect`/`Ops`/`Architect`/`Communication`).
|
|
161
|
+
Each skill reads it at run-start and applies its section that fire
|
|
162
|
+
(conventions §14). Local machine state — never committed. The **Reflect** agent (the
|
|
163
|
+
daily retrospective role) is the one agent that *writes* this file — it curates it
|
|
164
|
+
from recurring, evidence-cited patterns it observes across runs (conventions §17).
|
|
165
|
+
Reflect may edit only `lessons.md` autonomously (reversible, per-operator); it must
|
|
166
|
+
NOT auto-edit the SKILLs or `conventions.md` — those changes are drafted as proposals
|
|
167
|
+
for the human. Reflect bounds its window from Linear + git (always present) and the
|
|
168
|
+
`*-state.json` files; if a launcher happens to tee agent output to
|
|
169
|
+
`logs/<agent>-<date>.log` in the data dir, it reads that too, but degrades silently
|
|
170
|
+
when absent. It writes no new config keys.
|
|
171
|
+
- **`models`** (optional): a per-agent model map the **launcher** applies at session
|
|
172
|
+
start (`claude --model <m> …`) — the model is a *launch-time* choice, not something a
|
|
173
|
+
SKILL sets, so this is consumed by `run-loop.sh` / your launch command, not by the
|
|
174
|
+
agents. **The default is `opus` for EVERY agent** (the launcher applies `--model opus`
|
|
175
|
+
per pane unless you override) — maximize correctness across the whole loop. Tune an
|
|
176
|
+
agent **down** (`sonnet`/`haiku`) only to economize — e.g. the mechanical/high-frequency
|
|
177
|
+
ones (`sweep`, `qa`, `ops`, `communication`) tolerate `sonnet` well; the reasoning-heavy ones
|
|
178
|
+
(`dev`, `pm`, `architect`, `reflect`) are where `opus` earns its keep. Omitting an
|
|
179
|
+
agent ⇒ it falls back to the launcher's opus default.
|
|
180
|
+
**Per-agent EFFORT tiers** (the launcher's `--effort` per pane, distinct from the model):
|
|
181
|
+
`pm=max` and `dev=max` (legacy single dev) reason deepest; `reflect`/`architect`=`xhigh`;
|
|
182
|
+
`qa`/`sweep`=`high`.
|
|
183
|
+
- **Two-tier Dev** (`senior-dev` / `junior-dev`; conventions §21a): an opt-in split of the single
|
|
184
|
+
Dev role, enabled at LAUNCH by the launcher knob (`DEV_SPLIT=1` in `run-loop.sh`) — it replaces
|
|
185
|
+
the single `dev` pane with two panes: a **`senior-dev`** pane (`claude-opus-4-8`, effort **max**)
|
|
186
|
+
that designs-and-delegates new modules/features and direct-codes escalations, and a
|
|
187
|
+
**`junior-dev`** pane (`claude-sonnet-4-6`, effort **high**) that implements pre-designed tickets
|
|
188
|
+
against the linked design. Their models come from `models{}` (`senior-dev`/`junior-dev`),
|
|
189
|
+
defaulting to opus / sonnet respectively. The split is **per-project and opt-in**: with
|
|
190
|
+
`DEV_SPLIT` off (default) the launcher keeps the **legacy single `dev` pane** and non-split
|
|
191
|
+
projects are 100% unaffected — `dev` stays an active actor and `dev-agent` stays the canonical
|
|
192
|
+
single-dev SKILL. PM routes each ticket to its tier at filing (new module / new feature ⇒
|
|
193
|
+
`senior-dev`; improvement / bug-fix, or borderline ⇒ `junior-dev`), encoded per backend
|
|
194
|
+
(the ticket `assignee` on `service`; a `senior-dev`/`junior-dev` LABEL on `linear`/`local`).
|
|
195
|
+
- **Design docs** (the two-tier Dev's `design` doc tier, conventions §21a): senior-dev authors a
|
|
196
|
+
LIVING per-MODULE technical-design doc — autonomously, like PM commits the `strategyDoc` (it is
|
|
197
|
+
NOT a §17 governing file and is NOT operator-publish-gated; the gate is the design parent ticket
|
|
198
|
+
reaching `In Review`). Its **home depends on the backend**: on `backend:"service"` it is the hub
|
|
199
|
+
**`design`** doc-kind (versioned, multi-instance by module slug, read at its latest version —
|
|
200
|
+
`doc.save`/`doc.get`, not publish-gated); on `backend:"linear"`/`"local"` it is a committed repo
|
|
201
|
+
file **`docs/design/<slug>.md`** under the doc-home repo. Small features get NO separate doc — the
|
|
202
|
+
design lives in the parent + child ticket specs. Every child dev-ticket carries a `Design:`
|
|
203
|
+
pointer line (`hubDoc:design/<slug>` | `docs/design/<slug>.md` | `parent <id>`) that junior-dev
|
|
204
|
+
reads before coding.
|
|
205
|
+
- **`backend`** (optional; default `"linear"`): the coordination substrate
|
|
206
|
+
(conventions §18). `"linear"` is the Linear MCP, exactly as today — absent ⇒
|
|
207
|
+
`"linear"`, so existing projects are unchanged. `"local"` uses a machine-local file
|
|
208
|
+
board under `${CLAUDE_PLUGIN_DATA}/<key>/board/` (one markdown file per ticket; state
|
|
209
|
+
in the frontmatter; same state machine, labels, and protocols). `localBoard`
|
|
210
|
+
overrides the board path; `ticketPrefix` sets the ID prefix (default `"DL"`). Both
|
|
211
|
+
are ignored under `"linear"`. In `"local"` mode `strategyDoc` must be a **repo file**
|
|
212
|
+
(a Linear document can't back a local board), and `/dev-loop:init` scaffolds `board/`
|
|
213
|
+
while skipping the Linear label/project steps. `"service"` routes to the **local hub**
|
|
214
|
+
— a machine-local MCP system-of-record (`hub.db`, node:sqlite; see
|
|
215
|
+
`docs/HUB-ARCHITECTURE.md`) registered as the `dev-loop-hub` MCP server, whose tools
|
|
216
|
+
mirror the Linear op-shapes 1:1 so the SKILLs port unchanged. Its win over Linear:
|
|
217
|
+
**real per-agent identity** — each pane sets `DEVLOOP_ACTOR` (launcher-set; see
|
|
218
|
+
`docs/RUNNING.md`) so every write is attributable, not the single shared Linear user.
|
|
219
|
+
`hub.db` path via `hub.db` / `DEVLOOP_HUB_DB`; `strategyDoc` is a **repo file** (as in
|
|
220
|
+
`local`; first-class hub docs are a later phase); `ticketPrefix` applies. Like `local`,
|
|
221
|
+
the hub is machine-local runtime state, never committed.
|
|
222
|
+
- **Project resolution (`backend:"service"`, DL-13).** The hub picks its project by this
|
|
223
|
+
precedence: **explicit `DEVLOOP_PROJECT`** (a non-empty, trimmed value — `""` is treated as
|
|
224
|
+
unset, and `"demo"`/`"default"` are NOT sentinels: an operator may legitimately pin a project
|
|
225
|
+
keyed `demo`/`default`) **>** the spawned process's **cwd** matched against each project's
|
|
226
|
+
`repoPath`/`repos[].path` (§19; realpath-canonical, segment-boundary safe so `/work/repo` ≠
|
|
227
|
+
`/work/repo-2`, nearest-ancestor wins, an ambiguous tie or a cwd outside every repo → no match)
|
|
228
|
+
**>** the `demo` default. A cwd that resolves to a **configured-but-unseeded** project **errors
|
|
229
|
+
loudly** (it never silently falls through to `demo`). The shared matcher is exposed as
|
|
230
|
+
`dev-loop-hub resolve-project [--cwd <path>]` so a launcher reuses exactly one rule. So
|
|
231
|
+
`DEVLOOP_PROJECT` is **optional** when an agent is launched from inside a project's repo; the
|
|
232
|
+
`.mcp.json`/launcher templates default it to empty for that reason (see `config/mcp.*.example`,
|
|
233
|
+
`docs/RUNNING.md`).
|
|
234
|
+
- **`repos`** (optional; default absent ⇒ single-repo, conventions §19): an array of
|
|
235
|
+
`{ name, path, role, lang, contributorSkill?, defaultBranch?, build?, deploy? }`
|
|
236
|
+
entries for a **multi-repo** product. Absent (or a single entry) ⇒ the top-level
|
|
237
|
+
`repoPath`/`build`/`git`/`deploy` remain authoritative and the loop emits **zero**
|
|
238
|
+
routing artifacts (no `repo:<name>` labels, no provisioning) — single-repo is 100%
|
|
239
|
+
unchanged. **Resolution:** each per-repo-overridable setting (`build`, `defaultBranch`,
|
|
240
|
+
`deploy`, `contributorSkill`, `lang`) is the repo's value if present, else the
|
|
241
|
+
top-level value; `autoCommit`/`autoPush`/`autoDeploy` stay product-level in `git`.
|
|
242
|
+
`role` is load-bearing (`"docs"`/`"primary"` picks the **doc-home** repo that roots
|
|
243
|
+
`strategyDoc`); `lang` is informational. Multi-repo tickets carry a `repo:<name>`
|
|
244
|
+
label (the authoritative target). If both `repoPath` and `repos` are set, `repos`
|
|
245
|
+
wins and init verifies `repoPath` is among them.
|
|
246
|
+
- **`deploy.healthCheck`** (optional): a URL (must return 2xx) or a command (must
|
|
247
|
+
exit 0) that Dev runs in Step 6.5 right after an unattended prod deploy. On a
|
|
248
|
+
repeated failure Dev rolls the deploy back (revert + redeploy) rather than leaving
|
|
249
|
+
prod broken. Absent → Dev smoke-checks `testEnv.baseUrl` root for a non-5xx.
|
|
250
|
+
- **`ops`** (optional; `ops-agent` only, conventions §21): probes for the Ops/SRE
|
|
251
|
+
watcher of RUNNING prod. `ops.checks` (extra synthetic probes — URL/2xx or
|
|
252
|
+
command/exit-0), `ops.criticalRoutes` (core user-flow paths that must be up), and a
|
|
253
|
+
read-only `ops.logsCommand` (error-rate/5xx signal) are **all optional**; absent ⇒ Ops
|
|
254
|
+
polls only the resolved per-repo `deploy.healthCheck` + `testEnv.baseUrl` root. Ops
|
|
255
|
+
re-checks before filing (anti-flap), files/refreshes ONE `Bug`+`qa`+`incident` (Urgent
|
|
256
|
+
when prod is down), dedupes via `ops-state.json`, and never rolls back. Opt-in to launch.
|
|
257
|
+
- **`communication`** (optional; `communication-agent` only, conventions §21): enables the
|
|
258
|
+
PR/media drafting agent. Its default job is one **draft** article per day, sourced from the
|
|
259
|
+
strategy/roadmap, recent verified Done tickets, changelog/git facts, and the public product
|
|
260
|
+
surface. It never publishes externally, never commits/pushes/deploys, and never edits product
|
|
261
|
+
code. `output:"data"` writes machine-local drafts under
|
|
262
|
+
`${CLAUDE_PLUGIN_DATA}/<project-key>/<outputDir>/YYYY-MM-DD.md`; `output:"repo"` writes the
|
|
263
|
+
draft markdown under the doc-home repo at `repoOutputDir` for operator review. `language`,
|
|
264
|
+
`audience`, `tone`, `maxWords`, and `sourceWindowDays` shape the article; `includeUnreleased`
|
|
265
|
+
must stay false unless the operator is comfortable with clearly-labelled roadmap/upcoming
|
|
266
|
+
language. Absent ⇒ scheduled Communication fires no-op, so existing projects are unchanged.
|
|
267
|
+
- **`mirror`** (optional; conventions §18/§23; requires `backend:"service"`): the P7 **one-way
|
|
268
|
+
Linear mirror** — projects the hub's tickets to Linear so humans who live in Linear can SEE
|
|
269
|
+
the loop without the hub ceasing to be the source of truth. **Strictly one-way** (hub →
|
|
270
|
+
Linear): the hub WRITES Linear (and reads only to reconcile its own id mapping), NEVER imports
|
|
271
|
+
Linear state; a human edit on a mirrored issue is **overwritten** on the next push (a banner
|
|
272
|
+
says so). **Sweep Job 5** pushes it (idempotent + incremental — an unchanged ticket is skipped
|
|
273
|
+
by content hash; cheap when nothing changed). `tokenEnv` is the env-var **NAME** of the Linear
|
|
274
|
+
API key (§16 secret, read server-side, never returned/logged/persisted); `stateMap` maps hub
|
|
275
|
+
states → workspace-specific Linear state ids (a missing one ⇒ state lives in the body, the push
|
|
276
|
+
never fails). **§23 audience-widening (same as `reports.sink:"linear"`):** the mirror publishes
|
|
277
|
+
ticket bodies to a hosted/shared/searchable Linear — so a mirrored body must already be §16-safe
|
|
278
|
+
(no secrets/PII; the agents never put those in ticket bodies anyway). A hub Canceled/Duplicate
|
|
279
|
+
is mirrored as a state change, **never** a hard-delete (no data loss). Absent ⇒ no mirror; a
|
|
280
|
+
`mirror` under `backend:"linear"`/`"local"` is a config error (no hub to mirror from). Distinct
|
|
281
|
+
from `reports.sink` (that mirrors *reports*, this mirrors *tickets*) — they may coexist.
|
|
282
|
+
- **`codex`** (optional; conventions §24 + `references/codex-integration.md`; **absent ⇒
|
|
283
|
+
off, 100% unchanged**): wires the **Codex** companion (`codex` CLI + the codex-plugin-cc
|
|
284
|
+
plugin) as an optional accelerant. Used **only** when `codex.enabled:true` **and** the
|
|
285
|
+
`codex` CLI is on `PATH` — otherwise every agent behaves exactly as today (a missing
|
|
286
|
+
Codex is a graceful fallback, not an error). Sub-flags gate each capability independently:
|
|
287
|
+
`review` (Dev Step 5.5 + Architect run an **independent, advisory** codex review — a
|
|
288
|
+
second model on the diff/codebase; Critical/High block like Dev's own, never a veto),
|
|
289
|
+
`imageGen` (PM mockups + Dev production assets via Codex's native `image_generation` tool
|
|
290
|
+
— the one thing the loop can't do itself; assets land in `assetsDir` and ship through the
|
|
291
|
+
normal gates), and `rescue` (Dev delegates **one** pass to Codex before a `fix-exhausted`
|
|
292
|
+
block; its patch ships only if it passes Dev's own gates + self-review). `assetsDir` is
|
|
293
|
+
the repo-relative dir Dev commits generated assets into; `model`/`effort` optionally pin
|
|
294
|
+
Codex's model/reasoning (null ⇒ Codex defaults / its `config.toml`). **No secret here** —
|
|
295
|
+
Codex uses your local `codex login` auth (§16). Codex is **advisory and never touches
|
|
296
|
+
Linear** — it only ever touches code/files/a review of them; the agent owns every ship.
|
|
297
|
+
Prereqs (install `@openai/codex`, `codex login`, install codex-plugin-cc) are
|
|
298
|
+
operator-present and one-time; `/dev-loop:init` notes the option but won't install the
|
|
299
|
+
vendor CLI.
|
|
300
|
+
- **`notify`** (optional; PM only, conventions §9): pings the operator **out-of-band** when
|
|
301
|
+
a ticket is left human-parked (`blocked`+`needs-pm`+`Bail-shape: external-prereq`) — the
|
|
302
|
+
fix for a parked ticket sitting unseen. `type` is `"slack"` | `"lark"`; the webhook URL is
|
|
303
|
+
a **§16 secret** — set `webhookEnv` (an env-var name; **preferred**) or, since
|
|
304
|
+
`projects.json` is machine-local/never-committed, an inline `webhook` (never commit or echo
|
|
305
|
+
it). Lark signature verification uses `secretEnv`/`secret` (§16-class too). PM announces
|
|
306
|
+
each parked ticket **once** (the `notified` label, §4), POSTs with a short timeout, treats
|
|
307
|
+
only a 2xx (Lark: + body `code==0`) as success, and **never** writes the URL into a
|
|
308
|
+
ticket/comment/report/log. **Absent ⇒ NO-OP** (no ping, no extra work — full back-compat).
|
|
309
|
+
Out-of-band by design: a Linear @mention would be a self-mention (shared identity) and
|
|
310
|
+
suppressed.
|
|
311
|
+
- **`models`** covers the eight base agents plus the two opt-in two-tier-Dev agents
|
|
312
|
+
(`senior-dev`/`junior-dev`) and **defaults to `opus` for all of them** (the launcher applies
|
|
313
|
+
`--model opus` per pane unless overridden; `senior-dev`→opus, `junior-dev`→sonnet defaults under
|
|
314
|
+
`DEV_SPLIT=1`); tune an agent down to economize. The outward agents are **opt-in to launch**
|
|
315
|
+
(off by default in the launcher), and the two-tier Dev split is also opt-in (`DEV_SPLIT=1`);
|
|
316
|
+
none of these change any other agent's behavior, and a legacy single-dev project is
|
|
317
|
+
byte-for-byte unchanged.
|
|
318
|
+
- **Agent state files** (`pm-state.json`, `qa-state.json`, and the outward observe-and-file
|
|
319
|
+
agents' `ops-state.json` / `architect-state.json`, §21) live next to `projects.json` and
|
|
320
|
+
hold per-project loop state: last-reviewed/swept SHA, swept
|
|
321
|
+
review lenses (PM), swept surfaces (QA); Ops's open incidents + last-check;
|
|
322
|
+
Architect's per-repo SHA map + swept audit dimensions. **Multi-repo (conventions §19):** the
|
|
323
|
+
last-reviewed/swept SHA becomes a **per-repo map** `{ "<repo-name>": "<sha>" }` (one
|
|
324
|
+
entry per `repos[]`); a new SHA in *any* watched repo re-opens the sweep. Single-repo
|
|
325
|
+
keeps the single-SHA form, unchanged. Local per-operator runtime state — never
|
|
326
|
+
committed, never shared. Created lazily on first run, or up-front by `/dev-loop:init`
|
|
327
|
+
(which also seeds the per-project `lessons.md` skeleton (under `<project-key>/`, conventions §14) and gathers/writes back
|
|
328
|
+
the per-project fields above WITH the operator — operator-present setup, so asking
|
|
329
|
+
for unknowable values like `repoPath`/`linearProject`/`deploy.command` is expected
|
|
330
|
+
there, unlike the unattended loop agents). Creates only what's missing. The default
|
|
331
|
+
**`files`** report sink (§22) adds **NO new state-file field** — the daily/weekly/monthly
|
|
332
|
+
report cadence and acted-review status live entirely in the reports tree (newest file per
|
|
333
|
+
level; `<report>.review.acted` sidecars), so there is no marker to key per-project or
|
|
334
|
+
reconcile. The opt-in **`linear`** sink (§23) adds one machine-local file,
|
|
335
|
+
`reports-state.json` (doc-id cache + acted-review ledger + `lastReviewPollAt`) — also
|
|
336
|
+
never committed.
|
|
337
|
+
- **Reports** (optional output, conventions §22; **on by default, no config needed**):
|
|
338
|
+
every agent writes daily / weekly / monthly reports to
|
|
339
|
+
`${CLAUDE_PLUGIN_DATA}/<project-key>/reports/<agent>/{daily,weekly,monthly}/`
|
|
340
|
+
(machine-local, never committed, located by `reports.sink` — independent of the §18
|
|
341
|
+
backend, **§16-bound — no secrets/PII**), created lazily on first write (or scaffolded by
|
|
342
|
+
`/dev-loop:init`). The operator may critique any report by dropping a sibling
|
|
343
|
+
`<report>.review.md`; the agent reads an un-acted review at run-start and distills it into
|
|
344
|
+
a `lessons.md` rule under its own section (§22). Retention default ≈ **90 days of dailies**
|
|
345
|
+
(tune per product); roll-ups preserve the summaries. No config key is required — an
|
|
346
|
+
operator who writes no review just gets dated files to read or ignore.
|
|
347
|
+
- **`reports.sink`** (optional, conventions §23; **absent ⇒ `"files"`**): `"files"` (the
|
|
348
|
+
default machine-local tree above) or `"linear"` (route the report **body** + the 点评
|
|
349
|
+
channel to Linear — for a **cloud / remote** runtime where the operator can't reach the
|
|
350
|
+
data dir; reads/reviews happen in a browser). **Decoupled from the §18 `backend`** (a
|
|
351
|
+
`linear` backend does not auto-enable it). The `linear` sink trades away a §16
|
|
352
|
+
defense-in-depth layer (Linear is hosted/shared/searchable), so it is **opt-in, never the
|
|
353
|
+
default**, and carries the §23 guardrails. Linear-sink-only keys:
|
|
354
|
+
`reports.linearProject` / `reports.linearInitiative` (the **dedicated** reports container,
|
|
355
|
+
never the §20 doc-base), `reports.localOnlyAgents` (agents pinned to files regardless —
|
|
356
|
+
**defaults to `ops-agent` + `dev-agent`**, the highest-PII authors), and
|
|
357
|
+
`reports.reviewToken` (the operator's **opaque** high-entropy 点评 sentinel — not a
|
|
358
|
+
dictionary word). `lessons.md` stays machine-local in both sinks.
|