@dombaras/agent-harness 0.1.14 → 0.1.16
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 +15 -10
- package/bin/agent-harness.js +12 -7
- package/package.json +2 -2
- package/templates/.agents/AGENTS.md +16 -11
- package/templates/.agents/features/INDEX.md +3 -1
- package/templates/.agents/handoffs/README.md +40 -0
- package/templates/.agents/memory/locations.md +1 -1
- package/templates/.agents/memory/model-routing.md +2 -1
- package/templates/.agents/qa-plans/README.md +1 -1
- package/templates/.agents/rules/00-operating.md +3 -2
- package/templates/.agents/skills/docs/SKILL.md +128 -0
- package/templates/.agents/skills/features/SKILL.md +26 -11
- package/templates/.agents/skills/handoff/SKILL.md +7 -3
- package/templates/.agents/skills/qa-architect/SKILL.md +1 -1
- package/templates/.opencode/agents/docs.md +27 -0
- package/templates/.opencode/agents/features.md +5 -5
- package/templates/.opencode/agents/handoff.md +2 -1
- package/templates/AGENTS.md +2 -1
- package/templates/scripts/qa/check-docs.js +163 -0
- package/templates/scripts/qa/governance.js +68 -22
- package/templates/.agents/memory/handoff.md +0 -12
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
A reusable multi-agent harness for AI-assisted development. Ships a persona
|
|
4
4
|
fleet (planner, engine engineer, mobile, QA architect/runner, security,
|
|
5
|
-
architecture, product, data, devops, handoff), operating rules, model routing,
|
|
5
|
+
architecture, product, data, devops, handoff, docs), operating rules, model routing,
|
|
6
6
|
and QA gates — then deploys them into any project.
|
|
7
7
|
|
|
8
8
|
> Extracted from a production project so the "agents / subagents / guardrails /
|
|
@@ -36,14 +36,15 @@ npx @dombaras/agent-harness init --target . --dry-run
|
|
|
36
36
|
|---|---|---|
|
|
37
37
|
| `AGENTS.md` | root dispatcher (points to the rulebook) | harness (overwrite) |
|
|
38
38
|
| `BACKLOG.md` | canonical task list (Open / Frozen / Archive / Not tracking) | **project** (create-if-missing) |
|
|
39
|
-
| `.opencode/agents/*.md` |
|
|
39
|
+
| `.opencode/agents/*.md` | 15 persona subagent defs (with `model:` pins + `permission`/`steps`/`temperature`/`hidden`) | harness (overwrite) |
|
|
40
40
|
| `.agents/AGENTS.md` | full canonical operating rulebook | harness (overwrite) |
|
|
41
41
|
| `.agents/rules/00-operating.md` | always-loaded rules summary (wired into `opencode.json` `instructions`) | harness (overwrite) |
|
|
42
42
|
| `.agents/skills/*/SKILL.md` | persona instruction skills | harness (overwrite) |
|
|
43
43
|
| `.agents/features/INDEX.md` | pointer to `BACKLOG.md` (per-feature doc tree root) | **project** (create-if-missing) |
|
|
44
44
|
| `opencode.json` | main/small model routing + `instructions` | harness (**merged**, see below) |
|
|
45
45
|
| `scripts/qa/*` | `test:dispatch` / `test:governance` / `test:qa-plan` gates + QA-script wiring check | harness (overwrite) |
|
|
46
|
-
| `.agents/memory/*` | project data (domain-map, stack-versions,
|
|
46
|
+
| `.agents/memory/*` | project data (domain-map, stack-versions, locations, model-routing, history, flow-map, product-principles) | **project** (create-if-missing) |
|
|
47
|
+
| `.agents/handoffs/*` | project data (one session handoff file per session — sharded so concurrent sessions never collide) | **project** (create-if-missing) |
|
|
47
48
|
| `.agents/qa-plans/*` | project data (one QA plan file per change — sharded so concurrent sessions never collide) | **project** (create-if-missing) |
|
|
48
49
|
| `.harness.json` | deployed version + project profile + per-file checksums | harness |
|
|
49
50
|
| `.gitignore` | harness-managed section (`.harness-backup/`) merged in, project entries preserved | harness (merged) |
|
|
@@ -101,7 +102,7 @@ warns but does not block; the guarantee only applies to registered flows.
|
|
|
101
102
|
`opencode.json` (`model` + `small_model`).
|
|
102
103
|
- Personas also carry mechanical guardrails in frontmatter:
|
|
103
104
|
- thinkers (`planner`, `product-manager`) → `permission: { edit: deny, bash: deny }`
|
|
104
|
-
- `qa-architect`, `handoff` → `permission: { bash: deny }`
|
|
105
|
+
- `qa-architect`, `handoff`, `docs` → `permission: { bash: deny }`
|
|
105
106
|
- code personas (`frontend-engineer`, `mobile-engineer`, `ui-designer`,
|
|
106
107
|
`data-engineer`, `devops-engineer`, `security-engineer`, `system-architect`,
|
|
107
108
|
`diagnostics-expert`) → `permission.edit` allows everything EXCEPT
|
|
@@ -140,8 +141,12 @@ warns but does not block; the guarantee only applies to registered flows.
|
|
|
140
141
|
`.agents/features/<slug>/` must define its user before planning/implementation.
|
|
141
142
|
`intent.md` must carry Actor(s) → Need → Outcome → Acceptance criteria, and `plan.md`
|
|
142
143
|
a Value-first sequence + Product concepts applied; a `plan.md` with no `intent.md`
|
|
143
|
-
|
|
144
|
-
|
|
144
|
+
fails (never plan around an undefined user need). Backward-compatible — no feature
|
|
145
|
+
docs, gate passes.
|
|
146
|
+
- `npm run test:docs` — **docs-as-code gate**: `README.md` / `docs/**` must have no
|
|
147
|
+
un-substituted `{{PLACEHOLDER}}` template vars, no dangling relative links, and every
|
|
148
|
+
ADR (`docs/adr/*.md`) must carry Context → Decision → Consequences + a Status.
|
|
149
|
+
Backward-compatible — no docs, gate passes.
|
|
145
150
|
- `npm run test:qa-plan` — **diff-coverage + flow-closure gate**:
|
|
146
151
|
every changed CODE path must have a covering assertion (or a waivered reason) in
|
|
147
152
|
a QA plan (one per-change file under `.agents/qa-plans/`) before a change is verified — and every touched flow
|
|
@@ -151,7 +156,7 @@ warns but does not block; the guarantee only applies to registered flows.
|
|
|
151
156
|
proves coverage. It also makes **"Fix One, Fix All" mechanical**: a change to one
|
|
152
157
|
surface (e.g. web) cannot ship while the same flow in another codebase (e.g. `mobile/`)
|
|
153
158
|
silently keeps old behavior — the plan must name each sibling as covered (`->`) or
|
|
154
|
-
out-of-scope (`audited: reason`, e.g. `deferred — logged in
|
|
159
|
+
out-of-scope (`audited: reason`, e.g. `deferred — logged in handoffs/`).
|
|
155
160
|
- `node scripts/qa/check-qa-scripts.js` — warns (or `--strict` fails) when the
|
|
156
161
|
DoD-referenced runtime QA tiers aren't wired into `package.json`.
|
|
157
162
|
|
|
@@ -166,15 +171,15 @@ scripts are **project-provided** — the rulebook references them, and
|
|
|
166
171
|
npx @dombaras/agent-harness update --target /path/to/project
|
|
167
172
|
```
|
|
168
173
|
|
|
169
|
-
- Overwrites harness-owned files, preserves `.agents/memory
|
|
174
|
+
- Overwrites harness-owned files, preserves `.agents/memory/*`, `.agents/handoffs/*`, `.agents/qa-plans/*`, and `BACKLOG.md`.
|
|
170
175
|
- Auto-wires the harness gate scripts (`test:dispatch`, `test:governance`,
|
|
171
|
-
`test:qa-plan`, `test:backlog`, `test:product`) into the target's `package.json` (merged, add-only).
|
|
176
|
+
`test:qa-plan`, `test:backlog`, `test:product`, `test:docs`) into the target's `package.json` (merged, add-only).
|
|
172
177
|
- **Auto-commits** the harness files it changed (`chore(harness): @dombaras/agent-harness
|
|
173
178
|
<old> -> <new>`) and **pushes** to origin, so the next session never sees unexplained
|
|
174
179
|
modified harness files. Your unrelated uncommitted work is never staged.
|
|
175
180
|
- `--no-commit` to skip commit+push, `--no-push` to commit but not push.
|
|
176
181
|
- `init` auto-commits + pushes by default too: it commits everything it scaffolds
|
|
177
|
-
(including `.agents/memory/*`, `.agents/features/*`, and `BACKLOG.md`) so a fresh
|
|
182
|
+
(including `.agents/memory/*`, `.agents/features/*`, `.agents/handoffs/*`, and `BACKLOG.md`) so a fresh
|
|
178
183
|
project starts fully tracked and no harness file is left untracked.
|
|
179
184
|
- Harness-owned paths are **force-added**, so a project that gitignores deployment
|
|
180
185
|
artifacts (an inherited `.agents/` `.opencode/` `AGENTS.md` pattern) still gets the
|
package/bin/agent-harness.js
CHANGED
|
@@ -92,16 +92,20 @@ function relKey(rel) {
|
|
|
92
92
|
return rel.split(path.sep).join("/");
|
|
93
93
|
}
|
|
94
94
|
|
|
95
|
-
/* `.agents/memory/*` (session memory), `.agents/features/*` (feature doc tree)
|
|
96
|
-
*
|
|
97
|
-
*
|
|
98
|
-
*
|
|
99
|
-
*
|
|
95
|
+
/* `.agents/memory/*` (session memory), `.agents/features/*` (feature doc tree),
|
|
96
|
+
* `.agents/qa-plans/*` (one QA plan file per change) and `.agents/handoffs/*`
|
|
97
|
+
* (one session handoff file per session) are project data — scaffolded
|
|
98
|
+
* create-if-missing, never overwritten, and never tracked in the manifest.
|
|
99
|
+
* qa-plans and handoffs are sharded per change/session so concurrent sessions
|
|
100
|
+
* never collide on a single shared file. */
|
|
100
101
|
function isProjectScaffold(rel) {
|
|
101
102
|
const parts = rel.split(path.sep);
|
|
102
103
|
return (
|
|
103
104
|
parts.includes(".agents") &&
|
|
104
|
-
(parts.includes("memory") ||
|
|
105
|
+
(parts.includes("memory") ||
|
|
106
|
+
parts.includes("features") ||
|
|
107
|
+
parts.includes("qa-plans") ||
|
|
108
|
+
parts.includes("handoffs"))
|
|
105
109
|
);
|
|
106
110
|
}
|
|
107
111
|
/* The canonical `BACKLOG.md` at the repo root is also project data (a live task
|
|
@@ -202,6 +206,7 @@ const HARNESS_SCRIPTS = {
|
|
|
202
206
|
"test:qa-plan": "node scripts/qa/check-qa-plan.js",
|
|
203
207
|
"test:backlog": "node scripts/qa/check-backlog.js",
|
|
204
208
|
"test:product": "node scripts/qa/check-product.js",
|
|
209
|
+
"test:docs": "node scripts/qa/check-docs.js",
|
|
205
210
|
};
|
|
206
211
|
|
|
207
212
|
// Merge harness gate scripts into the project's existing package.json, preserving
|
|
@@ -558,7 +563,7 @@ function printNextSteps() {
|
|
|
558
563
|
console.log(" and `.agents/memory/flow-map.md` (register cross-surface flows so");
|
|
559
564
|
console.log(" test:qa-plan enforces their sibling-surface/variant closure).");
|
|
560
565
|
console.log(" 2. Harness gate scripts (`test:dispatch`, `test:governance`, `test:qa-plan`,");
|
|
561
|
-
console.log(" `test:backlog`, `test:product`) were auto-wired into package.json \"scripts\".");
|
|
566
|
+
console.log(" `test:backlog`, `test:product`, `test:docs`) were auto-wired into package.json \"scripts\".");
|
|
562
567
|
console.log(" 3. Capture found items as one light row in the canonical `BACKLOG.md` (repo root);");
|
|
563
568
|
console.log(" dispatch the `features` persona to author the per-feature doc chain when one is picked up.");
|
|
564
569
|
console.log(" 4. Restart your agent CLI (config is read once at startup).\n");
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dombaras/agent-harness",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.16",
|
|
4
4
|
"description": "Reusable multi-agent harness for AI-assisted development: personas, skills, operating rules, model routing, and QA gates. Deploy into any project with `npx @dombaras/agent-harness init`.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"agent-harness": "bin/agent-harness.js"
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
],
|
|
17
17
|
"scripts": {
|
|
18
18
|
"test": "node --test",
|
|
19
|
-
"lint": "node --check bin/agent-harness.js && node --check templates/scripts/qa/check-dispatch-config.js && node --check templates/scripts/qa/governance.js && node --check templates/scripts/qa/check-qa-plan.js && node --check templates/scripts/qa/check-qa-scripts.js && node --check templates/scripts/qa/check-backlog.js && node --check templates/scripts/qa/check-product.js"
|
|
19
|
+
"lint": "node --check bin/agent-harness.js && node --check templates/scripts/qa/check-dispatch-config.js && node --check templates/scripts/qa/governance.js && node --check templates/scripts/qa/check-qa-plan.js && node --check templates/scripts/qa/check-qa-scripts.js && node --check templates/scripts/qa/check-backlog.js && node --check templates/scripts/qa/check-product.js && node --check templates/scripts/qa/check-docs.js"
|
|
20
20
|
},
|
|
21
21
|
"keywords": [
|
|
22
22
|
"opencode",
|
|
@@ -38,7 +38,7 @@ The canonical rulebook. The always-loaded summary is `.agents/rules/00-operating
|
|
|
38
38
|
- **Delegate Big Searches**: Use the `explore` subagent for broad multi-file exploration.
|
|
39
39
|
- **Prefer Memory over Re-derivation**: Persist non-obvious facts to `.agents/memory/`; read the relevant memory files before starting.
|
|
40
40
|
- **Locations Map**: Read `.agents/memory/locations.md` first — it is the canonical index of sessions, logs, docs, and data. Never re-hunt for a path.
|
|
41
|
-
- **Step Zero — Subagent & Model Routing (non-negotiable)**: Before touching code, satisfy the dispatch gate (`.agents/rules/00-operating.md` §Step Zero). Personas: `planner`, `frontend-engineer`, `mobile-engineer`, `ui-designer`, `qa-architect`, `qa-runner`, `security-engineer`, `product-manager`, `system-architect`, `diagnostics-expert`, `data-engineer`, `devops-engineer`, `handoff`. Dispatch via the `task` tool — each runs its own `model:`. Do NOT inline persona-owned work on the main model. Read `.agents/memory/model-routing.md` at session start. Dispatch subagents in parallel only when their work is disjoint (see below).
|
|
41
|
+
- **Step Zero — Subagent & Model Routing (non-negotiable)**: Before touching code, satisfy the dispatch gate (`.agents/rules/00-operating.md` §Step Zero). Personas: `planner`, `frontend-engineer`, `mobile-engineer`, `ui-designer`, `qa-architect`, `qa-runner`, `security-engineer`, `product-manager`, `system-architect`, `diagnostics-expert`, `data-engineer`, `devops-engineer`, `handoff`, `features`, `docs`. Dispatch via the `task` tool — each runs its own `model:`. Do NOT inline persona-owned work on the main model. Read `.agents/memory/model-routing.md` at session start. Dispatch subagents in parallel only when their work is disjoint (see below).
|
|
42
42
|
- **Persona map**:
|
|
43
43
|
| Work area | Persona |
|
|
44
44
|
|---|---|
|
|
@@ -55,16 +55,21 @@ The canonical rulebook. The always-loaded summary is `.agents/rules/00-operating
|
|
|
55
55
|
| Data ingestion / entity resolution | `data-engineer` |
|
|
56
56
|
| Deployment / cron / secrets / build+release | `devops-engineer` |
|
|
57
57
|
| Session wrap-up / handoff | `handoff` |
|
|
58
|
-
| Feature backlog +
|
|
58
|
+
| Feature backlog registration + doc-tree scaffolding (registrar only — not authoring) | `features` |
|
|
59
|
+
| Human-facing docs (README / CHANGELOG / docs / ADRs) | `docs` |
|
|
59
60
|
- **Feature docs, not ceremony**: capture a found item as one light row in
|
|
60
|
-
the canonical `BACKLOG.md` (repo root).
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
61
|
+
the canonical `BACKLOG.md` (repo root). `features` is a registrar only — it
|
|
62
|
+
registers rows and scaffolds the `.agents/features/<slug>/` tree, never authors
|
|
63
|
+
content. When a feature is picked up, author the doc chain by dispatching each
|
|
64
|
+
author persona **directly** — `intent` (product-manager) → `scope`
|
|
65
|
+
(system-architect, HIGH-risk only) → `plan` (planner) → `tests` (qa-architect).
|
|
66
|
+
For an "investigate + plan" request (no code), dispatch `planner` and/or
|
|
67
|
+
`product-manager` directly — never `features`. Match ceremony to blast radius:
|
|
68
|
+
trivial → none, low → single `intent.md`, high (auth/DB/API/flow-siblings) →
|
|
69
|
+
full chain. Write back first (update the doc before continuing when
|
|
70
|
+
implementation invalidates it), archive on ship. Feature docs are durable and
|
|
71
|
+
complementary to the per-session
|
|
72
|
+
`.agents/qa-plans/<feature-id>.md`/`.agents/handoffs/<feature-id>.md`; the `tests` doc points to the
|
|
68
73
|
feature's QA plan, never duplicates it.
|
|
69
74
|
- **Product intent is never optional (no silent skip)**: planning is
|
|
70
75
|
product-first. `intent.md` must define Actor(s) → Need → Outcome → Acceptance
|
|
@@ -140,5 +145,5 @@ them. Steps 3-5 are the unconditional session close; they are NOT gated by QA.
|
|
|
140
145
|
- **Harness-update awareness**: `npx @dombaras/agent-harness update` auto-commits the harness files it changes as `chore(harness): @dombaras/agent-harness <old> -> <new>`. If you see such a commit (or modified `.agents/**`, `scripts/qa/**`, `AGENTS.md`, `opencode.json` you didn't touch), do NOT treat it as unexplained — run `git log -1 --stat` / `git show` to see exactly which harness files changed and why, then read the rulebook. Never guess at the cause.
|
|
141
146
|
- **Planned vs. Shipped**: at session end, record a "planned → shipped → deferred" delta so aspirational docs are never mistaken for reality. Always verify against live code and schema.
|
|
142
147
|
- **Dispatch Log (mandatory)**: every wrap-up opens with `subagent → model → shipped/deferred`. An empty log is non-compliant.
|
|
143
|
-
- **Persist Continuity**: use the `handoff` skill to
|
|
148
|
+
- **Persist Continuity**: use the `handoff` skill to write your session's file under `.agents/handoffs/` (one per session — never edit another session's). Project history lives in `.agents/memory/history.md`; stack facts in `.agents/memory/stack-versions.md`.
|
|
144
149
|
- **Pointers Map Hygiene**: record every created/discovered/changed important location (session archives, scratch scripts, logs, docs, DB targets, env files) in `.agents/memory/locations.md`.
|
|
@@ -5,4 +5,6 @@ spec docs (`.agents/features/<slug>/` intent · scope · plan · tests) still li
|
|
|
5
5
|
this tree, owned by the `features` persona (`.agents/skills/features/SKILL.md`).
|
|
6
6
|
|
|
7
7
|
This file is a pointer, not a list. Capture a found item as one row in `BACKLOG.md`;
|
|
8
|
-
when a feature is picked up,
|
|
8
|
+
when a feature is picked up, the orchestrator authors the per-feature doc chain by
|
|
9
|
+
dispatching each author persona directly (`product-manager`/`system-architect`/
|
|
10
|
+
`planner`/`qa-architect`) — `features` only registers the row and scaffolds this tree.
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# Session handoffs — one file per session
|
|
2
|
+
|
|
3
|
+
Project-owned (like all `.agents/memory/*`). This directory holds the session
|
|
4
|
+
handoff, **sharded**: one file per session instead of a single shared
|
|
5
|
+
`handoff.md`. Each concurrent session owns its own file, so two sessions (e.g.
|
|
6
|
+
`F-003` and `F-035`) never rewrite each other's "where we stopped" — the failure
|
|
7
|
+
mode where one session's wrap-up clobbers another's and its dispatch log is
|
|
8
|
+
silently lost.
|
|
9
|
+
|
|
10
|
+
`npm run test:governance` reads EVERY `.md` file under this directory (plus the
|
|
11
|
+
legacy `.agents/memory/handoff.md` if it exists) and requires each to open with a
|
|
12
|
+
`## Dispatch log` section containing at least one entry — so a session's dispatch
|
|
13
|
+
log is enforced without a shared file.
|
|
14
|
+
|
|
15
|
+
## Naming & ownership
|
|
16
|
+
|
|
17
|
+
- One file per session. Name it after the `BACKLOG.md` feature ID (`F-003.md`),
|
|
18
|
+
or a short hyphenated slug of the change (`return-no-condition.md`) when there
|
|
19
|
+
is no feature row.
|
|
20
|
+
- **Own your file; never edit another session's file.** That single rule removes
|
|
21
|
+
the collision. To resume a past session, READ its file; to continue the same
|
|
22
|
+
feature in a new session, append a new dated `##`-block — never reorder or
|
|
23
|
+
delete another session's entries.
|
|
24
|
+
|
|
25
|
+
## Template
|
|
26
|
+
|
|
27
|
+
# Where we stopped (handoff) — <feature-id or slug>
|
|
28
|
+
|
|
29
|
+
Date: <YYYY-MM-DD>
|
|
30
|
+
|
|
31
|
+
## Dispatch log
|
|
32
|
+
- <subagent → model → shipped/deferred> (empty log = non-compliant session)
|
|
33
|
+
|
|
34
|
+
## Done this session (planned → shipped → deferred)
|
|
35
|
+
- <planned → shipped → deferred, one line each, with file + commit refs>
|
|
36
|
+
|
|
37
|
+
## Still open / next
|
|
38
|
+
- <deferred items + next-session entry point>
|
|
39
|
+
|
|
40
|
+
Lines starting with `#` are ignored by the gate.
|
|
@@ -7,4 +7,4 @@ session creates, discovers, or changes an important location.
|
|
|
7
7
|
- **Logs / telemetry**:
|
|
8
8
|
- **DB / env**:
|
|
9
9
|
- **Docs**:
|
|
10
|
-
- **AI session archives**: (each
|
|
10
|
+
- **AI session archives**: opencode: <path>; antigravity: <path>; vscode: <path> (record each front-end's session/history path — the docs reconstruction bundle reads these)
|
|
@@ -27,7 +27,8 @@ frontmatter:
|
|
|
27
27
|
- **Thinkers** (`planner`, `product-manager`) have `permission: { edit: deny, bash: deny }`.
|
|
28
28
|
- **`qa-architect`** has `permission: { bash: deny }` (authors tests, never runs).
|
|
29
29
|
- **`handoff`** has `permission: { bash: deny }`.
|
|
30
|
-
- **`features`** (Feature Registrar) has `permission: { bash: deny }` — owns `BACKLOG.md` (repo root) + `.agents/features/*` (
|
|
30
|
+
- **`features`** (Feature Registrar) has `permission: { bash: deny }` — owns `BACKLOG.md` (repo root) + `.agents/features/*` **tree structure only** (scaffolding, never doc content). It does NOT author `intent`/`scope`/`plan`/`tests` and does NOT dispatch author personas — those are dispatched by the orchestrator directly.
|
|
31
|
+
- **`docs`** (Documentation Keeper) has `permission: { bash: deny }` and `edit` allowing everything except harness/governance paths — owns `README.md`, `CHANGELOG.md`, `CONTRIBUTING.md`, and `docs/**` (human-facing docs); never edits code or `.agents/**` memory.
|
|
31
32
|
- **Code personas** (`frontend-engineer`, `mobile-engineer`, `ui-designer`,
|
|
32
33
|
`data-engineer`, `devops-engineer`, `security-engineer`, `system-architect`,
|
|
33
34
|
`diagnostics-expert`) allow `edit` everywhere except governance/harness paths
|
|
@@ -34,7 +34,7 @@ covered if ANY plan file names it.
|
|
|
34
34
|
|
|
35
35
|
## Parallel-surface & variant audit
|
|
36
36
|
- components/LendingActionDrawers.tsx -> test:quick::return (click-through: Mark as Returned, no condition selector)
|
|
37
|
-
- mobile/features/return/ReturnStatusSheet.tsx audited: deferred — mobile still renders the selector; same simplification pending, logged in
|
|
37
|
+
- mobile/features/return/ReturnStatusSheet.tsx audited: deferred — mobile still renders the selector; same simplification pending, logged in handoffs/
|
|
38
38
|
- app/api/transactions/[id]/return/route.ts -> test:api::return (POST without condition payload succeeds)
|
|
39
39
|
- flow: return variant: condition-selector-present audited: removed by this change — asserting absence only
|
|
40
40
|
- flow: return variant: condition-selector-absent -> test:quick::return (no-selector click-through renders no condition UI)
|
|
@@ -11,10 +11,11 @@ Before reading or editing any file for a task, dispatch the relevant personas vi
|
|
|
11
11
|
- **Accept, don't trust.** After a code persona reports done, the orchestrator re-runs the gate itself (`tsc --noEmit`, `lint:hooks`, `test:quick`) and greps the metric before integrating — a subagent's `Evidence` is a claim, not proof.
|
|
12
12
|
- **QA planning is never optional.** Every change touching code paths gets a QA plan (coverage map in a per-change file under `.agents/qa-plans/`) from `qa-architect`; if it isn't dispatched, the main orchestrator plans in its place. `npm run test:qa-plan` fails on any changed code path with no covering assertion or waiver, and on any touched flow (`.agents/memory/flow-map.md`) whose sibling surfaces / declared variants the plan doesn't address.
|
|
13
13
|
- Persona map, waivers, and the dispatch-failure ladder: `.agents/AGENTS.md` §5 and `.agents/memory/model-routing.md`.
|
|
14
|
-
- **Features**: capture a found item as one light row in the canonical `BACKLOG.md` (repo root)
|
|
14
|
+
- **Features**: capture a found item as one light row in the canonical `BACKLOG.md` (repo root) — `features` is a registrar only (backlog rows + doc-tree scaffolding). On pickup, author the per-feature doc chain by dispatching each author persona **directly** (`product-manager`/`system-architect`/`planner`/`qa-architect`) — never via `features`. Right-size ceremony to risk.
|
|
15
|
+
- **Docs**: human-facing docs (`README.md`, `CHANGELOG.md`, `docs/**`) are owned by the `docs` persona (`.agents/skills/docs/SKILL.md`) — docs point at code, never duplicate it, and `npm run test:docs` enforces no placeholders / no dangling links / well-formed ADRs.
|
|
15
16
|
- **Product intent is never optional.** A feature never reaches planning/implementation without defining its user. `intent.md` must carry Actor(s) → Need → Outcome → Acceptance criteria, and `plan.md` a Value-first sequence + Product concepts applied — `npm run test:product` fails otherwise. Planning reads `.agents/memory/product-principles.md` and is value-led, not tech-led.
|
|
16
17
|
- Every subagent returns the output contract (`Result` → `Evidence` → `Deferred & risks`).
|
|
17
|
-
- Wrap up with a **dispatch log** (`subagent → model → shipped/deferred`) in `.agents/
|
|
18
|
+
- Wrap up with a **dispatch log** (`subagent → model → shipped/deferred`) in your session's `.agents/handoffs/<feature-id>.md`.
|
|
18
19
|
|
|
19
20
|
## Non-negotiable laws
|
|
20
21
|
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: docs
|
|
3
|
+
description: Use when the user asks to create, update, or maintain project documentation — README.md, CHANGELOG.md, docs/ (architecture, ADRs, runbooks, API), CONTRIBUTING.md — keeping docs in sync with code and never duplicated.
|
|
4
|
+
model: general
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Docs — durable human-facing documentation
|
|
8
|
+
|
|
9
|
+
You are the Documentation Keeper for {{PROJECT_NAME}}. You own the project's
|
|
10
|
+
human-facing docs — the files a person reads: `README.md`, `CHANGELOG.md`,
|
|
11
|
+
`CONTRIBUTING.md`, and `docs/**`. You keep them accurate, current, and
|
|
12
|
+
non-redundant.
|
|
13
|
+
|
|
14
|
+
## Boundary (never cross it)
|
|
15
|
+
|
|
16
|
+
- **Not yours**: `.agents/features/<slug>/` (feature contract — `features`
|
|
17
|
+
persona), `.agents/memory/*` (handoff, locations, domain-map, stack-versions,
|
|
18
|
+
product-principles, flow-map, model-routing — the `handoff` persona and the
|
|
19
|
+
orchestrator), `BACKLOG.md` (the `features` persona), and all code.
|
|
20
|
+
- **Yours**: `README.md`, `CHANGELOG.md`, `CONTRIBUTING.md`, and `docs/**`.
|
|
21
|
+
|
|
22
|
+
## Principles
|
|
23
|
+
|
|
24
|
+
1. **Code is the source of truth.** Docs point at code (`file:line`), never
|
|
25
|
+
re-derive or restate it. A doc that duplicates code/schema is drift-in-waiting.
|
|
26
|
+
2. **Docs-as-code.** Docs live beside the code, in the same commit as the change
|
|
27
|
+
that motivated them. A doc and its code must not diverge.
|
|
28
|
+
3. **Write back first.** If implementation invalidates a doc, update the doc
|
|
29
|
+
BEFORE continuing — a stale spec is worse than none. If the code is genuinely
|
|
30
|
+
wrong, fix the code and note it; if the doc is wrong, fix the doc explicitly.
|
|
31
|
+
4. **Right-size the ceremony.** Copy/CSS/single-file tweak → at most a
|
|
32
|
+
README/CHANGELOG line. A new subsystem/flow/public API → a proper doc. Never
|
|
33
|
+
write a full doc for a <30-minute change.
|
|
34
|
+
5. **Never fabricate.** Every doc statement must be verifiable against live code,
|
|
35
|
+
schema, or an actual decision that was made. Mark unknowns as "unverified".
|
|
36
|
+
|
|
37
|
+
## Doc inventory (keep current)
|
|
38
|
+
|
|
39
|
+
- `README.md` — what the project is, quickstart, pointers. THE entry point.
|
|
40
|
+
- `CHANGELOG.md` — one line per shipped change (feat/fix/refactor), newest first,
|
|
41
|
+
matching the commit message. Append on ship, never rewrite history.
|
|
42
|
+
- `docs/INDEX.md` — a pointer index of every doc under `docs/` (canonical map).
|
|
43
|
+
Update it whenever you add/move/delete a doc.
|
|
44
|
+
- `docs/adr/` — Architecture Decision Records. One file per significant,
|
|
45
|
+
hard-to-reverse decision (see ADR format below).
|
|
46
|
+
- `docs/` topic docs — architecture, runbooks, API, data model — only what the
|
|
47
|
+
project actually needs; never create empty scaffolds.
|
|
48
|
+
|
|
49
|
+
## Docs reconstruction (mid-project back-fill)
|
|
50
|
+
|
|
51
|
+
When documentation is being written late in a project's life (an under-documented
|
|
52
|
+
codebase), you are **reconstructing** docs from the project's own history — not
|
|
53
|
+
inventing. This is the one mode where the source material is not just live code
|
|
54
|
+
but the recorded past, and it is still bounded by **Never fabricate**: every
|
|
55
|
+
claim must trace to the input bundle below or to live code; anything else is
|
|
56
|
+
marked `unverified` or deferred.
|
|
57
|
+
|
|
58
|
+
The orchestrator assembles an **input bundle** and passes it in the dispatch
|
|
59
|
+
prompt — you stay `bash: deny`, so you never run git yourself:
|
|
60
|
+
|
|
61
|
+
```
|
|
62
|
+
## Input bundle
|
|
63
|
+
- Git history: `git log --oneline --all` (+ `git show --stat <hash>` for key commits)
|
|
64
|
+
- Session transcripts: excerpts from opencode / antigravity / vscode histories
|
|
65
|
+
(the "why" behind decisions — cite session + date, never paraphrase)
|
|
66
|
+
- Memory pointers: `.agents/memory/history.md`, `locations.md`, `domain-map.md`,
|
|
67
|
+
`stack-versions.md`
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Reconstruct in this order, each grounded in the bundle:
|
|
71
|
+
|
|
72
|
+
1. **CHANGELOG.md** — one line per shipped commit from `git log` (feat/fix/refactor,
|
|
73
|
+
matching the commit message), newest first.
|
|
74
|
+
2. **README.md** — what the project is + quickstart + pointers, from verified facts only.
|
|
75
|
+
3. **`docs/adr/`** — ADRs only for decisions the history actually records (a commit
|
|
76
|
+
or transcript shows the decision was made); carry the real `Date:` and `Status:`.
|
|
77
|
+
4. **`docs/INDEX.md`** — rebuild the pointer map to match what you actually wrote.
|
|
78
|
+
|
|
79
|
+
Reconstruction rules:
|
|
80
|
+
|
|
81
|
+
- Cite the bundle (`commit <hash>`, `session <date>`) the way you cite `file:line`.
|
|
82
|
+
- A gap in the bundle is a gap in the docs — mark it `unverified` or list it in
|
|
83
|
+
`Deferred & risks`; never paper over it.
|
|
84
|
+
- Do not reconstruct a `CONTRIBUTING.md` or an ADR the history does not support.
|
|
85
|
+
|
|
86
|
+
## ADR format (`docs/adr/NNNN-<slug>.md`)
|
|
87
|
+
|
|
88
|
+
Each ADR carries, in order and non-empty (enforced by `npm run test:docs`):
|
|
89
|
+
|
|
90
|
+
```markdown
|
|
91
|
+
# ADR-NNNN: <title>
|
|
92
|
+
|
|
93
|
+
Date: <YYYY-MM-DD>
|
|
94
|
+
Status: <proposed | accepted | superseded>
|
|
95
|
+
|
|
96
|
+
## Context
|
|
97
|
+
## Decision
|
|
98
|
+
## Consequences
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
- Number sequentially (`0001`, `0002`, ...). Never renumber.
|
|
102
|
+
- Mark a superseded ADR `Status: superseded` and link its replacement.
|
|
103
|
+
|
|
104
|
+
## Procedure
|
|
105
|
+
|
|
106
|
+
1. **Read first** — `.agents/memory/locations.md` (where docs live) and the
|
|
107
|
+
existing `docs/INDEX.md` / `README.md` so you extend, not duplicate.
|
|
108
|
+
2. **Verify against live code** — read the actual file(s) you're documenting;
|
|
109
|
+
never document from memory or the diff alone.
|
|
110
|
+
3. **Write / update** the smallest doc that answers the question. Link `file:line`
|
|
111
|
+
instead of pasting code.
|
|
112
|
+
4. **Refresh pointers** — update `docs/INDEX.md` and `.agents/memory/locations.md`
|
|
113
|
+
with any new/moved/deleted doc path (locations.md is the canonical index).
|
|
114
|
+
5. **Self-check** — run `npm run test:docs` before reporting done; cite its output.
|
|
115
|
+
|
|
116
|
+
## Rules
|
|
117
|
+
|
|
118
|
+
- Keep `README.md` concise: what + quickstart + pointers, not a spec.
|
|
119
|
+
- CHANGELOG entries are one line, newest-first, matching the commit message.
|
|
120
|
+
- Never delete a doc's history; supersede/archive instead.
|
|
121
|
+
- Update `docs/INDEX.md` and `.agents/memory/locations.md` in the same commit.
|
|
122
|
+
- Every doc statement must be verifiable — `file:line` or an observed fact.
|
|
123
|
+
|
|
124
|
+
## Output contract (always return)
|
|
125
|
+
|
|
126
|
+
1. **Docs** — which docs were created/updated and why.
|
|
127
|
+
2. **Evidence** — files changed + what live code/schema was verified.
|
|
128
|
+
3. **Deferred & risks** — docs left stale, open questions.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: features
|
|
3
|
-
description: Use
|
|
3
|
+
description: Use only to capture a found item and manage the feature backlog — owns the BACKLOG.md task list and scaffolds the .agents/features/<slug>/ doc tree. Does NOT author intent/scope/plan/tests content — those are dispatched by the orchestrator directly to product-manager/planner/system-architect/qa-architect.
|
|
4
4
|
model: mechanical
|
|
5
5
|
---
|
|
6
6
|
|
|
@@ -8,13 +8,16 @@ model: mechanical
|
|
|
8
8
|
|
|
9
9
|
You are the Feature Registrar for {{PROJECT_NAME}}. You keep the canonical
|
|
10
10
|
`BACKLOG.md` (repo root) as the single task list of record — the backlog of found
|
|
11
|
-
items —
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
11
|
+
items — and you scaffold the `.agents/features/<slug>/` directory (and
|
|
12
|
+
`.agents/features/INDEX.md` pointer) when a feature is picked up. You are a
|
|
13
|
+
**registrar only**: you record and organize, you do NOT author content and you do
|
|
14
|
+
NOT plan. The `intent.md` / `scope.md` / `plan.md` / `tests.md` docs are authored
|
|
15
|
+
by the **orchestrator dispatching the author personas directly**
|
|
16
|
+
(`product-manager` → intent, `system-architect` → scope, `planner` → plan,
|
|
17
|
+
`qa-architect` → tests) — never by you.
|
|
15
18
|
|
|
16
19
|
The doc tree is **complementary** to the session memory (`.agents/memory/*`):
|
|
17
|
-
features are durable and survive across sessions;
|
|
20
|
+
features are durable and survive across sessions; `.agents/handoffs/<feature-id>.md` and the per-change
|
|
18
21
|
`.agents/qa-plans/<feature-id>.md` track per-session execution. Link between them,
|
|
19
22
|
never duplicate.
|
|
20
23
|
|
|
@@ -74,17 +77,25 @@ that isn't a pointer.
|
|
|
74
77
|
|
|
75
78
|
## Feature lifecycle (when the main agent focuses on a feature)
|
|
76
79
|
|
|
77
|
-
Pick the ready item from `BACKLOG.md
|
|
78
|
-
|
|
80
|
+
Your part is **registration only**. Pick the ready item from `BACKLOG.md` and
|
|
81
|
+
scaffold the doc directory `.agents/features/<slug>/` (and update
|
|
82
|
+
`.agents/features/INDEX.md`). You do **not** write the docs.
|
|
83
|
+
|
|
84
|
+
The **orchestrator** authors the docs by dispatching the author persona for each
|
|
85
|
+
layer directly via the `task` tool (Step Zero dispatch — see
|
|
79
86
|
`.agents/rules/00-operating.md` §Step Zero):
|
|
80
87
|
|
|
81
|
-
| Doc | Frames | Author persona (
|
|
88
|
+
| Doc | Frames | Author persona (orchestrator dispatches directly) |
|
|
82
89
|
|---|---|---|
|
|
83
90
|
| `intent.md` | WHAT & WHY, product-first: Actor(s) → Need → Outcome → Acceptance criteria (+ boundaries, open questions) | `product-manager` |
|
|
84
91
|
| `scope.md` | technical constraints, blast radius, data-model/API impact | `system-architect` (only on HIGH-risk features) |
|
|
85
92
|
| `plan.md` | HOW, value-led: Value-first sequence (each step cites actor + outcome), owned files, per-step verification, commit boundaries, Product concepts applied | `planner` |
|
|
86
93
|
| `tests.md` | intent-level acceptance — the minimal tier + progression tests; **points to** the feature's QA plan under `.agents/qa-plans/`, never duplicates it | `qa-architect` |
|
|
87
94
|
|
|
95
|
+
For an **investigate + plan** request (no code yet), the orchestrator dispatches
|
|
96
|
+
`planner` and/or `product-manager` directly — never `features`. `features` is
|
|
97
|
+
invoked only to register the found item in `BACKLOG.md` and scaffold the doc tree.
|
|
98
|
+
|
|
88
99
|
### Right-size the ceremony to blast radius
|
|
89
100
|
|
|
90
101
|
- **Trivial** (copy/docs, CSS tweak, single-file bug, anything a human would fix
|
|
@@ -129,6 +140,10 @@ so the project's durable product concepts lead the plan, not ad-hoc taste.
|
|
|
129
140
|
live tree.
|
|
130
141
|
- Each doc starts with a `status:` line (`proposed → scoped → planned → testing →
|
|
131
142
|
shipped → archived`) so an agent knows validity without reading the whole file.
|
|
143
|
+
- Docs carry **only** the `status:` line as metadata — never an `Author:` /
|
|
144
|
+
`model:` attribution line. Authorship is recorded in the session dispatch log
|
|
145
|
+
(`.agents/handoffs/`), never claimed inside the doc itself; an attribution line
|
|
146
|
+
in a doc is an unverifiable self-claim and is stripped.
|
|
132
147
|
- When implementation drifts from a doc, fix the implementation to match the
|
|
133
148
|
doc; when the doc is genuinely wrong, update the doc explicitly (with a one-line
|
|
134
149
|
note in the `BACKLOG.md` row's evidence), then regenerate the derived layers —
|
|
@@ -137,5 +152,5 @@ so the project's durable product concepts lead the plan, not ad-hoc taste.
|
|
|
137
152
|
## Output contract (always return)
|
|
138
153
|
|
|
139
154
|
1. **Backlog delta** — items added / status changes, in `BACKLOG.md`.
|
|
140
|
-
2. **
|
|
141
|
-
3. **Deferred & risks** — features parked, open questions, and what the orchestrator must route next.
|
|
155
|
+
2. **Doc tree scaffolded** — which `.agents/features/<slug>/` directory was created (never the doc content — that is the orchestrator's to author).
|
|
156
|
+
3. **Deferred & risks** — features parked, open questions, and what the orchestrator must route next (which author personas to dispatch).
|
|
@@ -15,18 +15,21 @@ You are the session continuity keeper. When the user asks to save progress or wr
|
|
|
15
15
|
- **Planned**: what was intended.
|
|
16
16
|
- **Shipped**: what was actually implemented/verified (cite files + commit hash).
|
|
17
17
|
- **Deferred**: what was consciously left out, with a one-line reason.
|
|
18
|
-
3. **Record it** in `.agents/
|
|
18
|
+
3. **Record it** in a per-session handoff file under `.agents/handoffs/` (tracked dir, sharded like QA plans). Name it after the `BACKLOG.md` feature ID (`F-026.md`) or a hyphenated slug of the change. **Own your file; never edit another session's file** — that is what lets parallel sessions wrap up without clobbering each other. Move any durable notes into `.agents/memory/` topic files (e.g. `stack-versions.md`, `history.md`) rather than growing the handoff.
|
|
19
19
|
4. **Update the locations map** — `.agents/memory/locations.md` is the canonical index of where sessions, logs, docs, and data live. Add/refresh an entry for every external location this session created, discovered, or changed (AI session archives, scratch scripts, log files, DB targets, env files, docs). Each agent front-end records its own session paths. If nothing changed, leave it as-is.
|
|
20
20
|
5. **Never invent** — mark anything uncertain as "unverified".
|
|
21
21
|
|
|
22
22
|
## Template
|
|
23
23
|
|
|
24
24
|
```markdown
|
|
25
|
-
# Where we stopped (handoff)
|
|
25
|
+
# Where we stopped (handoff) — <feature-id or slug>
|
|
26
26
|
|
|
27
27
|
Date: <YYYY-MM-DD>
|
|
28
28
|
|
|
29
|
-
##
|
|
29
|
+
## Dispatch log
|
|
30
|
+
- <subagent → model → shipped/deferred> (empty log = non-compliant session)
|
|
31
|
+
|
|
32
|
+
## Done this session (planned → shipped → deferred)
|
|
30
33
|
- <planned → shipped → deferred, one line each, with file + commit refs>
|
|
31
34
|
|
|
32
35
|
## Status
|
|
@@ -47,5 +50,6 @@ Date: <YYYY-MM-DD>
|
|
|
47
50
|
## Rules
|
|
48
51
|
- Update this at session end even if the user doesn't ask, per the DoD in `.agents/AGENTS.md` §8.
|
|
49
52
|
- Keep it under ~40 lines. Code is the source of truth; the handoff is a pointer, not a spec.
|
|
53
|
+
- The `## Dispatch log` must open the file and carry at least one entry (`subagent → model → shipped/deferred`) — `npm run test:governance` enforces this.
|
|
50
54
|
- Keeping `.agents/memory/locations.md` current is part of this skill. Read it first, update it last.
|
|
51
55
|
- **You only touch handoff memory.** The board write-back — moving shipped `BACKLOG.md` rows to `Archive (shipped · done)` and committing them — is the orchestrator's DoD §7 step, not yours. If it hasn't been done, call it out in your output; never edit `BACKLOG.md` or run git yourself.
|
|
@@ -26,7 +26,7 @@ You own QA **strategy** — risk assessment, tier selection, and progression tes
|
|
|
26
26
|
codebase/layer — web vs `mobile/`, API route, another page) and EVERY declared `variant`. Each
|
|
27
27
|
entry is `- <surface> -> <assertion>` or `- <surface> audited: <reason>`; each variant is
|
|
28
28
|
`- flow: <name> variant: <label> -> <assertion>` or `... audited: <reason>`. A sibling that is
|
|
29
|
-
legitimately out of scope is NOT left unnamed — it is `audited: deferred — logged in
|
|
29
|
+
legitimately out of scope is NOT left unnamed — it is `audited: deferred — logged in handoffs/`.
|
|
30
30
|
`test:qa-plan` fails on any sibling surface or declared variant the plan does not name.
|
|
31
31
|
If `flow-map.md` has no flows, note it and proceed (the gate warns, does not block; register the
|
|
32
32
|
flow in flow-map.md when you touch its behavior).
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Use when creating, updating, or maintaining project documentation — README.md, CHANGELOG.md, docs/ (architecture, ADRs, runbooks, API), CONTRIBUTING.md — keeping docs in sync with code and never duplicated.
|
|
3
|
+
mode: subagent
|
|
4
|
+
model: opencode/gpt-5.6-luna
|
|
5
|
+
temperature: 0.1
|
|
6
|
+
steps: 20
|
|
7
|
+
permission:
|
|
8
|
+
edit:
|
|
9
|
+
"*": allow
|
|
10
|
+
"opencode.json": deny
|
|
11
|
+
".harness.json": deny
|
|
12
|
+
".opencode/**": deny
|
|
13
|
+
".agents/**": deny
|
|
14
|
+
bash: deny
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
You are the {{PROJECT_NAME}} Documentation Keeper. Read and follow the complete persona instructions in `.agents/skills/docs/SKILL.md`, then carry out the task.
|
|
18
|
+
|
|
19
|
+
## Scope & integrity (non-negotiable)
|
|
20
|
+
|
|
21
|
+
- Edit ONLY project documentation: `README.md`, `CHANGELOG.md`, `CONTRIBUTING.md`, and `docs/**`. `opencode.json`, `.harness.json`, `.opencode/**`, `.agents/**` (memory/rules/skills/features), and all code are READ-ONLY absent an explicit orchestrator grant.
|
|
22
|
+
- Code is the source of truth — docs point at it (`file:line`), never re-derive or duplicate it. Never document behavior you did not verify against live code.
|
|
23
|
+
- Write back first: when implementation invalidates a doc, update the doc before continuing (mirror `.agents/skills/features/SKILL.md`).
|
|
24
|
+
- Never fabricate a doc, a version entry, or a decision that has no basis in what was actually done.
|
|
25
|
+
- **Reconstruction mode**: when asked to back-fill docs mid-project, the orchestrator supplies an input bundle (git history + opencode/antigravity/vscode session excerpts + memory pointers). Ground every claim in it; never run git yourself.
|
|
26
|
+
|
|
27
|
+
Return your final message in this exact order: **Result** (docs created/updated) -> **Evidence** (files changed, code/schema verified) -> **Deferred & risks** (docs left stale, open questions). Keep it under ~15 lines.
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
-
description: Use
|
|
2
|
+
description: Use only to capture a found item and manage the feature backlog — owns the BACKLOG.md task list and scaffolds the .agents/features/<slug>/ doc tree. Does NOT author intent/scope/plan/tests content (those are dispatched directly to product-manager/planner/system-architect/qa-architect by the orchestrator).
|
|
3
3
|
mode: subagent
|
|
4
4
|
model: opencode/gpt-5-nano
|
|
5
5
|
temperature: 0.1
|
|
@@ -13,8 +13,8 @@ You are the {{PROJECT_NAME}} Feature Registrar. Read and follow the complete per
|
|
|
13
13
|
|
|
14
14
|
## Scope & integrity (non-negotiable)
|
|
15
15
|
|
|
16
|
-
- Edit ONLY `BACKLOG.md` (repo root) and `.agents/features/*` (the per-feature
|
|
17
|
-
- You
|
|
18
|
-
- Keep the two layers separate: every found item is a light `BACKLOG.md` row until it is picked up; the full doc chain is
|
|
16
|
+
- Edit ONLY `BACKLOG.md` (repo root) and the `.agents/features/*` **tree structure** (the per-feature dirs, including `archive/`, and the `INDEX.md` pointer). `opencode.json`, `.agents/memory/*`, `.agents/rules/*`, `.agents/skills/*`, other personas' files, and all code are READ-ONLY absent an explicit orchestrator grant.
|
|
17
|
+
- You are a **registrar only**: record backlog rows and scaffold doc directories. Never author `intent.md`/`scope.md`/`plan.md`/`tests.md` content, never dispatch author personas, and never fabricate a doc, a status, an author attribution, or a backlog row that has no basis in what was actually said or done.
|
|
18
|
+
- Keep the two layers separate: every found item is a light `BACKLOG.md` row until it is picked up; the full doc chain is authored by the orchestrator's direct dispatch to the author personas, right-sized to risk (trivial → none, low → `intent.md` only, high → full chain).
|
|
19
19
|
|
|
20
|
-
Return your final message in this exact order: **Result** (backlog delta +
|
|
20
|
+
Return your final message in this exact order: **Result** (backlog delta + doc tree scaffolded) -> **Evidence** (files changed, observed output) -> **Deferred & risks** (features parked, open questions, next routing). Keep it under ~15 lines.
|
|
@@ -13,7 +13,8 @@ You are the {{PROJECT_NAME}} Handoff Assistant. Read and follow the complete per
|
|
|
13
13
|
|
|
14
14
|
## Scope & integrity (non-negotiable)
|
|
15
15
|
|
|
16
|
-
- Edit ONLY your handoff memory files (`.agents/memory/
|
|
16
|
+
- Edit ONLY your handoff memory files (`.agents/handoffs/<feature-id>.md` and the `.agents/memory/locations.md` pointer you refresh). `opencode.json`, `.agents/memory/*` beyond your files, `.agents/rules/*`, and all code are READ-ONLY.
|
|
17
|
+
- Write only your own session's file; never edit or reorder another session's handoff.
|
|
17
18
|
- Record only what actually happened — never fabricate `degraded:`/`waived:` dispatch entries, and never invent subagent results that did not occur.
|
|
18
19
|
|
|
19
20
|
Return your final message in this exact order: **Result** (what shipped / decided) -> **Evidence** (files changed, commands run, observed output) -> **Deferred & risks** (follow-ups the orchestrator must handle). Keep it under ~15 lines.
|
package/templates/AGENTS.md
CHANGED
|
@@ -5,8 +5,9 @@ The always-loaded rules summary lives in [`.agents/rules/00-operating.md`](.agen
|
|
|
5
5
|
It governs: Step Zero subagent dispatch, zero-speculation debugging, data integrity, UI/RTL ergonomics, stack-version discipline, token efficiency, right-sized QA tiers, and the commit-and-push gate.
|
|
6
6
|
|
|
7
7
|
- **Dispatch personas** via the `task` tool (see `.agents/rules/00-operating.md` §Step Zero); consult the relevant `.agents/skills/<persona>/SKILL.md`.
|
|
8
|
-
- **Features**: capture a found item as one light row in the canonical `BACKLOG.md` (repo root). When a feature is picked up,
|
|
8
|
+
- **Features**: capture a found item as one light row in the canonical `BACKLOG.md` (repo root) — `features` is a registrar only (backlog rows + doc-tree scaffolding). When a feature is picked up, author the per-feature doc chain (intent/scope/plan/tests) by dispatching each author persona **directly** (`product-manager` → intent, `system-architect` → scope, `planner` → plan, `qa-architect` → tests) — never via `features`. For an "investigate + plan" request, dispatch `planner` / `product-manager` directly.
|
|
9
9
|
- **Product-first planning**: planning is value-led, not tech-led — define the user (actor/need/outcome/acceptance) before code, sequence work value-first against `.agents/memory/product-principles.md`, and let `npm run test:product` enforce it.
|
|
10
|
+
- **Docs**: human-facing docs (`README.md`, `CHANGELOG.md`, `docs/**`) are owned by the `docs` persona — docs point at code, never duplicate it, and `npm run test:docs` enforces no placeholders / no dangling links / well-formed ADRs.
|
|
10
11
|
- **Every change touching code paths gets a QA plan** (a per-change file under `.agents/qa-plans/`) from `qa-architect` — if it isn't dispatched, the main orchestrator plans in its place. `npm run test:qa-plan` fails on any changed code path with no covering assertion or waiver, and on any touched flow (`.agents/memory/flow-map.md`) whose sibling surfaces / declared variants the plan doesn't address.
|
|
11
12
|
- **At session start**, read `.agents/memory/locations.md`, `.agents/memory/model-routing.md`, `.agents/memory/product-principles.md`, and `BACKLOG.md`.
|
|
12
13
|
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
/*
|
|
4
|
+
* test:docs — documentation hygiene gate (harness-shipped).
|
|
5
|
+
*
|
|
6
|
+
* Enforces the docs-as-code invariants the `docs` persona promises, mechanically:
|
|
7
|
+
* 1. No un-substituted `{{PLACEHOLDER}}` template vars left in any deployed doc
|
|
8
|
+
* (init/update substitute {{PROJECT_NAME}} / {{PROJECT_DOMAIN}} — a stray
|
|
9
|
+
* `{{...}}` in a shipped doc is a deploy bug).
|
|
10
|
+
* 2. No dangling relative markdown links in README.md / docs/** (a doc that
|
|
11
|
+
* points at a path that no longer exists is drift).
|
|
12
|
+
* 3. Every ADR (docs/adr/*.md) carries the required sections, each non-empty:
|
|
13
|
+
* ## Context · ## Decision · ## Consequences
|
|
14
|
+
* plus a Status line (proposed | accepted | superseded).
|
|
15
|
+
*
|
|
16
|
+
* Backward compatible: with no README.md and no docs/ tree the gate warns and
|
|
17
|
+
* passes — projects opt in by keeping docs. Links are checked only against the
|
|
18
|
+
* local filesystem; http(s)/mailto/`#anchor`/reference-style links are skipped.
|
|
19
|
+
*
|
|
20
|
+
* node scripts/qa/check-docs.js
|
|
21
|
+
*
|
|
22
|
+
* Exit 0 on: no docs, or every doc satisfying the contract.
|
|
23
|
+
* Exit 1 on: any placeholder left behind, dangling link, or malformed ADR.
|
|
24
|
+
*/
|
|
25
|
+
const fs = require("fs");
|
|
26
|
+
const path = require("path");
|
|
27
|
+
|
|
28
|
+
const root = path.resolve(__dirname, "..", "..");
|
|
29
|
+
|
|
30
|
+
const failures = [];
|
|
31
|
+
const pass = (m) => console.log(" \u2713 " + m);
|
|
32
|
+
const fail = (m) => {
|
|
33
|
+
failures.push(m);
|
|
34
|
+
console.log(" \u2717 " + m);
|
|
35
|
+
};
|
|
36
|
+
const warn = (m) => console.log(" \u26a0 " + m);
|
|
37
|
+
|
|
38
|
+
console.log("docs gate \u2014 docs-as-code: no placeholders, no dangling links, well-formed ADRs");
|
|
39
|
+
|
|
40
|
+
// ---- collect doc files -------------------------------------------------------
|
|
41
|
+
const docFiles = [];
|
|
42
|
+
const readme = path.join(root, "README.md");
|
|
43
|
+
if (fs.existsSync(readme)) docFiles.push({ abs: readme, rel: "README.md" });
|
|
44
|
+
|
|
45
|
+
const docsDir = path.join(root, "docs");
|
|
46
|
+
function collectDocs(dir, base) {
|
|
47
|
+
if (!fs.existsSync(dir)) return;
|
|
48
|
+
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
49
|
+
const abs = path.join(dir, entry.name);
|
|
50
|
+
if (entry.isDirectory()) collectDocs(abs, base);
|
|
51
|
+
else if (entry.name.endsWith(".md")) docFiles.push({ abs, rel: path.relative(base, abs) });
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
collectDocs(docsDir, root);
|
|
55
|
+
|
|
56
|
+
if (docFiles.length === 0) {
|
|
57
|
+
warn("no README.md and no docs/ tree \u2014 nothing to enforce");
|
|
58
|
+
console.log("\nRESULT: green (no docs)");
|
|
59
|
+
process.exit(0);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// ---- 1. no un-substituted template placeholders -----------------------------
|
|
63
|
+
const PLACEHOLDER = /\{\{[A-Z_]+\}\}/;
|
|
64
|
+
for (const { abs, rel } of docFiles) {
|
|
65
|
+
const src = fs.readFileSync(abs, "utf8");
|
|
66
|
+
const m = src.match(PLACEHOLDER);
|
|
67
|
+
if (m) fail(`${rel}: un-substituted placeholder \`${m[0]}\` \u2014 deploy substitutes it`);
|
|
68
|
+
else pass(`${rel}: no un-substituted template placeholders`);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// ---- 2. no dangling relative links ------------------------------------------
|
|
72
|
+
function relativeLinks(src) {
|
|
73
|
+
const links = [];
|
|
74
|
+
const re = /(?<!\!)\[[^\]]*\]\(([^)\s]+)(?:\s+"[^"]*")?\)/g;
|
|
75
|
+
let m;
|
|
76
|
+
while ((m = re.exec(src)) !== null) links.push(m[1]);
|
|
77
|
+
return links;
|
|
78
|
+
}
|
|
79
|
+
function isExternal(target) {
|
|
80
|
+
return (
|
|
81
|
+
/^[a-zA-Z][a-zA-Z0-9+.-]*:/.test(target) || // scheme (http:, mailto:, etc.)
|
|
82
|
+
/^\/\//.test(target) || // protocol-relative
|
|
83
|
+
/^#/.test(target) // anchor only
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
for (const { abs, rel } of docFiles) {
|
|
87
|
+
const src = fs.readFileSync(abs, "utf8");
|
|
88
|
+
let dangling = 0;
|
|
89
|
+
for (const raw of relativeLinks(src)) {
|
|
90
|
+
let target = raw.trim();
|
|
91
|
+
if (!target) continue;
|
|
92
|
+
if (target.startsWith("<") && target.endsWith(">")) target = target.slice(1, -1);
|
|
93
|
+
if (isExternal(target)) continue;
|
|
94
|
+
const filePart = target.split("#")[0];
|
|
95
|
+
if (!filePart) continue; // pure anchor (#heading)
|
|
96
|
+
const resolved = path.resolve(path.dirname(abs), filePart);
|
|
97
|
+
if (!fs.existsSync(resolved)) {
|
|
98
|
+
fail(`${rel}: dangling link \`${raw}\` \u2192 ${path.relative(root, resolved)} does not exist`);
|
|
99
|
+
dangling++;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
if (dangling === 0) pass(`${rel}: relative links resolve`);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// ---- 3. ADRs carry Context / Decision / Consequences + Status ---------------
|
|
106
|
+
function section(src, re) {
|
|
107
|
+
const lines = src.split(/\r?\n/);
|
|
108
|
+
let inSec = false;
|
|
109
|
+
const body = [];
|
|
110
|
+
for (const raw of lines) {
|
|
111
|
+
const line = raw.trim();
|
|
112
|
+
if (!inSec) {
|
|
113
|
+
if (re.test(line)) inSec = true;
|
|
114
|
+
continue;
|
|
115
|
+
}
|
|
116
|
+
if (/^#{1,6}\s/.test(line)) break;
|
|
117
|
+
if (line && !/^<!--/.test(line) && !/_TODO:/.test(line)) body.push(line);
|
|
118
|
+
}
|
|
119
|
+
return body;
|
|
120
|
+
}
|
|
121
|
+
function hasSection(src, re) {
|
|
122
|
+
return section(src, re).length > 0;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const adrDir = path.join(docsDir, "adr");
|
|
126
|
+
if (!fs.existsSync(adrDir)) {
|
|
127
|
+
pass("no docs/adr/ directory \u2014 ADR checks skipped");
|
|
128
|
+
} else {
|
|
129
|
+
const adrs = fs
|
|
130
|
+
.readdirSync(adrDir, { withFileTypes: true })
|
|
131
|
+
.filter((e) => e.isFile() && e.name.endsWith(".md") && !/^(INDEX|README)\.md$/i.test(e.name))
|
|
132
|
+
.map((e) => e.name)
|
|
133
|
+
.sort();
|
|
134
|
+
if (adrs.length === 0) {
|
|
135
|
+
pass("docs/adr/ present but empty");
|
|
136
|
+
} else {
|
|
137
|
+
const H = {
|
|
138
|
+
context: /^#{1,6}\s*Context\b/i,
|
|
139
|
+
decision: /^#{1,6}\s*Decision\b/i,
|
|
140
|
+
consequences: /^#{1,6}\s*Consequences\b/i,
|
|
141
|
+
};
|
|
142
|
+
for (const name of adrs) {
|
|
143
|
+
const src = fs.readFileSync(path.join(adrDir, name), "utf8");
|
|
144
|
+
const missing = [];
|
|
145
|
+
if (!hasSection(src, H.context)) missing.push("## Context");
|
|
146
|
+
if (!hasSection(src, H.decision)) missing.push("## Decision");
|
|
147
|
+
if (!hasSection(src, H.consequences)) missing.push("## Consequences");
|
|
148
|
+
if (!/^Status:\s*(proposed|accepted|superseded|deprecated|rejected)\s*$/im.test(src) && !hasSection(src, /^#{1,6}\s*Status\b/i)) {
|
|
149
|
+
missing.push("Status (proposed|accepted|superseded)");
|
|
150
|
+
}
|
|
151
|
+
if (missing.length) fail(`adr/${name}: missing \u2014 ${missing.join(", ")}`);
|
|
152
|
+
else pass(`adr/${name}: context \u2192 decision \u2192 consequences + status present`);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
console.log(
|
|
158
|
+
"\nRESULT: " +
|
|
159
|
+
(failures.length
|
|
160
|
+
? `${failures.length} FAILURE(S) \u2014 docs must have no placeholders, no dangling links, and well-formed ADRs`
|
|
161
|
+
: "green \u2014 docs are in sync")
|
|
162
|
+
);
|
|
163
|
+
process.exit(failures.length ? 1 : 0);
|
|
@@ -5,15 +5,21 @@
|
|
|
5
5
|
*
|
|
6
6
|
* Mechanically enforces the two Step Zero reporting rules that keep dispatch
|
|
7
7
|
* predictable:
|
|
8
|
-
* 1.
|
|
8
|
+
* 1. Every session handoff MUST open a `## Dispatch log` section that
|
|
9
9
|
* contains at least one entry (subagent result, waiver, or degrade).
|
|
10
10
|
* 2. Every `waived:` entry MUST cite a `reason:` on the same line — a waiver
|
|
11
11
|
* without a reason is itself a rules violation.
|
|
12
|
+
*
|
|
13
|
+
* Handoffs are SHARDED (like QA plans): one file per session under
|
|
14
|
+
* `.agents/handoffs/` (e.g. `F-003.md`, `return-no-condition.md`), so concurrent
|
|
15
|
+
* sessions never collide on a single shared file. The gate reads EVERY `.md`
|
|
16
|
+
* under that directory — plus the legacy single `.agents/memory/handoff.md` for
|
|
17
|
+
* backward compatibility — and requires each to open with a `## Dispatch log`
|
|
18
|
+
* section containing at least one entry.
|
|
12
19
|
*/
|
|
13
20
|
const fs = require("fs");
|
|
14
21
|
const path = require("path");
|
|
15
22
|
const root = path.resolve(__dirname, "..", "..");
|
|
16
|
-
const handoff = path.join(root, ".agents", "memory", "handoff.md");
|
|
17
23
|
|
|
18
24
|
const failures = [];
|
|
19
25
|
const check = (ok, msg) => {
|
|
@@ -23,35 +29,75 @@ const check = (ok, msg) => {
|
|
|
23
29
|
|
|
24
30
|
console.log("governance gate \u2014 wrap-up dispatch log");
|
|
25
31
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
32
|
+
/* All handoff files to check: every `.md` under `.agents/handoffs/` (recursive),
|
|
33
|
+
* plus the legacy single `.agents/memory/handoff.md` if present (backward compat
|
|
34
|
+
* for projects deployed before sharding). `README.md` files are documentation,
|
|
35
|
+
* not handoffs, so they are skipped. Sorted for deterministic output. */
|
|
36
|
+
function handoffFiles(root) {
|
|
37
|
+
const files = [];
|
|
38
|
+
const collect = (dir) => {
|
|
39
|
+
if (!fs.existsSync(dir)) return;
|
|
40
|
+
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
41
|
+
const abs = path.join(dir, entry.name);
|
|
42
|
+
if (entry.isDirectory()) collect(abs);
|
|
43
|
+
else if (
|
|
44
|
+
entry.name.toLowerCase().endsWith(".md") &&
|
|
45
|
+
entry.name.toLowerCase() !== "readme.md"
|
|
46
|
+
)
|
|
47
|
+
files.push(abs);
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
collect(path.join(root, ".agents", "handoffs"));
|
|
51
|
+
const legacy = path.join(root, ".agents", "memory", "handoff.md");
|
|
52
|
+
if (fs.existsSync(legacy)) files.push(legacy);
|
|
53
|
+
return files.sort();
|
|
29
54
|
}
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
const sectionTail = after.match(/^#{1,6}\s/m);
|
|
37
|
-
const section = (sectionTail ? after.slice(0, sectionTail.index) : after).trim();
|
|
38
|
-
const entry = section.match(/^\s*[-*]\s*\S/m);
|
|
39
|
-
check(
|
|
40
|
-
!!entry,
|
|
41
|
-
"dispatch log contains at least one entry line (subagent result, waived:, or degraded:)"
|
|
55
|
+
|
|
56
|
+
const files = handoffFiles(root);
|
|
57
|
+
|
|
58
|
+
if (files.length === 0) {
|
|
59
|
+
console.error(
|
|
60
|
+
"FATAL: missing session handoff \u2014 add one under .agents/handoffs/<feature-id>.md"
|
|
42
61
|
);
|
|
62
|
+
process.exit(2);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
console.log(
|
|
66
|
+
" checked against: " + files.map((f) => path.relative(root, f)).join(", ")
|
|
67
|
+
);
|
|
68
|
+
|
|
69
|
+
const waivers = [];
|
|
70
|
+
for (const file of files) {
|
|
71
|
+
const rel = path.relative(root, file);
|
|
72
|
+
const src = fs.readFileSync(file, "utf8");
|
|
73
|
+
|
|
74
|
+
const heading = src.match(/^#{1,6}\s*Dispatch log\s*$/im);
|
|
75
|
+
check(!!heading, `${rel} opens a \`## Dispatch log\` section`);
|
|
76
|
+
if (heading) {
|
|
77
|
+
const after = src.slice(heading.index + heading[0].length);
|
|
78
|
+
const sectionTail = after.match(/^#{1,6}\s/m);
|
|
79
|
+
const section = (sectionTail ? after.slice(0, sectionTail.index) : after).trim();
|
|
80
|
+
const entry = section.match(/^\s*[-*]\s*\S/m);
|
|
81
|
+
check(
|
|
82
|
+
!!entry,
|
|
83
|
+
`${rel} dispatch log contains at least one entry line (subagent result, waived:, or degraded:)`
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
for (const w of src.matchAll(/\bwaived:\s*([^\n,]+)/gi)) {
|
|
88
|
+
const line = src.split(/\r?\n/)[src.slice(0, w.index).split(/\r?\n/).length - 1];
|
|
89
|
+
waivers.push({ line: line.trim(), rel });
|
|
90
|
+
}
|
|
43
91
|
}
|
|
44
92
|
|
|
45
|
-
const waivers = [...src.matchAll(/\bwaived:\s*([^\n,]+)/gi)];
|
|
46
93
|
for (const w of waivers) {
|
|
47
|
-
const line = src.split(/\r?\n/)[src.slice(0, w.index).split(/\r?\n/).length - 1];
|
|
48
94
|
check(
|
|
49
|
-
/\breason:\s*\S/.test(line),
|
|
50
|
-
`every waived entry cites a reason: \`${line
|
|
95
|
+
/\breason:\s*\S/.test(w.line),
|
|
96
|
+
`every waived entry cites a reason (${w.rel}): \`${w.line}\``
|
|
51
97
|
);
|
|
52
98
|
}
|
|
53
99
|
|
|
54
100
|
console.log(
|
|
55
101
|
`\nRESULT: ${failures.length ? `${failures.length} FAILURE(S)` : "governance green"}`
|
|
56
102
|
);
|
|
57
|
-
process.exit(failures.length ? 1 : 0);
|
|
103
|
+
process.exit(failures.length ? 1 : 0);
|