@event4u/agent-config 14.18.0 → 14.19.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 (146) 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 +169 -130
  5. package/README.md +71 -149
  6. package/dist/agent-src/README.md +1 -1
  7. package/dist/agent-src/commands/agent-handoff.md +2 -2
  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 +30 -3
  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/skills/adversarial-review/SKILL.md +0 -8
  17. package/dist/agent-src/skills/canvas-design/SKILL.md +1 -1
  18. package/dist/agent-src/skills/design-intelligence/SKILL.md +1 -1
  19. package/dist/agent-src/skills/design-intelligence/data/app-interface.csv +1 -1
  20. package/dist/agent-src/skills/design-intelligence/data/motion.csv +17 -17
  21. package/dist/agent-src/skills/design-intelligence/data/ux-guidelines.csv +2 -2
  22. package/dist/agent-src/skills/design-intelligence/references/context-and-registers.md +17 -0
  23. package/dist/agent-src/skills/design-intelligence/references/design-rules-checklist.md +4 -4
  24. package/dist/agent-src/skills/design-review/SKILL.md +1 -1
  25. package/dist/agent-src/skills/design-variations/SKILL.md +1 -1
  26. package/dist/agent-src/skills/existing-ui-audit/SKILL.md +1 -1
  27. package/dist/agent-src/skills/fe-design/SKILL.md +32 -5
  28. package/dist/agent-src/skills/fe-design/references/craft-floor.md +7 -0
  29. package/dist/agent-src/skills/fe-design/references/design-patterns.md +47 -2
  30. package/dist/agent-src/skills/laravel-scheduling/SKILL.md +3 -1
  31. package/dist/agent-src/skills/project-analyzer/SKILL.md +0 -6
  32. package/dist/agent-src/skills/react-native-setup/SKILL.md +3 -3
  33. package/dist/agent-src/skills/react-shadcn-ui/SKILL.md +1 -1
  34. package/dist/agent-src/skills/sequential-thinking/SKILL.md +18 -8
  35. package/dist/agent-src/skills/tailwind-engineer/SKILL.md +1 -1
  36. package/dist/agent-src/skills/ui-apply-generic/SKILL.md +1 -1
  37. package/dist/agent-src/skills/ui-component-architect/SKILL.md +1 -1
  38. package/dist/agent-src/skills/using-git-worktrees/SKILL.md +17 -2
  39. package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
  40. package/dist/cli/commands/recordTriggerEval.js +2 -2
  41. package/dist/cli/main.js +1 -1
  42. package/dist/cli-delegate/{chunk-AKJGUQNO.js → chunk-GZTMRYCN.js} +405 -341
  43. package/dist/cli-delegate/{chunk-7RXIKF62.js → chunk-IDHUQIXP.js} +283 -132
  44. package/dist/cli-delegate/cmd_adr_effective.js +318 -81
  45. package/dist/cli-delegate/cmd_conformance.js +420 -16
  46. package/dist/cli-delegate/cmd_doctor.js +1 -1
  47. package/dist/cli-delegate/cmd_export.js +1 -1
  48. package/dist/cli-delegate/cmd_handoff.js +1 -1
  49. package/dist/cli-delegate/cmd_prune.js +1 -1
  50. package/dist/cli-delegate/cmd_route_audit.js +1 -1
  51. package/dist/cli-delegate/cmd_settings_migrate.js +1 -1
  52. package/dist/cli-delegate/cmd_sync.js +1 -1
  53. package/dist/cli-delegate/cmd_uninstall.js +1 -1
  54. package/dist/cli-delegate/cmd_validate.js +1 -1
  55. package/dist/discovery/deprecation-report.md +1 -1
  56. package/dist/discovery/discovery-manifest.json +28 -27
  57. package/dist/discovery/discovery-manifest.json.sha256 +1 -1
  58. package/dist/discovery/discovery-manifest.summary.md +1 -1
  59. package/dist/discovery/dormancy-report.md +1 -1
  60. package/dist/discovery/orphan-report.md +1 -1
  61. package/dist/discovery/packs.json +2 -2
  62. package/dist/discovery/trust-report.md +1 -1
  63. package/dist/discovery/workspaces.json +2 -2
  64. package/dist/hooks/dispatch.js +2132 -1462
  65. package/dist/install/install.mjs +588 -527
  66. package/dist/mcp/registry-manifest.json +1 -1
  67. package/dist/mcp/server.json +2 -2
  68. package/dist/mcp/server.mjs +1619 -1045
  69. package/dist/scripts/_lib/model_tier.js +1 -1
  70. package/docs/guidelines/agent-infra/evaluator-independence-mechanics.md +1 -1
  71. package/docs/guidelines/agent-infra/roadmap-progress-mechanics.md +1 -1
  72. package/docs/guidelines/design-antipatterns.md +87 -64
  73. package/package.json +1 -1
  74. package/src/agent-src/README.md +1 -1
  75. package/src/config/ci-local-parity.yml +6 -0
  76. package/src/config/gate-coverage.yml +170 -0
  77. package/src/config/gate-violation-baselines.json +3 -3
  78. package/src/config/release-gate-locality.yml +12 -0
  79. package/src/scripts/_cli/cmd_doctor.ts +2 -2
  80. package/src/scripts/_cli/cmd_handoff.ts +1 -1
  81. package/src/scripts/_cli/cmd_update.ts +1 -1
  82. package/src/scripts/_cli/cmd_versions.ts +1 -1
  83. package/src/scripts/_lib/adr_frontmatter.ts +44 -0
  84. package/src/scripts/_lib/consequence_objects.ts +152 -0
  85. package/src/scripts/_lib/exec_evidence.ts +7 -0
  86. package/src/scripts/_lib/global_deploy_inventory.ts +2 -2
  87. package/src/scripts/_lib/host_permission_checks.ts +251 -0
  88. package/src/scripts/_lib/model_tier.ts +1 -1
  89. package/src/scripts/_lib/obligation_frequency.ts +2 -2
  90. package/src/scripts/_lib/release_highlights.ts +93 -0
  91. package/src/scripts/_lib/release_material.ts +64 -1
  92. package/src/scripts/_lib/runtime_journal.ts +26 -3
  93. package/src/scripts/_lib/runtime_wiring_checks.ts +12 -1
  94. package/src/scripts/_lib/session_register.ts +11 -6
  95. package/src/scripts/_lib/sqlite_schema.ts +22 -0
  96. package/src/scripts/_lib/surface.ts +86 -0
  97. package/src/scripts/_lib/ui_authority.ts +116 -0
  98. package/src/scripts/adr/regenerate_index.ts +54 -1
  99. package/src/scripts/adr_cite_check.ts +71 -15
  100. package/src/scripts/ai_council/clients.ts +1 -1
  101. package/src/scripts/autonomy_friction_corpus.ts +229 -0
  102. package/src/scripts/build_proof.ts +15 -2
  103. package/src/scripts/check_beta_review_markers.ts +141 -17
  104. package/src/scripts/check_enforcement_coverage.ts +1 -1
  105. package/src/scripts/check_enforcement_denominator.ts +121 -11
  106. package/src/scripts/check_finding_dispositions.ts +66 -9
  107. package/src/scripts/check_gate_coverage.ts +96 -0
  108. package/src/scripts/check_release_highlights.ts +74 -1
  109. package/src/scripts/check_skill_activation_claim.ts +179 -0
  110. package/src/scripts/check_trigger_evals.ts +1 -1
  111. package/src/scripts/conformance_scan.ts +44 -3
  112. package/src/scripts/explain_run.ts +1 -1
  113. package/src/scripts/generate_capability_matrix.ts +1 -1
  114. package/src/scripts/git_authorization_hook.ts +75 -1
  115. package/src/scripts/hook_manifest.json +1 -1
  116. package/src/scripts/hook_manifest.yaml +14 -11
  117. package/src/scripts/hooks/augment-dispatcher.sh +1 -1
  118. package/src/scripts/hooks/category_a.ts +291 -0
  119. package/src/scripts/hooks/cline-dispatcher.sh +1 -1
  120. package/src/scripts/hooks/cursor-dispatcher.sh +11 -5
  121. package/src/scripts/hooks/dispatch_hook.ts +17 -22
  122. package/src/scripts/hooks/git_command_classifier.ts +156 -1
  123. package/src/scripts/hooks/host_lowering.ts +215 -0
  124. package/src/scripts/hooks/host_lowering.yaml +185 -0
  125. package/src/scripts/hooks/host_semantics.ts +105 -37
  126. package/src/scripts/hooks/journal_record_hook.ts +10 -0
  127. package/src/scripts/hooks/permission_gate.ts +117 -0
  128. package/src/scripts/hooks_status.ts +12 -10
  129. package/src/scripts/install.ts +53 -82
  130. package/src/scripts/install_anthropic_key.sh +2 -2
  131. package/src/scripts/lint_design_antipattern_parity.ts +80 -0
  132. package/src/scripts/lint_evidence_artifacts.ts +45 -1
  133. package/src/scripts/lint_hook_manifest.ts +87 -3
  134. package/src/scripts/lint_motion_authority_drift.ts +731 -0
  135. package/src/scripts/lint_readme_size.ts +158 -12
  136. package/src/scripts/lint_reasoning_fixtures.ts +216 -0
  137. package/src/scripts/lint_skill_trigger_contradiction.ts +161 -0
  138. package/src/scripts/measure_sibling_naming.ts +150 -0
  139. package/src/scripts/redact_hook_capture.ts +1 -1
  140. package/src/scripts/report_skill_activation.ts +319 -7
  141. package/src/scripts/schemas/review-findings.schema.json +5 -0
  142. package/src/scripts/schemas/skill.schema.json +2 -2
  143. package/src/scripts/session_canary_hook.ts +1 -1
  144. package/src/scripts/session_register_hook.ts +4 -3
  145. package/src/scripts/trigger_eval_grandfather.json +5 -1
  146. package/src/scripts/validate_agent_settings.ts +1 -1
@@ -11,7 +11,7 @@
11
11
  * - the repo generator (`condense.py generate_claude_skills`), which
12
12
  * builds the package's own `.claude/skills/` tree, and
13
13
  * - the consumer install finalizer
14
- * (`install.py finalize_claude_model_tiers`), which rewrites the
14
+ * (the installer's `finalize_claude_model_tiers`), which rewrites the
15
15
  * installed `.claude/skills/` tree so Claude Code performs the
16
16
  * per-turn model switch on a consumer with `model.auto_switch: auto`.
17
17
  *
@@ -51,7 +51,7 @@ drew.** It said `pre_tool_use` "exists on three hosts" and that the guard has
51
51
  tree. **Downward:** the manifest's own `native_event_aliases` table already maps
52
52
  `preToolUse` (cursor), `PreToolUse` (cline) and `BeforeTool` (gemini) onto
53
53
  `pre_tool_use`, so there the guard is **unbound, not unbindable**; only windsurf
54
- and copilot carry no pre-tool surface at all. **Upward, and worse:**
54
+ and copilot carry no pre-tool alias row at all. **Upward, and worse:**
55
55
  `host_semantics.ts` certifies **claude alone**, and the augment and cowork
56
56
  trampolines discard dispatcher output and `exit 0` unconditionally — so on two
57
57
  of the three "enforcing" hosts this guard runs and is then ignored. Nor may the
@@ -19,7 +19,7 @@ _Origin: migrated from `.agent-src.uncondensed/rules/roadmap-progress-sync.md` p
19
19
  > stale dashboard even if local hooks were bypassed.
20
20
  >
21
21
  > Hook is primary; the prose below is the specification the hook
22
- > implements and the fallback when the platform has no hook surface.
22
+ > implements and the fallback where no hook is bound on the platform.
23
23
 
24
24
  ## Iron Law — dashboard sync
25
25
 
@@ -68,105 +68,128 @@ generation ships a visibly different default aesthetic. It is a *when-does-it-
68
68
  rotate* note, not a new pattern — never add a tell here that lacks a catalog
69
69
  entry in the sections below.
70
70
 
71
+ ### The three census columns — class, remediation, verification
72
+
73
+ `Class` says what KIND of rule an entry is; **status** (§ Detector status) says
74
+ how its enforcement is wired. The two axes are independent, and a reader who
75
+ conflates them draws the wrong conclusion in both directions: a `floor` may be
76
+ `judgment-only` because nothing can detect it yet, and a `style-preference` may
77
+ be `backed` because it happens to be trivially greppable. Detectability is not
78
+ authority.
79
+
80
+ - **Class** — one of `floor` (an objective threshold, accessibility or
81
+ correctness) · `invariant` (a structural rule that must hold) ·
82
+ `craft-presumption` (a default a competent designer usually follows,
83
+ overridable on evidence) · `style-preference` (taste) ·
84
+ `reference-constraint` (bound to a supplied reference, brand, or `DESIGN.md`).
85
+ The two soft classes lose to a supplied reference artifact, a coherent
86
+ incumbent, and a project `DESIGN.md` — asserted on the resolver in
87
+ `src/scripts/_lib/ui_authority.ts` rather than stated here as prose.
88
+ - **Remediation** — the order to try, worst-first: `delete` before `reduce`
89
+ before `retune`, with `replace` where the pattern has a direct substitute.
90
+ - **Verification** — how a fix is checked: `static` (readable from the source)
91
+ · `render` (needs the rendered surface) · `feel` (perceptual, per the `feel`
92
+ evidence type in `docs/contracts/evidence-artifact-types.md`) · `judgment`.
93
+
71
94
  ### Consistency Locks & layout caps (taste-dials roadmap)
72
95
 
73
96
  Within-project invariants + repetition caps. Override = declare the value in
74
97
  `DESIGN.md` (`## Consistency Locks` / a brutalist or uniform-grid style note).
75
98
 
76
- | # | Pattern | Why it reads as AI-generated | Override condition |
77
- |---|---|---|---|
78
- | V8 | Shape Lock — ≥ 4 distinct corner-radius scales in one surface | A fragmented radius system reads as no shape decision | Declare the radius scale in `DESIGN.md`; a genuine multi-tier scale documents each tier |
79
- | C6 | Colour Lock — ≥ 3 distinct saturated accent hue families | No single accent identity; reads as default-palette sprawl | Brand genuinely uses a multi-accent system, declared in `DESIGN.md` / `brand-to-tokens` |
80
- | C7 | Theme inversion — a light island mid-dark surface (or vice versa) not driven by the theme system | Mid-surface inversion breaks the theme rhythm | Intentional inverted callout with a documented reason — **judgment-only**, design-review calls it (deterministic detection is unreliable) |
81
- | L9 | Section monotony — ≥ 8 sections, < 4 distinct layout families | Uniform section rhythm collapses scannability | Deliberately uniform grid (e.g. a gallery), declared as such |
82
- | L10 | Zigzag — > 2 consecutive image-left/text-right two-column sections | The alternating-zigzag landing-page tell | A genuinely sequenced walkthrough where alternation aids comprehension |
99
+ | # | Pattern | Why it reads as AI-generated | Override condition | Class | Remediation | Verification |
100
+ |---|---|---|---|---|---|---|
101
+ | V8 | Shape Lock — ≥ 4 distinct corner-radius scales in one surface | A fragmented radius system reads as no shape decision | Declare the radius scale in `DESIGN.md`; a genuine multi-tier scale documents each tier | invariant | reduce → retune | static |
102
+ | C6 | Colour Lock — ≥ 3 distinct saturated accent hue families | No single accent identity; reads as default-palette sprawl | Brand genuinely uses a multi-accent system, declared in `DESIGN.md` / `brand-to-tokens` | invariant | reduce → retune | static |
103
+ | C7 | Theme inversion — a light island mid-dark surface (or vice versa) not driven by the theme system | Mid-surface inversion breaks the theme rhythm | Intentional inverted callout with a documented reason — **judgment-only**, design-review calls it (deterministic detection is unreliable) | craft-presumption | delete → retune | judgment |
104
+ | L9 | Section monotony — ≥ 8 sections, < 4 distinct layout families | Uniform section rhythm collapses scannability | Deliberately uniform grid (e.g. a gallery), declared as such | craft-presumption | retune | render |
105
+ | L10 | Zigzag — > 2 consecutive image-left/text-right two-column sections | The alternating-zigzag landing-page tell | A genuinely sequenced walkthrough where alternation aids comprehension | craft-presumption | delete → retune | static |
83
106
 
84
107
  ---
85
108
 
86
109
  ## Visual
87
110
 
88
- | # | Pattern | Why it reads as AI-generated | Override condition |
89
- |---|---|---|---|
90
- | V1 | A colored accent border above 1px — the side stripe (`border-left` / `border-right`), a fully colored border on all four sides, or a gradient `border-image` | The single most recognizable AI-UI signature; every scaffold-default card uses it. Widened 2026-09-03: the side stripe was the 2024 form, the four-sided colored or gradient card border is the current one, and the entry as written reached only the first | Brand token explicitly defines the accent border; document the decision in the design brief. A 1px neutral border, a `border-color`-only focus treatment, and `transparent` / `currentColor` at any width are not accents and do not flag |
91
- | V2 | Glassmorphism as decoration (`backdrop-blur` + `bg-white/10`) with no functional depth hierarchy | Model defaults to it for "premium feel"; result looks like 2021 Material You clip-art | Element genuinely sits above a complex, blurred background layer; use sparingly and only for top-level floating surfaces |
92
- | V3 | Ghost card: `border: 1px solid` + box-shadow ≥ 16px spread together | The combination creates visual noise — the ghost card signals no confident decision about depth | Choose either border OR shadow to indicate depth; never both on the same surface |
93
- | V4 | Over-rounded small cards or badges: `border-radius` > 16px on elements < 200px wide | Excessive rounding on small surfaces looks toy-ish; standard cap is 12–16px | Pill shape (`border-radius: 9999px`) is intentional on single-line tags/chips only |
94
- | V5 | Hand-drawn or illustrated SVG icons mixed with crisp icon-system icons | Visual register collision; instantly reads as "generated asset" | Intentional illustrative section (hero illustration, empty-state art); isolate from the icon system |
95
- | V6 | Repeating diagonal stripes as a background texture | A cliché background default; reads as generated CSS art | Brand explicitly uses pattern backgrounds (e.g., a textile brand); document the stripe definition |
96
- | V7 | Nested cards: a card inside a card inside a card | Depth hierarchy collapses; user cannot parse elevation intent | A truly three-level hierarchy (e.g., project → board → card); ensure each level has distinct visual weight |
97
- | V9 | Noise or grain texture laid over a gradient as a "premium" surface default | The visual sibling of V6's diagonal stripes — a generated-CSS-art texture reached for to make a flat gradient look considered, applied because it is available rather than because the surface needed a texture | The texture is part of a documented brand surface treatment (a print-inspired or analog-photographic identity) with the grain opacity defined as a token, not chosen per surface |
111
+ | # | Pattern | Why it reads as AI-generated | Override condition | Class | Remediation | Verification |
112
+ |---|---|---|---|---|---|---|
113
+ | V1 | A colored accent border above 1px — the side stripe (`border-left` / `border-right`), a fully colored border on all four sides, or a gradient `border-image` | The single most recognizable AI-UI signature; every scaffold-default card uses it. Widened 2026-09-03: the side stripe was the 2024 form, the four-sided colored or gradient card border is the current one, and the entry as written reached only the first | Brand token explicitly defines the accent border; document the decision in the design brief. A 1px neutral border, a `border-color`-only focus treatment, and `transparent` / `currentColor` at any width are not accents and do not flag | craft-presumption | delete → retune | static |
114
+ | V2 | Glassmorphism as decoration (`backdrop-blur` + `bg-white/10`) with no functional depth hierarchy | Model defaults to it for "premium feel"; result looks like 2021 Material You clip-art | Element genuinely sits above a complex, blurred background layer; use sparingly and only for top-level floating surfaces | craft-presumption | delete → reduce | static |
115
+ | V3 | Ghost card: `border: 1px solid` + box-shadow ≥ 16px spread together | The combination creates visual noise — the ghost card signals no confident decision about depth | Choose either border OR shadow to indicate depth; never both on the same surface | invariant | delete → reduce | static |
116
+ | V4 | Over-rounded small cards or badges: `border-radius` > 16px on elements < 200px wide | Excessive rounding on small surfaces looks toy-ish; standard cap is 12–16px | Pill shape (`border-radius: 9999px`) is intentional on single-line tags/chips only | craft-presumption | retune | static |
117
+ | V5 | Hand-drawn or illustrated SVG icons mixed with crisp icon-system icons | Visual register collision; instantly reads as "generated asset" | Intentional illustrative section (hero illustration, empty-state art); isolate from the icon system | invariant | replace | render |
118
+ | V6 | Repeating diagonal stripes as a background texture | A cliché background default; reads as generated CSS art | Brand explicitly uses pattern backgrounds (e.g., a textile brand); document the stripe definition | style-preference | delete | static |
119
+ | V7 | Nested cards: a card inside a card inside a card | Depth hierarchy collapses; user cannot parse elevation intent | A truly three-level hierarchy (e.g., project → board → card); ensure each level has distinct visual weight | invariant | delete → reduce | render |
120
+ | V9 | Noise or grain texture laid over a gradient as a "premium" surface default | The visual sibling of V6's diagonal stripes — a generated-CSS-art texture reached for to make a flat gradient look considered, applied because it is available rather than because the surface needed a texture | The texture is part of a documented brand surface treatment (a print-inspired or analog-photographic identity) with the grain opacity defined as a token, not chosen per surface | style-preference | delete | render |
98
121
 
99
122
  ---
100
123
 
101
124
  ## Typography
102
125
 
103
- | # | Pattern | Why it reads as AI-generated | Override condition |
104
- |---|---|---|---|
105
- | T1 | Flat type hierarchy: body and headings use the same optical weight and nearly the same size | Type hierarchy is invisible; user cannot scan the page | Deliberately minimal aesthetic where all text is body-weight; must be an explicit brand decision |
106
- | T2 | Oversized italic serif as the primary hero headline | The "AI startup landing page" template; seen on ~60% of AI-generated marketing pages | Brand adopts editorial-magazine aesthetic with a documented italic serif rationale |
107
- | T3 | Icon tile (small rounded-square container) above heading | The universal AI feature-card template; reads as scaffold | Listed features genuinely benefit from visual-icon-then-title affordance AND the icons are custom, not library defaults |
108
- | T4 | Eyebrow / ALL-CAPS label above every section heading | Overuse collapses the emphasis; maximum is 1 eyebrow per 3 sections (countable: grep tracking classes) | Used for genuine section-type differentiation with meaningful vocabulary (not just "FEATURES", "PRICING") |
109
- | T5 | Repeated section kickers ("Our mission", "What we offer", "Why choose us") | Filler vocabulary; every AI-generated page has the same three section titles | Every kicker is substantive and uses brand-specific vocabulary, not generic marketing speak |
110
- | T6 | Crushed letter-spacing on display text (`letter-spacing` < −0.04em) | Below −0.04em glyphs collide optically; reads as "designer-ish effect" with no grounding | Intentional tight display treatment with a documented minimum floor; verify at the actual render size |
111
- | T7 | Overused fonts without a stated reason: Inter, Roboto, DM Sans, Geist, Space Grotesk, Instrument Serif | These are the default AI-coding-tool font picks; every generated UI uses one of them | **Register-scoped.** In the **brand** register: the brand has explicitly adopted one of these AND there is a documented reason. In the **product** register: a single reliable family is the sanctioned strategy ([`design-modes`](design-modes.md) § The two registers), so one of these is legitimate on a **stated** product-register choice — the register itself is the reason, and it must be stated (DESIGN.md, the surface brief, or `design-slop-disable`). Neither register admits an *undeclared* pick. |
112
- | T8 | Single typeface for everything | Missed typographic contrast opportunity; in the **brand** register an unexamined single family reads as "I did not think about fonts" | **Register-scoped like T7.** Intentional mono-typeface aesthetic (e.g., terminal-inspired), stated in the brief — *or* the **product** register, where one reliable family is the sanctioned strategy ([`design-modes`](design-modes.md) § The two registers) and contrast is carried by weight and scale rather than by a second family. |
113
- | T9 | All-caps body paragraphs or field labels | Reduces legibility below WCAG AA for sustained reading; reads as flair over function | Short UI labels (e.g., button text in a brutalist layout); never for body paragraphs |
114
- | T10 | Wide letter-spacing on body text (`letter-spacing` > 0.05em) | Reduces readability for sustained reading; reserved for display use | Display text (headlines, pull quotes) where intentional tracking is a brand element |
126
+ | # | Pattern | Why it reads as AI-generated | Override condition | Class | Remediation | Verification |
127
+ |---|---|---|---|---|---|---|
128
+ | T1 | Flat type hierarchy: body and headings use the same optical weight and nearly the same size | Type hierarchy is invisible; user cannot scan the page | Deliberately minimal aesthetic where all text is body-weight; must be an explicit brand decision | craft-presumption | retune | render |
129
+ | T2 | Oversized italic serif as the primary hero headline | The "AI startup landing page" template; seen on ~60% of AI-generated marketing pages | Brand adopts editorial-magazine aesthetic with a documented italic serif rationale | style-preference | replace | render |
130
+ | T3 | Icon tile (small rounded-square container) above heading | The universal AI feature-card template; reads as scaffold | Listed features genuinely benefit from visual-icon-then-title affordance AND the icons are custom, not library defaults | style-preference | delete | static |
131
+ | T4 | Eyebrow / ALL-CAPS label above every section heading | Overuse collapses the emphasis; maximum is 1 eyebrow per 3 sections (countable: grep tracking classes) | Used for genuine section-type differentiation with meaningful vocabulary (not just "FEATURES", "PRICING") | craft-presumption | delete → reduce | static |
132
+ | T5 | Repeated section kickers ("Our mission", "What we offer", "Why choose us") | Filler vocabulary; every AI-generated page has the same three section titles | Every kicker is substantive and uses brand-specific vocabulary, not generic marketing speak | craft-presumption | replace | judgment |
133
+ | T6 | Crushed letter-spacing on display text (`letter-spacing` < −0.04em) | Below −0.04em glyphs collide optically; reads as "designer-ish effect" with no grounding | Intentional tight display treatment with a documented minimum floor; verify at the actual render size | floor | retune | static |
134
+ | T7 | Overused fonts without a stated reason: Inter, Roboto, DM Sans, Geist, Space Grotesk, Instrument Serif | These are the default AI-coding-tool font picks; every generated UI uses one of them | **Register-scoped.** In the **brand** register: the brand has explicitly adopted one of these AND there is a documented reason. In the **product** register: a single reliable family is the sanctioned strategy ([`design-modes`](design-modes.md) § The two registers), so one of these is legitimate on a **stated** product-register choice — the register itself is the reason, and it must be stated (DESIGN.md, the surface brief, or `design-slop-disable`). Neither register admits an *undeclared* pick. | reference-constraint | replace | judgment |
135
+ | T8 | Single typeface for everything | Missed typographic contrast opportunity; in the **brand** register an unexamined single family reads as "I did not think about fonts" | **Register-scoped like T7.** Intentional mono-typeface aesthetic (e.g., terminal-inspired), stated in the brief — *or* the **product** register, where one reliable family is the sanctioned strategy ([`design-modes`](design-modes.md) § The two registers) and contrast is carried by weight and scale rather than by a second family. | reference-constraint | retune | judgment |
136
+ | T9 | All-caps body paragraphs or field labels | Reduces legibility below WCAG AA for sustained reading; reads as flair over function | Short UI labels (e.g., button text in a brutalist layout); never for body paragraphs | floor | delete → retune | static |
137
+ | T10 | Wide letter-spacing on body text (`letter-spacing` > 0.05em) | Reduces readability for sustained reading; reserved for display use | Display text (headlines, pull quotes) where intentional tracking is a brand element | craft-presumption | retune | static |
115
138
 
116
139
  ---
117
140
 
118
141
  ## Color
119
142
 
120
- | # | Pattern | Why it reads as AI-generated | Override condition |
121
- |---|---|---|---|
122
- | C1 | Purple/violet + cyan-on-dark palette as the primary scheme | The most recognizable AI-UI color combination in 2024–2026; instantly signals "generated" | Brand color system genuinely defines a violet primary; must come from `brand-to-tokens`, not from model defaults |
123
- | C2 | Gradient text via `background-clip: text` as a primary accent | Overused to the point of invisibility; also has rendering edge cases on some platforms | Headline treatment in a brand that explicitly uses gradient identity (e.g., a product whose logo is a gradient) |
124
- | C3 | Glowing colored accents on dark mode (bright neon `box-shadow` or `text-shadow`) | Looks like a 2020 gaming UI; reads as an AI attempt at "moody" | Brand explicitly defines neon accents with a documented usage rule (e.g., error or alert states in a security product) |
125
- | C4 | Gray text on colored background without WCAG AA check | Muted gray chosen for "elegance" often fails 4.5:1 contrast; the failure is invisible until audited | Any use — always verify the contrast ratio; if it fails, either darken the text or lighten/darken the background |
126
- | C5 | Cream/sand body background (OKLCH L 0.84–0.97, C < 0.06) with brass/clay/oxblood accents | The 2025 "premium-consumer" AI palette; every generated "sophisticated" product uses this exact combination | Brand has an explicit warm-neutral design language documented in `brand-to-tokens`; the palette is first-party, not a default reach |
143
+ | # | Pattern | Why it reads as AI-generated | Override condition | Class | Remediation | Verification |
144
+ |---|---|---|---|---|---|---|
145
+ | C1 | Purple/violet + cyan-on-dark palette as the primary scheme | The most recognizable AI-UI color combination in 2024–2026; instantly signals "generated" | Brand color system genuinely defines a violet primary; must come from `brand-to-tokens`, not from model defaults | style-preference | replace | static |
146
+ | C2 | Gradient text via `background-clip: text` as a primary accent | Overused to the point of invisibility; also has rendering edge cases on some platforms | Headline treatment in a brand that explicitly uses gradient identity (e.g., a product whose logo is a gradient) | style-preference | delete | static |
147
+ | C3 | Glowing colored accents on dark mode (bright neon `box-shadow` or `text-shadow`) | Looks like a 2020 gaming UI; reads as an AI attempt at "moody" | Brand explicitly defines neon accents with a documented usage rule (e.g., error or alert states in a security product) | style-preference | delete → reduce | static |
148
+ | C4 | Gray text on colored background without WCAG AA check | Muted gray chosen for "elegance" often fails 4.5:1 contrast; the failure is invisible until audited | Any use — always verify the contrast ratio; if it fails, either darken the text or lighten/darken the background | floor | retune | static |
149
+ | C5 | Cream/sand body background (OKLCH L 0.84–0.97, C < 0.06) with brass/clay/oxblood accents | The 2025 "premium-consumer" AI palette; every generated "sophisticated" product uses this exact combination | Brand has an explicit warm-neutral design language documented in `brand-to-tokens`; the palette is first-party, not a default reach | style-preference | replace | static |
127
150
 
128
151
  ---
129
152
 
130
153
  ## Layout
131
154
 
132
- | # | Pattern | Why it reads as AI-generated | Override condition |
133
- |---|---|---|---|
134
- | L1 | Hero metric template: giant number, small label, 3–4 stats in a row, gradient | The default SaaS dashboard hero; appears on ~70% of AI-generated dashboard screens | Data storytelling genuinely requires a primary metric with supporting context; must vary the arrangement |
135
- | L2 | Three identical feature cards in a grid with the same visual weight | The scaffold-default; no visual rhythm, no hierarchy among features | Product genuinely has three equally weighted features; must still vary padding, or use a different composition |
136
- | L3 | Monotonous spacing: one spacing value (usually 24px) used for everything | Spacing is rhythm; uniform spacing collapses the hierarchy of proximity | Intentional grid where every element has equal weight; must document the grid definition |
137
- | L4 | Numbered section markers (01 / 02 / 03) as a primary section-order device | Universal AI-generated marketing page tell; seen on virtually every generated landing page | A genuinely numbered process or sequenced guide where the step number carries meaning beyond decoration |
138
- | L5 | Line length > 80ch in body copy without column constraint | Reduces readability; WCAG recommends 45–75 characters; beyond 80 the eye struggles to track | Data-dense dashboards where column width is driven by tabular data, not narrative text |
139
- | L6 | `position: absolute` dropdown inside `overflow: hidden` container | Dropdown clips; a common AI-generated layout bug | Refactor: use `<dialog>`, the Popover API, or `position: fixed` / portal to escape the stacking context |
140
- | L7 | Content that overflows its container | Text truncation or reflow failures at non-default viewport widths; reads as untested | Never acceptable in production output; test at 320px, 768px, 1280px minimally |
141
- | L8 | Z-index values of 99, 999, 9999 without a semantic z-index scale | Magic numbers; the first time two elements compete, the scale breaks | Establish a named semantic z-index scale (`--z-modal: 400`, `--z-toast: 500`, etc.) and use it |
155
+ | # | Pattern | Why it reads as AI-generated | Override condition | Class | Remediation | Verification |
156
+ |---|---|---|---|---|---|---|
157
+ | L1 | Hero metric template: giant number, small label, 3–4 stats in a row, gradient | The default SaaS dashboard hero; appears on ~70% of AI-generated dashboard screens | Data storytelling genuinely requires a primary metric with supporting context; must vary the arrangement | craft-presumption | retune | render |
158
+ | L2 | Three identical feature cards in a grid with the same visual weight | The scaffold-default; no visual rhythm, no hierarchy among features | Product genuinely has three equally weighted features; must still vary padding, or use a different composition | craft-presumption | retune | render |
159
+ | L3 | Monotonous spacing: one spacing value (usually 24px) used for everything | Spacing is rhythm; uniform spacing collapses the hierarchy of proximity | Intentional grid where every element has equal weight; must document the grid definition | craft-presumption | retune | static |
160
+ | L4 | Numbered section markers (01 / 02 / 03) as a primary section-order device | Universal AI-generated marketing page tell; seen on virtually every generated landing page | A genuinely numbered process or sequenced guide where the step number carries meaning beyond decoration | style-preference | delete | static |
161
+ | L5 | Line length > 80ch in body copy without column constraint | Reduces readability; WCAG recommends 45–75 characters; beyond 80 the eye struggles to track | Data-dense dashboards where column width is driven by tabular data, not narrative text | floor | retune | render |
162
+ | L6 | `position: absolute` dropdown inside `overflow: hidden` container | Dropdown clips; a common AI-generated layout bug | Refactor: use `<dialog>`, the Popover API, or `position: fixed` / portal to escape the stacking context | invariant | replace | render |
163
+ | L7 | Content that overflows its container | Text truncation or reflow failures at non-default viewport widths; reads as untested | Never acceptable in production output; test at 320px, 768px, 1280px minimally | invariant | retune | render |
164
+ | L8 | Z-index values of 99, 999, 9999 without a semantic z-index scale | Magic numbers; the first time two elements compete, the scale breaks | Establish a named semantic z-index scale (`--z-modal: 400`, `--z-toast: 500`, etc.) and use it | invariant | replace | static |
142
165
 
143
166
  ---
144
167
 
145
168
  ## Motion
146
169
 
147
- | # | Pattern | Why it reads as AI-generated | Override condition |
148
- |---|---|---|---|
149
- | M1 | Bounce or elastic easing curves on UI elements | Bounce draws attention to the animation itself; UI motion should serve the action, not perform | Intentional playful affordance (game UI, onboarding celebration state); must be brief and non-repeating |
150
- | M2 | Animating layout properties (width, height, top, left, padding) | Triggers browser layout recalculation; janky on mid-range devices; always solvable with `transform` | Never — use `transform: scaleX()` for width-like effects, `transform: translateY()` for position |
151
- | M3 | Animating `<img>` element on hover (scale, filter, transform) | Triggers composite + image decode; often causes content shift | Animate the card's background, border, or shadow instead; leave the image static |
152
- | M4 | `transition: all` shorthand | Animates every property including layout, color, opacity simultaneously; unpredictable and expensive | Enumerate only the properties that should animate: `transition: transform 200ms ease-out, opacity 200ms` |
153
- | M5 | Missing `@media (prefers-reduced-motion: reduce)` alternative for any animation | Accessibility violation; vestibular disorders make animated UI unusable | Never acceptable; always add a `prefers-reduced-motion` variant — gentler animation (reduced distance/opacity) is preferred over `display: none` |
154
- | M6 | Every section fading and rising into view on scroll | The most recognisable interaction tell of the current generation; a reveal applied per section carries no information — the reader has already scrolled to the content, so the animation delays what they asked for | The reveal carries meaning: a stepped narrative where each beat must land before the next, or a long-form article using position to signal progress. A reveal on *every* section is a default, not a decision |
155
- | M7 | A radial highlight tracking the pointer across a dark hero | A `mousemove` handler feeding a radial gradient; it draws the eye to the cursor, which is the one thing on the page the user already knows the position of | The spotlight is the interaction — a genuine reveal mechanic, a canvas inspection tool, an accessibility magnifier. Ambience is not a purpose |
156
- | M8 | An interactive control that reduces its own opacity on hover | Reads as the element retreating from the pointer rather than responding to it, and a fade is the one hover treatment that *reduces* legibility at the moment the user is committing to the target — see the six interaction states in [`design-review`](../../src/skills/design-review/SKILL.md) | The fade communicates a real state change (an item being dismissed, a layer being peeled back). Hover must stay legible; brightness, background or border carry hover without spending contrast |
170
+ | # | Pattern | Why it reads as AI-generated | Override condition | Class | Remediation | Verification |
171
+ |---|---|---|---|---|---|---|
172
+ | M1 | Bounce or elastic easing curves on UI elements | Bounce draws attention to the animation itself; UI motion should serve the action, not perform | Intentional playful affordance (game UI, onboarding celebration state); must be brief and non-repeating | invariant | replace | feel |
173
+ | M2 | Animating layout properties (width, height, top, left, padding) | Triggers browser layout recalculation; janky on mid-range devices; always solvable with `transform` | Never — use `transform: scaleX()` for width-like effects, `transform: translateY()` for position | floor | replace | static |
174
+ | M3 | Animating `<img>` element on hover (scale, filter, transform) | Triggers composite + image decode; often causes content shift | Animate the card's background, border, or shadow instead; leave the image static | invariant | replace | render |
175
+ | M4 | `transition: all` shorthand | Animates every property including layout, color, opacity simultaneously; unpredictable and expensive | Enumerate only the properties that should animate: `transition: transform 200ms ease-out, opacity 200ms` | invariant | replace | static |
176
+ | M5 | Missing `@media (prefers-reduced-motion: reduce)` alternative for any animation | Accessibility violation; vestibular disorders make animated UI unusable | Never acceptable; always add a `prefers-reduced-motion` variant — gentler animation (reduced distance/opacity) is preferred over `display: none` | floor | delete → retune | static |
177
+ | M6 | Every section fading and rising into view on scroll | The most recognisable interaction tell of the current generation; a reveal applied per section carries no information — the reader has already scrolled to the content, so the animation delays what they asked for | The reveal carries meaning: a stepped narrative where each beat must land before the next, or a long-form article using position to signal progress. A reveal on *every* section is a default, not a decision | craft-presumption | delete → reduce | feel |
178
+ | M7 | A radial highlight tracking the pointer across a dark hero | A `mousemove` handler feeding a radial gradient; it draws the eye to the cursor, which is the one thing on the page the user already knows the position of | The spotlight is the interaction — a genuine reveal mechanic, a canvas inspection tool, an accessibility magnifier. Ambience is not a purpose | style-preference | delete | feel |
179
+ | M8 | An interactive control that reduces its own opacity on hover | Reads as the element retreating from the pointer rather than responding to it, and a fade is the one hover treatment that *reduces* legibility at the moment the user is committing to the target — see the six interaction states in [`design-review`](../../src/skills/design-review/SKILL.md) | The fade communicates a real state change (an item being dismissed, a layer being peeled back). Hover must stay legible; brightness, background or border carry hover without spending contrast | craft-presumption | retune | feel |
157
180
 
158
181
  ---
159
182
 
160
183
  ## Copy
161
184
 
162
- | # | Pattern | Why it reads as AI-generated | Override condition |
163
- |---|---|---|---|
164
- | CP1 | Em-dash (`—`) overuse | The #1 syntactic LLM signature; appears in AI copy at 3–5× the frequency of human editorial copy; maximum 2 per 500 words | Specific editorial voice that uses em-dashes deliberately; count them before shipping |
165
- | CP2 | Marketing buzzwords: streamline, empower, supercharge, world-class, enterprise-grade, seamlessly, robust, leverage | These words carry no information; an AI-slop test: replace each with nothing — the sentence still communicates the same thing | No override; if a word can be deleted without loss, delete it |
166
- | CP3 | Aphoristic manufactured contrast ("We don't do X. We do Y." / "Not just A, but B.") | A generated sentence rhythm that sounds like brand writing but carries no specificity | Aphoristic copy is legitimate when the contrast reveals a genuine product distinction; test by asking whether the Y side says something concrete |
167
- | CP4 | "Theater" framing — copy that announces what the product does without saying how or why | "The platform for modern teams." communicates nothing; it is content-shaped noise | Every tagline must pass the "so what" test: can a skeptic follow it with "but how?" and get a concrete answer? |
168
- | CP5 | Emoji-decoration in UI markup — 🚀/✅/🎉 prepending headings, buttons, list items, or CTAs (`🚀 Get Started`) | Performative-enthusiasm coating; the default-startup-template tell. No emoji is better than decorative emoji | Brand/`DESIGN.md` declares a systematic emoji strategy, or the emoji is functional (status indicator, category marker tied to real meaning) |
169
- | CP6 | Stock render subject named instead of the product's own subject matter — "floating abstract shapes", "gradient mesh", "glowing orb", "particle field", "futuristic HUD" | These name the default thing a render becomes when nobody decided what it is about; same delete-test as CP2, applied to art direction — swap the phrase for what the product actually shows and the brief says more | The subject IS abstract by decision (a generative-art surface, a data-driven field whose data is named), and the brief says which decision and why |
185
+ | # | Pattern | Why it reads as AI-generated | Override condition | Class | Remediation | Verification |
186
+ |---|---|---|---|---|---|---|
187
+ | CP1 | Em-dash (`—`) overuse | The #1 syntactic LLM signature; appears in AI copy at 3–5× the frequency of human editorial copy; maximum 2 per 500 words | Specific editorial voice that uses em-dashes deliberately; count them before shipping | craft-presumption | delete → reduce | static |
188
+ | CP2 | Marketing buzzwords: streamline, empower, supercharge, world-class, enterprise-grade, seamlessly, robust, leverage | These words carry no information; an AI-slop test: replace each with nothing — the sentence still communicates the same thing | No override; if a word can be deleted without loss, delete it | craft-presumption | delete | static |
189
+ | CP3 | Aphoristic manufactured contrast ("We don't do X. We do Y." / "Not just A, but B.") | A generated sentence rhythm that sounds like brand writing but carries no specificity | Aphoristic copy is legitimate when the contrast reveals a genuine product distinction; test by asking whether the Y side says something concrete | style-preference | delete → replace | judgment |
190
+ | CP4 | "Theater" framing — copy that announces what the product does without saying how or why | "The platform for modern teams." communicates nothing; it is content-shaped noise | Every tagline must pass the "so what" test: can a skeptic follow it with "but how?" and get a concrete answer? | craft-presumption | replace | judgment |
191
+ | CP5 | Emoji-decoration in UI markup — 🚀/✅/🎉 prepending headings, buttons, list items, or CTAs (`🚀 Get Started`) | Performative-enthusiasm coating; the default-startup-template tell. No emoji is better than decorative emoji | Brand/`DESIGN.md` declares a systematic emoji strategy, or the emoji is functional (status indicator, category marker tied to real meaning) | craft-presumption | delete | static |
192
+ | CP6 | Stock render subject named instead of the product's own subject matter — "floating abstract shapes", "gradient mesh", "glowing orb", "particle field", "futuristic HUD" | These name the default thing a render becomes when nobody decided what it is about; same delete-test as CP2, applied to art direction — swap the phrase for what the product actually shows and the brief says more | The subject IS abstract by decision (a generative-art surface, a data-driven field whose data is named), and the brief says which decision and why | craft-presumption | replace | judgment |
170
193
 
171
194
  ---
172
195
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@event4u/agent-config",
3
3
  "mcpName": "io.github.event4u-app/agent-config",
4
- "version": "14.18.0",
4
+ "version": "14.19.0",
5
5
  "description": "Governed skills, rules and commands for AI coding tools (Claude Code, Cursor, Windsurf, Copilot) — every claim machine-checked, including the counts in these badges.",
6
6
  "license": "MIT",
7
7
  "private": false,
@@ -17,7 +17,7 @@ it as `.augment/` (local projection).
17
17
  ├── contexts/ # Background context documents for the agent
18
18
  ├── guidelines/ # Coding guidelines by language (reference material)
19
19
  ├── rules/ # Always-active behavior rules (auto-loaded every conversation)
20
- ├── skills/ # On-demand expertise (matched by topic)
20
+ ├── skills/ # On-demand expertise (named or trigger-matched)
21
21
  └── templates/ # File templates for roadmaps, features, skills, etc.
22
22
  ```
23
23
 
@@ -174,6 +174,9 @@ ci_only:
174
174
  - id: check_finding_dispositions
175
175
  class: release
176
176
  reason: "adjudicates the release-PR self-review findings ledger, whose input JSON is produced by the self-review workflow job"
177
+ - id: report_skill_activation
178
+ class: release
179
+ reason: "declared for the reason `sweep_source_surfaces` is, and with the same nuance: it is not unreachable locally, it is outside the `task ci` closure on purpose. `src/config/release-gate-locality.yml` gives its release-validation job a `local:` command with `verify: true`, so `task release:verify` runs it before a release PR exists — and that is the only run that measures anything, because the transcript store the census reads is keyed on the directory it runs in and does not exist on a runner. Adding it to `task ci` would put an advisory report that gates on nothing into every local run"
177
180
  # road-to-source-silence Phase 5.3. Two modes, and the distinction is the whole
178
181
  # reason this is a declaration rather than a `task ci` entry.
179
182
  #
@@ -192,6 +195,9 @@ ci_only:
192
195
  # BEFORE the metadata is public, which is the only moment fixing it is free.
193
196
  # A contributor therefore discovers that failure locally by construction,
194
197
  # which is exactly the harm this checker exists to prevent.
198
+ - id: check_skill_activation_claim
199
+ class: release
200
+ reason: "the gating half of the same release job — it compares the published census figure against its record. Reachable locally through the same `task release:verify` row in src/config/release-gate-locality.yml, and outside the `task ci` closure for the same reason its sibling is"
195
201
  - id: sweep_source_surfaces
196
202
  class: network
197
203
  reason: "the CI invocation is the weekly five-surface census — needs `gh` for the PR surface and a full-depth clone for the ~9,000-commit trunk surface, writes counts not a verdict, and is deliberately non-failing; its GATE mode (`--gate-metadata`) is reachable locally on pre-push via src/scripts/hooks/prepush_metadata_sources.sh and by hand, so no failure is discoverable only after a push"
@@ -38,6 +38,18 @@
38
38
  # pending = not yet emitting `scanned:`. The guard REPORTS these
39
39
  # rather than skipping silently: its own partial coverage must be
40
40
  # visible, or it repeats the sin it exists to catch.
41
+ # ci_invocation
42
+ # optional repo-relative workflow path this row's `argv` is pinned
43
+ # to. Rule 2 above was an instruction to the author and nothing
44
+ # more — the guard ran whatever `argv` said and never looked at
45
+ # how CI calls the gate, so a row could drift from its workflow,
46
+ # or the workflow step could be deleted, and this file would keep
47
+ # reporting healthy coverage for a gate CI no longer runs. Where
48
+ # a row names a workflow, that file must contain an invocation of
49
+ # the gate whose argument list equals `argv` exactly. Opt-in per
50
+ # row because most gates here have several CI invocations with
51
+ # different arguments, and one `argv` cannot be identical to all
52
+ # of them.
41
53
  # canary optional mutation recipe (Phase 7) — see CANARY below
42
54
  #
43
55
  # COVERAGE — THE HONEST DENOMINATOR (re-measured 2026-08-28)
@@ -52,6 +64,14 @@
52
64
  # CI-identical argv, and 75 of those 78 are listed below.** The manifest is
53
65
  # complete for every gate that can carry a floor, covering ~25 % of the population.
54
66
  #
67
+ # RECOUNTED 2026-09-06, on the change that adds `lint_motion_authority_drift`.
68
+ # Recomputed on this tree rather than incremented, because the block below
69
+ # measured a tree that no longer exists: `count_gate_scripts()` returns **318**
70
+ # and `grep -c '^ - id:'` returns **89** (88 before this row), so 89/318 =
71
+ # 28.0 %. Five scripts and four rows arrived between 2026-09-03 and this change
72
+ # without a recount in between — the same drift every block here keeps
73
+ # describing.
74
+
55
75
  # RECOUNTED 2026-09-03, on the change that adds `check_agent_artifact_location`.
56
76
  # Recomputed on this tree rather than incremented: `count_gate_scripts()` returns
57
77
  # **313** and `grep -c '^ - id:'` returns **84** (83 before this row), so
@@ -380,6 +400,33 @@
380
400
  # contract is `dist == rewrite(src)`; `check_ci_local_parity`, whose corpus is
381
401
  # workflow files) carries no recipe and is reported UNPROVEN, never green.
382
402
  gates:
403
+ - id: lint_motion_authority_drift
404
+ argv: ["--quiet"]
405
+ min_scanned: 90
406
+ corpus: "every motion statement in the five carriers bound to `design-patterns.md` § Motion — 16 `motion.csv` rows, 84 `styles.csv` rows, 16 in-scope `design-rules-checklist.md` § 7 statements, 8 `ux-guidelines.csv` Animation rows and 4 `app-interface.csv` Animation rows: 128 at the time of writing. The floor is 90 rather than near 128 because `styles.csv` is 84 of the 128 and a style catalog legitimately loses rows; below 90 the scan has lost a whole carrier, which is the failure this floor exists to see. A `> 0` check could not: four of the five carriers could vanish and one 16-row CSV would still report success."
407
+ status: enforced
408
+ no_canary_reason: >-
409
+ A create-only canary cannot express this gate's violation. The corpus is
410
+ five NAMED files, so a planted file lands in no scanned root; every
411
+ violation it can have is a MODIFICATION — a duration widened past its
412
+ band, an easing family swapped, an allowlisted sentence reworded. Same
413
+ class as `lint_metric_consumers`. Discrimination is proven instead by the
414
+ gate's own `--self-test`: 13 cases, 8 rejecting, each driving the real CLI
415
+ against a throwaway five-carrier tree, and covering both directions of the
416
+ two override paths — an elastic curve refused without an override
417
+ condition and accepted with one, an overshoot refused in `styles.csv`
418
+ without a declared playful register and accepted with one.
419
+ note: >
420
+ road-to-one-motion-authority step 1.4. Six shipped carriers stated motion
421
+ timing and easing and disagreed; this gate makes § Motion the sole carrier
422
+ and checks the other five against it. It does not restate the authority's
423
+ numbers, it PARSES them, so the authority cannot drift away from its own
424
+ enforcement. HONEST LIMIT, stated in the gate's own docstring: the
425
+ authority bands five named interactions, and a dependent row naming an
426
+ interaction it does not band — scroll reveal, stagger list, parallax,
427
+ loading loop — is reported as `unbanded` and NOT failed, because failing it
428
+ would mean inventing a band the authority never stated. 12 rows are
429
+ unbanded today and the count is printed on the green path.
383
430
  - id: lint_metric_consumers
384
431
  argv: ["--quiet"]
385
432
  min_scanned: 5
@@ -687,6 +734,35 @@ gates:
687
734
  # canary. The gate also carries a `--self-test` with seven rejecting cases;
688
735
  # the two prove different things and neither replaces the other.
689
736
 
737
+ - id: check_finding_dispositions
738
+ argv: []
739
+ ci_invocation: .github/workflows/consistency.yml
740
+ min_scanned: 0
741
+ corpus: "recorded findings in the ledger for the CURRENT package version (agents/evidence/release-findings/<package.json version>.json)"
742
+ status: enforced
743
+ no_canary_reason: >
744
+ Both violations this gate detects are ABSENCES — a ledger file that is not
745
+ there, and an empty ledger missing its `no_findings_reason`. The canary
746
+ contract plants a file at a path that must not already exist, and neither
747
+ absence can be produced that way: an extra ledger planted under a canary
748
+ name changes nothing, because the bare invocation reads only
749
+ `<package.json version>.json`. The negative controls that do exist are in
750
+ `tests/scripts/check_finding_dispositions.test.ts` — present-empty-with-reason
751
+ exits 0, present-empty-without-reason exits 1, and the released-vs-unreleased
752
+ discriminator is pinned in both directions so the absent-ledger branch cannot
753
+ silently invert.
754
+ note: >
755
+ The floor is 0 and CANNOT be higher, which is the honest reading of this
756
+ row rather than a weakening of it. The corpus is one release's finding
757
+ count, and zero findings is a legitimate state — 14.17.0 and 14.18.0 both
758
+ legitimately record zero, because their self-review never completed. What
759
+ this row buys is therefore not a floor: it is `ci_invocation`. Before it,
760
+ the gate had exactly one caller, on a `release/*` head branch that is
761
+ deleted after the merge, and its removal would have been noticed by
762
+ nothing — which is how two releases shipped with no ledger. The row now
763
+ fails if `.github/workflows/consistency.yml` stops calling the gate, or
764
+ calls it with arguments this manifest does not reproduce.
765
+
690
766
  - id: check_review_schema
691
767
  argv: []
692
768
  min_scanned: 1
@@ -2531,3 +2607,97 @@ gates:
2531
2607
 
2532
2608
  - [ ] **1.1 A planted step.** Carries all three shape signals so the
2533
2609
  gate must report it from a directory that is not the roadmap root.
2610
+
2611
+ - id: lint_readme_size
2612
+ argv: ["--quiet"]
2613
+ ci_invocation: .github/workflows/consistency.yml
2614
+ min_scanned: 1
2615
+ corpus: >
2616
+ exactly one file — README.md — so the floor is 1 and a scan of 0 means the
2617
+ gate ran somewhere the file does not exist rather than that it found
2618
+ nothing to say. The gate is registered here because the failure it exists
2619
+ to end was not a wrong measurement but an unwatched one: an earlier
2620
+ roadmap brought this file under control, archived complete on 2026-05-18,
2621
+ and the file drifted back over the following three months while the only
2622
+ guard — a line budget with 112 lines of headroom — stayed green through
2623
+ every step. A budget that is not registered can be removed as quietly as
2624
+ it was added, which is how the previous fix stopped holding.
2625
+ no_canary_reason: >-
2626
+ the corpus is ONE file at a fixed path, and the canary contract is
2627
+ create-only. A planted `README.md` would have to overwrite the real one to
2628
+ land in scope, so the plant is either impossible or destructive; every
2629
+ violation this gate can have is a MODIFICATION of that single file — words
2630
+ added, a fence pushed down, lines grown. Discrimination is proven by
2631
+ `--self-test` instead (4 cases, 3 rejecting, one per budget), which is the
2632
+ stronger control here: a single planted file could only exercise one of the
2633
+ three budgets, and measuring one while two regress is precisely the failure
2634
+ that made this gate insufficient in the first place.
2635
+ - id: lint_reasoning_fixtures
2636
+ argv: ["--quiet"]
2637
+ min_scanned: 21
2638
+ corpus: >
2639
+ the 21 rows of tests/reasoning-layer-eval/trigger-fixtures.json. The floor
2640
+ is the live count rather than a number below it because the corpus is a
2641
+ committed fixture set that changes only by a deliberate edit — a reading
2642
+ below 21 means a row was dropped, which is exactly what this gate should
2643
+ refuse to pass over. The gate exists because the corpus had ZERO
2644
+ validators: both were deleted in the Python retirement while five
2645
+ surviving references still told a reader to run them, and nothing in
2646
+ Taskfile.yml or .github/workflows/ called the corpus at all.
2647
+ no_canary_reason: >-
2648
+ the corpus is a single committed JSON file, and the canary contract is
2649
+ create-only. A planted file lands in no row; every violation this gate can
2650
+ have is a MODIFICATION of that file — a key added, a field dropped, an
2651
+ enum value changed. Discrimination is proven by `--self-test` instead
2652
+ (4 cases, 3 rejecting), which drives the real CLI over a fixture root for
2653
+ each of the three malformation classes the roadmap names by hand: a sixth
2654
+ key, a missing `discipline`, and `host: "medium"`.
2655
+
2656
+ - id: lint_skill_trigger_contradiction
2657
+ argv: ["--quiet"]
2658
+ min_scanned: 250
2659
+ corpus: >
2660
+ every SKILL.md under src/skills/ — 299 at the time of writing. The floor
2661
+ sits at 250 rather than at 299 because the skill count moves with ordinary
2662
+ work in both directions, while a reading far below it means the walk lost
2663
+ the directory rather than that skills were deleted. The gate exists
2664
+ because three skills shipped saying "explicit request only" in the
2665
+ description a catalogue reads while publishing an "## Auto-trigger
2666
+ keywords" section nothing in src/ parses — so the operational-looking half
2667
+ was the inert one, and a reader met two opposite answers in one file.
2668
+ no_canary_reason: >-
2669
+ a create-only canary CAN plant a SKILL.md, but planting one that carries
2670
+ both halves would prove only that the matcher fires, which the self-test
2671
+ already proves over a fixture root — and the plant would land inside
2672
+ src/skills/, a projected tree, where a stray file reddens unrelated
2673
+ generator and count gates before this one is reached. Discrimination is
2674
+ proven by `--self-test` (3 cases, 1 rejecting) covering both directions:
2675
+ the contradicting pair reds, and each half ALONE stays green, which is
2676
+ what stops a matcher that fired on the keyword section alone.
2677
+
2678
+ - id: check_skill_activation_claim
2679
+ argv: []
2680
+ ci_invocation: .github/workflows/release-validation.yml
2681
+ min_scanned: 1
2682
+ corpus: >
2683
+ exactly one claim block — `skill-activation-census-zero` in
2684
+ `docs/CLAIMS.md` — checked against the census record it was derived from,
2685
+ so the floor is 1 and a scan of 0 means the claim was renamed or deleted
2686
+ rather than that the gate found nothing to say. The gate deliberately does
2687
+ NOT floor the census's own corpus: the report that reads it is
2688
+ `report_skill_activation`, which is not a gate and must not become one, and
2689
+ registering a `report_*` id here would fork the population
2690
+ `_lib/gate_population.ts` classifies by prefix.
2691
+ status: enforced
2692
+ no_canary_reason: >-
2693
+ the corpus is ONE claim block at a fixed id, and the canary contract is
2694
+ create-only. A planted second `docs/CLAIMS.md` would have to overwrite the
2695
+ real one to land in scope, and every violation this gate can have is a
2696
+ MODIFICATION of that one block — a figure moved, a population redrawn, a
2697
+ figure the sentence stops stating. Discrimination is proven by
2698
+ `--self-test` instead (4 cases, 3 rejecting, one per class), and end to end
2699
+ by hand: editing the published turn count, the human-named population count
2700
+ and the `last_verified` date each exits 1, and restoring each exits 0.
2701
+ What the ROW buys on top of that is `ci_invocation` — the census had a
2702
+ Taskfile target and no caller at all, so its removal would have been
2703
+ noticed by nothing.
@@ -34,9 +34,9 @@
34
34
  "note": "The nine active non-draft roadmaps that predate the `relates:` requirement (road-to-agentic-engineering-assurance, road-to-component-library-lifecycle, road-to-council-evidence-integrity, road-to-frontend-fidelity-calibration, road-to-monorepo-scope-and-detection, road-to-observability-plate, road-to-role-scoped-spawn-profiles, road-to-skill-delivery-over-mcp, road-to-standing-payload-diet). This is a NEW obligation rather than a repaired scan root, and the ratchet is the point: `depends:` shipped in the template with nothing requiring it and measured 0 adoption, so requiring `relates:` retroactively across the estate would be an owner-reserved write with no analysis behind the edges it would have to invent (held as step 4.3, deferred). Clearing an entry here is one roadmap at a time: run `agent-config roadmap:context` on it, write the rows the probe surfaces, and lower the count."
35
35
  },
36
36
  "check_source_size_budget": {
37
- "count": 18068,
38
- "landed": "2026-09-05",
39
- "note": "Lowered 18,245 -> 18,230 on 2026-09-02 by drain/publication-integrity-option-a. Phase 2 Option A moved the authoring instruction out of the generator: release.ts, which sits ~530 lines PAST the 1500 cap so every line there costs one excess, loses the comment emission, a 16-line HEAD_DEFAULT doc block and its alias const, and gains a shorter writer docstring. The doc block moved to _lib/release_material.ts, which is 289 lines and therefore UNDER the cap, so the same prose now costs nothing. Net -15. The branch first came in at +8 and paid its own way down rather than raising the baseline.",
37
+ "count": 18062,
38
+ "landed": "2026-09-06",
39
+ "note": "Lowered 18,068 -> 18,062 on 2026-09-06 by drain/host-enforcement-truth. Collapsing the five per-host binding constants in install.ts into src/scripts/hooks/host_lowering.yaml removed 29 lines from a file ~3,900 past the 1500 cap, where every line costs one excess; the journal's surface column and its extracted migration helper gave 23 back. Lowered 18,245 -> 18,230 on 2026-09-02 by drain/publication-integrity-option-a. Phase 2 Option A moved the authoring instruction out of the generator: release.ts, which sits ~530 lines PAST the 1500 cap so every line there costs one excess, loses the comment emission, a 16-line HEAD_DEFAULT doc block and its alias const, and gains a shorter writer docstring. The doc block moved to _lib/release_material.ts, which is 289 lines and therefore UNDER the cap, so the same prose now costs nothing. Net -15. The branch first came in at +8 and paid its own way down rather than raising the baseline.",
40
40
  "reason": "Lowered 18,230 -> 18,209 on 2026-09-03 by drain/artifact-location. `doctor --strict` needed 16 lines of integration wiring in cmd_doctor.ts, which sits ~2,100 lines PAST the 1500 cap so every line there costs one excess. The branch first came in at +137 and paid its own way down rather than raising the baseline: the strict semantics, threshold, kill switch, payload helper and argv fragments moved to _cli/_doctor_strict.ts (~160 lines, under the cap, costs nothing), taking it to +15; then the 37-line drift renderer moved to _cli/_doctor_emit.ts. Net -21. The renderer was chosen deliberately: pure rendering, two dependencies, no argparse involvement and therefore none of the byte-parity risk the parser surface carries against the retired Python implementation; nothing outside cmd_doctor.ts imported it. An AI council on the residual split (a: raise by 15 / d: descope the feature); neither was taken, because a parallel branch demonstrated the same day that paying down is achievable in this codebase, which is the evidence the split lacked. This gate's own test comment prescribes it: a commit that splits a god-file lowers the excess and must carry the lowered baseline. MERGE RECONCILIATION 2026-09-03, 18,209 -> 18,205: drain/wired-instruments and drain/artifact-location each lowered from the SAME 18,230 base in parallel -- the branch to 18,226 (net -4, run_continuation_hook.ts's termination ladder moved to _lib/continuation_ladder.ts and runtime_journal.ts's prose to _lib/outcome_vocabularies.ts, both under the cap) and main to 18,209 (net -21, the doctor --strict split above). Taking either side alone is wrong in a different direction: main's value silently discards the branch's earned -4, and the branch's value RAISES the baseline against main and weakens the gate. The committed number is neither -- it is the gate's own output on the merged tree, where both reductions apply. THIS LOCAL READING IS ADMISSIBLE HERE, and the discriminator matters because this file's ci-parity:local-only entry records a 165 -> 164 lowering that was reverted for exactly the opposite case: that count depends on how the environment resolves the projection surface, whereas this gate walks only SOURCE_ROOTS = ['src'] (check_source_size_budget.ts:88) counting excess lines over the 1500 ceiling, and `git status --porcelain -uall -- src/` reported ZERO untracked files, so a clean CI checkout of this commit measures the same 18,205. If CI reads anything else, CI's number is the correct one and this note is the record of why. LOWERED 18,205 -> 18,193 on 2026-09-03 by fix/release-curation-ask. The change adds a fifth publication guard, and its own additions landed at +10 excess: release.ts 2,008 -> 2,018, every line of which is past the 1500 cap. Paid down rather than baselined, in the two ways this gate's doctrine prescribes. The guard's rationale (~13 lines of docstring, including why prMerged is a parameter rather than a call-site if) lives in release_publication.ts, 915 lines and therefore UNDER the cap, so the prose costs nothing; and the two pure text helpers _splitlines and _rstrip moved out of release.ts into release_env.ts (333 lines, also under the cap), taking release.ts to 1,996 -- 12 BELOW where it started. The branch came in at +10 and leaves at -12. Recorded while moving them: _splitlines is duplicated a third and fourth time at adoption_report.ts:106 and readme_linter.ts:418; collapsing all three onto one definition touches three modules and was left out of this change rather than done silently. LOWERED 18,193 -> 18,180 on 2026-09-03 by fix/release-branch-reuse. The step-1 branch decision -- twenty lines of if/else in release.ts -- became one call to checkout_release_branch in release_publication.ts, which is 1,019 lines and therefore UNDER the 1500 cap, so the twenty lines plus a ~35-line rationale now cost nothing. release.ts 1,996 -> 1,983, every one of those lines being past the cap. The drill grew 765 -> 918 for two new scenarios and the snapshot/restore that keeps them from writing the real CHANGELOG, and it is under the cap too. A change that adds a feature and lowers the excess is the shape this gate's doctrine asks for; the alternative was a +13 raise for logic that had no business in the god-file. LOWERED 18,180 -> 18,068 on 2026-09-05 by fix/release-gate-producer-parity. The change wires the governance-mix obligation into the writer and the two local guards and adds a release-gate locality registry; its own additions to release.ts landed at +19 excess, every one of those lines past the 1500 cap. Paid down rather than baselined, in the way this entry's doctrine prescribes: the 132-line test-count-trend unit (_TEST_COUNT_LINE_RE, _TEST_LIST_MAX_BUFFER, _count_from_list_result, _count_tests_current, _previous_test_count_from_changelog, _render_test_trend_line) moved from release.ts into release_publication.ts, which is under the cap and where it now belongs on merit rather than only on arithmetic -- the same diff makes the missing `Tests: N` footer a section-level publication blocker, so the writer that emits the footer and the guard that refuses its absence sit in one module. release.ts 1,983 -> 1,871. All four names are re-exported from release.ts, so tests/scripts/release.test.ts is unchanged. LOCAL READING, ADMISSIBLE ON THIS ENTRY'S OWN DISCRIMINATOR: the gate walks only SOURCE_ROOTS=['src'] counting excess over the ceiling, and `git status --porcelain -uall -- src/` reports ZERO untracked files with the two new src/ files staged, so a clean CI checkout of this commit measures the same 18,068. If CI reads anything else, CI's number is the correct one and this note is the record of why."
41
41
  },
42
42
  "ci-parity:local-only": {
@@ -127,3 +127,15 @@ jobs:
127
127
  verify: false
128
128
  script: ~
129
129
  guard: ~
130
+
131
+ activation-census:
132
+ # The one row where the local command asserts MORE than the job it mirrors,
133
+ # not less, so `partial` does not apply: the transcript store the census
134
+ # reads is keyed on the directory it runs in and does not exist on a runner,
135
+ # so CI can only re-take an empty reading and check the published figure
136
+ # against its record. Run locally, the same command reads the real store,
137
+ # which is the only place the census means anything.
138
+ local: './scripts-run src/scripts/report_skill_activation && ./scripts-run src/scripts/check_skill_activation_claim'
139
+ verify: true
140
+ script: src/scripts/report_skill_activation.ts
141
+ guard: ~
@@ -2881,7 +2881,7 @@ function _run_checks(
2881
2881
  'claude-command-wrappers': _check_claude_command_wrappers,
2882
2882
  'surface-state': _check_surface_state,
2883
2883
  'hook-wiring': _check_hook_wiring,
2884
- ...(runtime_wiring.wiringRunners({ packageRoot: _package_root(), iterOverrides: () => iter_setting_overrides({ cwd: project_root }) }) as unknown as Record<string, CheckRunner>),
2884
+ ...(runtime_wiring.wiringRunners({ packageRoot: _package_root(), projectRoot: project_root, iterOverrides: () => iter_setting_overrides({ cwd: project_root }) }) as unknown as Record<string, CheckRunner>),
2885
2885
  ...(install_reach.reachRunners({ projectRoot: project_root, resolvableVersion: _current_package_version() }) as unknown as Record<string, CheckRunner>),
2886
2886
  'stale-orphans': _check_stale_orphans,
2887
2887
  overrides: () => _check_overrides(project_root),
@@ -2967,7 +2967,7 @@ function _run_checks_no_manifest(
2967
2967
  'claude-command-wrappers': _check_claude_command_wrappers,
2968
2968
  'surface-state': _check_surface_state,
2969
2969
  'hook-wiring': _check_hook_wiring,
2970
- ...(runtime_wiring.wiringRunners({ packageRoot: _package_root(), iterOverrides: () => iter_setting_overrides({ cwd: project_root }) }) as unknown as Record<string, CheckRunner>),
2970
+ ...(runtime_wiring.wiringRunners({ packageRoot: _package_root(), projectRoot: project_root, iterOverrides: () => iter_setting_overrides({ cwd: project_root }) }) as unknown as Record<string, CheckRunner>),
2971
2971
  ...(install_reach.reachRunners({ projectRoot: project_root, resolvableVersion: _current_package_version() }) as unknown as Record<string, CheckRunner>),
2972
2972
  'stale-orphans': _check_stale_orphans,
2973
2973
  // BOTH registries need the id. Registering only the first one crashed with
@@ -17,7 +17,7 @@
17
17
  * file (legacy copy-paste mode)
18
18
  * --launch <host> spawn a fresh interactive host session after
19
19
  * generating (claude: hook-seeded; codex: handoff as
20
- * initial prompt — no hook surface there)
20
+ * initial prompt — this package binds no hook there)
21
21
  * --llm narrative-polish seam; v1: clear not-implemented error
22
22
  * --root <path> repo root override (default: cwd)
23
23
  *
@@ -446,7 +446,7 @@ export async function main(argv: string[] | null = null, options: MainOptions =
446
446
  const installed_version = options.installed_version || _detect_installed_version();
447
447
  const state_path = options.state_path || update_check.DEFAULT_STATE_PATH;
448
448
 
449
- // AGENT_CONFIG_OFFLINE=1 (set by `install.py --offline`) is honored as an
449
+ // AGENT_CONFIG_OFFLINE=1 (set by `agent-config install --offline`) is honored as an
450
450
  // env-level kill-switch. Mirrors cmd_versions.py.
451
451
  const offline = args.offline || process.env['AGENT_CONFIG_OFFLINE'] === '1';
452
452
 
@@ -426,7 +426,7 @@ export function main(argv: string[] | null = null, options: MainOptions = {}): n
426
426
  const err = options.err ?? _stderrSink();
427
427
  const opts = _parse(argv ?? process.argv.slice(2), out, err);
428
428
 
429
- // AGENT_CONFIG_OFFLINE=1 (set by `install.py --offline`) is honored as a
429
+ // AGENT_CONFIG_OFFLINE=1 (set by `agent-config install --offline`) is honored as a
430
430
  // global kill-switch even when the per-command --offline flag is absent.
431
431
  const offline = opts.offline || process.env['AGENT_CONFIG_OFFLINE'] === '1';
432
432