@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 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.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 independent subagents in parallel.
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, and the output-contract marker.
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`). Dispatch independent subagents in parallel; serialize only on dependencies. Keep only coordination/mechanical work (reads, git, commits) on the main model.
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
- 1. **Dispatch plan** ordered list of `persona model exact prompt scope → dependencies`.
14
- 2. **Parallel batches** group independent dispatches to run concurrently (one message, multiple `task` calls).
15
- 3. **Orchestrator-only work** — glue/mechanical steps (reads, git, commits, final integration) kept on the main model.
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") +