@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.
Files changed (36) hide show
  1. package/README.md +30 -10
  2. package/dist/agentops.js +5 -68
  3. package/dist/cli.js +4 -0
  4. package/dist/db.js +0 -26
  5. package/dist/doctor.js +2 -2
  6. package/dist/install-claude-plugin.js +78 -0
  7. package/dist/mcp-merge.js +18 -19
  8. package/dist/mirrorstore.js +1 -1
  9. package/dist/plugin/.claude-plugin/marketplace.json +13 -0
  10. package/dist/plugin/.claude-plugin/plugin.json +11 -0
  11. package/dist/plugin/config/mcp.codex.toml.example +33 -0
  12. package/dist/plugin/config/mcp.example.json +15 -0
  13. package/dist/plugin/config/mcp.opencode.json.example +16 -0
  14. package/dist/plugin/config/projects.example.json +82 -0
  15. package/dist/plugin/hooks/hooks.json +16 -0
  16. package/dist/plugin/references/codex-integration.md +282 -0
  17. package/dist/plugin/references/config-schema.md +358 -0
  18. package/dist/plugin/references/conventions.md +2159 -0
  19. package/dist/plugin/skills/architect-agent/SKILL.md +231 -0
  20. package/dist/plugin/skills/communication-agent/SKILL.md +247 -0
  21. package/dist/plugin/skills/dev-agent/SKILL.md +373 -0
  22. package/dist/plugin/skills/init/SKILL.md +496 -0
  23. package/dist/plugin/skills/junior-dev-agent/SKILL.md +348 -0
  24. package/dist/plugin/skills/ops-agent/SKILL.md +219 -0
  25. package/dist/plugin/skills/pm-agent/SKILL.md +427 -0
  26. package/dist/plugin/skills/qa-agent/SKILL.md +299 -0
  27. package/dist/plugin/skills/reflect-agent/SKILL.md +271 -0
  28. package/dist/plugin/skills/senior-dev-agent/SKILL.md +353 -0
  29. package/dist/plugin/skills/sweep-agent/SKILL.md +180 -0
  30. package/dist/run-agents.js +373 -0
  31. package/dist/seed.js +4 -3
  32. package/dist/server.js +1 -1
  33. package/dist/shim.js +3 -4
  34. package/dist/tooldefs.js +3 -25
  35. package/package.json +5 -5
  36. 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.