@dombaras/agent-harness 0.1.1 → 0.1.2
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 +8 -2
- package/package.json +1 -1
- package/templates/.agents/AGENTS.md +6 -1
- package/templates/.agents/memory/model-routing.md +7 -1
- package/templates/.agents/rules/00-operating.md +4 -1
- package/templates/.agents/skills/frontend-engineer/SKILL.md +1 -0
- package/templates/.agents/skills/mobile-engineer/SKILL.md +4 -0
- package/templates/.agents/skills/planner/SKILL.md +14 -3
- package/templates/.opencode/agents/data-engineer.md +9 -0
- package/templates/.opencode/agents/devops-engineer.md +9 -0
- package/templates/.opencode/agents/diagnostics-expert.md +9 -0
- package/templates/.opencode/agents/frontend-engineer.md +9 -0
- package/templates/.opencode/agents/mobile-engineer.md +9 -0
- package/templates/.opencode/agents/security-engineer.md +9 -0
- package/templates/.opencode/agents/system-architect.md +9 -0
- package/templates/.opencode/agents/ui-designer.md +9 -0
- package/templates/scripts/qa/check-dispatch-config.js +32 -0
package/README.md
CHANGED
|
@@ -66,17 +66,23 @@ hardcoding domain assumptions.
|
|
|
66
66
|
- Personas also carry mechanical guardrails in frontmatter:
|
|
67
67
|
- thinkers (`planner`, `product-manager`) → `permission: { edit: deny, bash: deny }`
|
|
68
68
|
- `qa-architect`, `handoff` → `permission: { bash: deny }`
|
|
69
|
+
- code personas (`frontend-engineer`, `mobile-engineer`, `ui-designer`,
|
|
70
|
+
`data-engineer`, `devops-engineer`, `security-engineer`, `system-architect`,
|
|
71
|
+
`diagnostics-expert`) → `permission.edit` allows everything EXCEPT
|
|
72
|
+
`opencode.json`, `.harness.json`, `.opencode/**`, `.agents/**` (governance
|
|
73
|
+
and harness files are read-only), plus `bash: allow`
|
|
69
74
|
- deterministic personas → `temperature: 0.1`
|
|
70
75
|
- all personas → a `steps:` cap (cost ceiling)
|
|
71
76
|
- `planner`, `handoff` → `hidden: true`
|
|
72
77
|
- `.agents/skills/*/SKILL.md` carries a `model:` label only (informational).
|
|
73
78
|
- `npx @dombaras/agent-harness list` prints the persona → model mapping.
|
|
74
79
|
- `npm run test:dispatch` mechanically verifies model pins against
|
|
75
|
-
`scripts/qa/models.allowlist.txt
|
|
80
|
+
`scripts/qa/models.allowlist.txt` and that `model-routing.md` does not name a
|
|
81
|
+
stale model no longer pinned anywhere (doc↔config drift).
|
|
76
82
|
|
|
77
83
|
## QA gates
|
|
78
84
|
|
|
79
|
-
- `npm run test:dispatch` — persona model-pin preflight + persona↔skill parity.
|
|
85
|
+
- `npm run test:dispatch` — persona model-pin preflight + persona↔skill parity + model-routing↔pin drift check.
|
|
80
86
|
- `npm run test:governance` — session wrap-up dispatch-log enforcement.
|
|
81
87
|
- `node scripts/qa/check-qa-scripts.js` — warns (or `--strict` fails) when the
|
|
82
88
|
DoD-referenced runtime QA tiers aren't wired into `package.json`.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dombaras/agent-harness",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
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"
|
|
@@ -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
|
|
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).
|
|
42
42
|
- **Persona map**:
|
|
43
43
|
| Work area | Persona |
|
|
44
44
|
|---|---|
|
|
@@ -57,6 +57,10 @@ The canonical rulebook. The always-loaded summary is `.agents/rules/00-operating
|
|
|
57
57
|
| Session wrap-up / handoff | `handoff` |
|
|
58
58
|
- **Waivers** (the only way to skip a persona): a persona may be skipped only when (a) the change is fully covered by an automated gate on push or in the QA tiers, AND (b) the skip is pre-audited in `.agents/memory/` with a cited pointer. Log every waiver as `waived: <persona>` with `reason: <gate|pointer>`.
|
|
59
59
|
- **Dispatch failure ladder** (never silent): retry once (resume the same `task_id`); if it still fails, do the work inline and log `degraded: <persona> model: <reason>`; never silently skip.
|
|
60
|
+
- **Bound every dispatch (task spec)** — never hand a persona a bare metric ("get under N lines", "type everything"). Each dispatch prompt carries: `Objective` (one deliverable) → `Owned files` (exact paths) → `Read-only files` → `Shared contracts (owns|consumes)` → `Done = <gate command + observable metric>` → `Out-of-scope`.
|
|
61
|
+
- **Parallel = disjoint** — run personas in parallel only when their owned files AND shared contracts are disjoint; otherwise serialize, with the shared-contract owner dispatched first.
|
|
62
|
+
- **Orchestrator acceptance gate** — after a persona reports done, re-run the gate yourself (`tsc --noEmit`, `npm run lint:hooks`, `npm run test:quick`) and grep the metric before integrating or committing. A subagent's `Evidence` is a claim, not proof.
|
|
63
|
+
- **No suppression shortcuts** — never accept (or produce) file-level `/* eslint-disable */`, `@ts-nocheck`, or `@ts-ignore` to hit a metric. Suppressions are line-level only and each carries a reason — a file-level disable is a rules violation, not a fix.
|
|
60
64
|
- **Synchronous Terminal Commands**: Prefer sync one-shot commands (build/test/lint) that return inline, over background servers/watchers.
|
|
61
65
|
- **No Output Bloat**: Never paste large files/diffs into chat; apply edits with the edit tool, never print a code block of a change.
|
|
62
66
|
- **Right-Size the QA Tier**: Never run the full regression suite for a CSS/copy tweak.
|
|
@@ -88,6 +92,7 @@ Every completed task passes this gate, in order:
|
|
|
88
92
|
- [ ] RTL traced if layout touched; logical properties used.
|
|
89
93
|
- [ ] Cross-screen audit if a new UX pattern was introduced.
|
|
90
94
|
- [ ] No modified file over ~500 lines without extracting components.
|
|
95
|
+
- [ ] No blanket file-level `eslint-disable`/`@ts-nocheck`/`@ts-ignore` suppressions (line-level only, each with a reason).
|
|
91
96
|
1. **QA tier** — `qa-architect` picks the tier (and authors progression tests); `qa-runner` executes and makes it pass (§6).
|
|
92
97
|
2. **Security check** — apply `security-engineer` when the change touches data/auth/input/secrets/deps.
|
|
93
98
|
3. **Commit** — concise `feat:` / `fix:` / `refactor:` message.
|
|
@@ -27,6 +27,11 @@ 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
|
+
- **Code personas** (`frontend-engineer`, `mobile-engineer`, `ui-designer`,
|
|
31
|
+
`data-engineer`, `devops-engineer`, `security-engineer`, `system-architect`,
|
|
32
|
+
`diagnostics-expert`) allow `edit` everywhere except governance/harness paths
|
|
33
|
+
— `opencode.json`, `.harness.json`, `.opencode/**`, `.agents/**` are denied —
|
|
34
|
+
so out-of-scope edits are blocked mechanically, not just by prose.
|
|
30
35
|
- **Deterministic personas** (`planner`, `qa-architect`, `qa-runner`, `handoff`,
|
|
31
36
|
`diagnostics-expert`) pin `temperature: 0.1`.
|
|
32
37
|
- **`steps:`** caps iterations per persona (cost ceiling).
|
|
@@ -50,7 +55,8 @@ Every subagent must return a single final message with, in order:
|
|
|
50
55
|
## Gates
|
|
51
56
|
|
|
52
57
|
- `npm run test:dispatch` — `scripts/qa/check-dispatch-config.js` verifies model
|
|
53
|
-
pins, persona↔skill parity,
|
|
58
|
+
pins, persona↔skill parity, the output-contract marker, and that this doc does
|
|
59
|
+
not name a concrete model no longer pinned anywhere (doc↔config drift).
|
|
54
60
|
- `npm run test:governance` — `scripts/qa/governance.js` enforces the wrap-up
|
|
55
61
|
dispatch log.
|
|
56
62
|
- `scripts/qa/check-qa-scripts.js` — verifies the DoD-referenced QA tier scripts
|
|
@@ -4,8 +4,11 @@ Always in effect. Full detail lives in `.agents/AGENTS.md`; read it before non-t
|
|
|
4
4
|
|
|
5
5
|
## Step Zero — subagent dispatch gate (non-negotiable)
|
|
6
6
|
|
|
7
|
-
Before reading or editing any file for a task, dispatch the relevant personas via the `task` tool — each runs on its own `model:` (`.opencode/agents/<name>.md`).
|
|
7
|
+
Before reading or editing any file for a task, dispatch the relevant personas via the `task` tool — each runs on its own `model:` (`.opencode/agents/<name>.md`). Keep only coordination/mechanical work (reads, git, commits) on the main model.
|
|
8
8
|
|
|
9
|
+
- **Bound every task before dispatching.** No persona gets a bare metric ("get under N lines", "type everything"). Each dispatch prompt carries a task spec: `Objective` (one deliverable) → `Owned files` (exact paths) → `Read-only files` → `Shared contracts (owns|consumes)` → `Done = <gate command + observable metric>` → `Out-of-scope`.
|
|
10
|
+
- **Parallel = disjoint.** Dispatch two personas in parallel only when their owned files AND shared contracts are disjoint; otherwise serialize and put the shared-contract owner first.
|
|
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.
|
|
9
12
|
- Persona map, waivers, and the dispatch-failure ladder: `.agents/AGENTS.md` §5 and `.agents/memory/model-routing.md`.
|
|
10
13
|
- Every subagent returns the output contract (`Result` → `Evidence` → `Deferred & risks`).
|
|
11
14
|
- Wrap up with a **dispatch log** (`subagent → model → shipped/deferred`) in `.agents/memory/handoff.md`.
|
|
@@ -57,6 +57,7 @@ Whenever you are writing frontend code or dealing with build errors, you MUST st
|
|
|
57
57
|
- **500-Line Rule**: No single `.tsx` screen file should exceed ~500 lines. If it does, extract logically distinct sections (modals, list items, action handlers, sub-views) into separate component files in a co-located directory (e.g., `mobile/components/transactions/`). "Extract" means MOVE code into typed modules — never minify/compress JSX onto single lines to dodge the count, never swap real types for `any`, and never add `.d.ts` overrides or inline `require()` to silence tsc.
|
|
58
58
|
- **Before adding code to a file that already exceeds 500 lines**: STOP. Refactor first, then add. Never grow a monolith.
|
|
59
59
|
- **One component = one responsibility**: A screen file should orchestrate layout and state. Rendering logic for individual cards, list items, modals, or drawers should be in dedicated components.
|
|
60
|
+
- **No suppression shortcuts**: never silence lint/type errors with file-level `/* eslint-disable */`, `@ts-nocheck`, or `@ts-ignore` to hit a metric. Suppressions are line-level only, and each must carry a reason.
|
|
60
61
|
|
|
61
62
|
## Frontend Quality Exit Checklist
|
|
62
63
|
Before declaring any UI work done, verify:
|
|
@@ -38,3 +38,7 @@ You own the native React Native layer and its gotchas — not shared web UI (tha
|
|
|
38
38
|
config, handoff, other screens) to satisfy a metric.
|
|
39
39
|
- **Verify before done**: run `tsc --noEmit`, `npm run lint:hooks`, `npm run test:quick`
|
|
40
40
|
yourself and report the observed output — never claim success you did not run.
|
|
41
|
+
- **No suppression shortcuts**: never silence the gate with a file-level
|
|
42
|
+
`/* eslint-disable */`, `@ts-nocheck`, or `@ts-ignore`. Suppressions are
|
|
43
|
+
line-level only and each carries a reason — a file-level disable to hit a
|
|
44
|
+
metric is a rules violation, not a fix.
|
|
@@ -10,9 +10,20 @@ You decompose the task and produce the dispatch plan the orchestrator executes.
|
|
|
10
10
|
|
|
11
11
|
## Output contract (always return)
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
13
|
+
Never hand a persona a bare metric ("get under N lines", "type everything"). If a
|
|
14
|
+
task cannot be bounded by the spec below, split it further or flag it — do not
|
|
15
|
+
dispatch it broad.
|
|
16
|
+
|
|
17
|
+
1. **Dispatch plan** — ordered list of entries, each with:
|
|
18
|
+
- `persona → model`
|
|
19
|
+
- **Objective** — one sentence naming a single deliverable.
|
|
20
|
+
- **Owned files** — exact paths this persona may edit.
|
|
21
|
+
- **Read-only files** — explicit out-of-scope paths (governance, memory, skills, other screens).
|
|
22
|
+
- **Shared contracts** — for each shared module/type: `owns` or `consumes` (exactly one owner per contract).
|
|
23
|
+
- **Done =** — the exact gate command(s) + observable metric (e.g. `tsc --noEmit` clean, `\bany\b` count 0), so success is verifiable, not self-reported.
|
|
24
|
+
- **Dependencies** — which other dispatch, if any, must complete first.
|
|
25
|
+
2. **Parallel batches** — group only DISJOINT entries (disjoint owned files AND disjoint shared contracts). Serialize personas that would touch the same files or emit the same shared contract; put the contract owner first.
|
|
26
|
+
3. **Orchestrator-only work** — glue/mechanical steps (reads, git, commits, final integration, acceptance re-verification) kept on the main model.
|
|
16
27
|
4. **Risk flags** — any ambiguity that needs the user before proceeding.
|
|
17
28
|
|
|
18
29
|
## Rules
|
|
@@ -3,6 +3,15 @@ description: Use for catalog/data ingestion and entity resolution — external-s
|
|
|
3
3
|
mode: subagent
|
|
4
4
|
model: opencode/gpt-5.6-luna
|
|
5
5
|
steps: 40
|
|
6
|
+
permission:
|
|
7
|
+
edit:
|
|
8
|
+
"*": allow
|
|
9
|
+
"opencode.json": deny
|
|
10
|
+
".harness.json": deny
|
|
11
|
+
".opencode/**": deny
|
|
12
|
+
".agents/**": deny
|
|
13
|
+
bash:
|
|
14
|
+
"*": allow
|
|
6
15
|
---
|
|
7
16
|
|
|
8
17
|
You are the {{PROJECT_NAME}} Data Engineer. Read and follow the complete persona instructions in `.agents/skills/data-engineer/SKILL.md`, then carry out the task.
|
|
@@ -3,6 +3,15 @@ description: Use for deployment, CI/CD, cron/scheduling, env & secrets managemen
|
|
|
3
3
|
mode: subagent
|
|
4
4
|
model: opencode/gpt-5.6-luna
|
|
5
5
|
steps: 40
|
|
6
|
+
permission:
|
|
7
|
+
edit:
|
|
8
|
+
"*": allow
|
|
9
|
+
"opencode.json": deny
|
|
10
|
+
".harness.json": deny
|
|
11
|
+
".opencode/**": deny
|
|
12
|
+
".agents/**": deny
|
|
13
|
+
bash:
|
|
14
|
+
"*": allow
|
|
6
15
|
---
|
|
7
16
|
|
|
8
17
|
You are the {{PROJECT_NAME}} DevOps Engineer. Read and follow the complete persona instructions in `.agents/skills/devops-engineer/SKILL.md`, then carry out the task.
|
|
@@ -4,6 +4,15 @@ mode: subagent
|
|
|
4
4
|
model: opencode/deepseek-v4-pro
|
|
5
5
|
temperature: 0.1
|
|
6
6
|
steps: 30
|
|
7
|
+
permission:
|
|
8
|
+
edit:
|
|
9
|
+
"*": allow
|
|
10
|
+
"opencode.json": deny
|
|
11
|
+
".harness.json": deny
|
|
12
|
+
".opencode/**": deny
|
|
13
|
+
".agents/**": deny
|
|
14
|
+
bash:
|
|
15
|
+
"*": allow
|
|
7
16
|
---
|
|
8
17
|
|
|
9
18
|
You are the {{PROJECT_NAME}} Diagnostics Expert. Read and follow the complete persona instructions in `.agents/skills/diagnostics-expert/SKILL.md`, then carry out the task.
|
|
@@ -3,6 +3,15 @@ description: Use when writing or fixing web React / Next.js / Tailwind / shadcn
|
|
|
3
3
|
mode: subagent
|
|
4
4
|
model: opencode/gpt-5.6-luna
|
|
5
5
|
steps: 40
|
|
6
|
+
permission:
|
|
7
|
+
edit:
|
|
8
|
+
"*": allow
|
|
9
|
+
"opencode.json": deny
|
|
10
|
+
".harness.json": deny
|
|
11
|
+
".opencode/**": deny
|
|
12
|
+
".agents/**": deny
|
|
13
|
+
bash:
|
|
14
|
+
"*": allow
|
|
6
15
|
---
|
|
7
16
|
|
|
8
17
|
You are the {{PROJECT_NAME}} Frontend Engineer. Read and follow the complete persona instructions in `.agents/skills/frontend-engineer/SKILL.md`, then carry out the task.
|
|
@@ -3,6 +3,15 @@ description: Use when writing or fixing Expo / React Native native code — nati
|
|
|
3
3
|
mode: subagent
|
|
4
4
|
model: opencode/gpt-5.6-luna
|
|
5
5
|
steps: 40
|
|
6
|
+
permission:
|
|
7
|
+
edit:
|
|
8
|
+
"*": allow
|
|
9
|
+
"opencode.json": deny
|
|
10
|
+
".harness.json": deny
|
|
11
|
+
".opencode/**": deny
|
|
12
|
+
".agents/**": deny
|
|
13
|
+
bash:
|
|
14
|
+
"*": allow
|
|
6
15
|
---
|
|
7
16
|
|
|
8
17
|
You are the {{PROJECT_NAME}} Mobile Engineer. Read and follow the complete persona instructions in `.agents/skills/mobile-engineer/SKILL.md`, then carry out the task.
|
|
@@ -3,6 +3,15 @@ description: Use when a change touches auth, secrets, input validation, external
|
|
|
3
3
|
mode: subagent
|
|
4
4
|
model: opencode/deepseek-v4-pro
|
|
5
5
|
steps: 30
|
|
6
|
+
permission:
|
|
7
|
+
edit:
|
|
8
|
+
"*": allow
|
|
9
|
+
"opencode.json": deny
|
|
10
|
+
".harness.json": deny
|
|
11
|
+
".opencode/**": deny
|
|
12
|
+
".agents/**": deny
|
|
13
|
+
bash:
|
|
14
|
+
"*": allow
|
|
6
15
|
---
|
|
7
16
|
|
|
8
17
|
You are the {{PROJECT_NAME}} Security Engineer. Read and follow the complete persona instructions in `.agents/skills/security-engineer/SKILL.md`, then carry out the task.
|
|
@@ -3,6 +3,15 @@ description: Use when changing the Prisma schema, data model, multi-tenant bound
|
|
|
3
3
|
mode: subagent
|
|
4
4
|
model: opencode/deepseek-v4-pro
|
|
5
5
|
steps: 40
|
|
6
|
+
permission:
|
|
7
|
+
edit:
|
|
8
|
+
"*": allow
|
|
9
|
+
"opencode.json": deny
|
|
10
|
+
".harness.json": deny
|
|
11
|
+
".opencode/**": deny
|
|
12
|
+
".agents/**": deny
|
|
13
|
+
bash:
|
|
14
|
+
"*": allow
|
|
6
15
|
---
|
|
7
16
|
|
|
8
17
|
You are the {{PROJECT_NAME}} System Architect. Read and follow the complete persona instructions in `.agents/skills/system-architect/SKILL.md`, then carry out the task.
|
|
@@ -3,6 +3,15 @@ description: Use when designing the visual system — design tokens, theming, an
|
|
|
3
3
|
mode: subagent
|
|
4
4
|
model: opencode/gpt-5.6-luna
|
|
5
5
|
steps: 40
|
|
6
|
+
permission:
|
|
7
|
+
edit:
|
|
8
|
+
"*": allow
|
|
9
|
+
"opencode.json": deny
|
|
10
|
+
".harness.json": deny
|
|
11
|
+
".opencode/**": deny
|
|
12
|
+
".agents/**": deny
|
|
13
|
+
bash:
|
|
14
|
+
"*": allow
|
|
6
15
|
---
|
|
7
16
|
|
|
8
17
|
You are the {{PROJECT_NAME}} UI Designer. Read and follow the complete persona instructions in `.agents/skills/ui-designer/SKILL.md`, then carry out the task.
|
|
@@ -12,6 +12,8 @@
|
|
|
12
12
|
* 3. Every agent body carries the output-contract marker (Result / Evidence /
|
|
13
13
|
* Deferred & risks) so subagents return a consistent report shape.
|
|
14
14
|
* 4. `opencode.json` main `model` + `small_model` are also real provider IDs.
|
|
15
|
+
* 5. `.agents/memory/model-routing.md` does not name a concrete model that is
|
|
16
|
+
* no longer pinned by any agent or opencode.json (doc↔config drift).
|
|
15
17
|
*
|
|
16
18
|
* Allowlist refresh (only when the provider adds/removes models):
|
|
17
19
|
* npx opencode models > scripts/qa/models.allowlist.txt
|
|
@@ -62,7 +64,12 @@ function frontmatterModel(file) {
|
|
|
62
64
|
return modelLine.replace(/^model:\s*/i, "").trim();
|
|
63
65
|
}
|
|
64
66
|
|
|
67
|
+
function escapeRe(s) {
|
|
68
|
+
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
69
|
+
}
|
|
70
|
+
|
|
65
71
|
// 1. every agent file exists, pins a real model, references a real skill
|
|
72
|
+
const agentModels = new Set();
|
|
66
73
|
const agentNames = fs
|
|
67
74
|
.readdirSync(agentsDir)
|
|
68
75
|
.filter((f) => f.endsWith(".md"))
|
|
@@ -72,6 +79,7 @@ for (const f of agentNames) {
|
|
|
72
79
|
const name = f.replace(/\.md$/, "");
|
|
73
80
|
const file = path.join(agentsDir, f);
|
|
74
81
|
const model = frontmatterModel(file);
|
|
82
|
+
if (model) agentModels.add(model);
|
|
75
83
|
const body = fs.readFileSync(file, "utf8");
|
|
76
84
|
const skillMd = path.join(skillsDir, name, "SKILL.md");
|
|
77
85
|
if (!model) fail(`agent ${f}: missing frontmatter model: (all agents must pin a model)`);
|
|
@@ -95,16 +103,40 @@ if (skillNames.join("|") === [...agentNames].map((f) => f.replace(/\.md$/, "")).
|
|
|
95
103
|
pass(`persona parity: ${agentNames.length} agents == ${skillNames.length} skills`);
|
|
96
104
|
|
|
97
105
|
// 3. opencode.json main + small model are real provider IDs
|
|
106
|
+
const configModels = new Set();
|
|
98
107
|
if (fs.existsSync(opencodeJson)) {
|
|
99
108
|
const cfg = JSON.parse(fs.readFileSync(opencodeJson, "utf8"));
|
|
100
109
|
for (const key of ["model", "small_model"]) {
|
|
101
110
|
const v = cfg[key];
|
|
111
|
+
if (v) configModels.add(v);
|
|
102
112
|
if (!v) warn(`opencode.json: no \`${key}\` set`);
|
|
103
113
|
else if (allowlist.has(v)) pass(`opencode.json ${key} \`${v}\` is a real provider model`);
|
|
104
114
|
else fail(`opencode.json ${key} \`${v}\` NOT in provider allowlist`);
|
|
105
115
|
}
|
|
106
116
|
} else warn("opencode.json not found (skip)");
|
|
107
117
|
|
|
118
|
+
// 4. model-routing.md ↔ model: pin consistency (doc↔config drift)
|
|
119
|
+
const usedModels = new Set([...agentModels, ...configModels]);
|
|
120
|
+
const modelRoutingFile = path.join(root, ".agents", "memory", "model-routing.md");
|
|
121
|
+
if (fs.existsSync(modelRoutingFile)) {
|
|
122
|
+
const doc = fs.readFileSync(modelRoutingFile, "utf8");
|
|
123
|
+
const modelPattern = [...allowlist].map(escapeRe).join("|");
|
|
124
|
+
const mentioned = new Set();
|
|
125
|
+
const re = new RegExp(modelPattern, "g");
|
|
126
|
+
let m;
|
|
127
|
+
while ((m = re.exec(doc)) !== null) mentioned.add(m[0]);
|
|
128
|
+
if (mentioned.size === 0) {
|
|
129
|
+
pass("model-routing.md names no concrete model IDs (tier policy only) — nothing to cross-check");
|
|
130
|
+
} else {
|
|
131
|
+
for (const mid of mentioned) {
|
|
132
|
+
if (usedModels.has(mid)) pass(`model-routing.md \`${mid}\` matches an actual pin`);
|
|
133
|
+
else fail(`model-routing.md references \`${mid}\` but no agent/config pins it (doc↔config drift)`);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
} else {
|
|
137
|
+
warn("model-routing.md not found (skip doc↔config drift check)");
|
|
138
|
+
}
|
|
139
|
+
|
|
108
140
|
console.log(
|
|
109
141
|
`\nRESULT: ${agentNames.length} agents checked \u2014 ` +
|
|
110
142
|
(failures.length ? `${failures.length} FAILURE(S)` : "0 failures") +
|