@event4u/agent-config 8.9.0 → 8.11.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 (267) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/CHANGELOG.md +189 -0
  3. package/README.md +5 -5
  4. package/dist/agent-src/commands/challenge-me/vision.md +121 -32
  5. package/dist/agent-src/commands/challenge-me/with-docs.md +21 -8
  6. package/dist/agent-src/commands/commit/in-chunks.md +1 -1
  7. package/dist/agent-src/commands/commit.md +2 -1
  8. package/dist/agent-src/commands/cost/report.md +14 -3
  9. package/dist/agent-src/commands/feature/dev.md +4 -4
  10. package/dist/agent-src/commands/humanize.md +101 -0
  11. package/dist/agent-src/commands/jira-ticket.md +2 -2
  12. package/dist/agent-src/commands/memory/load.md +15 -0
  13. package/dist/agent-src/commands/optimize/project.md +12 -0
  14. package/dist/agent-src/commands/roadmap/create.md +12 -11
  15. package/dist/agent-src/commands/tests/create.md +29 -7
  16. package/dist/agent-src/commands/work.md +33 -0
  17. package/dist/agent-src/commands/worktree/cleanup.md +83 -0
  18. package/dist/agent-src/commands/worktree/create.md +64 -0
  19. package/dist/agent-src/commands/worktree/status.md +75 -0
  20. package/dist/agent-src/commands/worktree/verify.md +70 -0
  21. package/dist/agent-src/commands/worktree.md +85 -0
  22. package/dist/agent-src/contexts/execution/autonomy-mechanics.md +66 -0
  23. package/dist/agent-src/contexts/execution/orchestration-benchmark-gate.md +1 -0
  24. package/dist/agent-src/contexts/execution/orchestration-telemetry.md +91 -34
  25. package/dist/agent-src/contexts/execution/roadmap-process-loop.md +14 -0
  26. package/dist/agent-src/contexts/execution/roadmap-writing-source-derived.md +65 -0
  27. package/dist/agent-src/contexts/execution/subagent-modes-detail.md +84 -0
  28. package/dist/agent-src/rules/autonomous-execution.md +7 -37
  29. package/dist/agent-src/rules/broken-access-control.md +7 -45
  30. package/dist/agent-src/rules/code-comment-discipline.md +148 -0
  31. package/dist/agent-src/rules/context-hygiene.md +14 -0
  32. package/dist/agent-src/rules/delegation-policy.md +10 -8
  33. package/dist/agent-src/rules/design-fidelity.md +44 -0
  34. package/dist/agent-src/rules/git-history-discipline.md +4 -79
  35. package/dist/agent-src/rules/legal-safety-floor.md +11 -55
  36. package/dist/agent-src/rules/roadmap-progress-sync.md +5 -56
  37. package/dist/agent-src/rules/spreadsheet-source-quality.md +61 -0
  38. package/dist/agent-src/rules/think-before-action.md +10 -0
  39. package/dist/agent-src/rules/verify-before-complete.md +12 -0
  40. package/dist/agent-src/skills/accessibility-auditor/evals/evals.json +15 -0
  41. package/dist/agent-src/skills/agent-docs-writing/SKILL.md +13 -12
  42. package/dist/agent-src/skills/ai-council/SKILL.md +19 -9
  43. package/dist/agent-src/skills/analysis-skill-router/evals/evals.json +13 -0
  44. package/dist/agent-src/skills/api-design/SKILL.md +7 -5
  45. package/dist/agent-src/skills/api-testing/SKILL.md +16 -2
  46. package/dist/agent-src/skills/authz-review/SKILL.md +59 -2
  47. package/dist/agent-src/skills/blast-radius-analyzer/SKILL.md +1 -1
  48. package/dist/agent-src/skills/bug-analyzer/SKILL.md +19 -16
  49. package/dist/agent-src/skills/character-consistency/evals/evals.json +16 -0
  50. package/dist/agent-src/skills/code-refactoring/SKILL.md +9 -9
  51. package/dist/agent-src/skills/command-routing/evals/evals.json +14 -0
  52. package/dist/agent-src/skills/content-funnel-design/SKILL.md +2 -0
  53. package/dist/agent-src/skills/dashboard-design/evals/evals.json +16 -0
  54. package/dist/agent-src/skills/data-flow-mapper/SKILL.md +1 -1
  55. package/dist/agent-src/skills/dcf-modeling/evals/evals.json +20 -0
  56. package/dist/agent-src/skills/decision-record/evals/evals.json +20 -0
  57. package/dist/agent-src/skills/defense-in-depth/SKILL.md +2 -2
  58. package/dist/agent-src/skills/dependency-upgrade/SKILL.md +7 -7
  59. package/dist/agent-src/skills/design-intelligence/SKILL.md +8 -8
  60. package/dist/agent-src/skills/design-intelligence/evals/evals.json +14 -0
  61. package/dist/agent-src/skills/design-review/SKILL.md +29 -2
  62. package/dist/agent-src/skills/design-system-capture/SKILL.md +21 -0
  63. package/dist/agent-src/skills/design-system-capture/evals/evals.json +15 -0
  64. package/dist/agent-src/skills/developer-like-execution/evals/evals.json +16 -0
  65. package/dist/agent-src/skills/doc-coauthoring/SKILL.md +29 -0
  66. package/dist/agent-src/skills/doc-coauthoring/evals/evals.json +16 -0
  67. package/dist/agent-src/skills/editorial-calendar/SKILL.md +2 -0
  68. package/dist/agent-src/skills/editorial-calendar/evals/domain-truth.json +16 -0
  69. package/dist/agent-src/skills/editorial-calendar/evals/evals.json +16 -0
  70. package/dist/agent-src/skills/estimate-ticket/evals/evals.json +16 -0
  71. package/dist/agent-src/skills/existing-ui-audit/SKILL.md +30 -0
  72. package/dist/agent-src/skills/existing-ui-audit/evals/triggers.json +47 -0
  73. package/dist/agent-src/skills/fe-design/SKILL.md +43 -14
  74. package/dist/agent-src/skills/fe-design/evals/triggers.json +47 -0
  75. package/dist/agent-src/skills/forecasting/evals/evals.json +20 -0
  76. package/dist/agent-src/skills/fundraising-narrative/evals/domain-truth.json +16 -0
  77. package/dist/agent-src/skills/fundraising-narrative/evals/evals.json +16 -0
  78. package/dist/agent-src/skills/git-workflow/SKILL.md +86 -0
  79. package/dist/agent-src/skills/html-deck/SKILL.md +27 -0
  80. package/dist/agent-src/skills/humanizer/SKILL.md +147 -0
  81. package/dist/agent-src/skills/humanizer/data/patterns.md +190 -0
  82. package/dist/agent-src/skills/humanizer/evals/triggers.json +17 -0
  83. package/dist/agent-src/skills/iconography/SKILL.md +24 -0
  84. package/dist/agent-src/skills/incident-commander/evals/domain-truth.json +16 -0
  85. package/dist/agent-src/skills/incident-commander/evals/evals.json +20 -0
  86. package/dist/agent-src/skills/laravel/SKILL.md +14 -1
  87. package/dist/agent-src/skills/launch-readiness/SKILL.md +20 -19
  88. package/dist/agent-src/skills/launch-readiness/evals/evals.json +21 -0
  89. package/dist/agent-src/skills/legal-practice-profile/SKILL.md +126 -0
  90. package/dist/agent-src/skills/logging-monitoring/SKILL.md +1 -1
  91. package/dist/agent-src/skills/logging-monitoring/evals/evals.json +16 -0
  92. package/dist/agent-src/skills/mcp/SKILL.md +1 -1
  93. package/dist/agent-src/skills/memory-consolidation/SKILL.md +14 -1
  94. package/dist/agent-src/skills/messaging-architecture/evals/domain-truth.json +16 -0
  95. package/dist/agent-src/skills/messaging-architecture/evals/evals.json +16 -0
  96. package/dist/agent-src/skills/migration-architect/SKILL.md +2 -1
  97. package/dist/agent-src/skills/perf-feedback-craft/evals/evals.json +16 -0
  98. package/dist/agent-src/skills/performance/SKILL.md +1 -1
  99. package/dist/agent-src/skills/pest-testing/SKILL.md +1 -1
  100. package/dist/agent-src/skills/php-debugging/SKILL.md +17 -0
  101. package/dist/agent-src/skills/playwright-testing/SKILL.md +16 -0
  102. package/dist/agent-src/skills/prompt-engineering-patterns/evals/evals.json +16 -0
  103. package/dist/agent-src/skills/readme-writing/SKILL.md +2 -0
  104. package/dist/agent-src/skills/readme-writing-package/SKILL.md +2 -0
  105. package/dist/agent-src/skills/refine-prompt/evals/evals.json +16 -0
  106. package/dist/agent-src/skills/refine-ticket/evals/evals.json +16 -0
  107. package/dist/agent-src/skills/release-comms/SKILL.md +2 -0
  108. package/dist/agent-src/skills/release-comms/evals/evals.json +16 -0
  109. package/dist/agent-src/skills/rice-prioritization/evals/evals.json +20 -0
  110. package/dist/agent-src/skills/roadmap-writing/SKILL.md +22 -51
  111. package/dist/agent-src/skills/runway-cognition/evals/evals.json +20 -0
  112. package/dist/agent-src/skills/scenario-modeling/evals/evals.json +20 -0
  113. package/dist/agent-src/skills/security-audit/SKILL.md +6 -9
  114. package/dist/agent-src/skills/sentry-integration/SKILL.md +2 -2
  115. package/dist/agent-src/skills/skill-improvement-pipeline/SKILL.md +8 -0
  116. package/dist/agent-src/skills/source-discovery/SKILL.md +26 -24
  117. package/dist/agent-src/skills/spreadsheet-authoring/SKILL.md +98 -0
  118. package/dist/agent-src/skills/spreadsheet-authoring/evals/triggers.json +47 -0
  119. package/dist/agent-src/skills/subagent-orchestration/SKILL.md +55 -46
  120. package/dist/agent-src/skills/subagent-orchestration/evals/evals.json +42 -0
  121. package/dist/agent-src/skills/systematic-debugging/SKILL.md +10 -0
  122. package/dist/agent-src/skills/systematic-debugging/evals/evals.json +20 -0
  123. package/dist/agent-src/skills/test-case-discovery/SKILL.md +118 -0
  124. package/dist/agent-src/skills/test-case-discovery/evals/triggers.json +52 -0
  125. package/dist/agent-src/skills/test-driven-development/SKILL.md +32 -6
  126. package/dist/agent-src/skills/test-driven-development/evals/evals.json +16 -0
  127. package/dist/agent-src/skills/testing-anti-patterns/SKILL.md +7 -6
  128. package/dist/agent-src/skills/threat-modeling/evals/evals.json +21 -0
  129. package/dist/agent-src/skills/typography-system/evals/evals.json +14 -0
  130. package/dist/agent-src/skills/unit-economics-modeling/evals/evals.json +24 -0
  131. package/dist/agent-src/skills/using-git-worktrees/SKILL.md +10 -0
  132. package/dist/agent-src/skills/verify-completion-evidence/evals/evals.json +16 -0
  133. package/dist/agent-src/skills/vision-articulation/evals/evals.json +16 -0
  134. package/dist/agent-src/skills/voice-and-tone-design/evals/evals.json +16 -0
  135. package/dist/agent-src/skills/worktree-lifecycle/SKILL.md +187 -0
  136. package/dist/agent-src/skills/worktree-lifecycle/evals/evals.json +38 -0
  137. package/dist/agent-src/skills/worktree-lifecycle/evals/triggers.json +52 -0
  138. package/dist/agent-src/templates/agent-settings.md +3 -2
  139. package/dist/agent-src/templates/agents/agent-project-settings.example.yml +15 -1
  140. package/dist/agent-src/templates/roadmaps.md +14 -0
  141. package/dist/cli/commands/recordTriggerEval.js +4 -14
  142. package/dist/cli/commands/recordTriggerEval.js.map +1 -1
  143. package/dist/discovery/deprecation-report.md +1 -1
  144. package/dist/discovery/discovery-manifest.json +374 -88
  145. package/dist/discovery/discovery-manifest.json.sha256 +1 -1
  146. package/dist/discovery/discovery-manifest.summary.md +9 -9
  147. package/dist/discovery/orphan-report.md +1 -1
  148. package/dist/discovery/packs.json +30 -18
  149. package/dist/discovery/trust-report.md +5 -5
  150. package/dist/discovery/workspaces.json +31 -15
  151. package/dist/install/install.mjs +12 -7
  152. package/dist/mcp/registry-manifest.json +4 -4
  153. package/dist/router.json +1 -1
  154. package/dist/scripts/_lib/claude_settings_hooks.js +8 -0
  155. package/dist/scripts/_lib/claude_settings_hooks.js.map +1 -1
  156. package/dist/scripts/_lib/trigger_eval_floors.js +24 -0
  157. package/dist/scripts/_lib/trigger_eval_floors.js.map +1 -0
  158. package/dist/scripts/condense.js +12 -2
  159. package/dist/scripts/condense.js.map +1 -1
  160. package/dist/server/schemas/settings.js +5 -2
  161. package/dist/server/schemas/settings.js.map +1 -1
  162. package/docs/CLAIMS.md +24 -3
  163. package/docs/architecture.md +3 -3
  164. package/docs/benchmark.md +51 -0
  165. package/docs/catalog.md +23 -9
  166. package/docs/command-flows.md +7 -1
  167. package/docs/contracts/CHANGELOG-conventions.md +6 -0
  168. package/docs/contracts/ai-council-config.md +92 -0
  169. package/docs/contracts/command-clusters.md +1 -0
  170. package/docs/contracts/design-artifact-lifecycle.md +93 -0
  171. package/docs/contracts/design-artifact-verification.md +30 -1
  172. package/docs/contracts/evidence-based-pruning.md +1 -0
  173. package/docs/contracts/hook-architecture-v1.md +25 -0
  174. package/docs/contracts/kernel-membership.md +35 -0
  175. package/docs/contracts/measurement-baseline.md +1 -0
  176. package/docs/contracts/release-pr-gating.md +26 -0
  177. package/docs/contracts/release-sizing.md +107 -0
  178. package/docs/contracts/surface-agent-contracts.md +153 -0
  179. package/docs/contracts/write-engine.md +38 -1
  180. package/docs/customization.md +12 -0
  181. package/docs/decisions/ADR-002-kernel-bucket-overrides.md +11 -0
  182. package/docs/decisions/ADR-003-flat-cluster-subs-and-colon-syntax.md +11 -0
  183. package/docs/decisions/ADR-006-skill-tools-python-pilot.md +1 -1
  184. package/docs/decisions/ADR-028-root-layout.md +1 -1
  185. package/docs/decisions/ADR-038-canonical-settings-path.md +10 -0
  186. package/docs/decisions/ADR-092-defer-command-tier-alias-removal.md +29 -5
  187. package/docs/decisions/ADR-093-ai-council-config-user-global.md +1 -1
  188. package/docs/decisions/ADR-100-global-knowledge-card-sharing.md +7 -0
  189. package/docs/decisions/ADR-103-global-knowledge-default-off-until-measured.md +2 -2
  190. package/docs/decisions/ADR-105-automatic-subagent-orchestration.md +1 -1
  191. package/docs/decisions/ADR-114-commit-policy-iron-law-override.md +11 -0
  192. package/docs/decisions/ADR-115-command-cluster-consolidation-phase-4.md +1 -1
  193. package/docs/decisions/ADR-116-memory-tripwire-activation-path.md +11 -1
  194. package/docs/decisions/ADR-117-subagents-auto-default-on.md +2 -1
  195. package/docs/decisions/ADR-118-loop-engineering-boundaries.md +162 -0
  196. package/docs/decisions/ADR-119-global-knowledge-default-on.md +104 -0
  197. package/docs/decisions/ADR-200-python-to-typescript-migration.md +11 -1
  198. package/docs/decisions/ADR-rule-kernel-and-router.md +11 -0
  199. package/docs/decisions/INDEX.md +11 -9
  200. package/docs/design/cross-model-parity-eval.md +82 -0
  201. package/docs/design/length-neutral-judge-rerun.md +92 -0
  202. package/docs/distribution/consumer-matrix.md +96 -0
  203. package/docs/featured-skills.md +2 -2
  204. package/docs/getting-started-by-role.md +1 -1
  205. package/docs/getting-started.md +2 -2
  206. package/docs/governance-advantage.md +1 -1
  207. package/docs/guidelines/agent-infra/roadmap-progress-mechanics.md +80 -5
  208. package/docs/guidelines/code-clarity.md +106 -1
  209. package/docs/guidelines/design-antipatterns.md +20 -0
  210. package/docs/guidelines/php/php-coding-patterns.md +12 -2
  211. package/docs/proof.md +21 -15
  212. package/docs/release-runbook.md +3 -0
  213. package/docs/self-review-gate.md +51 -0
  214. package/docs/wedge/production-validator/production-validator.md +27 -0
  215. package/package.json +1 -1
  216. package/src/config/agent-settings.template.yml +29 -9
  217. package/src/scripts/_cli/cmd_doctor.ts +34 -1
  218. package/src/scripts/_lib/auto_dispatch.corpus.test.ts +87 -0
  219. package/src/scripts/_lib/claude_settings_hooks.ts +8 -0
  220. package/src/scripts/_lib/knowledge_global.ts +6 -2
  221. package/src/scripts/_lib/knowledge_global_redaction.ts +42 -4
  222. package/src/scripts/_lib/lexical_index.ts +15 -2
  223. package/src/scripts/_lib/orchestration_record.ts +27 -0
  224. package/src/scripts/_lib/orchestration_savings.ts +52 -0
  225. package/src/scripts/_lib/trigger_eval_floors.ts +31 -0
  226. package/src/scripts/ai_council/chairman.ts +90 -0
  227. package/src/scripts/ai_council/clients.ts +18 -3
  228. package/src/scripts/ai_council/config.ts +157 -0
  229. package/src/scripts/ai_council/debate_gates.ts +89 -0
  230. package/src/scripts/ai_council/orchestrator.ts +65 -2
  231. package/src/scripts/ai_council/prompts.ts +121 -11
  232. package/src/scripts/ai_council/replay.ts +17 -0
  233. package/src/scripts/ai_council/stance_tally.ts +184 -0
  234. package/src/scripts/ai_tells_rules.ts +433 -0
  235. package/src/scripts/bench_humanizer_eval.ts +240 -0
  236. package/src/scripts/build_discovery_manifest.ts +6 -0
  237. package/src/scripts/build_proof.ts +21 -9
  238. package/src/scripts/check_claims.ts +40 -0
  239. package/src/scripts/check_release_adjacent_health.ts +121 -0
  240. package/src/scripts/check_rule_invariants.ts +282 -0
  241. package/src/scripts/condense.ts +16 -2
  242. package/src/scripts/consumer_matrix.ts +392 -0
  243. package/src/scripts/council_cli.ts +147 -0
  244. package/src/scripts/detect_ai_tells.ts +312 -0
  245. package/src/scripts/generate_index.ts +3 -2
  246. package/src/scripts/install.ts +12 -0
  247. package/src/scripts/knowledge_global_cli.ts +1 -1
  248. package/src/scripts/lint_changelog_rollback.ts +293 -0
  249. package/src/scripts/lint_framework_leakage.ts +88 -7
  250. package/src/scripts/lint_framework_leakage_allowlist.json +59 -572
  251. package/src/scripts/lint_knowledge_scale.ts +30 -0
  252. package/src/scripts/lint_originality_shingles.ts +210 -0
  253. package/src/scripts/orchestration_record.ts +17 -0
  254. package/src/scripts/orchestration_savings_report.ts +36 -3
  255. package/src/scripts/schemas/agent-settings.schema.json +11 -0
  256. package/src/scripts/second_brain_retrieval.ts +44 -2
  257. package/src/scripts/self_review_gate.test.ts +65 -0
  258. package/src/scripts/self_review_gate.ts +298 -0
  259. package/src/scripts/skill_eval_coverage.ts +71 -3
  260. package/src/scripts/skill_linter.ts +1 -0
  261. package/src/scripts/trigger_eval_grandfather.json +223 -224
  262. package/src/scripts/trigger_eval_rotation.ts +344 -0
  263. package/src/scripts/update_counts.ts +5 -17
  264. package/src/scripts/worktree_cleanup_check.ts +263 -0
  265. package/src/server/schemas/settings.ts +8 -3
  266. package/src/scripts/analysis_freshness.ts +0 -362
  267. package/src/scripts/export_replay_corpus.ts +0 -208
@@ -0,0 +1,344 @@
1
+ #!/usr/bin/env tsx
2
+ /**
3
+ * trigger_eval_rotation — weekly live trigger-eval pass-rate rotation (ADR-118 §4).
4
+ *
5
+ * The ONE measure→adjust loop closure from `road-to-loop-engineering`: the
6
+ * weekly cross-model canary runs the live trigger eval over a deterministic
7
+ * rotating subset of the skills that carry `evals/triggers.json`, enforcing
8
+ * the shared per-domain precision/recall floors
9
+ * (`_lib/trigger_eval_floors.ts`). A floor breach fails the SCHEDULED job —
10
+ * the failure is the maintainer notification; PRs are never blocked by live
11
+ * results.
12
+ *
13
+ * Boundaries (deliberate):
14
+ * - The local interactive `skill_trigger_eval` CLI and its /dev/tty
15
+ * confirmation gate are untouched — this script is the CI-only path, and
16
+ * its live authorization derives exclusively from the key file the canary
17
+ * workflow materializes from repo secrets (same pattern as
18
+ * `cross_model_smoke.ts`). No env-var key fallback, no local bypass.
19
+ * - `--dry-run` (MockRouter, no key, no spend) exercises the plumbing only;
20
+ * floors are REPORTED but never fail the run — mock routing says nothing
21
+ * about real trigger accuracy.
22
+ * - Rotation is a pure function of (week index, batch size, sorted suite
23
+ * list): ~batch suites per week, wrapping so every suite is visited on a
24
+ * fixed cadence regardless of suite-count drift.
25
+ *
26
+ * Exit codes: 0 pass (always, in --dry-run) · 1 live floor breach ·
27
+ * 2 usage / IO error.
28
+ */
29
+ import * as fs from 'node:fs';
30
+ import * as path from 'node:path';
31
+ import { fileURLToPath, pathToFileURL } from 'node:url';
32
+
33
+ import {
34
+ DEFAULT_MODEL,
35
+ MockRouter,
36
+ Query,
37
+ compute_metrics,
38
+ load_skill_metas,
39
+ write_result,
40
+ type EvalResult,
41
+ type SkillMeta,
42
+ type TriggerRouter,
43
+ } from './skill_trigger_eval.js';
44
+ import { AnthropicFetchRouter, loadKeyFromFile } from './_lib/trigger_routers.js';
45
+ import { floor_for } from './_lib/trigger_eval_floors.js';
46
+
47
+ const _HERE = path.dirname(fileURLToPath(import.meta.url));
48
+ const REPO_ROOT = path.resolve(_HERE, '..', '..');
49
+ const SKILLS_DIR = path.join(REPO_ROOT, 'src', 'skills');
50
+ const DEFAULT_OUT_DIR = path.join(REPO_ROOT, 'internal', 'evals', 'results', 'rotation');
51
+ const DEFAULT_BATCH = 5;
52
+
53
+ /** Router that may expose an async route (fetch-based live routers). */
54
+ type AsyncCapableRouter = TriggerRouter & {
55
+ routeAsync?: (query: string, skills: SkillMeta[]) => Promise<[string[], number, number]>;
56
+ };
57
+
58
+ /** UTC week index since epoch — monotonic, deterministic rotation key. */
59
+ export function week_index(d: Date): number {
60
+ const utcDays = Math.floor(
61
+ Date.UTC(d.getUTCFullYear(), d.getUTCMonth(), d.getUTCDate()) / 86_400_000,
62
+ );
63
+ return Math.floor(utcDays / 7);
64
+ }
65
+
66
+ /**
67
+ * Deterministic rotation: batch consecutive suites starting at
68
+ * `(week * batch) mod total`, wrapping. Every suite is visited within
69
+ * ceil(total / batch) weeks; adding/removing suites shifts but never
70
+ * starves the cycle.
71
+ */
72
+ export function pick_rotation<T>(suites: readonly T[], week: number, batch: number): T[] {
73
+ if (suites.length === 0 || batch <= 0) {
74
+ return [];
75
+ }
76
+ const n = Math.min(batch, suites.length);
77
+ const total = suites.length;
78
+ const start = ((((week * batch) % total) + total) % total);
79
+ const picked: T[] = [];
80
+ for (let i = 0; i < n; i += 1) {
81
+ picked.push(suites[(start + i) % total] as T);
82
+ }
83
+ return picked;
84
+ }
85
+
86
+ /** Enumerate skills carrying `evals/triggers.json`, sorted for determinism. */
87
+ export function list_trigger_suites(skillsDir: string = SKILLS_DIR): string[] {
88
+ let entries: string[];
89
+ try {
90
+ entries = fs.readdirSync(skillsDir);
91
+ } catch {
92
+ return [];
93
+ }
94
+ return entries
95
+ .filter((name) => {
96
+ try {
97
+ return fs.statSync(path.join(skillsDir, name, 'evals', 'triggers.json')).isFile();
98
+ } catch {
99
+ return false;
100
+ }
101
+ })
102
+ .sort((a, b) => (a < b ? -1 : a > b ? 1 : 0));
103
+ }
104
+
105
+ /**
106
+ * Load a triggers.json in EITHER supported shape (mirrors
107
+ * `check_trigger_evals.ts`): a `queries` list of {q, trigger:bool}, or split
108
+ * `should_trigger` / `should_not_trigger` lists whose items are query strings
109
+ * or {q} objects. `load_triggers` from the pinned CLI only reads the first
110
+ * shape, so the rotation normalizes here instead of touching the pinned code.
111
+ */
112
+ export function load_triggers_any(p: string, fallbackSkill: string): [string, Query[]] {
113
+ const data = JSON.parse(fs.readFileSync(p, 'utf-8')) as Record<string, unknown>;
114
+ const skill = typeof data['skill'] === 'string' && data['skill'] ? (data['skill'] as string) : fallbackSkill;
115
+ const queries: Query[] = [];
116
+ if (Array.isArray(data['queries'])) {
117
+ for (const item of data['queries'] as Array<{ q?: unknown; trigger?: unknown }>) {
118
+ if (typeof item?.q === 'string' && item.q.trim()) {
119
+ queries.push(Query(item.q, Boolean(item.trigger)));
120
+ }
121
+ }
122
+ } else {
123
+ for (const [key, trigger] of [
124
+ ['should_trigger', true],
125
+ ['should_not_trigger', false],
126
+ ] as const) {
127
+ const items = data[key];
128
+ if (!Array.isArray(items)) continue;
129
+ for (const it of items) {
130
+ const q = typeof it === 'string' ? it : (it as { q?: unknown } | null)?.q;
131
+ if (typeof q === 'string' && q.trim()) {
132
+ queries.push(Query(q, trigger));
133
+ }
134
+ }
135
+ }
136
+ }
137
+ if (queries.length === 0) {
138
+ throw new Error(`${p}: no usable queries in either supported shape`);
139
+ }
140
+ return [skill, queries];
141
+ }
142
+
143
+ /** run_eval, but awaiting routeAsync when the router provides it. */
144
+ export async function run_eval_async(
145
+ skill_name: string,
146
+ queries: Query[],
147
+ router: AsyncCapableRouter,
148
+ skills: SkillMeta[],
149
+ model: string,
150
+ ): Promise<EvalResult> {
151
+ const result: EvalResult = {
152
+ skill: skill_name,
153
+ model,
154
+ timestamp: new Date().toISOString().replace(/\.\d{3}Z$/, '+00:00'),
155
+ router: router.name,
156
+ queries: [],
157
+ metrics: compute_metrics([]),
158
+ input_tokens: 0,
159
+ output_tokens: 0,
160
+ cost_usd_estimate: 0.0,
161
+ };
162
+ for (const q of queries) {
163
+ const [loaded, inTok, outTok] =
164
+ typeof router.routeAsync === 'function'
165
+ ? await router.routeAsync(q.q, skills)
166
+ : router.route(q.q, skills);
167
+ const observed = loaded.includes(skill_name);
168
+ result.queries.push({
169
+ q: q.q,
170
+ expected: q.trigger,
171
+ observed,
172
+ loaded_skills: [...loaded].sort(),
173
+ passed: observed === q.trigger,
174
+ });
175
+ result.input_tokens += inTok;
176
+ result.output_tokens += outTok;
177
+ }
178
+ result.metrics = compute_metrics(result.queries);
179
+ return result;
180
+ }
181
+
182
+ export interface RotationOptions {
183
+ week?: number;
184
+ batch?: number;
185
+ dryRun?: boolean;
186
+ outDir?: string;
187
+ model?: string;
188
+ /** Injectable router (tests). Default: MockRouter (dry) / AnthropicFetchRouter (live). */
189
+ router?: AsyncCapableRouter;
190
+ skillsDir?: string;
191
+ }
192
+
193
+ export interface RotationSuiteOutcome {
194
+ skill: string;
195
+ precision: number;
196
+ recall: number;
197
+ minPrecision: number;
198
+ minRecall: number;
199
+ passed: boolean;
200
+ resultPath: string;
201
+ }
202
+
203
+ export interface RotationSummary {
204
+ week: number;
205
+ batch: number;
206
+ total_suites: number;
207
+ dry_run: boolean;
208
+ outcomes: RotationSuiteOutcome[];
209
+ breaches: number;
210
+ }
211
+
212
+ export async function run_rotation(opts: RotationOptions = {}): Promise<RotationSummary> {
213
+ const dryRun = opts.dryRun ?? false;
214
+ const batch = opts.batch ?? DEFAULT_BATCH;
215
+ const week = opts.week ?? week_index(new Date());
216
+ const outDir = opts.outDir ?? DEFAULT_OUT_DIR;
217
+ const model = opts.model ?? DEFAULT_MODEL;
218
+ const skillsDir = opts.skillsDir ?? SKILLS_DIR;
219
+
220
+ const suites = list_trigger_suites(skillsDir);
221
+ const picked = pick_rotation(suites, week, batch);
222
+ const catalogue = load_skill_metas();
223
+
224
+ const router: AsyncCapableRouter =
225
+ opts.router ??
226
+ (dryRun
227
+ ? new MockRouter((q, skills) =>
228
+ skills.filter((s) => q.toLowerCase().includes(s.name)).map((s) => s.name),
229
+ )
230
+ : (new AnthropicFetchRouter({
231
+ apiKey: loadKeyFromFile('anthropic.key'),
232
+ }) as AsyncCapableRouter));
233
+
234
+ fs.mkdirSync(outDir, { recursive: true });
235
+
236
+ const outcomes: RotationSuiteOutcome[] = [];
237
+ for (const skill of picked) {
238
+ const triggersPath = path.join(skillsDir, skill, 'evals', 'triggers.json');
239
+ const [skillName, queries] = load_triggers_any(triggersPath, skill);
240
+ const result = await run_eval_async(skillName, queries, router, catalogue, model);
241
+ const floor = floor_for(skillName);
242
+ const passed =
243
+ result.metrics.precision >= floor.minPrecision &&
244
+ result.metrics.recall >= floor.minRecall;
245
+ const resultPath = path.join(outDir, `${skillName}.json`);
246
+ write_result(result, resultPath);
247
+ outcomes.push({
248
+ skill: skillName,
249
+ precision: result.metrics.precision,
250
+ recall: result.metrics.recall,
251
+ minPrecision: floor.minPrecision,
252
+ minRecall: floor.minRecall,
253
+ passed,
254
+ resultPath,
255
+ });
256
+ }
257
+
258
+ return {
259
+ week,
260
+ batch,
261
+ total_suites: suites.length,
262
+ dry_run: dryRun,
263
+ outcomes,
264
+ breaches: outcomes.filter((o) => !o.passed).length,
265
+ };
266
+ }
267
+
268
+ function parse_args(argv: string[]): RotationOptions & { help?: boolean } {
269
+ const out: RotationOptions & { help?: boolean } = {};
270
+ for (let i = 0; i < argv.length; i += 1) {
271
+ const a = argv[i] as string;
272
+ const take = (): string => {
273
+ const v = argv[++i];
274
+ if (v === undefined) {
275
+ process.stderr.write(`trigger_eval_rotation: error: argument ${a}: expected one argument\n`);
276
+ process.exit(2);
277
+ }
278
+ return v as string;
279
+ };
280
+ if (a === '--week') out.week = Number(take());
281
+ else if (a === '--batch') out.batch = Number(take());
282
+ else if (a === '--dry-run') out.dryRun = true;
283
+ else if (a === '--out-dir') out.outDir = path.resolve(take());
284
+ else if (a === '--model') out.model = take();
285
+ else if (a === '-h' || a === '--help') out.help = true;
286
+ else {
287
+ process.stderr.write(`trigger_eval_rotation: error: unrecognized arguments: ${a}\n`);
288
+ process.exit(2);
289
+ }
290
+ }
291
+ if (out.week !== undefined && !Number.isInteger(out.week)) {
292
+ process.stderr.write('trigger_eval_rotation: error: --week must be an integer\n');
293
+ process.exit(2);
294
+ }
295
+ if (out.batch !== undefined && (!Number.isInteger(out.batch) || out.batch <= 0)) {
296
+ process.stderr.write('trigger_eval_rotation: error: --batch must be a positive integer\n');
297
+ process.exit(2);
298
+ }
299
+ return out;
300
+ }
301
+
302
+ export async function main(argv?: string[]): Promise<number> {
303
+ const opts = parse_args(argv ?? process.argv.slice(2));
304
+ if (opts.help) {
305
+ process.stdout.write(
306
+ 'usage: trigger_eval_rotation [--week N] [--batch N] [--dry-run]\n' +
307
+ ' [--out-dir DIR] [--model MODEL]\n',
308
+ );
309
+ return 0;
310
+ }
311
+ let summary: RotationSummary;
312
+ try {
313
+ summary = await run_rotation(opts);
314
+ } catch (err) {
315
+ process.stderr.write(`❌ trigger_eval_rotation: ${err instanceof Error ? err.message : String(err)}\n`);
316
+ return 2;
317
+ }
318
+ process.stdout.write(
319
+ `trigger-eval rotation · week=${summary.week} · batch=${summary.batch} · ` +
320
+ `${summary.outcomes.length}/${summary.total_suites} suites this run` +
321
+ `${summary.dry_run ? ' · DRY-RUN (floors advisory)' : ''}\n`,
322
+ );
323
+ for (const o of summary.outcomes) {
324
+ const mark = o.passed ? '✅' : '❌';
325
+ process.stdout.write(
326
+ ` ${mark} ${o.skill} · precision ${o.precision} (floor ${o.minPrecision}) · ` +
327
+ `recall ${o.recall} (floor ${o.minRecall}) · ${o.resultPath}\n`,
328
+ );
329
+ }
330
+ if (summary.breaches > 0 && !summary.dry_run) {
331
+ process.stderr.write(
332
+ `❌ ${summary.breaches} suite(s) below floor — see result JSONs. ` +
333
+ 'Demotion revisit-if: ADR-118 (advisory if >~10% spurious over 50+ runs).\n',
334
+ );
335
+ return 1;
336
+ }
337
+ process.stdout.write('✅ rotation complete\n');
338
+ return 0;
339
+ }
340
+
341
+ const argvUrl = process.argv[1] === undefined ? '' : pathToFileURL(path.resolve(process.argv[1])).href;
342
+ if (import.meta.url === argvUrl) {
343
+ main().then((code) => process.exit(code));
344
+ }
@@ -158,26 +158,14 @@ export const TARGETS: ReadonlyArray<[string, ReadonlyArray<[string, string]>]> =
158
158
  [
159
159
  'README.md',
160
160
  [
161
- // Browse-content line: `… [all NNN commands](…)`. Raw file
162
- // count (the hero `Commands-N` badge carries the *active*
163
- // count and is owned by check_command_count_messaging.py).
164
- ['(\\[all )(\\d+)( commands\\])', 'commands'],
165
- // Hero badges: shields.io URLs `Skills-NNN-<color>` etc.
161
+ // Counts live only in the hero badges — shields.io URLs
162
+ // `Skills-NNN-<color>` etc. Prose counts were removed from the
163
+ // README on purpose (the badges carry the numbers); the Commands
164
+ // badge is owned by check_command_count_messaging.
166
165
  ['(/badge/Skills-)(\\d+)(-)', 'skills'],
167
166
  ['(/badge/Rules-)(\\d+)(-)', 'rules'],
168
167
  ['(/badge/Guidelines-)(\\d+)(-)', 'guidelines'],
169
168
  ['(/badge/Personas-)(\\d+)(-)', 'personas'],
170
- // Hero prose sentence: `**N skills, N commands, N governed rules**`
171
- // (road-to-truth-and-reference-hygiene Phase 1: prose counts are
172
- // generated, never hand-typed).
173
- ['(\\*\\*)(\\d+)( skills, )', 'skills'],
174
- ['( skills, )(\\d+)( commands, )', 'commands'],
175
- ['( commands, )(\\d+)( governed rules\\*\\*)', 'rules'],
176
- // Feature-list line: `— N skills + N commands, with a capability router`
177
- ['(— )(\\d+)( skills \\+ )', 'skills'],
178
- ['( skills \\+ )(\\d+)( commands,)', 'commands'],
179
- // Work-journey line: `You don't memorize N commands`
180
- ['(memorize )(\\d+)( commands)', 'commands'],
181
169
  ],
182
170
  ],
183
171
  [
@@ -186,7 +174,7 @@ export const TARGETS: ReadonlyArray<[string, ReadonlyArray<[string, string]>]> =
186
174
  // The three count claims — their numbers are generated from the
187
175
  // same source the gate checks, closing the ledger's own
188
176
  // "count-source binding" debt (B1.2).
189
- ['(- claim: )(\\d+)( skills \\(README hero)', 'skills'],
177
+ ['(- claim: )(\\d+)( skills\\.)', 'skills'],
190
178
  ['(- claim: )(\\d+)( commands\\.)', 'commands'],
191
179
  ['(- claim: )(\\d+)( governed rules\\.)', 'rules'],
192
180
  ],
@@ -0,0 +1,263 @@
1
+ #!/usr/bin/env tsx
2
+ /**
3
+ * Deterministic core of the /worktree cleanup + verify gates
4
+ * (road-to-fable-feedback-5 Phase 5; extracted from the prose in
5
+ * src/domains/engineering-base/worktree/cleanup/command.md so the refusal
6
+ * semantics are testable — the command doc calls this instead of ad-hoc
7
+ * shell).
8
+ *
9
+ * Modes:
10
+ * check <worktree-path> Cleanup gates for ONE worktree, in order:
11
+ * 1. detached HEAD → refuse
12
+ * 2. dirty (incl. untracked) → refuse
13
+ * 3. commits unique to branch → refuse
14
+ * Refusal is the SUCCESS path of the safety gate;
15
+ * this tool never removes anything.
16
+ * scope-overlap Scan every worktree's .worktree-scope.md and
17
+ * report pairwise `owns:` overlaps (the hazard
18
+ * /worktree status + verify must surface).
19
+ *
20
+ * Reachability counts ALL other refs — local branches, remotes, AND tags
21
+ * (a branch whose only other ref is a tag is still reachable elsewhere).
22
+ * Exit codes: 0 = allowed / no overlap, 1 = refused / overlap found,
23
+ * 2 = usage error, 3 = internal error.
24
+ */
25
+
26
+ import { execFileSync } from 'node:child_process';
27
+ import * as fs from 'node:fs';
28
+ import * as path from 'node:path';
29
+ import { fileURLToPath, pathToFileURL } from 'node:url';
30
+
31
+ export interface CheckResult {
32
+ allowed: boolean;
33
+ reasons: string[];
34
+ }
35
+
36
+ export interface ScopeOverlap {
37
+ a: { worktree: string; own: string };
38
+ b: { worktree: string; own: string };
39
+ }
40
+
41
+ function git(cwd: string, args: string[]): string {
42
+ return execFileSync('git', ['-C', cwd, ...args], {
43
+ encoding: 'utf-8',
44
+ stdio: ['ignore', 'pipe', 'pipe'],
45
+ });
46
+ }
47
+
48
+ function tryGit(cwd: string, args: string[]): string | null {
49
+ try {
50
+ return git(cwd, args);
51
+ } catch {
52
+ return null;
53
+ }
54
+ }
55
+
56
+ /** Refs (heads + remotes + tags) EXCLUDING the given branch's own head ref. */
57
+ function otherRefs(cwd: string, branch: string): string[] {
58
+ const out = git(cwd, ['for-each-ref', '--format=%(refname)']);
59
+ return out
60
+ .split('\n')
61
+ .map((l) => l.trim())
62
+ .filter((l) => l.length > 0 && l !== `refs/heads/${branch}`);
63
+ }
64
+
65
+ export function checkWorktree(worktreePath: string): CheckResult {
66
+ const reasons: string[] = [];
67
+ if (!fs.existsSync(worktreePath)) {
68
+ return { allowed: false, reasons: [`worktree path does not exist: ${worktreePath}`] };
69
+ }
70
+
71
+ const headRef = tryGit(worktreePath, ['symbolic-ref', '-q', 'HEAD']);
72
+ if (headRef === null) {
73
+ reasons.push(
74
+ 'detached HEAD — no branch to judge reachability for; resolve the state manually before cleanup',
75
+ );
76
+ return { allowed: false, reasons };
77
+ }
78
+ const branch = headRef.trim().replace(/^refs\/heads\//, '');
79
+
80
+ const status = git(worktreePath, ['status', '--porcelain']);
81
+ if (status.trim().length > 0) {
82
+ const files = status
83
+ .trimEnd()
84
+ .split('\n')
85
+ .map((l) => ` ${l}`)
86
+ .join('\n');
87
+ reasons.push(`unsaved work (dirty or untracked files count as work):\n${files}`);
88
+ }
89
+
90
+ const others = otherRefs(worktreePath, branch);
91
+ const revListArgs = ['rev-list', '--oneline', branch, '--not', ...others];
92
+ const unique = git(worktreePath, revListArgs).trimEnd();
93
+ if (unique.length > 0) {
94
+ const commits = unique
95
+ .split('\n')
96
+ .map((l) => ` ${l}`)
97
+ .join('\n');
98
+ reasons.push(
99
+ `commits on '${branch}' reachable from NO other ref (branch, remote, or tag):\n${commits}`,
100
+ );
101
+ }
102
+
103
+ return { allowed: reasons.length === 0, reasons };
104
+ }
105
+
106
+ interface WorktreeEntry {
107
+ path: string;
108
+ branch: string | null;
109
+ }
110
+
111
+ export function listWorktrees(repoPath: string): WorktreeEntry[] {
112
+ const out = git(repoPath, ['worktree', 'list', '--porcelain']);
113
+ const entries: WorktreeEntry[] = [];
114
+ let current: Partial<WorktreeEntry> = {};
115
+ for (const line of out.split('\n')) {
116
+ if (line.startsWith('worktree ')) {
117
+ if (current.path !== undefined) {
118
+ entries.push({ path: current.path, branch: current.branch ?? null });
119
+ }
120
+ current = { path: line.slice('worktree '.length) };
121
+ } else if (line.startsWith('branch ')) {
122
+ current.branch = line.slice('branch '.length).replace(/^refs\/heads\//, '');
123
+ } else if (line === 'detached') {
124
+ current.branch = null;
125
+ }
126
+ }
127
+ if (current.path !== undefined) {
128
+ entries.push({ path: current.path, branch: current.branch ?? null });
129
+ }
130
+ return entries;
131
+ }
132
+
133
+ /** Non-glob prefix of an `owns:` entry (`src/middleware/**` → `src/middleware/`). */
134
+ function globPrefix(own: string): string {
135
+ const starIdx = own.indexOf('*');
136
+ const prefix = starIdx === -1 ? own : own.slice(0, starIdx);
137
+ return prefix.replace(/\/+$/, '') + '/';
138
+ }
139
+
140
+ /** Two owns entries overlap when one's non-glob prefix contains the other's. */
141
+ export function ownsOverlap(a: string, b: string): boolean {
142
+ const pa = globPrefix(a);
143
+ const pb = globPrefix(b);
144
+ return pa.startsWith(pb) || pb.startsWith(pa);
145
+ }
146
+
147
+ function readOwns(worktreePath: string): string[] {
148
+ const p = path.join(worktreePath, '.worktree-scope.md');
149
+ if (!fs.existsSync(p)) return [];
150
+ const text = fs.readFileSync(p, 'utf-8');
151
+ // owns: entries are a YAML list under an `owns:` key (see
152
+ // worktree-lifecycle § Scope lock). Tolerant line parser — the scope
153
+ // note is hand-written, often inside a fenced block.
154
+ const owns: string[] = [];
155
+ let inOwns = false;
156
+ for (const raw of text.split('\n')) {
157
+ const line = raw.trim();
158
+ if (/^-?\s*owns:\s*$/.test(line)) {
159
+ inOwns = true;
160
+ continue;
161
+ }
162
+ if (inOwns) {
163
+ const m = line.match(/^-\s+(.+)$/);
164
+ if (m) {
165
+ owns.push(m[1]!.trim());
166
+ } else if (line.length > 0 && !line.startsWith('#')) {
167
+ inOwns = false;
168
+ }
169
+ }
170
+ }
171
+ return owns;
172
+ }
173
+
174
+ export function findScopeOverlaps(repoPath: string): ScopeOverlap[] {
175
+ const entries = listWorktrees(repoPath).filter((e) => fs.existsSync(e.path));
176
+ const scoped = entries
177
+ .map((e) => ({ worktree: e.path, owns: readOwns(e.path) }))
178
+ .filter((e) => e.owns.length > 0);
179
+ const overlaps: ScopeOverlap[] = [];
180
+ for (let i = 0; i < scoped.length; i++) {
181
+ for (let j = i + 1; j < scoped.length; j++) {
182
+ for (const a of scoped[i]!.owns) {
183
+ for (const b of scoped[j]!.owns) {
184
+ if (ownsOverlap(a, b)) {
185
+ overlaps.push({
186
+ a: { worktree: scoped[i]!.worktree, own: a },
187
+ b: { worktree: scoped[j]!.worktree, own: b },
188
+ });
189
+ }
190
+ }
191
+ }
192
+ }
193
+ }
194
+ return overlaps;
195
+ }
196
+
197
+ function usage(): never {
198
+ process.stderr.write(
199
+ 'usage: worktree_cleanup_check check <worktree-path> | scope-overlap [repo-path]\n',
200
+ );
201
+ process.exit(2);
202
+ }
203
+
204
+ export function main(argv: readonly string[] = process.argv.slice(2)): number {
205
+ const mode = argv[0];
206
+ if (mode === 'check') {
207
+ const p = argv[1];
208
+ if (p === undefined) usage();
209
+ const r = checkWorktree(path.resolve(p));
210
+ if (r.allowed) {
211
+ process.stdout.write(`✅ cleanup allowed: ${p}\n`);
212
+ return 0;
213
+ }
214
+ process.stdout.write(`⛔ cleanup refused: ${p}\n`);
215
+ for (const reason of r.reasons) {
216
+ process.stdout.write(` - ${reason}\n`);
217
+ }
218
+ process.stdout.write(
219
+ '\nRefusal is the safety gate working — commit/stash the work or merge/preserve ' +
220
+ 'the unique commits, then re-run. Never bypass with --force or branch -D.\n',
221
+ );
222
+ return 1;
223
+ }
224
+ if (mode === 'scope-overlap') {
225
+ const repo = path.resolve(argv[1] ?? '.');
226
+ const overlaps = findScopeOverlaps(repo);
227
+ if (overlaps.length === 0) {
228
+ process.stdout.write('✅ no scope-lock overlaps across live worktrees.\n');
229
+ return 0;
230
+ }
231
+ process.stdout.write(`⚠️ ${overlaps.length} scope-lock overlap(s):\n`);
232
+ for (const o of overlaps) {
233
+ process.stdout.write(
234
+ ` - ${o.a.worktree} owns '${o.a.own}' × ${o.b.worktree} owns '${o.b.own}'\n`,
235
+ );
236
+ }
237
+ return 1;
238
+ }
239
+ usage();
240
+ }
241
+
242
+ function _isCliEntry(): boolean {
243
+ if (process.argv[1] === undefined) return false;
244
+ const argvUrl = pathToFileURL(path.resolve(process.argv[1])).href;
245
+ if (import.meta.url === argvUrl) return true;
246
+ try {
247
+ return (
248
+ fs.realpathSync(fileURLToPath(import.meta.url)) ===
249
+ fs.realpathSync(path.resolve(process.argv[1]))
250
+ );
251
+ } catch {
252
+ return false;
253
+ }
254
+ }
255
+
256
+ if (_isCliEntry()) {
257
+ try {
258
+ process.exit(main());
259
+ } catch (e) {
260
+ process.stderr.write(`Internal error: ${e instanceof Error ? e.message : String(e)}\n`);
261
+ process.exit(3);
262
+ }
263
+ }
@@ -208,6 +208,11 @@ export const settingsSchema = z.object({
208
208
  'Whether skills marked token_budget_class: rich may load in full (exempt from telegraph-speak + thin-projector trimming), consumed by the token-budget-discipline rule. on = allowed (default); off = fall back to standard condensed behavior; ask = surface an estimated token delta (tokens, not dollars) and ask once per session before loading.',
209
209
  ),
210
210
  }).default({ rich_skills: 'on' }),
211
+ code_style: z.object({
212
+ docblocks: z.enum(['minimal', 'full']).default('minimal').describe(
213
+ 'Consumed by the code-comment-discipline rule. minimal (default) = no signature-mirroring docblocks; docblocks only for machine-relevant precision (generics, array shapes) or genuine why-context. full = the exported public surface of a library package may carry one-line summary docblocks; the redundancy ban still holds.',
214
+ ),
215
+ }).default({ docblocks: 'minimal' }),
211
216
  reasoning: z.object({
212
217
  enabled: z.boolean().default(true).describe(
213
218
  'Master switch for the Reasoning Discipline Protocol (RDP). false = the whole layer is inert (zero overhead).',
@@ -331,10 +336,10 @@ export const settingsSchema = z.object({
331
336
  }),
332
337
  knowledge: z.object({
333
338
  global_sharing: z.object({
334
- enabled: z.boolean().default(false).describe(
335
- 'Master switch for the file-first global knowledge-card store (ADR-100, default-off per ADR-101 / Evidence v2 Phase 0 until cross-project reuse is measured). User-global setting — keep in ~/.event4u/agent-config/agent-settings.yml. false fully no-ops the layer; project-local cards (v1) are unaffected.',
339
+ enabled: z.boolean().default(true).describe(
340
+ 'Master switch for the file-first global knowledge-card store (ADR-100; default-ON per ADR-119, the validated bounded-downside flip superseding ADR-103 — write-time redaction incl. hidden-unicode hardening, narrowest tier default, pre-registered demotion trigger). User-global setting — keep in ~/.event4u/agent-config/agent-settings.yml. false fully no-ops the layer (single-key revert); project-local cards (v1) are unaffected.',
336
341
  ),
337
- allowed_tiers: z.array(z.string()).default(['public', 'vendor']).describe(
342
+ allowed_tiers: z.array(z.string()).default(['public']).describe(
338
343
  'Origin tiers auto-eligible to cross a project boundary. proprietary is manual-only regardless (the gate hard-codes it), so an in-house schema never auto-shares.',
339
344
  ),
340
345
  redaction: z.object({