@event4u/agent-config 14.18.0 → 14.20.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 (210) 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 +120 -129
  5. package/README.md +71 -149
  6. package/dist/agent-src/README.md +1 -1
  7. package/dist/agent-src/commands/agent-handoff.md +44 -43
  8. package/dist/agent-src/commands/review/changes.md +19 -19
  9. package/dist/agent-src/commands/review.md +2 -2
  10. package/dist/agent-src/contexts/execution/host-capability-manifest.md +37 -4
  11. package/dist/agent-src/rules/autonomous-execution.md +1 -1
  12. package/dist/agent-src/rules/git-history-discipline.md +2 -2
  13. package/dist/agent-src/rules/guidelines.md +1 -1
  14. package/dist/agent-src/rules/session-canary.md +2 -2
  15. package/dist/agent-src/rules/token-efficiency.md +1 -1
  16. package/dist/agent-src/rules/untrusted-input-defense.md +0 -2
  17. package/dist/agent-src/skills/adversarial-review/SKILL.md +0 -8
  18. package/dist/agent-src/skills/canvas-design/SKILL.md +1 -1
  19. package/dist/agent-src/skills/design-intelligence/SKILL.md +1 -1
  20. package/dist/agent-src/skills/design-intelligence/data/app-interface.csv +1 -1
  21. package/dist/agent-src/skills/design-intelligence/data/motion.csv +17 -17
  22. package/dist/agent-src/skills/design-intelligence/data/ux-guidelines.csv +2 -2
  23. package/dist/agent-src/skills/design-intelligence/references/context-and-registers.md +17 -0
  24. package/dist/agent-src/skills/design-intelligence/references/design-rules-checklist.md +4 -4
  25. package/dist/agent-src/skills/design-review/SKILL.md +1 -1
  26. package/dist/agent-src/skills/design-variations/SKILL.md +1 -1
  27. package/dist/agent-src/skills/evaluate-llm-feature/SKILL.md +1 -1
  28. package/dist/agent-src/skills/existing-ui-audit/SKILL.md +1 -1
  29. package/dist/agent-src/skills/fe-design/SKILL.md +32 -5
  30. package/dist/agent-src/skills/fe-design/references/craft-floor.md +7 -0
  31. package/dist/agent-src/skills/fe-design/references/design-patterns.md +47 -2
  32. package/dist/agent-src/skills/laravel-scheduling/SKILL.md +3 -1
  33. package/dist/agent-src/skills/markitdown/SKILL.md +1 -1
  34. package/dist/agent-src/skills/project-analyzer/SKILL.md +0 -6
  35. package/dist/agent-src/skills/react-native-setup/SKILL.md +3 -3
  36. package/dist/agent-src/skills/react-shadcn-ui/SKILL.md +1 -1
  37. package/dist/agent-src/skills/sequential-thinking/SKILL.md +18 -8
  38. package/dist/agent-src/skills/skill-improvement-pipeline/SKILL.md +28 -6
  39. package/dist/agent-src/skills/tailwind-engineer/SKILL.md +1 -1
  40. package/dist/agent-src/skills/test-driven-development/SKILL.md +1 -3
  41. package/dist/agent-src/skills/ui-apply-generic/SKILL.md +1 -1
  42. package/dist/agent-src/skills/ui-component-architect/SKILL.md +1 -1
  43. package/dist/agent-src/skills/using-git-worktrees/SKILL.md +17 -2
  44. package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
  45. package/dist/agent-src/templates/roadmaps.md +14 -1
  46. package/dist/agent-src/templates/scripts/telemetry/mcp_lite.ts +213 -0
  47. package/dist/agent-src/templates/scripts/telemetry/report_renderer.ts +16 -0
  48. package/dist/agent-src/templates/scripts/telemetry_report.ts +22 -2
  49. package/dist/cli/commands/recordTriggerEval.js +2 -2
  50. package/dist/cli/main.js +1 -1
  51. package/dist/cli/mcp/dispatch.js +10 -0
  52. package/dist/cli/mcp/stdio.js +33 -2
  53. package/dist/cli/mcp/telemetry.js +227 -0
  54. package/dist/cli-delegate/{chunk-AKJGUQNO.js → chunk-C6IPZRDA.js} +484 -353
  55. package/dist/cli-delegate/{chunk-CYH5SP6M.js → chunk-I6RHFQEG.js} +151 -16
  56. package/dist/cli-delegate/{chunk-7RXIKF62.js → chunk-NUMQHLS5.js} +304 -136
  57. package/dist/cli-delegate/{chunk-PMEKGDED.js → chunk-PI2GU3DP.js} +163 -1
  58. package/dist/cli-delegate/{chunk-7PRE6ZV2.js → chunk-YS6XTLEK.js} +199 -4
  59. package/dist/cli-delegate/chunk-ZVBOHSVF.js +1109 -0
  60. package/dist/cli-delegate/cmd_adr_effective.js +318 -81
  61. package/dist/cli-delegate/cmd_conformance.js +489 -121
  62. package/dist/cli-delegate/cmd_doctor.js +2 -2
  63. package/dist/cli-delegate/cmd_export.js +2 -2
  64. package/dist/cli-delegate/cmd_handoff.js +42 -12
  65. package/dist/cli-delegate/cmd_memory_get.js +1 -1
  66. package/dist/cli-delegate/cmd_prune.js +2 -2
  67. package/dist/cli-delegate/cmd_route_audit.js +2 -2
  68. package/dist/cli-delegate/cmd_session_recycle.js +59 -902
  69. package/dist/cli-delegate/cmd_settings_migrate.js +2 -2
  70. package/dist/cli-delegate/cmd_sync.js +2 -2
  71. package/dist/cli-delegate/cmd_uninstall.js +2 -2
  72. package/dist/cli-delegate/cmd_validate.js +2 -2
  73. package/dist/discovery/deprecation-report.md +1 -1
  74. package/dist/discovery/discovery-manifest.json +34 -33
  75. package/dist/discovery/discovery-manifest.json.sha256 +1 -1
  76. package/dist/discovery/discovery-manifest.summary.md +1 -1
  77. package/dist/discovery/dormancy-report.md +1 -1
  78. package/dist/discovery/orphan-report.md +1 -1
  79. package/dist/discovery/packs.json +2 -2
  80. package/dist/discovery/trust-report.md +1 -1
  81. package/dist/discovery/workspaces.json +2 -2
  82. package/dist/hooks/dispatch.js +4455 -3025
  83. package/dist/install/install.mjs +975 -689
  84. package/dist/mcp/registry-manifest.json +2 -2
  85. package/dist/mcp/server.json +2 -2
  86. package/dist/mcp/server.mjs +2087 -1134
  87. package/dist/scripts/_lib/model_tier.js +1 -1
  88. package/dist/scripts/mcp_server/telemetry.js +148 -0
  89. package/docs/guidelines/agent-infra/evaluator-independence-mechanics.md +1 -1
  90. package/docs/guidelines/agent-infra/naming.md +1 -1
  91. package/docs/guidelines/agent-infra/roadmap-progress-mechanics.md +1 -1
  92. package/docs/guidelines/agent-infra/security-lint-containment.md +46 -6
  93. package/docs/guidelines/agent-infra/self-improvement-pipeline.md +4 -4
  94. package/docs/guidelines/agent-infra/untrusted-input-spotlighting.md +1 -1
  95. package/docs/guidelines/design-antipatterns.md +87 -64
  96. package/docs/guidelines/redundancy-taxonomy.md +28 -0
  97. package/package.json +1 -1
  98. package/src/agent-src/README.md +1 -1
  99. package/src/agent-src/templates/scripts/telemetry/mcp_lite.ts +213 -0
  100. package/src/agent-src/templates/scripts/telemetry/report_renderer.ts +16 -0
  101. package/src/agent-src/templates/scripts/telemetry_report.ts +22 -2
  102. package/src/config/assurance-capability-registry.json +9 -0
  103. package/src/config/ci-local-parity.yml +6 -0
  104. package/src/config/gate-coverage.yml +225 -0
  105. package/src/config/gate-violation-baselines.json +18 -6
  106. package/src/config/release-gate-locality.yml +12 -0
  107. package/src/scripts/_cli/cmd_doctor.ts +2 -2
  108. package/src/scripts/_cli/cmd_handoff.ts +1 -1
  109. package/src/scripts/_cli/cmd_session_recycle.ts +51 -3
  110. package/src/scripts/_cli/cmd_update.ts +1 -1
  111. package/src/scripts/_cli/cmd_versions.ts +1 -1
  112. package/src/scripts/_lib/adr_frontmatter.ts +44 -0
  113. package/src/scripts/_lib/audit_field_provenance.ts +44 -3
  114. package/src/scripts/_lib/consequence_objects.ts +152 -0
  115. package/src/scripts/_lib/exec_evidence.ts +7 -0
  116. package/src/scripts/_lib/global_deploy_inventory.ts +2 -2
  117. package/src/scripts/_lib/host_capability.ts +38 -0
  118. package/src/scripts/_lib/host_permission_checks.ts +251 -0
  119. package/src/scripts/_lib/install_reach_checks.ts +33 -1
  120. package/src/scripts/_lib/mcp_bridge.ts +222 -10
  121. package/src/scripts/_lib/mcp_consent_residual.ts +173 -0
  122. package/src/scripts/_lib/model_tier.ts +1 -1
  123. package/src/scripts/_lib/obligation_frequency.ts +2 -2
  124. package/src/scripts/_lib/orchestration_record.ts +23 -1
  125. package/src/scripts/_lib/recycle_envelope_paths.ts +190 -0
  126. package/src/scripts/_lib/release_highlights.ts +93 -0
  127. package/src/scripts/_lib/release_material.ts +64 -1
  128. package/src/scripts/_lib/release_position.ts +96 -0
  129. package/src/scripts/_lib/review_skipped_record.ts +17 -1
  130. package/src/scripts/_lib/runtime_journal.ts +26 -3
  131. package/src/scripts/_lib/runtime_wiring_checks.ts +12 -1
  132. package/src/scripts/_lib/security_lint.ts +179 -7
  133. package/src/scripts/_lib/self_repair.ts +141 -6
  134. package/src/scripts/_lib/self_repair_store.ts +273 -7
  135. package/src/scripts/_lib/session_register.ts +11 -6
  136. package/src/scripts/_lib/sqlite_schema.ts +22 -0
  137. package/src/scripts/_lib/subagent_capsule.ts +155 -3
  138. package/src/scripts/_lib/surface.ts +86 -0
  139. package/src/scripts/_lib/ui_authority.ts +116 -0
  140. package/src/scripts/adr/regenerate_index.ts +54 -1
  141. package/src/scripts/adr_cite_check.ts +71 -15
  142. package/src/scripts/ai_council/clients.ts +1 -1
  143. package/src/scripts/autonomy_friction_corpus.ts +229 -0
  144. package/src/scripts/build_proof.ts +15 -2
  145. package/src/scripts/build_rule_trigger_matrix.ts +5 -0
  146. package/src/scripts/check_beta_review_markers.ts +141 -17
  147. package/src/scripts/check_enforcement_coverage.ts +1 -1
  148. package/src/scripts/check_enforcement_denominator.ts +121 -11
  149. package/src/scripts/check_finding_dispositions.ts +66 -9
  150. package/src/scripts/check_gate_coverage.ts +96 -0
  151. package/src/scripts/check_mcp_doc_drift.ts +217 -0
  152. package/src/scripts/check_pack_size.ts +321 -3
  153. package/src/scripts/check_release_highlights.ts +74 -1
  154. package/src/scripts/check_skill_activation_claim.ts +179 -0
  155. package/src/scripts/check_trigger_evals.ts +1 -1
  156. package/src/scripts/conformance_scan.ts +44 -3
  157. package/src/scripts/explain_run.ts +1 -1
  158. package/src/scripts/generate_capability_matrix.ts +1 -1
  159. package/src/scripts/git_authorization_hook.ts +75 -1
  160. package/src/scripts/handoff_context_hook.ts +54 -7
  161. package/src/scripts/hook_manifest.json +1 -1
  162. package/src/scripts/hook_manifest.yaml +14 -11
  163. package/src/scripts/hooks/augment-dispatcher.sh +1 -1
  164. package/src/scripts/hooks/category_a.ts +291 -0
  165. package/src/scripts/hooks/cline-dispatcher.sh +1 -1
  166. package/src/scripts/hooks/cursor-dispatcher.sh +11 -5
  167. package/src/scripts/hooks/dispatch_hook.ts +17 -22
  168. package/src/scripts/hooks/git_command_classifier.ts +156 -1
  169. package/src/scripts/hooks/host_lowering.ts +215 -0
  170. package/src/scripts/hooks/host_lowering.yaml +185 -0
  171. package/src/scripts/hooks/host_semantics.ts +105 -37
  172. package/src/scripts/hooks/journal_record_hook.ts +10 -0
  173. package/src/scripts/hooks/permission_gate.ts +117 -0
  174. package/src/scripts/hooks/session_eol_hook.ts +18 -7
  175. package/src/scripts/hooks_status.ts +12 -10
  176. package/src/scripts/install.ts +57 -86
  177. package/src/scripts/install_anthropic_key.sh +2 -2
  178. package/src/scripts/lint_agent_security.ts +316 -25
  179. package/src/scripts/lint_confusables.ts +42 -6
  180. package/src/scripts/lint_design_antipattern_parity.ts +80 -0
  181. package/src/scripts/lint_evidence_artifacts.ts +45 -1
  182. package/src/scripts/lint_handoffs.ts +0 -132
  183. package/src/scripts/lint_hidden_unicode.ts +49 -7
  184. package/src/scripts/lint_hook_manifest.ts +87 -3
  185. package/src/scripts/lint_instruction_smuggling.ts +42 -6
  186. package/src/scripts/lint_mcp_config_security.ts +42 -6
  187. package/src/scripts/lint_motion_authority_drift.ts +731 -0
  188. package/src/scripts/lint_readme_size.ts +158 -12
  189. package/src/scripts/lint_reasoning_fixtures.ts +216 -0
  190. package/src/scripts/lint_roadmap_later_disposition.ts +217 -27
  191. package/src/scripts/lint_skill_frontmatter_safety.ts +43 -7
  192. package/src/scripts/lint_skill_trigger_contradiction.ts +161 -0
  193. package/src/scripts/mcp_server/resources.ts +41 -0
  194. package/src/scripts/mcp_server/telemetry.ts +19 -4
  195. package/src/scripts/measure_sibling_naming.ts +150 -0
  196. package/src/scripts/orchestration_record.ts +12 -1
  197. package/src/scripts/redact_hook_capture.ts +1 -1
  198. package/src/scripts/release.ts +24 -24
  199. package/src/scripts/release_publication.ts +6 -2
  200. package/src/scripts/report_skill_activation.ts +319 -7
  201. package/src/scripts/schemas/review-findings.schema.json +5 -0
  202. package/src/scripts/schemas/skill.schema.json +2 -2
  203. package/src/scripts/self_repair_cli.ts +31 -1
  204. package/src/scripts/session_canary_hook.ts +1 -1
  205. package/src/scripts/session_register_hook.ts +4 -3
  206. package/src/scripts/skill_scout.ts +69 -0
  207. package/src/scripts/trigger_eval_grandfather.json +5 -1
  208. package/src/scripts/validate_agent_settings.ts +1 -1
  209. package/dist/cli-delegate/chunk-2GNWGMAL.js +0 -172
  210. package/dist/cli-delegate/chunk-LPPWO5CE.js +0 -161
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  model_tier: medium
3
3
  name: react-shadcn-ui
4
- description: "Use when building React UI on shadcn/ui primitives + Tailwind — the apply/review/polish skill dispatched by `directives/ui/*` for the `react-shadcn` stack."
4
+ description: "Building React UI on shadcn/ui primitives + Tailwind — the apply/review/polish skill dispatched by `directives/ui/*` for the `react-shadcn` stack; idiom from tailwind-engineer."
5
5
  domain: engineering
6
6
  workspaces:
7
7
  - engineering
@@ -129,12 +129,22 @@ Explore alternatives when:
129
129
  1. Numbered reasoning steps with conclusions
130
130
  2. Final answer or recommendation with confidence level
131
131
 
132
- ## Auto-trigger keywords
132
+ ## Tool availability — this is a procedure, not a tool call
133
133
 
134
- - problem solving
135
- - step-by-step
136
- - reasoning
137
- - decomposition
134
+ Everything above runs **in-session**: it is a way of structuring reasoning, and
135
+ it needs nothing installed.
136
+
137
+ The wider MCP ecosystem carries a `sequentialthinking` server exposing a tool of
138
+ that name. **This package registers no MCP server** — `mcp.json` ships
139
+ `{"servers": {}}` — so on a default install that tool is not present, and
140
+ nothing here tries to call it. Registering one is a consumer-side install
141
+ decision this package deliberately does not make for you.
142
+
143
+ So a reader on a default install can answer the question directly: the tool is
144
+ not available to you, and none of this skill depends on it. If you have
145
+ registered such a server yourself, the once-per-task limit below governs that
146
+ call too — the limit is about looping, not about which mechanism does the
147
+ thinking.
138
148
 
139
149
  ## Gotcha
140
150
 
@@ -145,9 +155,9 @@ Explore alternatives when:
145
155
  ## Do NOT
146
156
 
147
157
  - Do NOT use this for simple, well-understood tasks — it adds unnecessary overhead.
148
- - Do NOT call `sequentialthinking` more than **once per task**. If you're calling it repeatedly,
149
- you're looping — stop and act directly instead.
150
- - Do NOT use `sequentialthinking` as a "thinking proxy" — if the task is "view a file" or
158
+ - Do NOT run this procedure more than **once per task**. If you are reaching for it
159
+ repeatedly, you are looping — stop and act directly instead.
160
+ - Do NOT use it as a "thinking proxy" — if the task is "view a file" or
151
161
  "run a command", just do it. No thinking step needed.
152
162
  - Do NOT skip the validation step — always check the solution against the original problem.
153
163
  - Do NOT ignore contradictions — they are signals, not noise.
@@ -56,7 +56,7 @@ For each learning, apply ALL of these checks. ALL must be YES to promote:
56
56
  |---|---|
57
57
  | All YES | **Promote** — continue to Step 3 |
58
58
  | One-off, never seen before | **Reject** — do nothing |
59
- | Seen once, but generalizable | **Note** — use `remember` tool, act on second occurrence |
59
+ | Seen once, but generalizable | **Record** — write a `model-noticed` self-repair record naming the asset it is about; act on the second occurrence |
60
60
  | Already covered | **Update existing** — skip to Step 4 with the existing skill/rule |
61
61
  | Vague | **Reject** — not actionable |
62
62
 
@@ -66,9 +66,26 @@ Show the user:
66
66
  > Category: {category-tag}
67
67
  > Repeated: {yes/no} | Prevents failure: {yes/no} | Not covered: {yes/no} | Actionable: {yes/no}
68
68
  >
69
- > → {Promote / Reject / Note / Update existing}
69
+ > → {Promote / Reject / Record / Update existing}
70
70
  ```
71
71
 
72
+ ### Where a seen-once learning goes — and why not a host-native memory tool
73
+
74
+ The **Record** branch writes a `model-noticed` defect record through
75
+ `upsertFinding` in `src/scripts/_lib/self_repair_store.ts`, carrying a `target`
76
+ drawn from the closed `rule: | skill: | command: | hook:` vocabulary and
77
+ resolved against the tree at write time.
78
+
79
+ It used to route to a host-native memory tool, and that destination could not
80
+ work for this branch by construction: it is not target-addressed, nothing in
81
+ this repository can read it, it appears in no corroboration counter, and it is
82
+ simply absent on hosts that do not ship it. So the ≥2-origin counter at
83
+ `src/scripts/learning_sidecar.ts:39` restarted at one every time, and the second
84
+ occurrence could never be recognised AS the second — which is the whole
85
+ mechanism this branch exists to feed. A record is readable, joinable, and
86
+ counted per target, so a third manifestation against one asset is a fact the
87
+ tree can establish rather than one that depends on a human recalling it.
88
+
72
89
  #### Cross-project promotion signal (surfacing only — never an auto-writer)
73
90
 
74
91
  If the **same learning** has now been observed in **≥ 2 distinct projects**, it
@@ -82,10 +99,15 @@ to the user — do **not** auto-write it:
82
99
  > → surface for promotion (human decides; no auto-write)
83
100
  ```
84
101
 
85
- This is a *signal*, not a store: there is no auto-write, no decay, no runtime
86
- (the writable per-project learning store stays rejected —
87
- [[council-agent-memory-sunset]]). The human decides whether the cross-project
88
- recurrence justifies promotion.
102
+ This is a *signal*, not a promotion: there is **no auto-write and no
103
+ auto-promotion** — that half is the live boundary and is unchanged. The other
104
+ two clauses this sentence used to carry are retired, because the tree
105
+ contradicts them: `src/scripts/learning_sidecar.ts:37` sets
106
+ `HALF_LIFE_DAYS = 30` and decays a signal's weight over a per-project intake
107
+ store, and ADR-249 permits a **supervised** resident process in core,
108
+ superseding the blanket no-daemon clause this sentence inherited. Decay and
109
+ runtime are therefore **governed, not prohibited**. The human still decides
110
+ whether the cross-project recurrence justifies promotion.
89
111
 
90
112
  ### Step 3: Classify
91
113
 
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  model_tier: medium
3
3
  name: tailwind-engineer
4
- description: "Use when writing or reviewing Tailwind CSS — utility-first, design-token discipline, no inline-style drift, responsive variants, dark mode — even on 'style this' or 'mach das hübsch'."
4
+ description: "Writing or reviewing Tailwind CSS — utility-first, design tokens, no inline-style drift, responsive variants, dark mode — 'style this' or 'mach das hübsch'. Pairs with react-shadcn-ui."
5
5
  personas:
6
6
  - frontend-engineer
7
7
  domain: engineering
@@ -120,9 +120,7 @@ RED.
120
120
 
121
121
  ### Mode inference on resume
122
122
 
123
- Step 0 of any resume: if `agents/runtime/state/HANDOFF.md` exists, resume
124
- from its Mode + Contract-owed fields (see `/agent-handoff` file mode) instead
125
- of re-deriving. Otherwise infer the mode from observable state — never
123
+ Step 0 of any resume: infer the mode from observable state — never
126
124
  assume Design:
127
125
 
128
126
  | Observed state | Resume in |
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  model_tier: medium
3
3
  name: ui-apply-generic
4
- description: "Use when implementing a UI brief on a stack with no framework executor — Svelte, Astro, Angular, plain HTML. Carries the stack-independent contract; idiom comes from the stack corpus."
4
+ description: "Use when implementing a UI brief on a stack with no framework executor — Svelte, Astro, Angular, plain HTML. Stack-independent sibling of react-shadcn-ui; idiom from the stack corpus."
5
5
  personas:
6
6
  - frontend-engineer
7
7
  domain: engineering
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  model_tier: high
3
3
  name: ui-component-architect
4
- description: "Use when shaping a UI component tree — composition vs inheritance, slot patterns, prop API design, controlled vs uncontrolled, polymorphic — even on 'split this component'."
4
+ description: "Shaping a UI component tree — composition vs inheritance, slot patterns, prop API design, controlled vs uncontrolled, polymorphic — on 'split this component'. Feeds react-shadcn-ui."
5
5
  personas:
6
6
  - frontend-engineer
7
7
  domain: engineering
@@ -105,7 +105,9 @@ Ask format:
105
105
 
106
106
  > 1. `.worktrees/` — project-local, hidden
107
107
  > 2. `worktrees/` — project-local, visible
108
- > 3. `~/.event4u/agent-config/worktrees/<project>/` — global
108
+ > 3. `~/.event4u/agent-config/worktrees/<project>/` — global, **outside the
109
+ > repository root**: costs the persistent shell cwd (see below) and requires
110
+ > declaring the parent in the host's working-directory allowlist
109
111
 
110
112
  **Recommendation: 1 — `.worktrees/`** — project-local keeps the worktree next to the repo (easy cleanup), and the leading dot keeps it out of `ls`. Caveat: pick 3 if multiple repos must share a single worktree root.
111
113
 
@@ -135,9 +137,22 @@ passes. For the global location, skip — path is outside the repo.
135
137
 
136
138
  ```bash
137
139
  git worktree add .worktrees/<branch-name> -b <branch-name>
138
- cd .worktrees/<branch-name>
139
140
  ```
140
141
 
142
+ **Then address the worktree by flag, not by `cd`.** Every tool that matters
143
+ takes a directory flag, and the flag survives where a `cd` does not:
144
+
145
+ ```bash
146
+ git -C .worktrees/<branch-name> status
147
+ npm --prefix .worktrees/<branch-name> test
148
+ composer -d .worktrees/<branch-name> install
149
+ ```
150
+
151
+ A `cd X && …` compound re-enters the directory on every single call, and the
152
+ one call that forgets it edits the main checkout silently — the failure is
153
+ invisible because the command succeeds. Where a tool genuinely has no directory
154
+ flag, use a subshell so the change cannot leak: `( cd <path> && <cmd> )`.
155
+
141
156
  Branch names must match the project convention — see
142
157
  `commit-conventions` rule.
143
158
 
@@ -39,7 +39,7 @@ schema_version: 1
39
39
  # CI guard: a release bump of `package.json` must update this value
40
40
  # in lockstep — see scripts/check_template_pin_drift.ts (road-to-
41
41
  # portable-runtime-and-update-check P3.3).
42
- agent_config_version: "14.18.0"
42
+ agent_config_version: "14.20.0"
43
43
 
44
44
  # --- Project identity ---
45
45
  project:
@@ -43,7 +43,20 @@ that was never a judgement call.
43
43
  - `agents/roadmaps/` — active (in progress or planned **and workable now**)
44
44
  - `agents/roadmaps/archive/` — work happened (fully or partially); no further work planned
45
45
  - `agents/roadmaps/skipped/` — decision against pursuit; typically 0 items `[x]` (superseded, scope rejected)
46
- - `agents/roadmaps/later/` — open work remains but is **blocked-for-later** (gated on an external trigger or a decision); **will resume**. Set `status: later` + a `Blocked until` / `Trigger` line. Roadmaps with open tasks deferred for later are **always** moved here, never left active. Excluded from the dashboard and `/roadmap:process-*`; enforced by `lint_roadmap_later_disposition`.
46
+ - `agents/roadmaps/later/` — open work remains but is **blocked-for-later** (gated on an external condition or a decision); **will resume**. Roadmaps with open tasks deferred for later are **always** moved here, never left active. Excluded from the dashboard and `/roadmap:process-*`; enforced by `lint_roadmap_later_disposition`. Two frontmatter obligations, both in force **regardless of `status`** — the status word alone stopped satisfying this gate on 2026-09-07, and so did the bare word `trigger`:
47
+
48
+ - **A wake condition.** Preferred form is a structured `entry_condition:` mapping naming all three parts:
49
+
50
+ ```yaml
51
+ entry_condition:
52
+ what: what would change the decision
53
+ when: when that could arrive
54
+ who: who would have to act # `none` is a legal answer; blank is not
55
+ ```
56
+
57
+ A condition that cannot be observed is a deferral wearing a condition's clothes, which is why all three are required and why a **scalar** `entry_condition` or a blank part fails hard rather than being baselined. The legacy body-line form (`Blocked until` / `Resume when`) still passes and is what the shrink-only ratchet is walking down from.
58
+
59
+ - **`review_by:`** — a date. A park with no review date is indistinguishable from an abandonment. Ratcheted from the count measured when the obligation landed; a **newly** parked roadmap must carry it.
47
60
 
48
61
  See the `roadmap-management` skill for the exact trigger matrix and user-confirmation flow.
49
62
  13. **No tags, releases, or version numbers.** Roadmaps describe work, not shipping.
@@ -0,0 +1,213 @@
1
+ /**
2
+ * MCP-lite call reading for `telemetry:report` (roadmap step 4.2).
3
+ *
4
+ * Reads the sink the stdio-lite MCP server writes — the same
5
+ * `agents/runtime/mcp-telemetry/calls.jsonl` the kernel MCP server already
6
+ * used, not a second store — and aggregates it per tool and per host.
7
+ *
8
+ * WHY THIS IS A SEPARATE MODULE. The engagement aggregator models artefacts
9
+ * with a consulted/applied ratio; a `tools/call` has neither. Forcing MCP rows
10
+ * through it would have meant either widening the engagement schema or
11
+ * mislabelling a tool call as a "skill consulted", and both would have made the
12
+ * quartile buckets mean two different things at once.
13
+ *
14
+ * PUBLISH THE ZERO. `render_mcp_lite_markdown` emits the section even when the
15
+ * count is zero, and says so in words. The adjacent archived roadmaps in this
16
+ * family closed at `measured-null` precisely because nulls were published
17
+ * rather than buried; a section that disappears when empty would make "nobody
18
+ * used it" and "nobody looked" indistinguishable.
19
+ *
20
+ * WHAT A ZERO DOES NOT MEAN. The emitter that fills this sink did not exist
21
+ * before this roadmap, and it is gated off by default. A zero is therefore a
22
+ * statement about the age and the default of the instrument first, and about
23
+ * consumer behaviour only once a machine has run with the gate on. The rendered
24
+ * section says this itself, so a reader cannot lift the number away from it.
25
+ */
26
+
27
+ import * as fs from 'node:fs';
28
+ import { check_id_redaction } from './engagement.js';
29
+
30
+ /** Where the stdio-lite server writes, relative to the consumer root. */
31
+ export const MCP_LITE_LOG_REL = 'agents/runtime/mcp-telemetry/calls.jsonl';
32
+
33
+ /** The transport label the stdio-lite surface stamps on every row it writes. */
34
+ export const MCP_LITE_TRANSPORT = 'stdio-lite';
35
+
36
+ export interface McpLiteAggregate {
37
+ /** The file that was read, whether or not it existed. */
38
+ log_path: string;
39
+ /** Rows on this transport that parsed and fell inside the window. */
40
+ calls: number;
41
+ /** Lines that did not parse as JSON, or carried no usable tool name. */
42
+ skipped_lines: number;
43
+ /** Rows that parsed but belong to another transport (the kernel server). */
44
+ other_transport: number;
45
+ by_tool: Map<string, number>;
46
+ by_host: Map<string, number>;
47
+ by_outcome: Map<string, number>;
48
+ earliest_ts: string | null;
49
+ latest_ts: string | null;
50
+ }
51
+
52
+ function _empty(log_path: string): McpLiteAggregate {
53
+ return {
54
+ log_path,
55
+ calls: 0,
56
+ skipped_lines: 0,
57
+ other_transport: 0,
58
+ by_tool: new Map(),
59
+ by_host: new Map(),
60
+ by_outcome: new Map(),
61
+ earliest_ts: null,
62
+ latest_ts: null,
63
+ };
64
+ }
65
+
66
+ function _bump(m: Map<string, number>, k: string): void {
67
+ m.set(k, (m.get(k) ?? 0) + 1);
68
+ }
69
+
70
+ /** Epoch ms for an ISO-8601 stamp, or `null` when it does not parse. */
71
+ function _ms(ts: unknown): number | null {
72
+ if (typeof ts !== 'string') return null;
73
+ const v = Date.parse(ts);
74
+ return Number.isNaN(v) ? null : v;
75
+ }
76
+
77
+ /**
78
+ * Aggregate the lite surface's calls.
79
+ *
80
+ * A missing file is an empty reading, never an error: the overwhelmingly common
81
+ * case is a machine that has simply never run the server, and that is a zero,
82
+ * not a failure. `since` is an epoch-ms lower bound, matching the engagement
83
+ * aggregator's own window semantics; a row with an unreadable timestamp is
84
+ * kept when there is no window and dropped when there is, because it cannot be
85
+ * shown to fall inside one.
86
+ */
87
+ export function aggregate_mcp_lite(
88
+ log_path: string,
89
+ opts: { since?: number | null } = {},
90
+ ): McpLiteAggregate {
91
+ const since = opts.since ?? null;
92
+ const out = _empty(log_path);
93
+ let text: string;
94
+ try {
95
+ text = fs.readFileSync(log_path, 'utf-8');
96
+ } catch {
97
+ return out;
98
+ }
99
+ for (const raw of text.split(/\r\n|\r|\n/)) {
100
+ if (raw.trim() === '') continue;
101
+ let rec: Record<string, unknown>;
102
+ try {
103
+ const parsed: unknown = JSON.parse(raw);
104
+ if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {
105
+ out.skipped_lines += 1;
106
+ continue;
107
+ }
108
+ rec = parsed as Record<string, unknown>;
109
+ } catch {
110
+ out.skipped_lines += 1;
111
+ continue;
112
+ }
113
+ if (rec.transport !== MCP_LITE_TRANSPORT) {
114
+ out.other_transport += 1;
115
+ continue;
116
+ }
117
+ const tool = typeof rec.tool_name === 'string' ? rec.tool_name : '';
118
+ if (tool === '') {
119
+ out.skipped_lines += 1;
120
+ continue;
121
+ }
122
+ const ms = _ms(rec.ts);
123
+ if (since !== null && (ms === null || ms < since)) continue;
124
+
125
+ out.calls += 1;
126
+ _bump(out.by_tool, tool);
127
+ _bump(out.by_host, typeof rec.host === 'string' && rec.host !== '' ? rec.host : 'unknown');
128
+ _bump(out.by_outcome, typeof rec.outcome === 'string' ? rec.outcome : 'unknown');
129
+ const ts = typeof rec.ts === 'string' ? rec.ts : null;
130
+ if (ts !== null) {
131
+ if (out.earliest_ts === null || ts < out.earliest_ts) out.earliest_ts = ts;
132
+ if (out.latest_ts === null || ts > out.latest_ts) out.latest_ts = ts;
133
+ }
134
+ }
135
+ return out;
136
+ }
137
+
138
+ /** Descending by count, then by name, so the rendering is deterministic. */
139
+ function _sorted(m: Map<string, number>): [string, number][] {
140
+ return [...m.entries()].sort((a, b) => (b[1] - a[1]) || (a[0] < b[0] ? -1 : a[0] > b[0] ? 1 : 0));
141
+ }
142
+
143
+ const ZERO_NOTE =
144
+ '_No calls recorded. The lite-surface emitter is default-off '
145
+ + '(`telemetry.artifact_engagement.enabled`), so a zero here says the '
146
+ + 'instrument was off or unused on this machine — it is not a measurement '
147
+ + 'of what consumers do._';
148
+
149
+ /**
150
+ * The markdown section. Always renders, zero included.
151
+ *
152
+ * Ids are re-validated through the engagement redaction floor even though the
153
+ * emitter already clamps them: this renderer prints whatever is in the file,
154
+ * and a file is not a boundary the emitter controls.
155
+ */
156
+ export function render_mcp_lite_markdown(agg: McpLiteAggregate): string {
157
+ const lines: string[] = [];
158
+ lines.push('## MCP lite surface');
159
+ lines.push('');
160
+ lines.push(`- calls recorded: **${agg.calls}**`);
161
+ if (agg.earliest_ts && agg.latest_ts) {
162
+ lines.push(`- ts range: \`${agg.earliest_ts}\` → \`${agg.latest_ts}\``);
163
+ }
164
+ lines.push('');
165
+ if (agg.calls === 0) {
166
+ lines.push(ZERO_NOTE);
167
+ lines.push('');
168
+ return `${lines.join('\n')}`;
169
+ }
170
+ lines.push('| tool | calls |');
171
+ lines.push('|---|---:|');
172
+ for (const [tool, n] of _sorted(agg.by_tool)) {
173
+ check_id_redaction('mcp_lite.by_tool.id', tool);
174
+ lines.push(`| ${tool} | ${n} |`);
175
+ }
176
+ lines.push('');
177
+ lines.push('| host | calls |');
178
+ lines.push('|---|---:|');
179
+ for (const [host, n] of _sorted(agg.by_host)) {
180
+ check_id_redaction('mcp_lite.by_host.id', host);
181
+ lines.push(`| ${host} | ${n} |`);
182
+ }
183
+ lines.push('');
184
+ return `${lines.join('\n')}`;
185
+ }
186
+
187
+ /** Plain object for the JSON format. Same validation as the markdown path. */
188
+ export function mcp_lite_json(agg: McpLiteAggregate): Record<string, unknown> {
189
+ const by_tool: Record<string, number> = {};
190
+ for (const [tool, n] of _sorted(agg.by_tool)) {
191
+ check_id_redaction('mcp_lite.by_tool.id', tool);
192
+ by_tool[tool] = n;
193
+ }
194
+ const by_host: Record<string, number> = {};
195
+ for (const [host, n] of _sorted(agg.by_host)) {
196
+ check_id_redaction('mcp_lite.by_host.id', host);
197
+ by_host[host] = n;
198
+ }
199
+ const by_outcome: Record<string, number> = {};
200
+ for (const [outcome, n] of _sorted(agg.by_outcome)) {
201
+ by_outcome[outcome] = n;
202
+ }
203
+ return {
204
+ log_path: agg.log_path,
205
+ calls: agg.calls,
206
+ skipped_lines: agg.skipped_lines,
207
+ by_tool,
208
+ by_host,
209
+ by_outcome,
210
+ earliest_ts: agg.earliest_ts,
211
+ latest_ts: agg.latest_ts,
212
+ };
213
+ }
@@ -12,6 +12,7 @@ import {
12
12
  rank_artefacts,
13
13
  } from './aggregator.js';
14
14
  import { ALLOWED_OUTCOMES, check_id_redaction } from './engagement.js';
15
+ import { mcp_lite_json, render_mcp_lite_markdown, type McpLiteAggregate } from './mcp_lite.js';
15
16
 
16
17
  export const QUARTILE_TOP_RATIO = 0.20;
17
18
  export const QUARTILE_BOTTOM_RATIO = 0.20;
@@ -57,6 +58,17 @@ export function bucketise(stats: ArtefactStat[]): BucketedStat[] {
57
58
  export interface RenderOptions {
58
59
  top?: number | null;
59
60
  since_label?: string | null;
61
+ /**
62
+ * The MCP-lite reading, when the caller resolved one (step 4.2).
63
+ *
64
+ * Optional, and ABSENT is the default: a caller that does not supply it
65
+ * gets exactly the report this renderer produced before, byte for byte.
66
+ * That matters concretely — the Python→TS parity goldens for this script
67
+ * are frozen snapshots of a `.py` that no longer exists and can never be
68
+ * re-captured, so a section that rendered unconditionally would break a
69
+ * pin that cannot be repaired.
70
+ */
71
+ mcp_lite?: McpLiteAggregate | null;
60
72
  }
61
73
 
62
74
  /** Render a markdown report. `top` truncates each bucket; null keeps all. */
@@ -137,6 +149,9 @@ export function render_markdown(aggregate: AggregateResult, opts: RenderOptions
137
149
  }
138
150
  lines.push('');
139
151
  }
152
+ if (opts.mcp_lite) {
153
+ lines.push(render_mcp_lite_markdown(opts.mcp_lite));
154
+ }
140
155
  return `${_pyRStrip(lines.join('\n'))}\n`;
141
156
  }
142
157
 
@@ -181,6 +196,7 @@ export function render_json(aggregate: AggregateResult, opts: RenderOptions = {}
181
196
  },
182
197
  outcomes: outcomes_payload,
183
198
  buckets: grouped,
199
+ ...(opts.mcp_lite ? { mcp_lite: mcp_lite_json(opts.mcp_lite) } : {}),
184
200
  };
185
201
  return `${_py_json_dumps_indent2_sorted(payload)}\n`;
186
202
  }
@@ -19,6 +19,7 @@ import { aggregate } from './telemetry/aggregator.js';
19
19
  import { EngagementSchemaError } from './telemetry/engagement.js';
20
20
  import { render_json, render_markdown } from './telemetry/report_renderer.js';
21
21
  import { read_settings } from './telemetry/settings.js';
22
+ import { MCP_LITE_LOG_REL, aggregate_mcp_lite } from './telemetry/mcp_lite.js';
22
23
 
23
24
  const _DURATION_RE = /^\s*(\d+)\s*([dhm])\s*$/u;
24
25
 
@@ -59,6 +60,7 @@ interface ReportArgs {
59
60
  format: string;
60
61
  log_path: string | null;
61
62
  settings: string;
63
+ mcp_log_path: string | null;
62
64
  }
63
65
 
64
66
  class ArgError extends Error {}
@@ -70,6 +72,7 @@ function _parseArgs(argv: string[]): ReportArgs {
70
72
  format: 'markdown',
71
73
  log_path: null,
72
74
  settings: '.agent-settings.yml',
75
+ mcp_log_path: null,
73
76
  };
74
77
  const formatChoices = ['markdown', 'json'];
75
78
  for (let i = 0; i < argv.length; i += 1) {
@@ -104,6 +107,7 @@ function _parseArgs(argv: string[]): ReportArgs {
104
107
  }
105
108
  a.format = v;
106
109
  } else if (tok === '--log-path') a.log_path = next();
110
+ else if (tok === '--mcp-log-path') a.mcp_log_path = next();
107
111
  else if (tok === '--settings') a.settings = next();
108
112
  else throw new ArgError(`unrecognized arguments: ${argv[i]}`);
109
113
  }
@@ -155,13 +159,29 @@ export function main(argv: string[] = process.argv.slice(2)): number {
155
159
  return 2;
156
160
  }
157
161
 
162
+ // The MCP-lite section (step 4.2) renders in the DEFAULT, settings-driven
163
+ // invocation — which is what `agent-config telemetry:report` is — and when
164
+ // a caller names the sink explicitly. It does NOT render when the caller
165
+ // pointed `--log-path` at one specific engagement log and named no MCP
166
+ // sink: in that mode the report is scoped to the file it was handed and
167
+ // does not go looking for other stores on the machine. That rule also keeps
168
+ // the frozen Python-parity goldens intact, since every pinned invocation
169
+ // passes `--log-path`; the goldens cannot be re-captured, the `.py` having
170
+ // been retired, so an unconditional section would break a pin permanently.
171
+ const want_mcp = args.mcp_log_path !== null || args.log_path === null;
172
+ const mcp_lite = want_mcp
173
+ ? aggregate_mcp_lite(args.mcp_log_path ?? path.join(process.cwd(), MCP_LITE_LOG_REL), {
174
+ since: cutoff,
175
+ })
176
+ : null;
177
+
158
178
  const top = args.top <= 0 ? null : args.top;
159
179
  let rendered: string;
160
180
  try {
161
181
  if (args.format === 'json') {
162
- rendered = render_json(result, { top, since_label });
182
+ rendered = render_json(result, { top, since_label, mcp_lite });
163
183
  } else {
164
- rendered = render_markdown(result, { top, since_label });
184
+ rendered = render_markdown(result, { top, since_label, mcp_lite });
165
185
  }
166
186
  } catch (exc) {
167
187
  if (exc instanceof EngagementSchemaError) {
@@ -3,7 +3,7 @@
3
3
  *
4
4
  * Records a live trigger-eval result into a corpus manifest's `upstream`
5
5
  * block (the second half of the corpus-refresh DoD, ADR-061 §6). It does
6
- * NOT run the eval — `skill_trigger_eval.py`'s confirmation gate hard-aborts
6
+ * NOT run the eval — `skill_trigger_eval`'s confirmation gate hard-aborts
7
7
  * under automation by design, so the live run stays a human, interactive
8
8
  * step. This command is a *post-processor*: it reads the `EvalResult` JSON the
9
9
  * live run wrote (`--output`), enforces a precision/recall floor, and writes
@@ -36,7 +36,7 @@ import { logger } from '../log/logger.js';
36
36
  // Floors shared with the weekly canary rotation (ADR-118 §4) — values
37
37
  // unchanged, single source of truth.
38
38
  import { floor_for } from '../../scripts/_lib/trigger_eval_floors.js';
39
- /** Subset of `skill_trigger_eval.py`'s `EvalResult` we depend on. */
39
+ /** Subset of `skill_trigger_eval`'s `EvalResult` we depend on. */
40
40
  const EvalResultSchema = z.object({
41
41
  skill: z.string().optional(),
42
42
  model: z.string().optional(),
package/dist/cli/main.js CHANGED
@@ -83,7 +83,7 @@ async function main(rawArgv) {
83
83
  program
84
84
  .command('eval:record')
85
85
  .description('Record a live trigger-eval result into a corpus manifest (ADR-061 §6 refresh DoD)')
86
- .requiredOption('--eval-json <path>', 'EvalResult JSON written by skill_trigger_eval.py --output')
86
+ .requiredOption('--eval-json <path>', 'EvalResult JSON written by skill_trigger_eval --output')
87
87
  .requiredOption('--manifest <path>', 'corpus manifest.json to patch')
88
88
  .option('--min-recall <n>', 'Recall floor override (per-skill default otherwise)', (v) => Number.parseFloat(v))
89
89
  .option('--min-precision <n>', 'Precision floor override (per-skill default otherwise)', (v) => Number.parseFloat(v))
@@ -167,6 +167,16 @@ export const LITE_TOOLS = [
167
167
  },
168
168
  ];
169
169
  const LITE_TOOL_NAMES = new Set(LITE_TOOLS.map((t) => t.name));
170
+ /**
171
+ * Is this name one of the two tools this surface actually runs?
172
+ *
173
+ * Exported so the impure transport shell can classify a call's outcome without
174
+ * re-deriving the tool list — one reader of one fact, the same discipline
175
+ * `describeHostCapabilities` applies to the capability manifest.
176
+ */
177
+ export function isLiteTool(name) {
178
+ return LITE_TOOL_NAMES.has(name);
179
+ }
170
180
  /** Chars/4 over the served `tools/list` payload — the figure the cap is on. */
171
181
  export function liteToolsTokenCost() {
172
182
  return Math.round(JSON.stringify({ tools: LITE_TOOLS }).length / 4);
@@ -10,9 +10,16 @@
10
10
  * a single stray stdout byte breaks the JSON-RPC stream for the client.
11
11
  *
12
12
  * Notifications (no `id`) receive no response, per JSON-RPC 2.0.
13
+ *
14
+ * This shell is also where call telemetry is emitted (roadmap step 4.1):
15
+ * `dispatch` is pure — no I/O, no clock — and stays that way, so the one row
16
+ * per `tools/call` is written here, from the impure side, after the response
17
+ * has been produced. The recorder is default-off and never throws; see
18
+ * `./telemetry.js` for the gate and the closed host vocabulary.
13
19
  */
14
20
  import { createInterface } from 'node:readline';
15
- import { dispatch, rpcError, RPC_PARSE_ERROR, RPC_INVALID_REQUEST, } from './dispatch.js';
21
+ import { dispatch, isLiteTool, rpcError, RPC_PARSE_ERROR, RPC_INVALID_REQUEST, } from './dispatch.js';
22
+ import { HOST_UNKNOWN, normalizeHost, recordLiteCall } from './telemetry.js';
16
23
  function isNotification(value) {
17
24
  return typeof value === 'object' && value !== null && !('id' in value);
18
25
  }
@@ -24,6 +31,10 @@ function isNotification(value) {
24
31
  export function runStdioServer(tree, identity, opts = {}) {
25
32
  const input = opts.input ?? process.stdin;
26
33
  const output = opts.output ?? process.stdout;
34
+ // The client's own name, learned at `initialize` and resolved onto the
35
+ // closed host vocabulary. A client that calls a tool before initializing
36
+ // records `unknown` — which is the truth, not a guess.
37
+ let host = HOST_UNKNOWN;
27
38
  const write = (resp) => {
28
39
  output.write(`${JSON.stringify(resp)}\n`);
29
40
  };
@@ -51,7 +62,27 @@ export function runStdioServer(tree, identity, opts = {}) {
51
62
  write(rpcError(id, RPC_INVALID_REQUEST, 'Invalid Request'));
52
63
  return;
53
64
  }
54
- write(dispatch(tree, identity, parsed));
65
+ const req = parsed;
66
+ if (req.method === 'initialize') {
67
+ const p = (req.params ?? {});
68
+ const info = (p.clientInfo ?? {});
69
+ host = normalizeHost(info.name);
70
+ }
71
+ write(dispatch(tree, identity, req));
72
+ // Exactly one row per `tools/call` that named a tool. A call with
73
+ // no `name` is a malformed request, not a call to a tool, and has
74
+ // nothing to attribute a row to.
75
+ if (req.method === 'tools/call') {
76
+ const p = (req.params ?? {});
77
+ const name = typeof p.name === 'string' ? p.name : '';
78
+ recordLiteCall({
79
+ toolName: name,
80
+ isLiteTool: isLiteTool(name),
81
+ host,
82
+ consumerRoot: opts.consumerRoot,
83
+ settingsPath: opts.settingsPath,
84
+ });
85
+ }
55
86
  });
56
87
  rl.on('close', () => {
57
88
  // Drain any buffered stdout before resolving — on macOS a pipe write