@event4u/agent-config 9.16.0 → 9.18.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 (227) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/AGENTS.md +1 -1
  3. package/CHANGELOG.md +80 -130
  4. package/README.md +1 -1
  5. package/dist/agent-src/commands/agent-handoff.md +100 -48
  6. package/dist/agent-src/commands/challenge-me/vision.md +5 -0
  7. package/dist/agent-src/commands/commit.md +1 -1
  8. package/dist/agent-src/commands/condense.md +19 -64
  9. package/dist/agent-src/commands/evals/agent-handoff.json +13 -1
  10. package/dist/agent-src/commands/evals/rule-compliance-audit.json +46 -0
  11. package/dist/agent-src/commands/feature/plan.md +20 -0
  12. package/dist/agent-src/commands/feature/roadmap.md +19 -0
  13. package/dist/agent-src/commands/memory/promote.md +14 -1
  14. package/dist/agent-src/commands/mode.md +9 -5
  15. package/dist/agent-src/commands/optimize/deep.md +72 -11
  16. package/dist/agent-src/commands/pr/create.md +29 -0
  17. package/dist/agent-src/commands/roadmap/create.md +29 -0
  18. package/dist/agent-src/commands/rule-compliance-audit.md +28 -5
  19. package/dist/agent-src/contexts/authority/commit-mechanics.md +5 -5
  20. package/dist/agent-src/contexts/communication/rules-auto/guidelines-mechanics.md +1 -0
  21. package/dist/agent-src/contexts/communication/rules-auto/skill-quality-mechanics.md +1 -1
  22. package/dist/agent-src/contexts/communication/rules-auto/think-before-action-mechanics.md +6 -1
  23. package/dist/agent-src/contexts/communication/rules-auto/token-efficiency-mechanics.md +4 -1
  24. package/dist/agent-src/contexts/execution/autonomy-mechanics.md +27 -1
  25. package/dist/agent-src/contexts/execution/plan-confidence-gate.md +133 -0
  26. package/dist/agent-src/contexts/execution/roadmap-process-loop.md +5 -2
  27. package/dist/agent-src/profiles/agency.yml +3 -3
  28. package/dist/agent-src/profiles/content_creator.yml +2 -2
  29. package/dist/agent-src/profiles/developer.yml +2 -2
  30. package/dist/agent-src/profiles/finance.yml +1 -1
  31. package/dist/agent-src/profiles/founder.yml +2 -2
  32. package/dist/agent-src/profiles/ops.yml +2 -2
  33. package/dist/agent-src/rules/active-remediation.md +2 -0
  34. package/dist/agent-src/rules/architecture.md +4 -2
  35. package/dist/agent-src/rules/augment-edit-discipline.md +2 -0
  36. package/dist/agent-src/rules/autonomous-execution.md +1 -1
  37. package/dist/agent-src/rules/brand-consistency.md +10 -62
  38. package/dist/agent-src/rules/brand-source-of-truth.md +62 -11
  39. package/dist/agent-src/rules/broken-access-control.md +4 -0
  40. package/dist/agent-src/rules/cli-output-handling.md +5 -0
  41. package/dist/agent-src/rules/code-comment-discipline.md +3 -0
  42. package/dist/agent-src/rules/commit-conventions.md +2 -0
  43. package/dist/agent-src/rules/context-hygiene.md +3 -2
  44. package/dist/agent-src/rules/cross-source-consistency.md +3 -0
  45. package/dist/agent-src/rules/design-fidelity.md +2 -0
  46. package/dist/agent-src/rules/doc-screenshot-hygiene.md +2 -0
  47. package/dist/agent-src/rules/docker-commands.md +4 -0
  48. package/dist/agent-src/rules/domain-adoption-policy.md +2 -0
  49. package/dist/agent-src/rules/domain-safety-disclaimer.md +5 -2
  50. package/dist/agent-src/rules/domain-safety-pii.md +1 -1
  51. package/dist/agent-src/rules/engineering-safety-floor.md +2 -0
  52. package/dist/agent-src/rules/framework-neutrality-in-generic-skills.md +9 -0
  53. package/dist/agent-src/rules/image-likeness-and-rights.md +4 -0
  54. package/dist/agent-src/rules/improve-before-implement.md +4 -0
  55. package/dist/agent-src/rules/laravel-routing.md +4 -0
  56. package/dist/agent-src/rules/legal-safety-floor.md +3 -0
  57. package/dist/agent-src/rules/lethal-trifecta-guard.md +3 -0
  58. package/dist/agent-src/rules/media-governance-routing.md +5 -0
  59. package/dist/agent-src/rules/minimal-safe-diff.md +2 -0
  60. package/dist/agent-src/rules/no-roadmap-references.md +2 -0
  61. package/dist/agent-src/rules/notes-first-reasoning.md +2 -0
  62. package/dist/agent-src/rules/output-discipline.md +9 -2
  63. package/dist/agent-src/rules/php-coding.md +2 -0
  64. package/dist/agent-src/rules/prefer-enums-over-literals.md +3 -0
  65. package/dist/agent-src/rules/provider-lifecycle-discipline.md +5 -0
  66. package/dist/agent-src/rules/roadmap-ci-steps-policy.md +2 -0
  67. package/dist/agent-src/rules/roadmap-progress-sync.md +2 -0
  68. package/dist/agent-src/rules/rule-type-governance.md +2 -0
  69. package/dist/agent-src/rules/scale-discipline.md +3 -0
  70. package/dist/agent-src/rules/secret-vcs-guard.md +2 -0
  71. package/dist/agent-src/rules/security-sensitive-stop.md +9 -2
  72. package/dist/agent-src/rules/senior-engineering-discipline.md +6 -0
  73. package/dist/agent-src/rules/skill-quality.md +2 -0
  74. package/dist/agent-src/rules/source-confidentiality.md +4 -0
  75. package/dist/agent-src/rules/source-discovery-gate.md +3 -0
  76. package/dist/agent-src/rules/source-of-truth.md +2 -0
  77. package/dist/agent-src/rules/strategy-safety-floor.md +2 -0
  78. package/dist/agent-src/rules/think-before-action.md +5 -0
  79. package/dist/agent-src/rules/token-budget-discipline.md +2 -2
  80. package/dist/agent-src/rules/ui-audit-gate.md +2 -0
  81. package/dist/agent-src/rules/user-interaction.md +3 -4
  82. package/dist/agent-src/skills/build-buy-partner/SKILL.md +1 -1
  83. package/dist/agent-src/skills/copilot-agents-optimization/SKILL.md +4 -4
  84. package/dist/agent-src/skills/copilot-config/SKILL.md +1 -1
  85. package/dist/agent-src/skills/deep-reading-analyst/SKILL.md +7 -4
  86. package/dist/agent-src/skills/description-assist/SKILL.md +1 -1
  87. package/dist/agent-src/skills/design-intelligence/SKILL.md +6 -180
  88. package/dist/agent-src/skills/design-intelligence/references/context-and-registers.md +100 -0
  89. package/dist/agent-src/skills/design-intelligence/references/integration-mapping.md +91 -0
  90. package/dist/agent-src/skills/design-review/SKILL.md +4 -141
  91. package/dist/agent-src/skills/design-review/references/review-communication.md +92 -0
  92. package/dist/agent-src/skills/design-review/references/verification-automation.md +61 -0
  93. package/dist/agent-src/skills/existing-ui-audit/SKILL.md +8 -27
  94. package/dist/agent-src/skills/existing-ui-audit/references/anti-slop-cross-reference.md +31 -0
  95. package/dist/agent-src/skills/fe-design/SKILL.md +4 -289
  96. package/dist/agent-src/skills/fe-design/references/design-patterns.md +227 -0
  97. package/dist/agent-src/skills/fe-design/references/design-read-and-memory.md +75 -0
  98. package/dist/agent-src/skills/market-entry-analysis/SKILL.md +1 -1
  99. package/dist/agent-src/skills/persona-writing/SKILL.md +2 -2
  100. package/dist/agent-src/skills/readme-reviewer/SKILL.md +2 -1
  101. package/dist/agent-src/skills/roadmap-management/SKILL.md +10 -0
  102. package/dist/agent-src/skills/roadmap-writing/SKILL.md +12 -1
  103. package/dist/agent-src/skills/rule-writing/SKILL.md +4 -2
  104. package/dist/agent-src/skills/skill-reviewer/SKILL.md +1 -1
  105. package/dist/agent-src/skills/skill-writing/SKILL.md +2 -2
  106. package/dist/agent-src/skills/tailwind-engineer/SKILL.md +3 -1
  107. package/dist/agent-src/skills/token-optimizer/SKILL.md +2 -2
  108. package/dist/agent-src/skills/ui-component-architect/SKILL.md +6 -3
  109. package/dist/agent-src/templates/agent-settings.md +13 -15
  110. package/dist/agent-src/templates/agents/agent-project-settings.example.yml +6 -2
  111. package/dist/agent-src/templates/persona.md +2 -2
  112. package/dist/agent-src/templates/roadmaps.md +13 -1
  113. package/dist/agent-src/templates/rule.md +1 -1
  114. package/dist/agent-src/templates/scripts/work_engine/directives/ui/_fix_lane.ts +52 -0
  115. package/dist/agent-src/templates/scripts/work_engine/directives/ui/audit.ts +8 -0
  116. package/dist/agent-src/templates/scripts/work_engine/directives/ui/design.ts +6 -0
  117. package/dist/agent-src/templates/scripts/work_engine/intent/classify.ts +56 -4
  118. package/dist/agent-src/templates/skill.md +1 -1
  119. package/dist/cli/registry.js +3 -0
  120. package/dist/cli/registry.js.map +1 -1
  121. package/dist/cli-delegate/chunk-AIIUU73N.js +2040 -0
  122. package/dist/cli-delegate/{chunk-ZXXJLL5K.js → chunk-BSLUHV2F.js} +127 -1487
  123. package/dist/cli-delegate/{chunk-H574KOUH.js → chunk-ELMFOQPW.js} +35 -1
  124. package/dist/cli-delegate/chunk-LF2KKJ4Q.js +183 -0
  125. package/dist/cli-delegate/chunk-VSYT7OHO.js +1382 -0
  126. package/dist/cli-delegate/chunk-XGA2NTU4.js +3727 -0
  127. package/dist/cli-delegate/cmd_conformance.js +4 -3
  128. package/dist/cli-delegate/cmd_converge.js +3 -3
  129. package/dist/cli-delegate/cmd_doctor.js +3 -2
  130. package/dist/cli-delegate/cmd_explain.js +36 -36
  131. package/dist/cli-delegate/cmd_export.js +4 -4
  132. package/dist/cli-delegate/cmd_handoff.js +1169 -0
  133. package/dist/cli-delegate/cmd_memory_get.js +35 -3754
  134. package/dist/cli-delegate/cmd_migrate.js +2 -2
  135. package/dist/cli-delegate/cmd_prune.js +4 -4
  136. package/dist/cli-delegate/cmd_route_audit.js +249 -0
  137. package/dist/cli-delegate/cmd_route_explain.js +285 -0
  138. package/dist/cli-delegate/cmd_settings_migrate.js +4 -4
  139. package/dist/cli-delegate/cmd_sync.js +4 -4
  140. package/dist/cli-delegate/cmd_uninstall.js +4 -4
  141. package/dist/cli-delegate/cmd_update.js +3 -3
  142. package/dist/cli-delegate/cmd_upgrade.js +3 -3
  143. package/dist/cli-delegate/cmd_validate.js +4 -4
  144. package/dist/cli-delegate/cmd_versions.js +3 -3
  145. package/dist/discovery/deprecation-report.md +1 -1
  146. package/dist/discovery/discovery-manifest.json +84 -84
  147. package/dist/discovery/discovery-manifest.json.sha256 +1 -1
  148. package/dist/discovery/discovery-manifest.summary.md +1 -1
  149. package/dist/discovery/orphan-report.md +1 -1
  150. package/dist/discovery/packs.json +2 -2
  151. package/dist/discovery/trust-report.md +1 -1
  152. package/dist/discovery/workspaces.json +2 -2
  153. package/dist/hooks/dispatch.js +602 -507
  154. package/dist/install/install.mjs +34 -0
  155. package/dist/mcp/awesome-mcp-servers.row.md +1 -1
  156. package/dist/mcp/mcp-cloudflare-catalogue.json +2 -2
  157. package/dist/mcp/registry-manifest.json +4 -4
  158. package/dist/mcp/server.json +2 -2
  159. package/dist/mcp/server.mjs +25 -3
  160. package/dist/router.json +1 -1
  161. package/dist/scripts/_lib/agent_src.js +39 -6
  162. package/dist/scripts/_lib/agent_src.js.map +1 -1
  163. package/dist/server/schemas/settings.js +25 -0
  164. package/dist/server/schemas/settings.js.map +1 -1
  165. package/docs/guidelines/abstraction-thresholds.md +53 -0
  166. package/docs/guidelines/agent-infra/agent-interaction-and-decision-quality.md +1 -1
  167. package/docs/guidelines/agent-infra/asking-and-brevity-examples.md +7 -0
  168. package/docs/guidelines/agent-infra/context-hygiene-mechanics.md +2 -2
  169. package/docs/guidelines/agent-infra/minimal-safe-diff-mechanics.md +5 -3
  170. package/docs/guidelines/agent-infra/size-and-scope.md +8 -2
  171. package/docs/guidelines/component-oriented-and-oop-development.md +11 -6
  172. package/docs/guidelines/docs/readme-size-and-splitting.md +5 -2
  173. package/llms.txt +1 -0
  174. package/package.json +1 -1
  175. package/src/agent-src/templates/scripts/work_engine/directives/ui/_fix_lane.ts +52 -0
  176. package/src/agent-src/templates/scripts/work_engine/directives/ui/audit.ts +8 -0
  177. package/src/agent-src/templates/scripts/work_engine/directives/ui/design.ts +6 -0
  178. package/src/agent-src/templates/scripts/work_engine/intent/classify.ts +56 -4
  179. package/src/config/agent-settings.template.yml +68 -0
  180. package/src/config/evaluator-budgets.json +8 -11
  181. package/src/config/gate-coverage.yml +70 -0
  182. package/src/scripts/_cli/cmd_explain.ts +41 -36
  183. package/src/scripts/_cli/cmd_handoff.ts +405 -0
  184. package/src/scripts/_cli/cmd_route_audit.ts +290 -0
  185. package/src/scripts/_cli/cmd_route_explain.ts +269 -0
  186. package/src/scripts/_cli/handoff_generate.ts +453 -0
  187. package/src/scripts/_cli/handoff_sessions.ts +435 -0
  188. package/src/scripts/_dispatch.bash +32 -0
  189. package/src/scripts/_lib/agent_src.ts +36 -6
  190. package/src/scripts/_lib/git_env.ts +40 -0
  191. package/src/scripts/_lib/md_table.ts +27 -0
  192. package/src/scripts/_lib/planning_settings.ts +43 -0
  193. package/src/scripts/_lib/release_material.ts +156 -0
  194. package/src/scripts/_lib/router_match.ts +301 -0
  195. package/src/scripts/annotate_r1_outcomes.ts +258 -0
  196. package/src/scripts/check_completion_review.ts +1140 -0
  197. package/src/scripts/check_condensation.ts +2 -1
  198. package/src/scripts/check_finding_dispositions.ts +278 -0
  199. package/src/scripts/check_pack_size.ts +4 -3
  200. package/src/scripts/check_references.ts +1 -0
  201. package/src/scripts/check_release_highlights.ts +300 -0
  202. package/src/scripts/check_release_pr_shape.ts +4 -0
  203. package/src/scripts/check_release_surface_equality.ts +132 -0
  204. package/src/scripts/check_reply_consistency.ts +187 -138
  205. package/src/scripts/compile_router.ts +20 -3
  206. package/src/scripts/dispatch_r2_reviewer.ts +968 -0
  207. package/src/scripts/eval_ui_triviality.ts +156 -0
  208. package/src/scripts/handoff_context_hook.ts +148 -0
  209. package/src/scripts/hook_manifest.yaml +17 -7
  210. package/src/scripts/hooks/concern_registry.ts +2 -0
  211. package/src/scripts/lint_abstraction_thresholds.ts +327 -0
  212. package/src/scripts/lint_framework_leakage_allowlist.json +2 -2
  213. package/src/scripts/lint_plan_risk_register.ts +931 -0
  214. package/src/scripts/lint_profile_personas.ts +153 -0
  215. package/src/scripts/lint_token_budget_discipline.ts +148 -0
  216. package/src/scripts/lint_trigger_collisions.ts +257 -0
  217. package/src/scripts/release.ts +156 -32
  218. package/src/scripts/release_drill.ts +491 -0
  219. package/src/scripts/router_telemetry.ts +16 -290
  220. package/src/scripts/schemas/command.schema.json +33 -0
  221. package/src/scripts/schemas/rule.schema.json +10 -0
  222. package/src/scripts/self_review_gate.ts +41 -3
  223. package/src/scripts/skill_linter.ts +3 -1
  224. package/src/scripts/smoke/router.sh +1 -1
  225. package/src/scripts/update_counts.ts +60 -12
  226. package/src/server/schemas/settings.ts +41 -0
  227. package/dist/cli-delegate/{chunk-XX6TOPD7.js → chunk-WR6GTRHJ.js} +3 -3
@@ -0,0 +1,327 @@
1
+ #!/usr/bin/env tsx
2
+ /**
3
+ * Abstraction-threshold drift gate (ADR-213).
4
+ *
5
+ * ## The defect this prevents
6
+ *
7
+ * Before ADR-213, four artifacts stated four bare extraction thresholds
8
+ * (2 / 3+ / ≥4 / ≥3) with no scope tags — an agent loading them together
9
+ * received contradictory instructions. The per-class canon now lives in
10
+ * `docs/guidelines/abstraction-thresholds.md`; every other site names its
11
+ * artifact class and cites the canon. This gate makes that arrangement
12
+ * durable in both directions:
13
+ *
14
+ * 1. CITATION — a paragraph anywhere in the governed tree that states a
15
+ * numeric extraction threshold must contain the string
16
+ * `abstraction-thresholds` (the canon citation). A bare number is drift.
17
+ * A heading is its own block; a threshold-stating heading is satisfied
18
+ * when the block immediately below it carries the citation.
19
+ * 2. PINNING — the six deliberate sites are pinned to their current
20
+ * number + scope (SITES), and the canon's four table rows are pinned
21
+ * (CANON_ROWS). Changing either side without the other is a finding:
22
+ * the canon and its citing sites move together or not at all.
23
+ *
24
+ * ## Regex tuning record (2026-08-03, measured against the real tree)
25
+ *
26
+ * The ordinal branch accepts only `repetition|occurrence` — NOT
27
+ * `caller|use|strategy`. Those nouns appear in deliberately *qualitative*
28
+ * prose that is consistent with the canon and must stay finding-free:
29
+ * "No second caller, no second strategy → no abstraction"
30
+ * (simplicity-and-goal-demos), "cite the second caller — or inline it"
31
+ * (minimal-safe-diff-mechanics), "until the third use earns extraction"
32
+ * (tailwind-engineer, adjacent to its canon-citing section). The cardinal
33
+ * branch requires the count directly before its repetition-noun, which
34
+ * also excludes code-clarity's extract-to-VARIABLE row ("Used 2 or more
35
+ * times" — a different concern, and "or more" breaks adjacency).
36
+ * No QUALITATIVE_ALLOW list is needed at this tuning.
37
+ *
38
+ * Exit: 0 clean · 1 usage/IO error · 2 findings.
39
+ */
40
+ import * as fs from 'node:fs';
41
+ import * as path from 'node:path';
42
+ import { fileURLToPath, pathToFileURL } from 'node:url';
43
+
44
+ const _HERE = fileURLToPath(import.meta.url);
45
+ const REPO_ROOT = path.resolve(path.dirname(_HERE), '..', '..');
46
+
47
+ /** Governed roots — recursive `.md`. */
48
+ export const SCAN_ROOTS = [
49
+ 'src/rules',
50
+ 'src/skills',
51
+ 'src/agent-src/commands',
52
+ 'docs/guidelines',
53
+ ] as const;
54
+
55
+ /** Never scanned even when nested under a root or passed explicitly. */
56
+ export const EXCLUDED_SEGMENTS = ['docs/decisions', 'docs/archive', 'dist', 'agents', 'tests'] as const;
57
+
58
+ /** The canon file — exempt from the citation rule (it IS the citation target). */
59
+ export const CANON_FILE = 'docs/guidelines/abstraction-thresholds.md';
60
+
61
+ /** The citation every threshold statement outside the canon must carry. */
62
+ export const CANON_CITATION = 'abstraction-thresholds';
63
+
64
+ /** (a) the paragraph talks about extraction / componentization / abstraction … */
65
+ export const THRESHOLD_VERB = /\b(extract\w*|componenti[sz]\w*|abstraction)\b/i;
66
+
67
+ /**
68
+ * … and (b) states a numeric repetition bar. Three branches:
69
+ * cardinal count + repetition-noun · ordinal (repetition|occurrence only —
70
+ * see the tuning record above) · `≥ N` + repetition-noun.
71
+ */
72
+ export const NUMERIC_REPETITION =
73
+ /(\b(one|two|three|four|five|\d+)\s*(\+|×|x)?\s*(real\s+)?(repetitions?|repeats?|times|uses?|duplications?|occurrences?)\b)|(\b(second|third|fourth)\s+(real\s+)?(repetitions?|occurrences?)\b)|(≥\s*\d+\s*(real\s+)?(repetitions?|repeats?|times|uses?|duplications?|occurrences?))/i;
74
+
75
+ /** The canon's four table rows — all must match CANON_FILE. */
76
+ export const CANON_ROWS: ReadonlyArray<{ row: RegExp; why: string }> = [
77
+ { row: /\*\*2 real repetitions\*\*/, why: 'code-level abstraction bar' },
78
+ { row: /\*\*3\+ uses\*\*/, why: 'pure-markup UI-shell bar' },
79
+ { row: /\*\*~4\+ repeats AND real state\*\*/, why: 'stateful UI-component bar' },
80
+ { row: /\*\*≥ 3 duplications\*\*/, why: 'utility-class string bar' },
81
+ ];
82
+
83
+ /**
84
+ * The deliberate citing sites, pinned to their current number + scope.
85
+ * A regex that stops matching means the site was edited — update the canon
86
+ * AND this gate together (plus ADR-213 if the number itself moved).
87
+ */
88
+ export const SITES: ReadonlyArray<{ file: string; mustMatch: RegExp; why: string }> = [
89
+ {
90
+ file: 'src/rules/architecture.md',
91
+ mustMatch: /two real repetitions before you extract a \*\*code-level\*\*/,
92
+ why: 'the rule carrying the code-level two-repetition bar',
93
+ },
94
+ {
95
+ file: 'docs/guidelines/component-oriented-and-oop-development.md',
96
+ mustMatch:
97
+ /two real repetitions \(or a genuine second axis of change\)\s+before you extract a code-level abstraction/,
98
+ why: 'guideline restating the code-level bar',
99
+ },
100
+ {
101
+ file: 'docs/guidelines/component-oriented-and-oop-development.md',
102
+ mustMatch: /shell 3\+ · stateful\s+component ~4 \+ real state/,
103
+ why: 'guideline pointer to the scoped UI bars',
104
+ },
105
+ {
106
+ // Moved with the fe-design progressive-disclosure split (2026-08-04):
107
+ // the componentization bar lives in the design-patterns reference now.
108
+ file: 'src/skills/fe-design/references/design-patterns.md',
109
+ mustMatch: /Extract a props-only UI shell when used 3\+ times[\s\S]{0,160}?~4 \+ real state/,
110
+ why: 'UI-shell 3+ bar and the stateful ~4 pointer',
111
+ },
112
+ {
113
+ file: 'src/skills/ui-component-architect/SKILL.md',
114
+ mustMatch: /## Componentization threshold — ≥4 repeats AND real state/,
115
+ why: 'stateful-component bar (both-conditions form)',
116
+ },
117
+ {
118
+ file: 'src/skills/tailwind-engineer/SKILL.md',
119
+ mustMatch: /### 4\. Extract only when duplicated ≥ 3 times/,
120
+ why: 'utility-class string ≥3 bar',
121
+ },
122
+ {
123
+ file: 'docs/guidelines/agent-infra/minimal-safe-diff-mechanics.md',
124
+ mustMatch: /\*\*code-level\*\* threshold in this suite is the \*\*second real repetition\*\*/,
125
+ why: 'the second-repetition code-level lock (rule-of-three not adopted)',
126
+ },
127
+ ];
128
+
129
+ export interface Block {
130
+ /** 1-based line of the block's first line. */
131
+ startLine: number;
132
+ text: string;
133
+ heading: boolean;
134
+ }
135
+
136
+ export interface Finding {
137
+ /** repo-relative path */
138
+ file: string;
139
+ line: number;
140
+ message: string;
141
+ }
142
+
143
+ /**
144
+ * Blank-line-separated blocks; a markdown heading line is its own block;
145
+ * table rows (non-blank lines) belong to their surrounding block.
146
+ */
147
+ export function split_blocks(content: string): Block[] {
148
+ const lines = content.split('\n');
149
+ const blocks: Block[] = [];
150
+ let cur: string[] = [];
151
+ let start = 1;
152
+ const flush = () => {
153
+ if (cur.length > 0) {
154
+ blocks.push({ startLine: start, text: cur.join('\n'), heading: false });
155
+ }
156
+ cur = [];
157
+ };
158
+ for (let i = 0; i < lines.length; i++) {
159
+ const line = lines[i]!;
160
+ if (line.trim() === '') {
161
+ flush();
162
+ continue;
163
+ }
164
+ if (/^#{1,6}\s/.test(line)) {
165
+ flush();
166
+ blocks.push({ startLine: i + 1, text: line, heading: true });
167
+ continue;
168
+ }
169
+ if (cur.length === 0) {
170
+ start = i + 1;
171
+ }
172
+ cur.push(line);
173
+ }
174
+ flush();
175
+ return blocks;
176
+ }
177
+
178
+ export function is_threshold_statement(text: string): boolean {
179
+ return THRESHOLD_VERB.test(text) && NUMERIC_REPETITION.test(text);
180
+ }
181
+
182
+ function is_excluded(rel: string): boolean {
183
+ const parts = rel.split(path.sep).join('/');
184
+ return EXCLUDED_SEGMENTS.some(
185
+ (seg) => parts === seg || parts.startsWith(`${seg}/`) || parts.includes(`/${seg}/`),
186
+ );
187
+ }
188
+
189
+ function* iter_md_files(dir: string): Generator<string> {
190
+ for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
191
+ const p = path.join(dir, entry.name);
192
+ if (entry.isDirectory()) {
193
+ yield* iter_md_files(p);
194
+ } else if (entry.name.endsWith('.md')) {
195
+ yield p;
196
+ }
197
+ }
198
+ }
199
+
200
+ /**
201
+ * Citation rule over one file's content. Exported so the test can drive it
202
+ * against synthetic content without touching the tree.
203
+ */
204
+ export function scan_content(rel: string, content: string): Finding[] {
205
+ const findings: Finding[] = [];
206
+ const blocks = split_blocks(content);
207
+ for (let i = 0; i < blocks.length; i++) {
208
+ const b = blocks[i]!;
209
+ if (!is_threshold_statement(b.text)) {
210
+ continue;
211
+ }
212
+ if (b.text.includes(CANON_CITATION)) {
213
+ continue;
214
+ }
215
+ // A threshold-stating heading is satisfied by the block directly below.
216
+ if (b.heading && blocks[i + 1]?.text.includes(CANON_CITATION)) {
217
+ continue;
218
+ }
219
+ findings.push({
220
+ file: rel,
221
+ line: b.startLine,
222
+ message: `bare threshold, cite the canon (${CANON_FILE})`,
223
+ });
224
+ }
225
+ return findings;
226
+ }
227
+
228
+ /** Citation rule over a whole tree. Returns findings + files scanned. */
229
+ export function scan_citations(repoRoot: string): { findings: Finding[]; scanned: number } {
230
+ const findings: Finding[] = [];
231
+ let scanned = 0;
232
+ for (const root of SCAN_ROOTS) {
233
+ const abs = path.join(repoRoot, root);
234
+ if (!fs.existsSync(abs)) {
235
+ continue;
236
+ }
237
+ for (const f of iter_md_files(abs)) {
238
+ const rel = path.relative(repoRoot, f).split(path.sep).join('/');
239
+ if (is_excluded(rel)) {
240
+ continue;
241
+ }
242
+ scanned += 1;
243
+ if (rel === CANON_FILE) {
244
+ continue; // the canon states its own numbers — CANON_ROWS pins them
245
+ }
246
+ findings.push(...scan_content(rel, fs.readFileSync(f, 'utf-8')));
247
+ }
248
+ }
249
+ return { findings, scanned };
250
+ }
251
+
252
+ /** Bidirectional pins: canon rows + the six citing sites. */
253
+ export function check_pins(repoRoot: string): Finding[] {
254
+ const findings: Finding[] = [];
255
+
256
+ const canonAbs = path.join(repoRoot, CANON_FILE);
257
+ if (!fs.existsSync(canonAbs)) {
258
+ findings.push({ file: CANON_FILE, line: 1, message: 'canon file missing' });
259
+ } else {
260
+ const canon = fs.readFileSync(canonAbs, 'utf-8');
261
+ for (const { row, why } of CANON_ROWS) {
262
+ if (!row.test(canon)) {
263
+ findings.push({
264
+ file: CANON_FILE,
265
+ line: 1,
266
+ message: `canon row missing (${why}): expected /${row.source}/ — update the canon + this gate together`,
267
+ });
268
+ }
269
+ }
270
+ }
271
+
272
+ for (const { file, mustMatch, why } of SITES) {
273
+ const abs = path.join(repoRoot, file);
274
+ if (!fs.existsSync(abs)) {
275
+ findings.push({ file, line: 1, message: `canonical site missing (${why})` });
276
+ continue;
277
+ }
278
+ if (!mustMatch.test(fs.readFileSync(abs, 'utf-8'))) {
279
+ findings.push({
280
+ file,
281
+ line: 1,
282
+ message: `canonical site changed (${why}) — update the canon + this gate together`,
283
+ });
284
+ }
285
+ }
286
+
287
+ return findings;
288
+ }
289
+
290
+ export function main(argv?: readonly string[]): number {
291
+ let quiet = false;
292
+ for (const arg of argv ?? process.argv.slice(2)) {
293
+ if (arg === '--quiet') {
294
+ quiet = true;
295
+ } else {
296
+ process.stderr.write(`usage: lint_abstraction_thresholds [--quiet]\n`);
297
+ return 1;
298
+ }
299
+ }
300
+
301
+ let citations: { findings: Finding[]; scanned: number };
302
+ let pins: Finding[];
303
+ try {
304
+ citations = scan_citations(REPO_ROOT);
305
+ pins = check_pins(REPO_ROOT);
306
+ } catch (e) {
307
+ process.stderr.write(`error: ${String(e)}\n`);
308
+ return 1;
309
+ }
310
+
311
+ const findings = [...citations.findings, ...pins];
312
+ for (const f of findings) {
313
+ process.stdout.write(`❌ ${f.file}:${String(f.line)} ${f.message}\n`);
314
+ }
315
+ if (findings.length === 0 && !quiet) {
316
+ process.stdout.write(
317
+ `✅ abstraction thresholds: every numeric extraction threshold cites the canon; all pins hold\n`,
318
+ );
319
+ }
320
+ // gate-coverage contract (src/config/gate-coverage.yml): files inspected.
321
+ process.stdout.write(`scanned: ${String(citations.scanned)}\n`);
322
+ return findings.length > 0 ? 2 : 0;
323
+ }
324
+
325
+ if (process.argv[1] && import.meta.url === pathToFileURL(path.resolve(process.argv[1])).href) {
326
+ process.exit(main());
327
+ }
@@ -64,9 +64,9 @@
64
64
  {
65
65
  "file": "src/skills/rule-writing/SKILL.md",
66
66
  "lines": [
67
- 76
67
+ 78
68
68
  ],
69
- "reason": "Quoted rule-description example (the php-coding rule's own description string) — meta content."
69
+ "reason": "Quoted rule-description example (the php-coding rule's own description string) — meta content. Re-anchored 76→78 after the description-cap edit above it."
70
70
  },
71
71
  {
72
72
  "file": "src/skills/security-audit/SKILL.md",