@event4u/agent-config 14.19.0 → 14.21.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (161) hide show
  1. package/.augment-plugin/marketplace.json +3 -3
  2. package/.augment-plugin/plugin.json +1 -1
  3. package/.claude-plugin/marketplace.json +1 -1
  4. package/CHANGELOG.md +351 -156
  5. package/README.md +1 -1
  6. package/agents/templates/.ai-council.yml.example +10 -0
  7. package/dist/agent-src/commands/agent-handoff.md +42 -41
  8. package/dist/agent-src/commands/analyze/inbox.md +1 -1
  9. package/dist/agent-src/commands/analyze/{reference-repo.md → repo.md} +247 -32
  10. package/dist/agent-src/commands/analyze/roadmap-repos.md +129 -0
  11. package/dist/agent-src/commands/analyze.md +12 -7
  12. package/dist/agent-src/commands/feature/plan.md +55 -16
  13. package/dist/agent-src/commands/feature/refactor.md +8 -3
  14. package/dist/agent-src/commands/humanize.md +48 -4
  15. package/dist/agent-src/contexts/execution/contract-decision-sheet.md +22 -1
  16. package/dist/agent-src/contexts/execution/host-capability-manifest.md +11 -2
  17. package/dist/agent-src/contexts/execution/roadmap-process-loop.md +35 -2
  18. package/dist/agent-src/contexts/execution/terminal-states.md +4 -1
  19. package/dist/agent-src/rules/evaluator-independence.md +4 -4
  20. package/dist/agent-src/rules/external-reference-deep-dive.md +2 -2
  21. package/dist/agent-src/rules/untrusted-input-defense.md +0 -2
  22. package/dist/agent-src/skills/competitive-positioning/SKILL.md +2 -2
  23. package/dist/agent-src/skills/evaluate-llm-feature/SKILL.md +1 -1
  24. package/dist/agent-src/skills/humanizer/references/anti-aiisms.md +27 -11
  25. package/dist/agent-src/skills/learning-to-rule-or-skill/SKILL.md +1 -1
  26. package/dist/agent-src/skills/markitdown/SKILL.md +2 -2
  27. package/dist/agent-src/skills/repomix-packer/SKILL.md +1 -1
  28. package/dist/agent-src/skills/skill-improvement-pipeline/SKILL.md +28 -6
  29. package/dist/agent-src/skills/technical-specification/SKILL.md +4 -0
  30. package/dist/agent-src/skills/test-driven-development/SKILL.md +1 -3
  31. package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
  32. package/dist/agent-src/templates/features.md +5 -0
  33. package/dist/agent-src/templates/roadmaps.md +29 -1
  34. package/dist/agent-src/templates/scripts/telemetry/mcp_lite.ts +213 -0
  35. package/dist/agent-src/templates/scripts/telemetry/report_renderer.ts +16 -0
  36. package/dist/agent-src/templates/scripts/telemetry_report.ts +22 -2
  37. package/dist/cli/mcp/dispatch.js +10 -0
  38. package/dist/cli/mcp/stdio.js +33 -2
  39. package/dist/cli/mcp/telemetry.js +227 -0
  40. package/dist/cli-delegate/{chunk-KPBWFOPZ.js → chunk-2FTMENJL.js} +194 -37
  41. package/dist/cli-delegate/{chunk-PMEKGDED.js → chunk-2TAQ2K4V.js} +166 -1
  42. package/dist/cli-delegate/{chunk-GZTMRYCN.js → chunk-643Z7FIC.js} +79 -12
  43. package/dist/cli-delegate/{chunk-CYH5SP6M.js → chunk-I6RHFQEG.js} +151 -16
  44. package/dist/cli-delegate/{chunk-IDHUQIXP.js → chunk-J3RFNKG4.js} +41 -5
  45. package/dist/cli-delegate/{chunk-7PRE6ZV2.js → chunk-YS6XTLEK.js} +199 -4
  46. package/dist/cli-delegate/chunk-ZVBOHSVF.js +1109 -0
  47. package/dist/cli-delegate/cmd_conformance.js +88 -110
  48. package/dist/cli-delegate/cmd_doctor.js +3 -3
  49. package/dist/cli-delegate/cmd_export.js +2 -2
  50. package/dist/cli-delegate/cmd_handoff.js +42 -12
  51. package/dist/cli-delegate/cmd_memory_get.js +1 -1
  52. package/dist/cli-delegate/cmd_prune.js +2 -2
  53. package/dist/cli-delegate/cmd_route_audit.js +1 -1
  54. package/dist/cli-delegate/cmd_session_recycle.js +59 -902
  55. package/dist/cli-delegate/cmd_settings_migrate.js +2 -2
  56. package/dist/cli-delegate/cmd_sync.js +2 -2
  57. package/dist/cli-delegate/cmd_uninstall.js +2 -2
  58. package/dist/cli-delegate/cmd_validate.js +2 -2
  59. package/dist/discovery/deprecation-report.md +1 -1
  60. package/dist/discovery/discovery-manifest.json +62 -31
  61. package/dist/discovery/discovery-manifest.json.sha256 +1 -1
  62. package/dist/discovery/discovery-manifest.summary.md +3 -3
  63. package/dist/discovery/dormancy-report.md +1 -1
  64. package/dist/discovery/orphan-report.md +1 -1
  65. package/dist/discovery/packs.json +7 -6
  66. package/dist/discovery/trust-report.md +2 -2
  67. package/dist/discovery/workspaces.json +6 -5
  68. package/dist/hooks/dispatch.js +3801 -2803
  69. package/dist/install/install.mjs +396 -168
  70. package/dist/mcp/registry-manifest.json +3 -3
  71. package/dist/mcp/server.json +2 -2
  72. package/dist/mcp/server.mjs +809 -180
  73. package/dist/scripts/ai_council/content_ceiling.js +63 -0
  74. package/dist/scripts/ai_council/seat_policy.js +106 -0
  75. package/dist/scripts/ai_council/transport_resolver.js +21 -0
  76. package/dist/scripts/mcp_server/telemetry.js +148 -0
  77. package/docs/guidelines/agent-infra/naming.md +1 -1
  78. package/docs/guidelines/agent-infra/security-lint-containment.md +46 -6
  79. package/docs/guidelines/agent-infra/self-improvement-pipeline.md +4 -4
  80. package/docs/guidelines/agent-infra/untrusted-input-spotlighting.md +1 -1
  81. package/docs/guidelines/redundancy-taxonomy.md +28 -0
  82. package/llms.txt +14 -14
  83. package/package.json +1 -1
  84. package/src/agent-src/templates/scripts/telemetry/mcp_lite.ts +213 -0
  85. package/src/agent-src/templates/scripts/telemetry/report_renderer.ts +16 -0
  86. package/src/agent-src/templates/scripts/telemetry_report.ts +22 -2
  87. package/src/config/assurance-capability-registry.json +9 -0
  88. package/src/config/gate-coverage.yml +55 -0
  89. package/src/config/gate-violation-baselines.json +18 -6
  90. package/src/config/release-gate-locality.yml +4 -3
  91. package/src/scripts/_cli/cmd_session_recycle.ts +51 -3
  92. package/src/scripts/_lib/audit_field_provenance.ts +44 -3
  93. package/src/scripts/_lib/blocked_by_marker.ts +189 -0
  94. package/src/scripts/_lib/host_capability.ts +76 -1
  95. package/src/scripts/_lib/install_reach_checks.ts +33 -1
  96. package/src/scripts/_lib/mcp_bridge.ts +222 -10
  97. package/src/scripts/_lib/mcp_consent_residual.ts +173 -0
  98. package/src/scripts/_lib/orchestration_record.ts +23 -1
  99. package/src/scripts/_lib/recycle_envelope_paths.ts +190 -0
  100. package/src/scripts/_lib/release_highlights.ts +19 -211
  101. package/src/scripts/_lib/release_material.ts +27 -77
  102. package/src/scripts/_lib/release_position.ts +96 -0
  103. package/src/scripts/_lib/review_skipped_record.ts +17 -1
  104. package/src/scripts/_lib/security_lint.ts +179 -7
  105. package/src/scripts/_lib/self_repair.ts +141 -6
  106. package/src/scripts/_lib/self_repair_store.ts +273 -7
  107. package/src/scripts/_lib/structured_ask.ts +117 -0
  108. package/src/scripts/_lib/subagent_capsule.ts +155 -3
  109. package/src/scripts/_lib/tty_prompt.ts +76 -0
  110. package/src/scripts/ai_council/api_transport.ts +163 -0
  111. package/src/scripts/ai_council/clients.ts +58 -63
  112. package/src/scripts/ai_council/config.ts +13 -0
  113. package/src/scripts/ai_council/content_ceiling.ts +79 -0
  114. package/src/scripts/ai_council/jury_aggregate.ts +178 -0
  115. package/src/scripts/ai_council/seat_policy.ts +137 -0
  116. package/src/scripts/ai_council/status_surface.ts +79 -0
  117. package/src/scripts/ai_council/transport_resolver.ts +59 -1
  118. package/src/scripts/ai_tells_rules.ts +446 -5
  119. package/src/scripts/ask_block_census.ts +499 -0
  120. package/src/scripts/bench_humanizer_eval.ts +264 -40
  121. package/src/scripts/bench_prose_tells_fp.ts +198 -0
  122. package/src/scripts/build_rule_trigger_matrix.ts +5 -0
  123. package/src/scripts/check_branch_work_committed.ts +234 -0
  124. package/src/scripts/check_mcp_doc_drift.ts +217 -0
  125. package/src/scripts/check_pack_size.ts +321 -3
  126. package/src/scripts/check_release_highlights.ts +0 -157
  127. package/src/scripts/council_cli.ts +18 -27
  128. package/src/scripts/detect_ai_tells.ts +142 -20
  129. package/src/scripts/handoff_context_hook.ts +54 -7
  130. package/src/scripts/harvest_reference_tokens.ts +698 -0
  131. package/src/scripts/hook_manifest.json +1 -1
  132. package/src/scripts/hook_manifest.yaml +31 -1
  133. package/src/scripts/hooks/concern_registry.ts +2 -0
  134. package/src/scripts/hooks/delegation_nudge_hook.ts +45 -1
  135. package/src/scripts/hooks/one_question_per_ask_hook.ts +191 -0
  136. package/src/scripts/hooks/session_eol_hook.ts +18 -7
  137. package/src/scripts/install-hooks.sh +52 -0
  138. package/src/scripts/install.ts +4 -4
  139. package/src/scripts/lint_agent_security.ts +316 -25
  140. package/src/scripts/lint_confusables.ts +42 -6
  141. package/src/scripts/lint_handoffs.ts +0 -132
  142. package/src/scripts/lint_hidden_unicode.ts +49 -7
  143. package/src/scripts/lint_instruction_smuggling.ts +42 -6
  144. package/src/scripts/lint_mcp_config_security.ts +42 -6
  145. package/src/scripts/lint_roadmap_blockers.ts +51 -1
  146. package/src/scripts/lint_roadmap_later_disposition.ts +217 -27
  147. package/src/scripts/lint_skill_frontmatter_safety.ts +43 -7
  148. package/src/scripts/mcp_server/resources.ts +41 -0
  149. package/src/scripts/mcp_server/telemetry.ts +19 -4
  150. package/src/scripts/migrate_command_suggestions.ts +1 -1
  151. package/src/scripts/orchestration_record.ts +12 -1
  152. package/src/scripts/probe_unblocked_ask.ts +135 -2
  153. package/src/scripts/release.ts +50 -95
  154. package/src/scripts/release_drill.ts +6 -10
  155. package/src/scripts/release_env.ts +1 -0
  156. package/src/scripts/release_publication.ts +40 -31
  157. package/src/scripts/self_repair_cli.ts +31 -1
  158. package/src/scripts/self_review_gate.ts +409 -15
  159. package/src/scripts/skill_scout.ts +69 -0
  160. package/dist/cli-delegate/chunk-2GNWGMAL.js +0 -172
  161. package/dist/cli-delegate/chunk-LPPWO5CE.js +0 -161
package/README.md CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  [![Smoke](https://github.com/event4u-app/agent-config/actions/workflows/smoke.yml/badge.svg)](https://github.com/event4u-app/agent-config/actions/workflows/smoke.yml) [![Public install smoke (3 OS × 2 Node)](https://github.com/event4u-app/agent-config/actions/workflows/smoke-public-install.yml/badge.svg)](https://github.com/event4u-app/agent-config/actions/workflows/smoke-public-install.yml) [![npm](https://img.shields.io/npm/v/@event4u/agent-config?style=flat-square&label=npm&color=orange)](https://www.npmjs.com/package/@event4u/agent-config) [![agent-config MCP server](https://glama.ai/mcp/servers/event4u-app/agent-config/badges/score.svg)](https://glama.ai/mcp/servers/event4u-app/agent-config) [![MCP Toplist](https://mcptoplist.com/badge/glama%2Fevent4u-app%2Fagent-config.svg)](https://mcptoplist.com/server/glama%2Fevent4u-app%2Fagent-config)
6
6
 
7
- [![Skills](https://img.shields.io/badge/Skills-299-orange?style=flat-square)](dist/agent-src/skills/) [![Rules](https://img.shields.io/badge/Rules-120-orange?style=flat-square)](dist/agent-src/rules/) [![Commands](https://img.shields.io/badge/Commands-203-orange?style=flat-square)](dist/agent-src/commands/) [![Guidelines](https://img.shields.io/badge/Guidelines-118-orange?style=flat-square)](docs/guidelines/) [![Personas](https://img.shields.io/badge/Personas-29-orange?style=flat-square)](dist/agent-src/personas/) [![Advisors](https://img.shields.io/badge/Advisors-5-orange?style=flat-square)](dist/agent-src/personas/advisors/) <!-- claim:published-artifact-counts -->
7
+ [![Skills](https://img.shields.io/badge/Skills-299-orange?style=flat-square)](dist/agent-src/skills/) [![Rules](https://img.shields.io/badge/Rules-120-orange?style=flat-square)](dist/agent-src/rules/) [![Commands](https://img.shields.io/badge/Commands-204-orange?style=flat-square)](dist/agent-src/commands/) [![Guidelines](https://img.shields.io/badge/Guidelines-118-orange?style=flat-square)](docs/guidelines/) [![Personas](https://img.shields.io/badge/Personas-29-orange?style=flat-square)](dist/agent-src/personas/) [![Advisors](https://img.shields.io/badge/Advisors-5-orange?style=flat-square)](dist/agent-src/personas/advisors/) <!-- claim:published-artifact-counts -->
8
8
 
9
9
  <sub>**How these are counted** — one canonical counter, `agent-config` → `update_counts --check`, re-derives all six from the tree and fails CI on a drift of one. Two bases are not what the linked directory shows, so they are stated here rather than left to be inferred: **Commands 202** counts every command file **recursively** (the linked directory holds 61 at its top level), and **Rules 120** counts the **source** rules while the linked projection holds 119 — one rule is dormant and is not projected. **Personas 29** excludes the directory README. Counts are of files and directories: none of them measures quality, activation, or adoption. <!-- claim:published-artifact-counts --></sub>
10
10
 
@@ -393,6 +393,16 @@ members:
393
393
 
394
394
  gemini:
395
395
  enabled: false
396
+ # HONEST NULL, not a probe result (road-to-admissible-council-seats 1.2).
397
+ # `council:status` prints this line, so the seat is no longer silently off.
398
+ disabled_reason: >-
399
+ honest null 2026-09-07 — the CLI headless contract could not be
400
+ established: the free tier on the probing machine fails with
401
+ IneligibleTierError before the run reaches the point where a piped stdin
402
+ alone would decide it, so whether the gemini CLI stays non-interactive
403
+ without -p/--prompt is UNKNOWN, not false. The api transport for this
404
+ provider became live in the same change; this seat stays off until the
405
+ contract is observed on a machine that can reach it.
396
406
  # not-listed: gemini — the `gemini` CLI is installed here and its `--help` exposes only `-m, --model <string>` — no listing subcommand and no documented "latest" alias, so no sentinel could be substituted. An honest null for this provider, not a guess.
397
407
  # The pin is therefore stamped as-of the drain date with NO refresh claimed;
398
408
  # `verified_at` records when a human last looked, which is the only thing it
@@ -8,7 +8,7 @@ replaces: []
8
8
  visibility: visible
9
9
  skills: [agent-docs-writing]
10
10
  description: Pick a recent session, generate a handoff from its transcript, and seed a fresh session with it — or summarize the live conversation for copy-paste.
11
- argument-hint: "[--print | --file | with tasks]"
11
+ argument-hint: "[--print | with tasks]"
12
12
  suggestion:
13
13
  eligible: true
14
14
  trigger_description: "user asks for an agent handoff, to resume/continue a previous session in a fresh chat, or a context-summary to paste into a new chat"
@@ -114,10 +114,51 @@ Roadmap: {roadmap file if active, or "none"}
114
114
  ## Key decisions
115
115
  - {important decisions made during this conversation}
116
116
 
117
+ ## Least confident
118
+ - {the claim in this handoff you are least sure of} — verify: {command or
119
+ observable state that would confirm or kill it}
120
+
121
+ ## Biggest thing missed
122
+ - {what a reviewer would most likely find that you did not} — verify: {command}
123
+
124
+ ## Breaks in three months because
125
+ - {the assumption most likely to expire, and what expires it} — verify: {command}
126
+
127
+ ## Not done
128
+ - {work inside the stated scope that did not land} — verify: {command}
129
+
117
130
  ## Relevant files
118
131
  - {list of files that were edited or are important for context}
119
132
  ```
120
133
 
134
+ ### The four self-critique sections — why every line carries a `verify:`
135
+
136
+ Every other section states what HAPPENED. None of them says what the outgoing
137
+ session is **least sure of**, which is the one thing the incoming session cannot
138
+ reconstruct and the one thing that decides where it should look first.
139
+
140
+ Each line in those four sections MUST name the command or observable state that
141
+ would confirm or kill it. An unverifiable line of self-doubt is filler: it reads
142
+ as diligence, costs the next session a read, and cannot be acted on. `none` is
143
+ accepted as the whole body of a section — an honest "nothing here" is an answer
144
+ — and blankness is not, exactly as `## Open questions` already treats the same
145
+ distinction. Nothing enforces either direction: the handoff artifact validator
146
+ that would have was retired with `HANDOFF.md`, so the contract is model-carried
147
+ here.
148
+
149
+ ### Capture, do not chase
150
+
151
+ A finding surfaced **while writing the handoff** is written down, never fixed in
152
+ the handoff turn. The handoff exists because the session is ending, so a fix
153
+ started there is the least-verified change in the whole run — it lands after the
154
+ verification budget is spent, with no room to prove it.
155
+
156
+ The destination is a `model-noticed` self-repair record: `upsertFinding` in
157
+ `src/scripts/_lib/self_repair_store.ts`, carrying a `target` from the closed
158
+ `rule: | skill: | command: | hook:` vocabulary so the finding is joined to the
159
+ asset it is about. Put the same finding in `## Not done` with its `verify:`, so
160
+ the next session sees it without having to query the store.
161
+
121
162
  The CLI generator emits the same section set (minus the live-only
122
163
  *Repeatable workflow* / *Feedback history* refinements) — the template above
123
164
  stays the single source of the section contract.
@@ -150,46 +191,6 @@ block (e.g. *"Copy this into a new chat:"*) and nothing after — no follow-up
150
191
  questions, no numbered options. The picker in Step 2 is the one exception:
151
192
  it is the flow's single question, asked BEFORE any handoff is generated.
152
193
 
153
- ## 2b. File-artifact mode — `HANDOFF.md` (optional, host-neutral)
154
-
155
- On `/agent-handoff --file` (or when a workflow skill's phase boundary asks
156
- for a standing handoff), ALSO write the contract to
157
- `agents/runtime/state/HANDOFF.md` (gitignored runtime state — plain
158
- Markdown, no host API). Required fields, in order:
159
-
160
- ```
161
- # HANDOFF
162
- ## Mode
163
- {current workflow mode/phase, e.g. Implement (TDD)}
164
- ## Contract received
165
- {what the previous phase handed over}
166
- ## Contract owed
167
- {what the current phase must produce before yielding}
168
- ## Decisions
169
- - {decision taken, with one-line rationale} {optionally close the line with
170
- `[reversible]` or `[irreversible]` — those two spellings exactly}
171
- ## Open questions
172
- - {unresolved items the next session must not silently drop — each as a
173
- question ending in `?`; write `none` when there genuinely are none}
174
- ## Next command
175
- {the single command or step to run first on resume}
176
- ```
177
-
178
- **Resume rule:** a workflow skill's step 0 checks for this file and resumes
179
- from its contract (mode-inference table) instead of re-deriving state; a
180
- long phase refreshes the file before yielding. Validated by
181
- `lint_handoffs.ts` when present — a missing required field is red, and so is an
182
- `## Open questions` section that answers neither way (blank, or a bare `TBD` /
183
- `TODO` / `...`). A `?`-terminated question passes; so does an explicit `none` —
184
- the check exists to stop a blank section reading as an all-clear, not to force a
185
- question where there is none.
186
-
187
- **Critical-planning-file safety protocol** (applies to HANDOFF.md and agent
188
- roadmap edits): read the current file FIRST; take a timestamped backup copy
189
- next to it (`HANDOFF.md.<ts>.bak`) before overwrite; duplicate-check before
190
- appending (never double-append a section); preserve the section structure;
191
- post-verify the write by re-reading the required fields.
192
-
193
194
  ## Detection — when natural-language triggers count as explicit
194
195
 
195
196
  The user does not need to type `/agent-handoff` literally. Treat as
@@ -22,7 +22,7 @@ packs:
22
22
  Turn a file someone dropped in `agents/tmp/` into either a roadmap this repo can
23
23
  execute, or a written reason it was dropped.
24
24
 
25
- The sibling [`/analyze:reference-repo`](reference-repo.md) does this for an
25
+ The sibling [`/analyze:repo`](repo.md) does this for an
26
26
  **external** repository. This does it for a **local artifact** — a review, a
27
27
  prompt, a feature spec, a transcript, a persona, a competitor teardown — and the
28
28
  difference matters: an inbox file is a **frozen snapshot of an opinion about a
@@ -1,13 +1,20 @@
1
1
  ---
2
2
  model_tier: high
3
- name: analyze-reference-repo
3
+ name: analyze-repo
4
4
  pack: analysis-workbench
5
5
  visibility: internal
6
- sub: reference-repo
6
+ sub: repo
7
7
  cluster: analyze
8
+ replaces: [analyze-reference-repo, analyze:reference-repo]
8
9
  skills: [project-analyzer, learning-to-rule-or-skill]
9
10
  description: Analyze an external reference repository (competitor, inspiration, peer) and produce a structured comparison + adoption plan for this project.
10
- argument-hint: "<repo-url | owner/repo | archive-url> [--focus=<area>] [--deep] [--no-roadmap]"
11
+ argument-hint: "<repo-url|owner/repo|archive> [--mode=plan|execute] [--loops=N] [--focus=<area>] [--deep] [--refresh] [--no-roadmap]"
12
+ limits:
13
+ mode_default: plan
14
+ max_iterations: 3
15
+ hard_ceiling: 5
16
+ no_gain_stop: 2
17
+ target_metric: required
11
18
  suggestion:
12
19
  eligible: false
13
20
  rationale: "Cluster sub-command — reached via its cluster head's routing or its explicit /cluster:sub name; not independently suggested (surface-consolidation)."
@@ -17,7 +24,7 @@ packs:
17
24
  - meta
18
25
  ---
19
26
 
20
- # analyze-reference-repo
27
+ # analyze-repo
21
28
 
22
29
  Analyze a **different** repository (a competitor, a reference implementation, or
23
30
  a project the user admires) and produce a structured document that anchors on
@@ -35,26 +42,83 @@ The user provides a repository URL — a full GitHub URL
35
42
 
36
43
  Optional arguments:
37
44
 
45
+ - `--mode=plan|execute` — `plan` is the **default** (§ Enforced limits).
46
+ - `--loops=N` — refinement-loop budget. **Default: 3**; hard ceiling 5.
38
47
  - `--focus=<area>` — restrict analysis to one axis (e.g. `installer`,
39
48
  `skills`, `mcp`, `governance`, `ci`). Default: full-surface.
49
+ - `--refresh` — re-resolve the reference's head revision (§ 2a). Without it a
50
+ re-run reads the revision already pinned in the local manifest.
40
51
  - `--deep` — bounded deep-verification tier (§ 2b). Read-only clone at a pinned
41
52
  SHA, never executed. Requires a non-empty anchor table.
42
53
  - `--no-roadmap` — skip the roadmap-draft step.
43
54
 
55
+ ## Enforced limits
56
+
57
+ The frontmatter `limits:` block is the machine-readable pin
58
+ (`tests/scripts/analyze_repo_limits.test.ts` fails when flow and pin drift);
59
+ the steps below are the enforcement — each limit is a step the run MUST
60
+ execute, not advice.
61
+
62
+ ### Execution mode — plan-only default (`mode_default: plan`)
63
+
64
+ `--mode=plan` is the **default**: run §§ 1-6 — pin, fetch, extract, probe,
65
+ compare, classify, converge, write the local artifacts — then present the
66
+ result and STOP. No roadmap landing, no ledger rows, no tracked write of any
67
+ kind. `--mode=execute` must be **explicitly present in the invocation** to
68
+ reach §§ 7-8; treat its absence as plan mode even when conversation momentum
69
+ suggests otherwise. Execute mode does not remove the per-write confirmations in
70
+ §§ 7-8 — it only makes them reachable.
71
+
72
+ ### Pre-registered target metric — required before loop 1 (`target_metric: required`)
73
+
74
+ Before loop 1 may run, the analysis document MUST carry a `Target metric` line
75
+ naming the metric of § 5c, its **measured baseline** taken from the seed pass,
76
+ and the predicted direction. No baseline → REFUSE to enter loop 1 with:
77
+ `target metric not pre-registered — record the seed-pass baseline first`. The
78
+ metric named before loop 1 is the metric every loop is scored against; loops
79
+ never move the goalposts.
80
+
81
+ ### Loop budget (`max_iterations: 3`, `hard_ceiling: 5`)
82
+
83
+ `--loops=N` caps the refinement loops. **Default: 3** — one loop per lens
84
+ (§ 5b), which is what makes three the budget rather than a round number.
85
+ Hard ceiling: 5 — a larger `--loops` value is clamped to 5 with a warning.
86
+
87
+ **Halt-on-spin tripwire (`no_gain_stop: 2`), checked after every loop and
88
+ overriding the budget:**
89
+
90
+ - **Two consecutive zero-delta loops → STOP** before the next lens runs, and
91
+ say which two loops produced no delta and on which metric. Spinning without
92
+ gain is the failure the metric exists to catch.
93
+ - A verdict table still contested at the ceiling stops the run with a
94
+ maintainer question (§ 5b) — never silently.
95
+ - A single zero-delta loop **never** cancels the remaining lenses. It is a
96
+ signal about that lens, not a prediction about the next one.
97
+
98
+ ### Read ceiling per loop
99
+
100
+ Loop *n* cannot read deeper than the seed pass without a local clone, so
101
+ `--deep` (§ 2b) becomes the **default whenever the loop runs**. The three-part
102
+ read ceiling of § 2b applies per loop, and the loop that hits it logs one
103
+ read-ceiling line naming the bound that fired.
104
+
44
105
  ## Steps
45
106
 
46
- ### 1. Confirm scope
107
+ ### 1. Resolve scope from the invocation — do not ask
47
108
 
48
- Before touching anything, ask:
109
+ Scope comes from the arguments, never from a prompt. A bare repository argument
110
+ means **full scope**; `--focus=<area>` means focused. **Ask nothing before the
111
+ first fetch.** The four-option menu this step used to open with cost one prompt
112
+ per repository in a batch, which is a prompt the invocation had already
113
+ answered.
49
114
 
50
- > Found reference: `<owner/repo>`.
51
- >
52
- > 1. Full comparison — all axes (default)
53
- > 2. Focused — one axis (I'll ask which)
54
- > 3. Quick scan — README + top-level layout only
55
- > 4. Cancel
115
+ The one question that survives is an **unresolvable repository identity** — the
116
+ argument matches no repository, or matches more than one. That is a genuine
117
+ ambiguity the invocation did not settle, and it halts before any fetch.
56
118
 
57
- Wait for the user's choice.
119
+ Everything the menu's "Cancel" option protected is protected elsewhere: the
120
+ write-acts of §§ 7-8 keep their own confirmations, and plan mode (§ Enforced
121
+ limits) is the default, so a run that is never confirmed writes nothing tracked.
58
122
 
59
123
  ### 1b. Anchor table first
60
124
 
@@ -81,6 +145,26 @@ Use `web-fetch` for rendered files, GitHub REST (`/repos/{o}/{r}/contents/{p}`)
81
145
  for listings. **Max 40 fetches** — if more is needed, ask which subtree to
82
146
  expand.
83
147
 
148
+ ### 2a. Pin the upstream revision — once, before any reasoning
149
+
150
+ The **seed pass** resolves the reference's head revision exactly once and
151
+ records it in the local manifest (§ 6). Every loop of § 5b reads **that same
152
+ revision**. Only `--refresh` re-resolves it.
153
+
154
+ Why once: a loop that re-resolves is comparing two different repositories and
155
+ calling the difference a finding. Convergence over a moving target is not
156
+ convergence, and a delta produced by an upstream commit that landed between
157
+ loop 1 and loop 2 is upstream's news, not this analysis's.
158
+
159
+ Record in the iteration record, once per pass: `revision: <sha>` — the **same**
160
+ value on every pass of a run. A pass whose recorded revision differs from the
161
+ seed pass's is a defect in the run, not a finding about the reference.
162
+
163
+ Because loop *n* cannot read deeper than the seed pass without a local clone,
164
+ `--deep` (§ 2b) is the default whenever the loop runs. Each loop that hits the
165
+ § 2b three-part read ceiling logs **one** read-ceiling line naming the bound
166
+ that fired and the loop it fired in.
167
+
84
168
  ### 2b. Deep verification tier — `--deep`, opt-in
85
169
 
86
170
  Forty top-level fetches cannot reach a fact buried in an implementation file, so
@@ -179,38 +263,111 @@ mentions a ledger word — a check that mostly fires wrongly gets waved through.
179
263
  A **checklist obligation**, not a CI gate: `check_claims` guards the ledger's
180
264
  integrity, never a proposal against it.
181
265
 
182
- ### 5b. Converge the verdict table
266
+ ### 5b. Three loops, three different questions
267
+
268
+ One pass is a draft, not an analysis — but three passes asking the same
269
+ question are three rewordings, which costs three times the fetches and produces
270
+ prose churn. So the loops are **not** repetitions of one critique. Each has its
271
+ own lens, and the lens is what makes the loop worth its cost:
272
+
273
+ | Loop | Lens | The question it asks, and only it |
274
+ |---|---|---|
275
+ | 1 | **Coverage** | What was missed? What was wrongly marked absent? Which rows rest on a weak anchor — no `file:line`, or one that no longer resolves? |
276
+ | 2 | **Adversary** | Was the *form* copied instead of the principle? Is this only *different* rather than *better*? Does this package already solve it elsewhere? What does it cost in tokens, surface and maintenance? |
277
+ | 3 | **Convergence** | Resolve contradictions between the first two. Fold duplicates. Cut the speculative. Bind every remaining line to evidence on **both** sides. |
278
+
279
+ Each loop takes as input **both** the previous analysis **and** the previous
280
+ roadmap draft — a finding that changed the analysis but not the roadmap is a
281
+ finding the run has not finished acting on.
282
+
283
+ Each loop appends its own **delta block** to `## Iteration record`, naming its
284
+ lens, with one row per change and a reason per row:
285
+
286
+ ```markdown
287
+ ### Loop {n} — {Coverage | Adversary | Convergence} · revision: {sha}
288
+
289
+ | Change | Row | From → To | Reason |
290
+ |---|---|---|---|
291
+ | added / removed / flipped / folded | … | … | … |
292
+ ```
183
293
 
184
- One pass is a draft, not an analysis. Critique the verdict table at least
185
- **twice**: pass 2 applies solution-minimalism and the § 5 bound-claim gate to
186
- pass 1's ADOPT rows, using the § 3b probe findings as input. Record every flip
187
- with its reason in `## Iteration record`. **DONE** = a pass produces **zero**
188
- verdict changes. Cap at four passes; a table still flipping at four is itself
189
- the finding — mark it `contested — needs maintainer judgement`, never stop
294
+ **A loop whose delta block has no added, removed, flipped or folded entry is
295
+ recorded as a zero-delta loop, never omitted.** An omitted block is
296
+ indistinguishable from a loop that never ran, and the halt rule below counts
297
+ zero-delta loops — so omitting one silently disarms the tripwire.
298
+
299
+ A verdict table still contested at the ceiling is itself the finding — mark it
300
+ `contested — needs maintainer judgement` and stop with the question, never
190
301
  silently.
191
302
 
303
+ ### 5c. The target metric is a decision-quality measure
304
+
305
+ The metric is:
306
+
307
+ > the count of ADOPT/ADAPT rows carrying a concrete `file:line` on **both** the
308
+ > reference side and this package's side.
309
+
310
+ Not the number of rows, not the length of the document, not the count of
311
+ findings. An output-volume metric rewards a loop for saying more, which is
312
+ precisely the churn the three lenses exist to prevent; this metric only moves
313
+ when a verdict became *decidable* — someone can now go and look at both ends.
314
+
315
+ Its baseline is measured on the seed pass and pre-registered before loop 1
316
+ (§ Enforced limits). The halt rules that read it:
317
+
318
+ - **Two consecutive zero-delta loops → STOP** before the next lens runs, naming
319
+ the two loops and the metric. Spin, not convergence.
320
+ - **A single zero-delta loop never cancels the remaining lenses.** Loop 1
321
+ finding nothing to add on Coverage says nothing about whether the Adversary
322
+ lens will find something to remove — the lenses ask different questions, so
323
+ one lens's silence is not evidence about the next.
324
+ - A contested table stops the run with a maintainer question (§ 5b).
325
+
192
326
  A contested table is a **published finding, not an adoption proposal**: the
193
327
  reference surface holds elements this repo cannot mechanically classify, and no
194
328
  automation converts that into an ADOPT decision. Convergence is an analyst
195
329
  obligation with a recorded trail — **never an LLM-as-judge gate**, never a
196
330
  script.
197
331
 
198
- ### 6. Write the analysis document
332
+ ### 6. Write the analysis document — under the opaque id, in the local area
333
+
334
+ **Target: `agents/.harvest-local/repo/<opaque-id>/analysis.md`.** Every pass and
335
+ loop artifact of a run lives under that one directory, and the run writes
336
+ nothing under `agents/evidence/analysis/`.
337
+
338
+ The old target was `agents/evidence/analysis/compare-<slug>.md` with
339
+ `<slug>` = `<owner>-<repo>`, which put the reference's identity into a path.
340
+ `.gitignore` covers `compare-*.md` under the evidence directory but nothing
341
+ else there, so any other filename in that directory would have been unprotected
342
+ by construction — the protection was a glob, not a boundary.
343
+ `agents/.harvest-local/` is a whole gitignored directory, so the boundary is the
344
+ directory rather than a pattern one rename defeats.
345
+
346
+ **The opaque id.** Derived by
347
+ `./scripts-run src/scripts/harvest_reference_tokens` from the canonical
348
+ repository identity **and** the pinned revision of § 2a: `sha256(identity +
349
+ "@" + revision)`, first 16 hex characters. Two consequences that are the point
350
+ of including the revision: the id is **stable** across two runs at the same
351
+ revision, so a resumed or repeated run lands in the same directory; and it
352
+ **differs** across revisions, so one repository at two historical pins is two
353
+ pieces of evidence rather than one overwriting the other.
354
+
355
+ **The plaintext lives in exactly one place** — `identity` in the local
356
+ manifest, `agents/.harvest-local/manifest.jsonl`, inside the same gitignored
357
+ directory. Nothing else in the run resolves an opaque id back to a name.
199
358
 
200
- Target: `agents/evidence/analysis/compare-<slug>.md` (create the directory if
201
- missing, with `.gitkeep` — same convention as `project-analyzer`). Slug rule:
202
- `<owner>-<repo>` lowercased, non-alphanumeric → `-`, collapse runs. Structure:
359
+ Structure:
203
360
 
204
361
  ```markdown
205
362
  # Reference analysis: {owner}/{repo}
206
363
 
207
364
  > One-sentence framing of why this reference matters.
208
365
 
209
- - **Source:** https://github.com/{owner}/{repo}
366
+ - **Source:** `opaque:{opaque-id}` — resolvable only via the local manifest
210
367
  - **Fetched commit:** {sha} ({date})
211
368
  - **Own-tree SHA:** {sha}
212
369
  - **Focus:** {full | area} · **Depth:** {surface | deep}
213
- - **Analyst:** agent via `/analyze-reference-repo`
370
+ - **Analyst:** agent via `/analyze-repo`
214
371
 
215
372
  ## Anchor table
216
373
 
@@ -231,9 +388,31 @@ missing, with `.gitkeep` — same convention as `project-analyzer`). Slug rule:
231
388
  | Artifact | Our consumer (file:line) | Discovered? | Validates? | Exact failing axes or error |
232
389
  |---|---|---|---|---|
233
390
 
391
+ ## Target metric
392
+
393
+ - **Metric:** ADOPT/ADAPT rows with a `file:line` on both sides (§ 5c)
394
+ - **Seed-pass baseline:** {n} · **Predicted direction:** up
395
+
234
396
  ## Iteration record
235
397
 
236
- | Pass | Row | From → To | Reason |
398
+ `revision: {sha}` — the § 2a pin, identical on every pass below.
399
+
400
+ One delta block per loop, in order, each naming its lens. A zero-delta loop
401
+ keeps its block and records the zero (§ 5b).
402
+
403
+ ### Loop 1 — Coverage · revision: {sha}
404
+
405
+ | Change | Row | From → To | Reason |
406
+ |---|---|---|---|
407
+
408
+ ### Loop 2 — Adversary · revision: {sha}
409
+
410
+ | Change | Row | From → To | Reason |
411
+ |---|---|---|---|
412
+
413
+ ### Loop 3 — Convergence · revision: {sha}
414
+
415
+ | Change | Row | From → To | Reason |
237
416
  |---|---|---|---|
238
417
 
239
418
  ## Findings
@@ -270,6 +449,9 @@ omit the section entirely when every row extends something that exists.}
270
449
  does not fit — a seed that skipped this line is not ready to hand over}
271
450
 
272
451
  ## Open questions for the maintainer
452
+ {Each line is a question to ask the maintainer directly, one at a time, when
453
+ this report is handed over — the section records what is owed, it does not
454
+ discharge it.}
273
455
  ```
274
456
 
275
457
  The `## Seeds` section is the handoff shape
@@ -282,19 +464,52 @@ it makes the four-surface overlap scan that
282
464
  requires visible at handoff time, when the analysis is still open, instead of
283
465
  re-derived later by whoever picks the seed up.
284
466
 
285
- ### 7. Offer next steps
467
+ ### 7. Offer next steps — execute mode only
286
468
 
287
- After writing the file, present:
469
+ **Plan mode stops here** (§ Enforced limits). `--mode=execute` makes this step
470
+ reachable; it does not make it automatic.
288
471
 
289
- > Analysis written to `agents/evidence/analysis/compare-{slug}.md`.
472
+ > Analysis written to `agents/.harvest-local/repo/{opaque-id}/analysis.md`.
290
473
  >
291
- > 1. Draft roadmap from ADOPT/ADAPT — `agents/roadmaps/adopt-{slug}.md`
474
+ > 1. Draft roadmap from ADOPT/ADAPT — `agents/roadmaps/road-to-{defect}.md`
292
475
  > 2. Merge findings into an existing roadmap — say which
293
476
  > 3. Stop here
294
477
  > 4. Deep-dive on one axis — say which
295
478
 
296
479
  Never create the roadmap without explicit confirmation.
297
480
 
481
+ #### The landed roadmap is named after the defect, never after the source
482
+
483
+ A tracked filename is a published string. `adopt-{owner}-{repo}.md` publishes
484
+ the reference's identity in the one surface no `.gitignore` can retract, and it
485
+ also names the wrong thing: the roadmap exists because **this package has a
486
+ gap**, not because some other project exists.
487
+
488
+ - **Filename:** derived from the capability gap —
489
+ `agents/roadmaps/road-to-<the-thing-we-lack>.md`. It contains neither the
490
+ owner nor the repository name. If the only name you can think of is the
491
+ reference's, the finding is not yet understood well enough to land.
492
+ - **Header:** carries a provenance block with an **encrypted token**
493
+ (`ENC1:` via `src/scripts/_lib/link_crypto.ts`), never the plaintext URL —
494
+ the form
495
+ [`source-confidentiality`](../../../../rules/source-confidentiality.md)
496
+ requires of a tracked roadmap that cites a source. Sources are referenced as
497
+ *Source A / B / C* in the prose.
498
+ - **Verified by:** `./scripts-run src/scripts/check_no_external_sources`, which
499
+ is the deterministic backstop rather than the discipline itself.
500
+
501
+ #### Never hand a subagent a plaintext URL
502
+
503
+ Under the harvester (`/analyze:roadmap-repos`) the orchestrator passes the
504
+ subagent **the opaque id and nothing else**. The subagent reads the URL from the
505
+ local manifest itself, so the plaintext never appears in a dispatch argument.
506
+
507
+ The cost is one indirection, and it is taken **on precaution**: whether a given
508
+ host retains a subagent's arguments in a transcript or audit log is
509
+ host-dependent and was not verifiable from this tree. That is stated rather than
510
+ asserted away — the indirection is cheap enough not to need a proven threat, and
511
+ claiming a proven one would be a claim this package cannot support.
512
+
298
513
  ### 8. Close the loop — offer the ledger rows
299
514
 
300
515
  **Only after a roadmap draft is accepted** (option 1 or 2 above). This step
@@ -313,7 +528,7 @@ One row per **anchored ADOPT or ADAPT** finding, and nothing else:
313
528
  |---|---|
314
529
  | `harvest_id` | a kebab-case slug for the mechanism |
315
530
  | `stated_in` | the roadmap file the user just accepted |
316
- | `source_ref` | the reference's `<url>@<sha>` — the § 2 pin, already recorded in the document header |
531
+ | `source_ref` | `opaque:<id>` or an `ENC1:` token — never the plaintext URL; the § 2a pin is inside the id |
317
532
  | `evidence_locator` | the reference-side `file:line` from that row of the comparison matrix |
318
533
  | `harvested_at` | today |
319
534
  | `verdict` | `adopt` or `adapt` |
@@ -0,0 +1,129 @@
1
+ ---
2
+ model_tier: medium
3
+ name: analyze-roadmap-repos
4
+ pack: analysis-workbench
5
+ visibility: internal
6
+ sub: roadmap-repos
7
+ cluster: analyze
8
+ type: orchestrator
9
+ replaces: []
10
+ skills: [subagent-orchestration]
11
+ description: Walk the roadmap estate's encrypted reference tokens and feed each unique repository through analyze-repo, one subagent at a time, without producing one roadmap per repository.
12
+ argument-hint: "[--limit=N] [--resume] [--refresh] [--mode=plan|execute]"
13
+ suggestion:
14
+ eligible: false
15
+ rationale: "Cluster sub-command — reached via its cluster head's routing or its explicit /cluster:sub name; not independently suggested (surface-consolidation)."
16
+ workspaces:
17
+ - agent-config-maintainer
18
+ packs:
19
+ - meta
20
+ ---
21
+
22
+ # analyze-roadmap-repos
23
+
24
+ The thin harvester in front of [`analyze-repo`](repo.md). It walks the roadmap
25
+ estate's encrypted reference tokens, resolves each to a repository identity, and
26
+ feeds the unique ones through the analysis command one at a time.
27
+
28
+ **This command contains no analysis instruction and never will.** It resolves,
29
+ dispatches, and folds. Every judgement about a reference — what to fetch, what
30
+ to compare, what to adopt, how to converge — belongs to `analyze-repo`, which
31
+ owns the loop contract and its limits. A second copy of that reasoning here
32
+ would be a second implementation to keep in step with the first, which is the
33
+ condition step 1.1 of its roadmap removed.
34
+
35
+ ## Steps
36
+
37
+ ### 1. Discovery — run the script, read the manifest
38
+
39
+ ```bash
40
+ ./scripts-run src/scripts/harvest_reference_tokens --dry-run # census only
41
+ ./scripts-run src/scripts/harvest_reference_tokens --resume --limit=N # write the manifest
42
+ ```
43
+
44
+ The script is the only discovery mechanism: it extracts the `ENC1:` tokens
45
+ across the five estate levels, decrypts them, classifies each resolved URL,
46
+ canonicalizes the repositories, deduplicates on identity plus pinned revision,
47
+ and writes `agents/.harvest-local/manifest.jsonl`.
48
+
49
+ It **refuses without a link-encryption key** rather than reporting a short
50
+ census — a classification of zero repositories that actually means "nobody could
51
+ look" is the failure mode this whole pass exists to avoid.
52
+
53
+ Read the manifest. Do not re-derive any of it here.
54
+
55
+ ### 2. Dispatch — one subagent per repository, sequential
56
+
57
+ For each manifest entry with status `pending`, in manifest order, up to the
58
+ batch limit:
59
+
60
+ - Spawn **one subagent** and have it run `/analyze:repo` for that entry.
61
+ - Pass the subagent **the opaque id only** — never a resolved URL. The subagent
62
+ reads the URL from `agents/.harvest-local/manifest.jsonl` itself. One
63
+ indirection, taken on precaution (`analyze-repo` § 7).
64
+ - **Sequential, never a fan-out.** Over a hundred identities at four passes each
65
+ cannot share one session, and every external fetch is spent deliberately. The
66
+ manifest is the only shared state between subagents.
67
+ - Write the entry's status back as the run progresses: `running`, then `done` or
68
+ `failed`. A batch interrupted mid-flight resumes from those statuses.
69
+
70
+ Default batch limit is **small**. A harvester that walks the whole estate in one
71
+ invocation spends a hundred repositories' worth of fetches on a decision nobody
72
+ took.
73
+
74
+ ### 3. Ownership fold — one gap, one roadmap
75
+
76
+ After the batch, and never during it, run the fold pass of
77
+ [`analyze-repo`](repo.md) § 7 across the batch's findings as a set. Consult
78
+ `src/scripts/roadmap_context.ts` and the run's own candidate registry, then pick
79
+ exactly one disposition per repository:
80
+
81
+ | Disposition | When |
82
+ |---|---|
83
+ | no action | the finding is REJECT or ALREADY throughout |
84
+ | already planned | an active roadmap already owns the gap |
85
+ | extend an existing roadmap | the gap belongs to an owner that exists |
86
+ | create a follow-up | the gap belongs under an owner but is out of its scope |
87
+ | create new | no owner exists |
88
+ | contested | two owners claim it — stop and ask |
89
+
90
+ **Three repositories showing the same gap strengthen the evidence on one owning
91
+ roadmap. They never create three.** That is the whole reason this command exists
92
+ rather than a loop that invokes `analyze-repo` and keeps whatever it returns.
93
+
94
+ ### 4. Landing — local-only
95
+
96
+ The batch's consolidated result is written to `agents/.harvest-local/` and
97
+ nowhere else. **No per-run consolidate roadmap takes an active top-level estate
98
+ slot** — decided 2026-09-06 under the maintainer's standing delegation, recorded
99
+ in `road-to-bounded-reference-harvest-loop` § Blockers. A recurring process that
100
+ takes a slot per run converts the estate ratchet's allowance into a standing
101
+ exemption, which removes the pressure the ratchet exists to apply.
102
+
103
+ Accepted consequence, stated rather than hidden: **no batch is visible in the
104
+ roadmap dashboard.** Any roadmap the fold pass does land is an ordinary estate
105
+ entry landed for a named capability gap, and is subject to the estate gate like
106
+ any other.
107
+
108
+ ## Safety
109
+
110
+ - Read-only on every reference. The no-execute invariant of `analyze-repo`
111
+ § Safety binds every subagent this command spawns.
112
+ - No plaintext repository URL is ever passed as a dispatch argument.
113
+ - Nothing tracked is written by this command itself.
114
+ - Hard-Floor actions stay this-turn gated. A batch authorizes fetches, never a
115
+ commit, a push or a merge.
116
+
117
+ ## When **not** to use
118
+
119
+ - One known reference → [`analyze-repo`](repo.md) directly. The harvester's only
120
+ job is the estate walk.
121
+ - Analyzing this repository → `/project-analyze`.
122
+ - A dropped local artifact → [`/analyze:inbox`](inbox.md).
123
+
124
+ ## Related
125
+
126
+ - Command: [`analyze-repo`](repo.md) — the analysis engine this dispatches to.
127
+ - Script: `src/scripts/harvest_reference_tokens.ts` — discovery, the only one.
128
+ - Rule: [`source-confidentiality`](../../../../rules/source-confidentiality.md)
129
+ — why the identities stay in the gitignored area.