@yemi33/minions 0.1.2370 → 0.1.2371

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. package/bin/minions.js +38 -11
  2. package/dashboard/js/render-other.js +2 -30
  3. package/dashboard/js/render-work-items.js +0 -34
  4. package/dashboard/js/settings.js +12 -27
  5. package/dashboard/pages/tools.html +1 -1
  6. package/dashboard.js +80 -257
  7. package/docs/README.md +2 -3
  8. package/docs/architecture.excalidraw +2 -2
  9. package/docs/auto-discovery.md +3 -3
  10. package/docs/completion-reports.md +0 -121
  11. package/docs/copilot-cli-schema.md +9 -17
  12. package/docs/deprecated.json +0 -51
  13. package/docs/design-state-storage.md +4 -4
  14. package/docs/harness-propagation.md +33 -263
  15. package/docs/human-vs-automated.md +1 -1
  16. package/docs/named-agents.md +0 -2
  17. package/docs/plan-lifecycle.md +5 -6
  18. package/docs/qa-runbook-lifecycle.md +10 -25
  19. package/docs/shared-lifecycle-module-map.md +2 -10
  20. package/engine/ado-comment.js +5 -11
  21. package/engine/ado.js +10 -5
  22. package/engine/cleanup.js +16 -36
  23. package/engine/cli.js +13 -175
  24. package/engine/comment-format.js +8 -182
  25. package/engine/db/index.js +60 -10
  26. package/engine/db/migrations/018-sql-only-cutover.js +56 -0
  27. package/engine/dispatch-store.js +0 -114
  28. package/engine/dispatch.js +1 -6
  29. package/engine/gh-comment.js +2 -10
  30. package/engine/github.js +8 -6
  31. package/engine/lifecycle.js +58 -173
  32. package/engine/llm.js +9 -11
  33. package/engine/managed-spawn.js +1 -6
  34. package/engine/memory-store.js +8 -6
  35. package/engine/metrics-store.js +4 -128
  36. package/engine/pipeline.js +4 -7
  37. package/engine/playbook.js +8 -196
  38. package/engine/prd-store.js +115 -141
  39. package/engine/preflight.js +8 -55
  40. package/engine/projects.js +34 -41
  41. package/engine/pull-requests-store.js +9 -234
  42. package/engine/qa-runs.js +4 -15
  43. package/engine/qa-sessions.js +5 -17
  44. package/engine/queries.js +23 -103
  45. package/engine/routing.js +1 -1
  46. package/engine/runtimes/claude.js +1 -2
  47. package/engine/runtimes/codex.js +0 -2
  48. package/engine/runtimes/copilot.js +1 -4
  49. package/engine/shared-branch-pr-reconcile.js +2 -5
  50. package/engine/shared.js +174 -533
  51. package/engine/small-state-store.js +12 -647
  52. package/engine/spawn-agent.js +5 -96
  53. package/engine/state-operations.js +166 -0
  54. package/engine/supervisor.js +0 -1
  55. package/engine/watch-actions.js +2 -3
  56. package/engine/watches-store.js +2 -128
  57. package/engine/work-items-store.js +3 -254
  58. package/engine.js +102 -334
  59. package/package.json +2 -2
  60. package/playbooks/build-fix-complex.md +0 -4
  61. package/playbooks/fix.md +0 -4
  62. package/playbooks/implement-shared.md +0 -4
  63. package/playbooks/implement.md +0 -4
  64. package/playbooks/plan-to-prd.md +16 -11
  65. package/playbooks/plan.md +0 -4
  66. package/playbooks/review.md +0 -6
  67. package/playbooks/shared-rules.md +0 -65
  68. package/docs/harness-transparency.md +0 -199
  69. package/docs/project-skills.md +0 -193
  70. package/engine/discover-project-skills.js +0 -673
  71. package/engine/playbook-intents.js +0 -76
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yemi33/minions",
3
- "version": "0.1.2370",
3
+ "version": "0.1.2371",
4
4
  "description": "Multi-agent AI dev team that runs from ~/.minions/ — five autonomous agents share a single engine, dashboard, and knowledge base",
5
5
  "bin": {
6
6
  "minions": "bin/minions.js"
@@ -29,7 +29,7 @@
29
29
  "test:e2e:accept": "node test/playwright/accept-baseline.js",
30
30
  "test:e2e:accept-force": "node test/playwright/accept-baseline.js --force",
31
31
  "test:setup": "npx playwright install chromium",
32
- "lint": "eslint dashboard/ engine/ado.js engine/github.js engine/lifecycle.js engine/queries.js engine/watches.js engine/cli.js"
32
+ "lint": "eslint dashboard/ engine.js engine/ dashboard.js dashboard-build.js bin/"
33
33
  },
34
34
  "keywords": [
35
35
  "ai",
@@ -76,10 +76,6 @@ Use subagents only for genuinely parallel, independent tasks (e.g., editing file
76
76
 
77
77
  ## Health Check
78
78
 
79
- {{#project_skills_block}}
80
- {{project_skills_block}}
81
-
82
- {{/project_skills_block}}
83
79
  Before starting work, run `git status` and verify the worktree is clean and on the expected branch (`{{pr_branch}}`). If the worktree is dirty or on the wrong branch, report the issue and stop.
84
80
 
85
81
  ## Validation
package/playbooks/fix.md CHANGED
@@ -53,10 +53,6 @@ Before editing, split the feedback into:
53
53
 
54
54
  ## Health Check
55
55
 
56
- {{#project_skills_block}}
57
- {{project_skills_block}}
58
-
59
- {{/project_skills_block}}
60
56
  Before starting work, run `git status` and verify the worktree is clean and on the expected branch (`{{pr_branch}}`). If the worktree is dirty or on the wrong branch, report the issue and stop.
61
57
 
62
58
  ### Branch-mismatch guard (issue #2999)
@@ -45,10 +45,6 @@ Do ALL work in the worktree.
45
45
 
46
46
  ## Health Check
47
47
 
48
- {{#project_skills_block}}
49
- {{project_skills_block}}
50
-
51
- {{/project_skills_block}}
52
48
  Before starting work, run `git status` and verify the worktree is clean and on the expected branch (`{{branch_name}}`). If the worktree is dirty or on the wrong branch, report the issue and stop.
53
49
 
54
50
  ## Working Style
@@ -38,10 +38,6 @@ If this feature spans multiple projects, inspect the relevant repos, make change
38
38
 
39
39
  ## Health Check
40
40
 
41
- {{#project_skills_block}}
42
- {{project_skills_block}}
43
-
44
- {{/project_skills_block}}
45
41
  Before starting work, run `git status` and verify the worktree is clean and on the expected branch. If the worktree is dirty or on the wrong branch, report the issue and stop.
46
42
 
47
43
  ## Working Style
@@ -19,10 +19,6 @@ A user has provided a plan. Analyze it against the codebase and produce a struct
19
19
 
20
20
  ## Instructions
21
21
 
22
- {{#project_skills_block}}
23
- {{project_skills_block}}
24
-
25
- {{/project_skills_block}}
26
22
  1. **Read the plan carefully** — understand the goals, scope, and requirements
27
23
  - If the plan declares `Project: <name>` (including `**Project:** <name>`), the engine has resolved `{{project_name}}` from that declaration. Preserve `{{project_name}}` for the top-level `project`, default item `project`, filename, and implementation framing; do not let contextual mentions of another product or repository override it.
28
24
  2. **Check for an existing PRD** — if the engine provides `existing_prd_json` below, a PRD already exists for this plan. See "Reusing an Existing PRD" section for how to preserve item IDs and done statuses. If no existing PRD is provided, this is a fresh run — all items start as `"missing"`.
@@ -35,11 +31,20 @@ A user has provided a plan. Analyze it against the codebase and produce a struct
35
31
 
36
32
  ## Output
37
33
 
38
- Write the PRD to: `{{team_root}}/prd/{{prd_filename}}`
34
+ Write the PRD result sidecar to: `{{team_root}}/agents/{{agent_id}}/prd-result.json`
35
+
36
+ The sidecar must be an envelope with the exact pre-generated filename:
39
37
 
40
- **Use the exact filename above.** The engine pre-generates a unique filename to avoid collisions with existing or archived PRDs; renaming it will detach the PRD from the plan.
38
+ ```json
39
+ {
40
+ "filename": "{{prd_filename}}",
41
+ "prd": {
42
+ "...": "the PRD object described below"
43
+ }
44
+ }
45
+ ```
41
46
 
42
- This file is NOT checked into the repo. The engine reads it on every tick and dispatches implementation work automatically.
47
+ The engine validates and imports this one-shot sidecar into SQLite when the dispatch completes, then deletes it. Do not write runtime state under `prd/`.
43
48
 
44
49
  ```json
45
50
  {
@@ -190,14 +195,14 @@ If the task description contains `mode: diff-aware-update`, you are updating an
190
195
 
191
196
  ## Important
192
197
 
193
- - Write ONLY the single `.json` PRD file to `{{team_root}}/prd/` — do NOT write any `.md` files there
198
+ - Write ONLY the single result sidecar at `{{team_root}}/agents/{{agent_id}}/prd-result.json`
199
+ - Wrap the PRD object in `{ "filename": "{{prd_filename}}", "prd": { ... } }`
194
200
  - Do NOT create a git branch, worktree, or PR — this playbook writes minions-internal state only
195
201
  - Do NOT modify any files in the project repo
196
- - **NEVER run `git add prd/` or `git add -f prd/` — PRD files are gitignored runtime artifacts (`prd/*.json` is in `.gitignore`). Force-adding them exposes internal work plans in the public repo diff. A CI guard (`test/unit/prd-not-git-tracked.test.js`) will fail the PR if you do this, and you will be required to run `git rm --cached prd/<file>.json` to undo it.**
197
- - The engine will dispatch implementation agents automatically once the JSON file exists
202
+ - The engine will dispatch implementation agents automatically once the sidecar is imported
198
203
  - For `shared-branch`: agents commit to a single branch — one PR is created automatically when all items are done
199
204
  - For `parallel`: each agent creates its own branch and PR
200
205
 
201
206
  ## When to Stop
202
207
 
203
- Your task is complete once you have written the PRD JSON file to `{{team_root}}/prd/{{prd_filename}}`. The engine will detect the file and begin dispatching work. Do NOT run any git commands. Do NOT create branches, PRs, or modify project code. Stop after writing the JSON file.
208
+ Your task is complete once you have written `{{team_root}}/agents/{{agent_id}}/prd-result.json`. The engine imports it into SQLite after you exit. Do NOT run any git commands. Do NOT create branches, PRs, or modify project code. Stop after writing the sidecar.
package/playbooks/plan.md CHANGED
@@ -27,10 +27,6 @@ A user has described a feature they want built. Your job is to create a detailed
27
27
  - Identify the core goal, constraints, and success criteria
28
28
  - Note any ambiguities that need to be called out
29
29
 
30
- {{#project_skills_block}}
31
- {{project_skills_block}}
32
-
33
- {{/project_skills_block}}
34
30
  ### 2. Explore the Codebase
35
31
  - Read `CLAUDE.md` at repo root and relevant directories
36
32
  - Map the areas of code that this feature will touch
@@ -27,12 +27,6 @@ Use subagents only for genuinely parallel, independent tasks (e.g., reviewing un
27
27
  git diff {{main_branch}}...origin/{{pr_branch}}
28
28
  ```
29
29
 
30
- {{#project_skills_block}}
31
- {{project_skills_block}}
32
-
33
- > **Skipping an in-scope review skill is gated.** Before you record a `meta.review.skillSkipped` (or `meta.skill.skipped`) outcome for any skill listed above, re-read that skill's own SKILL.md checklist and confirm your skip reason does not contradict any explicit "Flag if…" / "Verify that…" / "Requirements" item it documents. If the diff trips ANY such item, do NOT skip — apply the skill (or at least that item). When you do skip, surface the exact same `{name, reason}` in your PR review comment via `minions pr comment … --skill-skipped-json '{"name":"…","reason":"…"}'` so a human sees it alongside your verdict before merge (no drift between the report and the comment). See `playbooks/shared-rules.md` → "Skipped an in-scope project skill?".
34
-
35
- {{/project_skills_block}}
36
30
  2. Think about deploy risk before commenting:
37
31
  - What user-visible behavior changed?
38
32
  - What dependencies, callers, or tests could be affected?
@@ -120,71 +120,6 @@ The engine provides a completion report path in the prompt and in `MINIONS_COMPL
120
120
 
121
121
  For the canonical schema — every field, the `failure_class` enum, `noop:true` semantics, `retryable` / `needs_rerun` shape, and the artifacts array — see `docs/completion-reports.md`. The JSON report is the primary signal; fenced `completion` blocks in stdout are accepted only as a fallback.
122
122
 
123
- ## Harness transparency / self-report
124
-
125
- Populate the **optional** `harnessUsed` field on your completion report with the harness affordances you actually consulted to do the work — the skills you ran, the MCP servers you called, the slash-commands you invoked, and the reference docs you read. This is observability, not a gate: it lets the team see which tooling drove a dispatch and lets the engine cross-check your claims against what it actually exposed. Omit the field entirely when you used none; do not pad it with affordances you didn't touch.
126
-
127
- Use this canonical `{ skills, mcpServers, commands, docs }` shape. Each kind keeps only the listed fields; the **required** field must be present or the entry is dropped:
128
-
129
- - `skills` — `{ name, source, path }` (required: `name`; `source` is `user` / `project`)
130
- - `mcpServers` — `{ name, scope }` (required: `name`; `scope` is `user` / `project`)
131
- - `commands` — `{ name, scope }` (required: `name`)
132
- - `docs` — `{ path, why }` (required: `path`; `why` is a short reason you read it)
133
-
134
- Copyable worked example:
135
-
136
- ```json
137
- {
138
- "status": "success",
139
- "summary": "Added the harnessUsed self-report contract to shared-rules + docs.",
140
- "harnessUsed": {
141
- "skills": [
142
- {"name": "run-tests", "source": "project", "path": ".claude/skills/run-tests/SKILL.md"}
143
- ],
144
- "mcpServers": [
145
- {"name": "azure-ado", "scope": "user"}
146
- ],
147
- "commands": [
148
- {"name": "simplify", "scope": "user"}
149
- ],
150
- "docs": [
151
- {"path": "docs/completion-reports.md", "why": "confirmed the harnessUsed field shape"}
152
- ]
153
- }
154
- }
155
- ```
156
-
157
- Entries you report are **cross-checked** against what the engine propagated into your worktree and annotated `grounded: true | false` — never dropped — so a `grounded: false` entry surfaces a discrepancy for a human rather than being hidden. Full contract: `docs/harness-transparency.md`.
158
-
159
- When you author a **PR review / fix summary comment**, the same harness footprint belongs in the comment body as a collapsible "Harnesses used" section so reviewers see which skills / MCPs / docs informed the change.
160
-
161
- **Preferred — `minions pr comment` (GitHub and Azure DevOps `--host ado`): do NOT hand-render the section.** Write your `harnessUsed` record — the same `{ skills, mcpServers, commands, docs }` shape as the completion field above — to a JSON file and pass `--harness-file <path>` (or inline via `--harness-json <json>`). The shared builder (`engine/comment-format.js#buildHarnessUsedSection`, consumed by both `engine/gh-comment.js` and `engine/ado-comment.js`) folds the byte-identical `<details>` section into the body for you, so agent-authored and engine-authored comments match. Each entry is cross-checked against the harness manifest the engine propagated into your worktree and annotated `grounded: true | false` (never dropped); a `grounded: false`, ⚠️-marked bullet means **the engine couldn't confirm that affordance against the propagated manifest — not that you did anything wrong**.
162
-
163
- **Raw fallbacks only — `gh pr comment` / `az repos pr comment` / ADO REST:** these bypass the CLI chokepoint, so you MUST hand-render the section yourself. Render it **identically on both hosts** — append this exact `<details>` block at the end of your comment body (omit it entirely when you used no affordances):
164
-
165
- ```markdown
166
- <details>
167
- <summary>🧰 Harnesses used (N)</summary>
168
-
169
- - 🧩 `skill-name` — source
170
- - 🔌 `mcp-name` — scope
171
- - ⌘ `command-name` — scope
172
- - 📄 `docs/some-doc.md` — why you read it
173
-
174
- </details>
175
- ```
176
-
177
- One bullet per affordance, in the order skills → MCP servers → commands → docs; `N` is the bullet count. Whether the CLI folds it in or you hand-render it on a raw fallback, the section is the human-readable mirror of your `harnessUsed` completion field — keep the two consistent.
178
-
179
- ### Skipped an in-scope project skill? Validate the skip, then surface it in the PR comment
180
-
181
- `meta.review.skillSkipped` / `meta.skill.skipped` (shape `{name, reason}`, see `docs/completion-reports.md`) lets you self-report that you intentionally skipped an available, **in-scope** project (review) skill. Two hard rules apply — this is not aspirational:
182
-
183
- 1. **Validate the skip against the skill's own scope before recording it.** Re-read the skipped skill's SKILL.md review criteria / checklist (the content already injected via the `## Project skills` / `## Project review skills` block). Confirm your skip reason does **not** contradict any explicit "Flag if…" / "Verify that…" / "Requirements" item the skill documents. If the diff trips ANY explicit checklist item the skill covers, skipping is **not** permitted — apply the skill (or at minimum that specific checklist item). A skip reason that waves away a gate the skill explicitly documents (e.g. "no CHANGELOG needed for this diff" when the skill's CHANGELOG Requirements section says otherwise) is invalid.
184
- 2. **Surface the skip in the PR comment, not just the JSON.** When you post a review/fix summary comment, the skip must be visible in the same comment as the verdict so a human sees it **before** merge. Via `minions pr comment` (GitHub and ADO `--host ado`), pass `--skill-skipped-file <path>` (JSON `{name, reason}`) or inline `--skill-skipped-json <json>`; the shared builder (`engine/comment-format.js#buildSkippedSkillSection`, consumed by both `engine/gh-comment.js` and `engine/ado-comment.js`) folds a byte-identical `> ⚠️ Skipped project skill: <name> — <reason>` callout in for you. On raw fallbacks (`gh pr comment` / `az repos pr comment` / ADO REST) render that exact one-line blockquote callout yourself.
185
-
186
- The **same** `{name, reason}` you record in `meta.review.skillSkipped` / `meta.skill.skipped` must be the one rendered in the PR comment — no drift between the two.
187
-
188
123
  ## Minions API access
189
124
 
190
125
  The Minions dashboard runs at `http://localhost:7331` whenever the engine is up. Agents may call its HTTP endpoints when (and only when) the playbook explicitly authorizes it for a particular task — most dispatches do not need any API access, and uninvited writes to engine-managed state are still prohibited (see "Do NOT write to `agents/*/status.json`" above).
@@ -1,199 +0,0 @@
1
- # Harness Transparency
2
-
3
- Plan P-a8f3c2d1. Minions gives dispatched agents a *harness* — the skills, MCP
4
- servers, slash-commands, and reference docs the engine makes reachable inside a
5
- worktree (user-scope `~/.claude` / `~/.copilot` / `~/.agents`, project-local
6
- `.claude/skills`, `.mcp.json`, propagated `--add-dir` roots, …). Harness
7
- transparency makes that surface **observable after the fact**: which of those
8
- affordances did the agent actually consult to do the work?
9
-
10
- The contract has three stages — **capture → ground → surface** — and a single
11
- canonical vocabulary that every stage speaks.
12
-
13
- ## Canonical vocabulary
14
-
15
- Defined in `engine/shared.js` as `HARNESS_USED_KINDS`. Four kinds; each declares
16
- the only fields that survive normalization plus the one field that MUST be
17
- present for an entry to be kept:
18
-
19
- | Kind | Fields | Required |
20
- |---|---|---|
21
- | `skills` | `name`, `source`, `path` | `name` |
22
- | `mcpServers` | `name`, `scope` | `name` |
23
- | `commands` | `name`, `scope` | `name` |
24
- | `docs` | `path`, `why` | `path` |
25
-
26
- `shared.normalizeHarnessUsed(raw)` validates and clamps any untrusted
27
- `harnessUsed`-shaped object into this canonical `{ skills, mcpServers, commands,
28
- docs }` form: it drops malformed entries (non-objects, or entries missing the
29
- kind's required field), keeps only whitelisted string fields (trimmed and
30
- clamped to `HARNESS_USED_MAX_FIELD_LEN` = 512 chars), caps each array at
31
- `HARNESS_USED_MAX_ENTRIES` = 50, and returns `null` when nothing survives so
32
- callers can treat "no harness data" as one falsy case. When non-null, all four
33
- kind keys are always present (empty arrays for kinds with no entries) for a
34
- stable downstream shape.
35
-
36
- ## Stage 1 — Capture (agent self-report)
37
-
38
- Every agent populates `completionReport.harnessUsed` with the affordances it
39
- actually used. This is a self-report: the agent is the only party that knows
40
- *which* skill it ran or *which* doc it read. The field is optional and
41
- backward-compatible — agents that omit it stay valid. See
42
- [completion-reports.md → harnessUsed](completion-reports.md#harness-usage-harnessused)
43
- for the field schema and a copyable example, and `playbooks/shared-rules.md`
44
- → "Harness transparency / self-report" for the per-task instruction every
45
- playbook inherits.
46
-
47
- ## Stage 2 — Ground (engine cross-check)
48
-
49
- A self-report is a *claim*. Grounding cross-checks it against what the engine
50
- actually exposed.
51
-
52
- At spawn time (`engine.js spawnAgent`, P-a8f3c2d1) the engine folds the
53
- resolved harness roots — `queries.getUserHarnesses` (user-scope) +
54
- `queries.getProjectHarnesses` (project-scope) — plus the computed `--add-dir`
55
- set into a `_harnessPropagated` manifest via
56
- `queries.buildHarnessPropagatedManifest({ userHarnesses, projectHarnesses,
57
- addDirs })`. The manifest unions **both** user-scope and project-scope
58
- contributions and dedups by path, then is persisted onto the dispatch record as
59
- `_harnessPropagated`. Capturing user-scope harnesses is load-bearing:
60
- otherwise a legitimate `~/.agents` / `~/.claude` skill usage would be wrongly
61
- flagged un-grounded downstream. This capture is observability only — it does
62
- **not** change which dirs are propagated at spawn time, and any failure here is
63
- non-fatal (the spawn proceeds).
64
-
65
- **Monorepo sub-project contributes `--add-dir` roots (P-f52d81ba).** The `addDirs` set
66
- folded into the manifest includes the project-local harness dirs propagated for
67
- the dispatch. When the engine auto-derives a dominant sub-project (see
68
- [project-skills.md → Monorepo sub-project scoping](project-skills.md#monorepo-sub-project-scoping)),
69
- those sub-project-scoped `--project-harness-dir` roots flow into `propagatedProjectHarnessDirs`
70
- → `addDirs` → the `_harnessPropagated` manifest. The clip that selects them is
71
- the **same** `shared.filterProjectHarnessDirsForWorkdir` used for an explicit
72
- `meta.workdir` — `spawnAgent` passes `workdir: validatedWorkdir || _effectiveSubproject
73
- || null`, so an explicit `meta.workdir` wins, else the auto-derived sub-project's
74
- first-level segment clips the surface, else no clip (whole-project harness).
75
- Because the manifest captures the same sub-project-clipped roots the agent was actually
76
- handed, a self-reported skill usage against a sub-project's skill (e.g.
77
- `ocm/.claude/skills/…`) still grounds `true` — grounding stays faithful under
78
- sub-project scoping instead of falsely flagging the sub-project's own skills.
79
-
80
- The grounding cross-check (P-c6f5e8b3) runs in `engine/lifecycle.js`
81
- `runPostCompletionHooks` once the completion report is parsed and nonce-trusted:
82
- `shared.groundHarnessUsed(completionReport.harnessUsed, _harnessPropagated)`
83
- normalizes the self-report (same clamp/drop rules as `normalizeHarnessUsed`) and
84
- annotates each surviving entry `grounded: true | false`. The grounded object
85
- replaces `structuredCompletion.harnessUsed` in place, so it rides the existing
86
- completion storage onto the completed dispatch record for the Stage-3 readouts.
87
- The spawn-time manifest is located via `lifecycle.resolveHarnessPropagated`
88
- (in-memory dispatch record first, then the persisted dispatch queues for the
89
- restart/re-attach case). It compares each self-reported entry against the
90
- `_harnessPropagated` manifest and annotates it `grounded: true | false`:
91
-
92
- - `grounded: true` — the engine demonstrably exposed this affordance.
93
- - `grounded: false` — the entry is **kept, not dropped**, and flagged. A
94
- false here means "the agent claims it used something the engine has no record
95
- of exposing" — which can be a genuine out-of-band tool, a stale path, or a
96
- hallucinated entry. The point is to surface the discrepancy for a human, not
97
- to silently discard the claim.
98
-
99
- An entry grounds `true` on any of these signals (OR'd):
100
-
101
- - **identity** — the entry's `name` matches a propagated entry's name of the
102
- same kind, or (skills/docs) its `path` is lexically contained under a
103
- propagated skill root or `--add-dir`, or `basename(path)` matches a propagated
104
- skill name.
105
- - **scope** — the entry's scope (skills use `source`) matches a propagated
106
- entry's scope of the same kind. The capture manifest records harness *roots*
107
- (skill/command dirs, MCP config files), not individual affordances, so a
108
- per-affordance self-report frequently can't match by name; scope membership is
109
- the coarse-but-honest "the engine exposed this surface" signal the manifest
110
- can attest. `docs` carry no scope, so path containment is their only signal —
111
- a relative doc path (`docs/foo.md`) can't be tested against absolute add-dirs
112
- and grounds `false`.
113
-
114
- ## Stage 3 — Surface (three readouts)
115
-
116
- Grounded harness usage is rendered on three surfaces so a human (or a later
117
- evaluation pass) can see what tooling drove a dispatch:
118
-
119
- 1. **PR comment** (P-e4b7a6d5) — the harness digest is folded into the agent's
120
- PR comment/summary so reviewers see which skills / MCPs / docs informed the
121
- change. `engine/comment-format.js#buildHarnessUsedSection(harnessUsed)` is
122
- the single, platform-neutral renderer: it turns the grounded
123
- `{ skills, mcpServers, commands, docs }` record into a collapsible Markdown
124
- `<details>` block (one bullet per affordance, `grounded: false` entries kept
125
- and ⚠️-marked), and returns `''` for an empty/absent record. The GitHub
126
- comment path folds it in via `engine/gh-comment.js#buildMinionsCommentBody`
127
- (optional `harnessUsed` arg, threaded through `postPrComment` /
128
- `postPrReviewComment`); the Azure DevOps path mirrors it via
129
- `engine/ado-comment.js#postAdoPrComment` (same builder, same `harnessUsed`
130
- arg). Both posters are reached from the `minions pr comment` CLI, which turns
131
- `--harness-file` / `--harness-json` into the grounded record and folds the
132
- **byte-identical** section in for the agent. Only the raw `gh pr comment` /
133
- `az repos pr comment` / REST fallbacks bypass that chokepoint, so on those
134
- paths review/fix agents append the section themselves per
135
- `playbooks/shared-rules.md` → "Harness transparency / self-report". Every
136
- surface consumes the one renderer, so there is no second formatter to drift.
137
-
138
- **Skipped in-scope project skill (W-mr287sc0000uc214).** The same PR-comment
139
- surface also folds in a VISIBLE (non-collapsed) `> ⚠️ Skipped project skill:
140
- <name> — <reason>` callout when the agent self-reported that it intentionally
141
- skipped an available, in-scope project (review) skill
142
- (`meta.review.skillSkipped` / `meta.skill.skipped`). This lived only in the
143
- completion-report JSON before — invisible to a human reading the PR pre-merge.
144
- `engine/comment-format.js#buildSkippedSkillSection(skillSkipped)` is the single
145
- neutral renderer (sibling of `buildHarnessUsedSection`); it is threaded through
146
- `buildMinionsCommentBody` (optional `skillSkipped` arg) and both posters
147
- (`gh-comment.js` / `ado-comment.js`), and the `minions pr comment` CLI turns
148
- `--skill-skipped-file` / `--skill-skipped-json` into the record. Two rules
149
- attach to the skip (see `docs/completion-reports.md` → Review/Project skill
150
- outcomes): the skip reason must be **validated against the skill's own
151
- documented checklist** before it is recorded (a skip that contradicts an
152
- explicit "Flag if…" / "Requirements" item the skill covers is invalid), and
153
- the `{name, reason}` rendered in the comment must match the one recorded in the
154
- report (no drift).
155
- 2. **Final agent note** — for a non-clean completion (failure / partial) the
156
- single final agent report (`engine/lifecycle.js#writeNonCleanAgentReport`)
157
- folds the grounded harness footprint in as the same `buildHarnessUsedSection`
158
- block, so the learnings / inbox stream that feeds consolidation still carries
159
- it without a separate per-attempt `harness-usage-*` note. Clean completions
160
- rely on the work-item modal surface (#3) instead — they write no inbox note
161
- at all (#308 removed the standalone harness-usage digest to cut note spam).
162
- 3. **Work-item detail modal** — the dashboard work-item modal shows the
163
- harness list alongside the completion artifacts (P-d5a6f7c4). On
164
- completion, `engine/lifecycle.js promoteCompletionArtifacts` persists the
165
- grounded `structuredCompletion.harnessUsed` onto the work item as
166
- `_harnessUsed` (it survives the slim `/api/work-items` list payload and
167
- modal hydration). `dashboard/js/render-work-items.js _wiRenderDetail` renders
168
- the "Repo harnesses used" section from `item._harnessUsed`: one plain solid
169
- pill per skill / MCP / command / doc. The grounding flag is **not** surfaced
170
- in this modal — every entry renders identically (the per-pill dashed-border /
171
- ⚠ marker distinction was dropped in PR #296). Grounding is surfaced only on
172
- the PR-comment surface above.
173
-
174
- ## Why it matters
175
-
176
- - **Tooling ROI.** Knowing which skills/MCPs are actually consulted (vs. shipped
177
- and ignored) tells the team where to invest in harness affordances.
178
- - **Grounding catches drift.** `grounded: false` entries flag stale paths,
179
- renamed skills, or agents reaching for tooling the engine didn't actually
180
- wire in — without dropping the signal.
181
- - **Evaluation.** Comparing skill-driven work against first-principles work
182
- (cf. `meta.skill` in [completion-reports.md](completion-reports.md#project-skill-outcomes))
183
- needs a faithful record of what the agent had and used.
184
-
185
- ## Related
186
-
187
- - `engine/shared.js` — `HARNESS_USED_KINDS`, `HARNESS_USED_MAX_ENTRIES`,
188
- `HARNESS_USED_MAX_FIELD_LEN`, `normalizeHarnessUsed()`, `groundHarnessUsed()`,
189
- `filterProjectHarnessDirsForWorkdir()` (the sub-project/workdir clip)
190
- - `engine/queries.js` — `buildHarnessPropagatedManifest()`,
191
- `getUserHarnesses()`, `getProjectHarnesses()`
192
- - `engine/lifecycle.js` — `resolveHarnessPropagated()` + the grounding
193
- cross-check wired into `runPostCompletionHooks`
194
- - `docs/completion-reports.md` — the `harnessUsed` completion-report field
195
- - `docs/project-skills.md` — project-local skill/command discovery that feeds
196
- the project-scope harness
197
- - `docs/workspace-manifests.md` — per-agent `allowed_tools` / `allowed_repos`
198
- scoping that bounds the harness surface
199
- - `playbooks/shared-rules.md` — the per-task self-report instruction
@@ -1,193 +0,0 @@
1
- # Project skill discovery & intent vocabulary
2
-
3
- W-mq1cczi90006b21f (follow-on to PR #82, W-mq16xtdx001a347e).
4
-
5
- Minions' project-local **skill discovery layer** surfaces purpose-built tooling that a project ships (`.claude/skills/`, `.claude/commands/`, slash-command mentions in `CLAUDE.md` / `.github/copilot-instructions.md`) so dispatched agents steer toward the right skill instead of reinventing the flow from first principles.
6
-
7
- This document is the contract every playbook author and project teammate needs to know.
8
-
9
- ## Where the code lives
10
-
11
- | File | Role |
12
- |---|---|
13
- | `engine/discover-project-skills.js` | Bounded filesystem walk + classification heuristic. The crash-safe primitive. |
14
- | `engine/playbook-intents.js` | Single source of truth for the playbook → intent set mapping. |
15
- | `engine/playbook.js` | Calls discovery + filter at render time, injects `{{project_skills_block}}` into the rendered prompt. |
16
-
17
- ## What gets discovered
18
-
19
- Per project worktree, the discovery walk visits:
20
-
21
- - `<projectRoot>/.claude/skills/*/SKILL.md` — top-level skill packs.
22
- - `<projectRoot>/.claude/commands/*.md` — top-level slash-commands.
23
- - `<projectRoot>/<area>/.claude/skills/*/SKILL.md` — **nested per-area skills** (one level deep). This is how OCM / loop / officemobile organize their skills in a monorepo.
24
- - `<projectRoot>/<area>/.claude/commands/*.md` — same, for nested commands.
25
- - `<projectRoot>/.github/copilot-instructions.md` — slash-command mentions (top 32 KB).
26
- - `<projectRoot>/CLAUDE.md` — slash-command mentions (top 32 KB).
27
-
28
- Each discovery entry has shape:
29
-
30
- ```js
31
- {
32
- kind: 'skill' | 'command' | 'slash-command',
33
- name: string, // e.g. "code-reviewer" or "/review-swarm"
34
- path: string, // relative to projectRoot
35
- oneLineDescription: string, // ≤200 chars
36
- intents: string[] // closed enum, see below
37
- }
38
- ```
39
-
40
- The walk is **cheap and bounded** — preserve every guardrail in
41
- `engine/discover-project-skills.js`:
42
-
43
- - ≤ 50 files per surface
44
- - 8 KB per skill / 32 KB per docs file
45
- - 250 ms walltime budget — bails rather than blocks dispatch
46
- - ≤ 50 nested areas probed
47
- - Missing worktree / unreadable → `[]` (never throws)
48
-
49
- ## Monorepo sub-project scoping
50
-
51
- P-f52d81ba. In a monorepo, a dispatch usually touches one sub-project (`ocm/`,
52
- `loop/`, `officemobile/`, …) even though discovery walks every sub-project's
53
- `.claude/skills`. Scoping surfaces that sub-project's skills **first** without hiding
54
- the rest, so an `ocm/`-focused agent isn't forced to scroll past `loop/` tooling
55
- to find the relevant pack.
56
-
57
- **Changed-path → sub-project detection.** `detectDominantSubproject({ projectPath,
58
- changedPaths })` (engine/discover-project-skills.js) counts the changed paths
59
- per first-level directory, keyed on the POSIX first segment. It reuses
60
- `_listAreas` for the authoritative set of valid sub-projects, so a stray first segment
61
- that isn't a real top-level dir never counts. It returns `{ subproject, matched,
62
- total, confident }`. The engine feeds it changed paths from:
63
-
64
- - **spawnAgent** (async, authoritative): `git diff --name-only <mainBranch>...HEAD`
65
- in the current dispatch's worktree — never a parent/follow-up's stale diff.
66
- - **renderProjectWorkItemPromptForAgent** (`_deriveDominantSubprojectSync`, build-time
67
- fallback with no git access): the work item's `references[*].path`.
68
-
69
- **Dominant-sub-project plurality threshold.** `detectDominantSubproject` only reports a sub-project
70
- as `confident` when the top sub-project holds **≥ 60%** of the sub-project-matched files **and**
71
- **≥ 2** files (`dominantSubprojectMinRatio` / `dominantSubprojectMinFiles` in `DEFAULTS`,
72
- overridable via `opts`). Ties break alphabetically for determinism. An empty,
73
- mixed, or no-known-sub-project diff returns `{ subproject: null, confident: false }` →
74
- flat discovery (today's global name-sort). This is deliberately conservative:
75
- a weakly-dominant or split diff falls back to flat surfacing rather than
76
- mis-scoping to the wrong sub-project.
77
-
78
- **Explicit `meta.workdir` wins over auto-detection.** When the operator declares
79
- `meta.workdir` on the work item, the effective sub-project is its first path segment —
80
- no diff work is done. Auto-detection only runs when `meta.workdir` is absent.
81
- The async git-diff-derived sub-project (threaded into the shared-branch re-render via
82
- `options.dominantSubproject`) overrides the sync `references`-derived guess.
83
-
84
- **Prioritized, not exclusive, surfacing.** The effective sub-project is threaded through
85
- `playbook.js` as `scopeSubproject` into `discoverProjectSkillsWithDiagnostics`. When
86
- set, entries under `<scopeSubproject>/` lead the list (name-sorted within the group),
87
- then root-level entries, then other-sub-project entries. **Discovery still walks
88
- everything — no skills are dropped; only ordering changes.** An empty/unset
89
- `scopeSubproject` reduces byte-for-byte to the historical global name-then-path sort.
90
-
91
- **Configurable per-surface cap.** `ENGINE_DEFAULTS.projectSkillMaxFilesPerSurface`
92
- (default **200**) caps how many skill packs / command files are read per surface
93
- (root `.claude/skills`, each `<area>/.claude/skills`, …). `playbook.js` threads
94
- it into the discovery `opts`; when no override is supplied the module falls back
95
- to its own `DEFAULTS.maxFilesPerSurface` (50). It's raised well above the
96
- fallback so large monorepo sub-projects (e.g. an `ocm/.claude/skills` with ~80 packs)
97
- don't silently drop the most-relevant skills. Entries are alphabetically sorted
98
- **before** the cap is applied, so the surviving subset is deterministic across
99
- runs and OSes rather than FS-readdir-order-dependent.
100
-
101
- **Truncation diagnostics.** `discoverProjectSkillsWithDiagnostics` returns
102
- `{ entries, truncations }` where each truncation is `{ surface, dir, scanned,
103
- total }` — recorded when a surface's `total` file count exceeds `scanned`
104
- (i.e. the cap dropped entries). `playbook.js` logs a `warn` when any surface
105
- truncates and stashes the diagnostics so `spawnAgent` can persist the detected
106
- sub-project (`_dominantSubproject`) and truncations (`_skillDiscoveryTruncations`) onto the
107
- dispatch record for later grounding/observability. Overflow is thus **observable**
108
- instead of silent.
109
-
110
- ## The intent vocabulary (CLOSED)
111
-
112
- | Intent | What belongs here |
113
- |---|---|
114
- | `review` | code review, PR review, swarm review (`code-reviewer`, `/review-swarm`, `/code-review`, etc.) |
115
- | `build` | scaffold, generate, bump, migrate, codemod, refactor (`bump-fmf-android`, `workspace`, `vhd`, etc.) |
116
- | `fix` | debug, triage, repro, regression, flake-killing (`triage-flake`, `cover-pr` for missing tests on PR…) |
117
- | `test` | author tests, increase coverage, validate (`integration-tests`, `run-tests`, `qa-validate`) |
118
- | `plan` | PRD authoring, ADR, design-doc, decompose (`substitute-scheduler-template-vars`, `decompose`) |
119
- | `research` | investigate, gather-impact, telemetry-query, audit (`gather-impact`, `audit-deprecated`) |
120
- | `deploy` | release, publish, buddy-build, rollout, ship (`buddy-build`, `track-pr`) |
121
- | `observability` | telemetry, logs, kusto, monitoring, metrics dashboards (`kusto-telemetry-lmf-quality-report`, `weekly-repo-summary`) |
122
- | `meta` | meta-tooling about the skill system itself. **Do NOT use as a catch-all** — empty `intents: []` is the honest signal when the heuristic doesn't fire. |
123
-
124
- ### How intents are assigned
125
-
126
- For each discovered entry, `classifyIntents(...)` does:
127
-
128
- 1. **Honor explicit author intent first.** A skill's `SKILL.md` frontmatter may declare `intents:` (or `tags:` / `intent:`) — only values that match the closed vocabulary above are accepted. Example:
129
- ```yaml
130
- ---
131
- name: bump-fmf-android
132
- description: Bump the FMF SDK version pin in OCM Android
133
- intents: [build, deploy]
134
- ---
135
- ```
136
- 2. **Fall back to keyword heuristics.** The skill's `name`, frontmatter `description`, first heading, and filename are matched against the per-intent keyword regexes in `INTENT_KEYWORDS` (engine/discover-project-skills.js). A skill matching multiple regexes carries multiple intents.
137
- 3. **Empty intents are honest.** Zero matches → `intents: []`. The entry is still discovered and returned, but no intent-filtered playbook will surface it. NEVER default to `meta` just to satisfy the field.
138
-
139
- ### Slash-commands scraped from docs
140
-
141
- For `/foo` mentions in `CLAUDE.md` / `.github/copilot-instructions.md`, the discovery layer is more conservative: it only surfaces slash-commands whose **name itself** matches at least one intent keyword. This keeps doc-scrape noise out of every prompt (a doc may name 50 `/foo` commands; we don't want to dump them all into an `implement` prompt).
142
-
143
- ## The playbook → intent map
144
-
145
- Defined in `engine/playbook-intents.js`. The default mapping at time of writing:
146
-
147
- | Playbook | Intents surfaced |
148
- |---|---|
149
- | `review` | `['review']` |
150
- | `fix` | `['fix', 'test', 'review']` |
151
- | `implement` / `implement-shared` | `['build', 'test', 'research']` |
152
- | `plan` / `plan-to-prd` / `decompose` | `['plan', 'research']` |
153
- | `docs` | `['research']` |
154
- | `verify` | `['test', 'review']` |
155
- | `build-and-test` | `['build', 'test']` |
156
- | `test` / `qa-validate` | `['test']` |
157
- | `explore` | `['research']` |
158
- | (all others) | `[]` — no block rendered |
159
-
160
- When a playbook's intent set is empty (or maps to none), the `{{project_skills_block}}` template variable resolves to the empty string and the splice fence collapses cleanly (no stray header, no padding).
161
-
162
- The `followup-dispatch` template **inherits** its parent dispatch's intent set via `vars.parent_dispatch_type` — there's no row in the map for it.
163
-
164
- ## Adding a new playbook
165
-
166
- 1. Add a row to `PLAYBOOK_INTENTS` in `engine/playbook-intents.js` mapping your playbook name to the intent set it should surface.
167
- 2. Splice the conditional template fence into your playbook markdown at the position where the agent should see purpose-built tooling — typically between the task-intake step and the first "actually do the work" step:
168
- ```md
169
- {{#project_skills_block}}
170
- {{project_skills_block}}
171
-
172
- {{/project_skills_block}}
173
- ```
174
- 3. If the playbook should opt out for some dispatches, the dispatcher can set `meta.skipProjectSkills: true` (or the PR-82 alias `meta.skipProjectReviewSkills: true`) on the work item.
175
-
176
- ## Adding a new intent
177
-
178
- 1. Add a regex to `INTENT_KEYWORDS` in `engine/discover-project-skills.js`. Use `\b` word boundaries.
179
- 2. Add the value to `INTENT_VOCABULARY` so explicit frontmatter `intents:` entries with the new name are accepted.
180
- 3. Add a row to the table above, with a one-line description of what belongs.
181
- 4. Update one or more playbook entries in `engine/playbook-intents.js` to include the new intent.
182
-
183
- Keep the vocabulary tight — every additional intent is noise risk in every prompt that surfaces it.
184
-
185
- ## Completion-report contract
186
-
187
- When an agent acts on a discovered skill, record the outcome in the completion report's `meta.skill` block. See [docs/completion-reports.md](completion-reports.md#project-skill-outcomes) for the full schema. The legacy `meta.review.skill*` fields (PR #82) remain accepted as aliases — new code should prefer `meta.skill.*`.
188
-
189
- ## Tests
190
-
191
- - `test/unit/discover-project-skills.test.js` — discovery + classification + intent filtering + render + per-playbook splice coverage.
192
-
193
- When you change discovery, classification, or the playbook→intent map, extend or update this test file.