@event4u/agent-config 9.34.0 → 9.36.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 (112) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/CHANGELOG.md +64 -77
  3. package/CONTRIBUTING.md +23 -14
  4. package/dist/agent-src/commands/analyze/reference-repo.md +135 -42
  5. package/dist/agent-src/commands/roadmap/next.md +37 -5
  6. package/dist/agent-src/contexts/augment-infrastructure.md +1 -1
  7. package/dist/agent-src/rules/design-review-after-ui-write.md +53 -6
  8. package/dist/agent-src/rules/improve-before-implement.md +1 -1
  9. package/dist/agent-src/rules/ui-audit-gate.md +35 -0
  10. package/dist/agent-src/scripts/roadmap_gates.ts +119 -4
  11. package/dist/agent-src/skills/design-system-capture/SKILL.md +2 -2
  12. package/dist/agent-src/skills/existing-ui-audit/SKILL.md +1 -1
  13. package/dist/agent-src/skills/fe-design/SKILL.md +84 -12
  14. package/dist/agent-src/skills/fe-design/evals/triggers.json +11 -3
  15. package/dist/agent-src/skills/prediction-pool-optimizer/SKILL.md +5 -5
  16. package/dist/agent-src/skills/react-shadcn-ui/SKILL.md +1 -1
  17. package/dist/agent-src/skills/skill-writing/SKILL.md +24 -0
  18. package/dist/agent-src/skills/subagent-orchestration/prompts/README.md +29 -0
  19. package/dist/agent-src/skills/testing-anti-patterns/SKILL.md +25 -0
  20. package/dist/agent-src/templates/AGENTS.md +1 -1
  21. package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
  22. package/dist/agent-src/templates/scripts/memory_lookup.ts +1 -2
  23. package/dist/agent-src/templates/scripts/memory_signal.ts +2 -4
  24. package/dist/agent-src/templates/scripts/work_engine/hooks/builtin/decision_gate.ts +35 -5
  25. package/dist/agent-src/templates/scripts/work_engine/hooks/builtin/staged_confirmation.ts +252 -0
  26. package/dist/agent-src/templates/scripts/work_engine/hooks/builtin/staged_confirmation_store.ts +307 -0
  27. package/dist/cli/registry.js +2 -2
  28. package/dist/cli/registry.js.map +1 -1
  29. package/dist/cli-delegate/{chunk-YPAWXXXR.js → chunk-55RI5UXK.js} +17 -0
  30. package/dist/cli-delegate/{chunk-UVKNIJH2.js → chunk-JP53LNVT.js} +12 -0
  31. package/dist/cli-delegate/{chunk-736JCWLM.js → chunk-M2DPGFZ4.js} +10 -0
  32. package/dist/cli-delegate/{chunk-MA4GFWDY.js → chunk-NR7HSN3P.js} +2 -2
  33. package/dist/cli-delegate/{chunk-REMEZSVP.js → chunk-SFGARMAB.js} +13 -3
  34. package/dist/cli-delegate/{chunk-DS6BLDMG.js → chunk-U7CMAIOC.js} +7 -2
  35. package/dist/cli-delegate/cmd_conformance.js +4 -4
  36. package/dist/cli-delegate/cmd_doctor.js +3 -3
  37. package/dist/cli-delegate/cmd_export.js +3 -3
  38. package/dist/cli-delegate/cmd_handoff.js +1 -1
  39. package/dist/cli-delegate/cmd_migrate.js +1 -1
  40. package/dist/cli-delegate/cmd_prune.js +3 -3
  41. package/dist/cli-delegate/cmd_refresh.js +1 -1
  42. package/dist/cli-delegate/cmd_session_recycle.js +8 -1
  43. package/dist/cli-delegate/cmd_settings_migrate.js +3 -3
  44. package/dist/cli-delegate/cmd_settings_set.js +1 -1
  45. package/dist/cli-delegate/cmd_sync.js +3 -3
  46. package/dist/cli-delegate/cmd_uninstall.js +3 -3
  47. package/dist/cli-delegate/cmd_upgrade.js +1 -1
  48. package/dist/cli-delegate/cmd_validate.js +3 -3
  49. package/dist/discovery/deprecation-report.md +1 -1
  50. package/dist/discovery/discovery-manifest.json +14 -14
  51. package/dist/discovery/discovery-manifest.json.sha256 +1 -1
  52. package/dist/discovery/discovery-manifest.summary.md +1 -1
  53. package/dist/discovery/dormancy-report.md +1 -1
  54. package/dist/discovery/orphan-report.md +1 -1
  55. package/dist/discovery/packs.json +2 -2
  56. package/dist/discovery/trust-report.md +1 -1
  57. package/dist/discovery/workspaces.json +2 -2
  58. package/dist/hooks/dispatch.js +789 -401
  59. package/dist/install/install.mjs +21 -4
  60. package/dist/mcp/registry-manifest.json +2 -2
  61. package/dist/mcp/server.json +2 -2
  62. package/dist/mcp/server.mjs +99 -7
  63. package/dist/router.json +1 -1
  64. package/dist/server/schemas/settings.js +6 -0
  65. package/dist/server/schemas/settings.js.map +1 -1
  66. package/docs/contracts/settings-classes.md +5 -2
  67. package/docs/guidelines/agent-infra/agent-interaction-and-decision-quality.md +46 -4
  68. package/package.json +2 -2
  69. package/src/agent-src/scripts/roadmap_gates.ts +119 -4
  70. package/src/agent-src/templates/AGENTS.md +1 -1
  71. package/src/agent-src/templates/scripts/memory_lookup.ts +1 -2
  72. package/src/agent-src/templates/scripts/memory_signal.ts +2 -4
  73. package/src/agent-src/templates/scripts/work_engine/hooks/builtin/decision_gate.ts +35 -5
  74. package/src/agent-src/templates/scripts/work_engine/hooks/builtin/staged_confirmation.ts +252 -0
  75. package/src/agent-src/templates/scripts/work_engine/hooks/builtin/staged_confirmation_store.ts +307 -0
  76. package/src/config/agent-settings.template.yml +43 -2
  77. package/src/config/gate-coverage.yml +12 -0
  78. package/src/config/quorum-attendance-budget.json +10 -2
  79. package/src/scripts/_cli/cmd_doctor.ts +32 -0
  80. package/src/scripts/_cli/cmd_migrate.ts +26 -3
  81. package/src/scripts/_cli/cmd_refresh.ts +29 -0
  82. package/src/scripts/_cli/cmd_session_recycle.ts +40 -2
  83. package/src/scripts/_dispatch.bash +6 -2
  84. package/src/scripts/_lib/hook_settings.ts +79 -0
  85. package/src/scripts/_lib/session_register.ts +67 -1
  86. package/src/scripts/_lib/ui_surface.ts +97 -0
  87. package/src/scripts/ai_council/config.ts +60 -0
  88. package/src/scripts/ai_council/events_log.ts +103 -4
  89. package/src/scripts/ai_council/quorum.ts +83 -6
  90. package/src/scripts/capture_skill_catalogue.ts +483 -0
  91. package/src/scripts/council_cli.ts +36 -1
  92. package/src/scripts/hook_manifest.yaml +18 -3
  93. package/src/scripts/hooks/concern_registry.ts +2 -0
  94. package/src/scripts/hooks/design_slop_hook.ts +1 -1
  95. package/src/scripts/hooks/turn_end_gate_hook.ts +172 -11
  96. package/src/scripts/hooks/ui_route_nudge_hook.ts +250 -0
  97. package/src/scripts/lint_framework_leakage.ts +195 -28
  98. package/src/scripts/lint_framework_leakage_allowlist.json +94 -86
  99. package/src/scripts/lint_roadmap_complexity.ts +60 -0
  100. package/src/scripts/lint_rule_skill_pack_reach.ts +263 -0
  101. package/src/scripts/memory_lookup.ts +1 -2
  102. package/src/scripts/report_consultation_rate.ts +358 -0
  103. package/src/scripts/report_skill_activation.ts +5 -1
  104. package/src/scripts/schemas/command.schema.json +4 -0
  105. package/src/scripts/schemas/skill.schema.json +4 -0
  106. package/src/scripts/session_register_hook.ts +238 -18
  107. package/src/scripts/sessions_cli.ts +377 -12
  108. package/src/server/schemas/settings.ts +12 -0
  109. /package/dist/agent-src/skills/design-system-capture/{reference → references}/design-system-json.md +0 -0
  110. /package/dist/agent-src/skills/prediction-pool-optimizer/{reference → references}/ev-fixtures.md +0 -0
  111. /package/dist/agent-src/skills/prediction-pool-optimizer/{reference → references}/odds-and-bonus.md +0 -0
  112. /package/dist/agent-src/skills/react-shadcn-ui/{reference → references}/registry.md +0 -0
@@ -6,6 +6,14 @@ description: "UI written or changed — review it against the design contract be
6
6
  triggers:
7
7
  - path_prefix: "resources/views/"
8
8
  - path_prefix: "resources/js/"
9
+ - path_prefix: "components/"
10
+ - path_prefix: "src/components/"
11
+ - path_prefix: "pages/"
12
+ - file_pattern: "*.vue"
13
+ - file_pattern: "*.svelte"
14
+ - file_pattern: "*.tsx"
15
+ - file_pattern: "*.jsx"
16
+ - file_pattern: "*.blade.php"
9
17
  - keyword: "component"
10
18
  - keyword: "design token"
11
19
  routes_to:
@@ -20,6 +28,14 @@ collision_ok:
20
28
  "design token": "same surface, opposite side: ui-audit-gate inventories tokens before the write, this reviews what the write emitted"
21
29
  "resources/views/": "both halves of one UI loop fire on the same path — audit before, review after; neither substitutes for the other"
22
30
  "resources/js/": "both halves of one UI loop fire on the same path — audit before, review after; neither substitutes for the other"
31
+ "components/": "both halves of one UI loop fire on the same path — audit before, review after; neither substitutes for the other"
32
+ "src/components/": "both halves of one UI loop fire on the same path — audit before, review after; neither substitutes for the other"
33
+ "pages/": "both halves of one UI loop fire on the same path — audit before, review after; neither substitutes for the other"
34
+ "*.vue": "one component file, two obligations: inventory before the write, review after it"
35
+ "*.svelte": "one component file, two obligations: inventory before the write, review after it"
36
+ "*.tsx": "one component file, two obligations: inventory before the write, review after it"
37
+ "*.jsx": "one component file, two obligations: inventory before the write, review after it"
38
+ "*.blade.php": "one template file, two obligations: inventory before the write, review after it"
23
39
  # obligation: line 39
24
40
  obligation_frequency: "per-edit"
25
41
  ---
@@ -87,18 +103,49 @@ enforcement. So this rule ships `enforced_by: none`, the same honesty boundary
87
103
  for their own obligations, rather than pretending a satisfiable-by-assertion
88
104
  condition is a gate.
89
105
 
106
+ **A runtime carrier now exists, and it does not change that.** The
107
+ `ui-route-nudge` PreToolUse concern emits one warning on a UI write with no
108
+ design consultation latched this session.
109
+
110
+ It runs **parallel** to this rule rather than consuming it. Nothing in the tree
111
+ reads this frontmatter at session time; the concern decides what counts as a UI
112
+ surface from `src/scripts/_lib/ui_surface.ts`, and the `keyword:` triggers here
113
+ still have no runtime consumer. A test — not a code path — keeps the two from
114
+ drifting: `ui_rule_triggers.test.ts` asserts every `file_pattern` declared here
115
+ is accepted by that predicate.
116
+
117
+ Warn-only, capped at two nudges per session, default-OFF, bound only on hosts
118
+ carrying a `pre_tool_use` slot. A nudge that can be ignored is not enforcement,
119
+ so `enforced_by:` stays `none` — it moves the day a mechanism can refuse, not
120
+ the day a reminder appears. `agent-config hooks:status` answers whether the
121
+ concern is bound on the host you are actually on.
122
+
90
123
  What that leaves, and it is the useful part: the write side now has a named
91
124
  obligation and a route to the skill that discharges it, where before it had
92
125
  neither. Full enforcement needs the work-engine `review`/`polish` gates (which
93
126
  already act on `a11y_violation` and `token_violation` findings) or pack-scoped
94
127
  rule projection — a maintainer decision, never an automated one.
95
128
 
96
- One asymmetry worth stating: `design-review` lives in `engineering-base` while
97
- this rule is scoped to `frontend-design`. The route still resolves — a rule may
98
- name a skill from another pack — but a consumer with `frontend-design` and
99
- without `engineering-base` gets the obligation without the skill. That is why
100
- `accessibility-auditor` is a second route: it is the part of the review that
101
- must not depend on the pairing.
129
+ One asymmetry worth stating, in the direction it actually runs: `design-review`
130
+ lives in `engineering-base` while this rule is scoped to `frontend-design`. The
131
+ route still resolves — a rule may name a skill from another pack.
132
+
133
+ The case this paragraph used to describe — `frontend-design` installed without
134
+ `engineering-base`, so the obligation arrives without the skill — **cannot
135
+ occur**: `frontend-design` declares `requires: [engineering-base]`, and the
136
+ resolver expands that graph transitively. The real gap is the reverse and was
137
+ undocumented: an **`engineering-base`-only** install (a plain `laravel` or
138
+ `react` consumer, since both only *suggest* `frontend-design`) carries
139
+ `existing-ui-audit`, `design-review` and `fe-design` while carrying **no rule
140
+ that routes to them**. It bites only where pack-scoped rule projection is
141
+ active — `rules.packs` ships inactive, so a default install still receives both
142
+ rules — which makes it a latent defect rather than a live one, and exactly the
143
+ kind that surfaces the day the projection axis is switched on.
144
+
145
+ `lint_rule_skill_pack_reach` is the invariant that keeps this honest: no rule
146
+ may route to a skill a pack-legal install of that rule's packs cannot receive.
147
+ `accessibility-auditor` remains a second route for the independent reason that
148
+ the a11y half of the review should not depend on the pairing at all.
102
149
 
103
150
  ## Failure modes
104
151
 
@@ -44,7 +44,7 @@ Before implementing:
44
44
 
45
45
  ## Demand gate — should this exist? (build / defer)
46
46
 
47
- On a "build me an app / add this feature" ask, ONE reflexive pre-check before the three checks: **who asked · what breaks if unbuilt · what's the evidence?** Recommend **build** only at a real retention/activation blocker (evidence, not anxiety); else **defer/validate**, naming the missing evidence. Advisory — "just build it" proceeds immediately; no network lookup. Hierarchy + build/defer table: guideline § 8-pre.
47
+ On a "build me an app / add this feature" ask, ONE reflexive pre-check before the three checks: **who asked · what breaks if unbuilt · what's the evidence?** **Read the addressee first** — `project.audience` (`agent-config settings:get project.audience`, absent → `public`): the demand hierarchy measures **market** demand and is meaningless where no market is intended, so at `self` the check is inert and the work is classified `L-self` → **build**. With a market: recommend **build** at a real retention/activation blocker (evidence, not anxiety); else **defer/validate**, naming the missing evidence. Never gate a roadmap on a user population the project is not meant to have. Advisory — "just build it" proceeds immediately; no network lookup. Hierarchy + build/defer table: guideline § 8-pre.
48
48
 
49
49
  ## The solution-size ladder — stop at the first rung that works
50
50
 
@@ -6,6 +6,14 @@ description: "Writing/editing UI — components, screens, layouts, design tokens
6
6
  triggers:
7
7
  - path_prefix: "resources/views/"
8
8
  - path_prefix: "resources/js/"
9
+ - path_prefix: "components/"
10
+ - path_prefix: "src/components/"
11
+ - path_prefix: "pages/"
12
+ - file_pattern: "*.vue"
13
+ - file_pattern: "*.svelte"
14
+ - file_pattern: "*.tsx"
15
+ - file_pattern: "*.jsx"
16
+ - file_pattern: "*.blade.php"
9
17
  - keyword: "component"
10
18
  - keyword: "design token"
11
19
  routes_to:
@@ -21,6 +29,14 @@ collision_ok:
21
29
  "design token": "same surface, opposite side: this inventories tokens before the write, design-review-after-ui-write reviews what the write emitted"
22
30
  "resources/views/": "both halves of one UI loop fire on the same path — audit before, review after; neither substitutes for the other"
23
31
  "resources/js/": "both halves of one UI loop fire on the same path — audit before, review after; neither substitutes for the other"
32
+ "components/": "both halves of one UI loop fire on the same path — audit before, review after; neither substitutes for the other"
33
+ "src/components/": "both halves of one UI loop fire on the same path — audit before, review after; neither substitutes for the other"
34
+ "pages/": "both halves of one UI loop fire on the same path — audit before, review after; neither substitutes for the other"
35
+ "*.vue": "one component file, two obligations: inventory before the write, review after it"
36
+ "*.svelte": "one component file, two obligations: inventory before the write, review after it"
37
+ "*.tsx": "one component file, two obligations: inventory before the write, review after it"
38
+ "*.jsx": "one component file, two obligations: inventory before the write, review after it"
39
+ "*.blade.php": "one template file, two obligations: inventory before the write, review after it"
24
40
  # obligation: line 39
25
41
  obligation_frequency: "per-edit"
26
42
  ---
@@ -91,6 +107,25 @@ Full enforcement requires the dispatcher (or the `frontend-design` pack once
91
107
  pack-scoped rule projection is enabled; that flip is a maintainer decision,
92
108
  never an automated one).
93
109
 
110
+ **A runtime carrier now exists, and it does not change the verdict.** The
111
+ `ui-route-nudge` PreToolUse concern warns once on a UI write with no design
112
+ consultation latched this session.
113
+
114
+ It is a **parallel** mechanism, not a consumer of the triggers above — the
115
+ distinction matters and the earlier wording here got it wrong. The concern
116
+ decides "is this a UI surface" from `src/scripts/_lib/ui_surface.ts`; nothing
117
+ in the tree parses this frontmatter at session time, so the `keyword:` triggers
118
+ still have no runtime consumer at all, and the two sets can drift. What holds
119
+ them together is a test, not a dependency: `ui_rule_triggers.test.ts` asserts
120
+ every `file_pattern` declared here is accepted by that predicate. The predicate
121
+ is deliberately wider (it also covers `.css`, `.scss`, `.astro`), because a
122
+ measurement denominator and a routing trigger are not the same population.
123
+
124
+ Warn-only, capped at two per session, default-OFF, bound only where a
125
+ `pre_tool_use` slot exists. A reminder that can be ignored is not a gate, so
126
+ `enforced_by:` stays `none`. Run `agent-config hooks:status` for the host you
127
+ are on rather than trusting this sentence.
128
+
94
129
  ## Failure modes
95
130
 
96
131
  - Writing the component first and "thinking about reuse later".
@@ -46,6 +46,13 @@
46
46
  * ./agent-config gates --all # every open blocker, grouped by owner
47
47
  * ./agent-config gates --json # machine-readable
48
48
  * ./agent-config gates --reply # reply-close form; empty when none
49
+ * ./agent-config gates --pending # staged actions awaiting confirmation
50
+ *
51
+ * `--pending` reads a different source — the staged-confirmation store, not the
52
+ * roadmap tree — and stays out of `--reply` on purpose. ADR-222 fixes the
53
+ * reply-close form at exactly ONE decision rendered in full; folding a second
54
+ * source into it would silently change which decision "the one" is, and that
55
+ * contract is not this step's to move.
49
56
  */
50
57
 
51
58
  import * as path from 'node:path';
@@ -58,6 +65,8 @@ import {
58
65
  blocker_needs_user as needsUser,
59
66
  type Blocker,
60
67
  } from './update_roadmap_progress.js';
68
+ import { listPending } from '../templates/scripts/work_engine/hooks/builtin/staged_confirmation_store.js';
69
+ import type { StagedAction } from '../templates/scripts/work_engine/hooks/builtin/staged_confirmation.js';
61
70
 
62
71
  const _HERE = fileURLToPath(import.meta.url);
63
72
 
@@ -327,6 +336,89 @@ function renderJson(entries: readonly Entry[], all: boolean): string {
327
336
  );
328
337
  }
329
338
 
339
+ /**
340
+ * `--pending`: staged actions awaiting a human confirmation.
341
+ *
342
+ * A second source under the same verb, and the reason it belongs here rather
343
+ * than in a new command is the verb's own definition — "open decisions that
344
+ * need you, as actions". A staged irreversible action IS one, and the most
345
+ * literal kind: the roadmap blockers above are decisions a human owes a plan,
346
+ * this is a decision a human owes an action that is already loaded and waiting.
347
+ *
348
+ * Expired stages are counted, never listed as actionable. An expired stage
349
+ * cannot execute (`stageStatus` derives that from the clock, not from a sweep
350
+ * having run), so rendering it beside a live one would put two rows in front of
351
+ * the reader where only one is a decision.
352
+ */
353
+ function renderPending(root: string, now: number): string {
354
+ const rows = listPending(root, now);
355
+ const live = rows.filter((r) => r.status === 'pending');
356
+ const expired = rows.filter((r) => r.status === 'expired');
357
+ const lines: string[] = [];
358
+
359
+ if (live.length === 0) {
360
+ lines.push(
361
+ expired.length === 0
362
+ ? 'No staged actions awaiting confirmation.'
363
+ : `Nothing awaits your confirmation. ${expired.length} stage` +
364
+ `${expired.length !== 1 ? 's' : ''} expired unconfirmed — the action never fired.`,
365
+ );
366
+ return lines.join('\n') + '\n';
367
+ }
368
+
369
+ lines.push(
370
+ `${live.length} staged action${live.length !== 1 ? 's' : ''} await` +
371
+ `${live.length === 1 ? 's' : ''} your confirmation` +
372
+ (expired.length > 0 ? ` · ${expired.length} expired unconfirmed` : ''),
373
+ );
374
+ let n = 0;
375
+ for (const { stage } of live) {
376
+ n += 1;
377
+ lines.push(...renderStage(stage, n));
378
+ }
379
+ lines.push('');
380
+ return lines.join('\n') + '\n';
381
+ }
382
+
383
+ function renderStage(stage: StagedAction, index: number): string[] {
384
+ const head = `${index} · ${stage.action}`;
385
+ const tail = `token: ${stage.token}`;
386
+ const dashes = Math.max(3, WIDTH - head.length - tail.length - 6);
387
+ return [
388
+ '',
389
+ `── ${head} ${'─'.repeat(dashes)} ${tail}`.trimEnd(),
390
+ // The object is the first field on purpose: an approval that does not
391
+ // name the exact object is the thing non-destructive-by-default forbids,
392
+ // so it is what the reader must see before anything else.
393
+ ...field('Object:', stage.object),
394
+ ...field('Staged by:', stage.source),
395
+ ...field('Expires:', stage.expires_at),
396
+ ];
397
+ }
398
+
399
+ function renderPendingJson(root: string, now: number): string {
400
+ const rows = listPending(root, now);
401
+ return (
402
+ JSON.stringify(
403
+ {
404
+ awaitingYou: rows.filter((r) => r.status === 'pending').length,
405
+ expired: rows.filter((r) => r.status === 'expired').length,
406
+ staged: rows.map(({ stage, status }) => ({
407
+ token: stage.token,
408
+ action: stage.action,
409
+ object: stage.object,
410
+ source: stage.source,
411
+ stagedAt: stage.staged_at,
412
+ expiresAt: stage.expires_at,
413
+ status,
414
+ })),
415
+ },
416
+ null,
417
+ 2,
418
+ ) + '\n'
419
+ );
420
+ }
421
+
330
422
  function _isDir(p: string): boolean {
331
423
  try {
332
424
  return fs.statSync(p).isDirectory();
@@ -340,8 +432,8 @@ function _isDir(p: string): boolean {
340
432
  * `agents/roadmaps`, otherwise fall back to the git toplevel (monorepo
341
433
  * sub-project support).
342
434
  */
343
- function _resolveRepoRoot(start: string): string {
344
- if (_isDir(path.join(start, 'agents', 'roadmaps'))) {
435
+ function _resolveRepoRoot(start: string, marker = path.join('agents', 'roadmaps')): string {
436
+ if (_isDir(path.join(start, marker))) {
345
437
  return start;
346
438
  }
347
439
  try {
@@ -351,7 +443,7 @@ function _resolveRepoRoot(start: string): string {
351
443
  timeout: 10_000,
352
444
  });
353
445
  const top = (r.stdout || '').trim();
354
- if (r.status === 0 && top !== '' && _isDir(path.join(top, 'agents', 'roadmaps'))) {
446
+ if (r.status === 0 && top !== '' && _isDir(path.join(top, marker))) {
355
447
  return top;
356
448
  }
357
449
  } catch {
@@ -365,6 +457,18 @@ function main(argv?: readonly string[]): number {
365
457
  const all = args.includes('--all');
366
458
  const json = args.includes('--json');
367
459
  const reply = args.includes('--reply');
460
+ const pending = args.includes('--pending');
461
+
462
+ // `--pending` resolves against `agents/runtime/`, not `agents/roadmaps/`,
463
+ // and is answered BEFORE the roadmaps-directory exit below: a staged action
464
+ // is independent of whether this project plans in roadmaps at all, so
465
+ // "no roadmaps here" is not an answer to "what awaits my confirmation".
466
+ if (pending) {
467
+ const root = _resolveRepoRoot(process.cwd(), path.join('agents', 'runtime'));
468
+ const now = Date.now();
469
+ process.stdout.write(json ? renderPendingJson(root, now) : renderPending(root, now));
470
+ return 0;
471
+ }
368
472
 
369
473
  const repoRoot = _resolveRepoRoot(process.cwd());
370
474
  const roadmapRoot = path.join(repoRoot, 'agents', 'roadmaps');
@@ -412,5 +516,16 @@ if (_isCliEntry()) {
412
516
  process.exitCode = main();
413
517
  }
414
518
 
415
- export { main, needsUser, regroupTodo, wrap, collectEntries, render, renderJson, renderReply };
519
+ export {
520
+ main,
521
+ needsUser,
522
+ regroupTodo,
523
+ wrap,
524
+ collectEntries,
525
+ render,
526
+ renderJson,
527
+ renderReply,
528
+ renderPending,
529
+ renderPendingJson,
530
+ };
416
531
  export type { Entry };
@@ -199,7 +199,7 @@ external static-extraction tool and hand the result to this skill as a
199
199
  `design-system.json` artifact. We own the **import contract**, not the crawler:
200
200
  the package never ships the Playwright runtime, a font-bundler, or a `.skill`
201
201
  auto-installer (out of scope). Full schema is lazy-loaded from
202
- [`reference/design-system-json.md`](reference/design-system-json.md) — read it
202
+ [`references/design-system-json.md`](references/design-system-json.md) — read it
203
203
  only when an import is requested.
204
204
 
205
205
  **Import procedure:**
@@ -281,7 +281,7 @@ most common handoff ambiguity.
281
281
  When the goal is to extract this repo's design system as a **reusable package
282
282
  artifact** (not just DESIGN.md prose), the extraction meets this floor — the
283
283
  handoff shape emitted alongside `design-system.json`
284
- ([import contract](reference/design-system-json.md)).
284
+ ([import contract](references/design-system-json.md)).
285
285
 
286
286
  - **Contract contents.** A complete extraction carries: a root guide / manifest;
287
287
  token CSS (or the stack's equivalent); the typography / colour / spacing /
@@ -256,7 +256,7 @@ Load on demand once the audit needs the pitfall catalog:
256
256
  **Design-system export (current-repo source):** when the goal is to seed
257
257
  `DESIGN.md` from *this* repo, emit the inventory as a `design-system.json`
258
258
  artifact (the import contract in
259
- [`design-system-capture`](../design-system-capture/reference/design-system-json.md))
259
+ [`design-system-capture`](../design-system-capture/references/design-system-json.md))
260
260
  and hand it to `design-system-capture` — the same shape an external extraction
261
261
  tool produces, so the import path is uniform.
262
262
 
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  model_tier: medium
3
3
  name: fe-design
4
- description: "Reference for frontend-design heuristics — component architecture, layout patterns, form/table design, responsive strategy, a11y, UX principles. Stack-agnostic; cited by directives/ui/design.ts."
4
+ description: "Frontend design heuristics — and, outside the ticket engine, the loop that applies them: audit, brief, build, review. Use when building or changing any UI, not only when planning one."
5
5
  personas:
6
6
  - frontend-engineer
7
7
  domain: engineering
@@ -11,15 +11,82 @@ packs:
11
11
  - engineering-base
12
12
  ---
13
13
 
14
- # Frontend Design Skill (Reference)
15
-
16
- ## Positioning — reference, not executor
17
-
18
- `fe-design` is a **universal reference skill**, not an executor. It carries
19
- stack-agnostic heuristics that the UI directive set cites; it does **not**
20
- own the flow.
21
-
22
- | Concern | Owner |
14
+ # Frontend Design Skill
15
+
16
+ ## Ad-hoc mode — outside the engine, YOU run this loop now
17
+
18
+ One question decides which mode you are in: **is a `/implement-ticket` run
19
+ dispatching this?**
20
+
21
+ | Situation | Who owns the UI write | This skill is |
22
+ |---|---|---|
23
+ | A ticketed run — the UI directive set is dispatching | `directives/ui/*` | a reference it cites |
24
+ | Anything else — "build me the page", "improve this form", "implement the approved design" | **you, in this turn** | the executor |
25
+
26
+ Outside the engine, nothing else owns the design quality of a UI write. Reading
27
+ this skill and then writing the UI from priors is the failure it exists to
28
+ prevent — the loop below is not optional context, it is the work.
29
+
30
+ **Skip only when the change is `ui-trivial`, decidable off the diff:** ≤ 1 file,
31
+ ≤ 5 changed lines, no new component, no new state, no new dependency. Anything
32
+ else runs the loop.
33
+
34
+ ### The loop
35
+
36
+ 1. **Audit first** — run [`existing-ui-audit`](../existing-ui-audit/SKILL.md).
37
+ What already exists (components, tokens, layout conventions) outranks every
38
+ heuristic below. Reinventing an existing component is the #1 failure mode.
39
+ 2. **Brief** — cover the same five keys the engine requires before any code:
40
+ `layout`, `components`, `states`, `microcopy`, `a11y`. `states` means all
41
+ five of `empty`, `loading`, `error`, `success`, `disabled` — a brief missing
42
+ one is unfinished, not concise.
43
+ 3. **Build** — against the audit's primitives, in the project's stack.
44
+ 4. **Review** — run [`design-review`](../design-review/SKILL.md) before calling
45
+ it done, and scope the verdict honestly: render-scoped when you can render
46
+ it, otherwise explicitly static-scoped, naming which checks actually ran.
47
+ "Looks good" with neither scope named is a verdict without evidence.
48
+
49
+ ### The heuristics — here, not one hop away
50
+
51
+ The load-bearing subset, inline so that "loaded" means the content is in
52
+ context. Depth stays in [`references/design-patterns.md`](references/design-patterns.md).
53
+
54
+ 1. **Audit outranks heuristic.** A token, primitive or convention the audit
55
+ found wins over anything on this list; say `[audit override]` when it does.
56
+ 2. **Reuse tokens, never raw values.** A hex, font or px literal where the
57
+ audit found a token is off-brand by construction.
58
+ 3. **All five states are designed.** `empty` is a helpful message, not a blank
59
+ region; `loading` prefers a skeleton over a spinner (it shows structure, so
60
+ it reads as faster); `error` says what to do next.
61
+ 4. **Labels are always visible.** No placeholder-only inputs.
62
+ 5. **Validate on blur and on submit**, never on every keystroke; the message
63
+ goes below the field and is specific.
64
+ 6. **Mobile-first, and 320 px actually works.** Default styles are the small
65
+ viewport; complexity is added at larger breakpoints, never removed at
66
+ smaller ones.
67
+ 7. **A11y minimums are non-negotiable:** 4.5:1 contrast for text (3:1 large),
68
+ every interactive element reachable by Tab, a visible focus ring, semantic
69
+ elements over `div`, and an `aria-label` wherever there is no visible text.
70
+ 8. **Every action gets feedback.** Users read silence as failure.
71
+ 9. **Prefer undo over a confirmation dialog** — confirmations get clicked
72
+ through; an undo affordance actually protects the user.
73
+ 10. **Tables:** numbers right-aligned, text left-aligned, a designed empty
74
+ state, and a sticky header on anything long enough to scroll.
75
+ 11. **No placeholder microcopy ships.** Lorem ipsum, `TODO`, and
76
+ `[Your text here]` are unfinished output, not drafts.
77
+
78
+ Before proposing a direction, run the anti-slop scan named under
79
+ [Anti-slop discipline](#anti-slop-discipline) below.
80
+
81
+ ## Positioning — reference inside the engine, executor outside it
82
+
83
+ Inside a `/implement-ticket` run, `fe-design` is a **universal reference
84
+ skill**: it carries stack-agnostic heuristics the UI directive set cites, and
85
+ it does not own the flow. The ownership table below applies **to that mode**.
86
+ Outside the engine the table has no dispatcher behind it — the ad-hoc loop
87
+ above is the owner instead.
88
+
89
+ | Concern | Owner (engine-mediated runs) |
23
90
  |---|---|
24
91
  | Layout / states / microcopy lock | [`directives/ui/design.ts`](../../templates/scripts/work_engine/directives/ui/design.ts) |
25
92
  | Stack-dispatched implementation | [`directives/ui/apply.ts`](../../templates/scripts/work_engine/directives/ui/apply.ts) → `blade-ui` / `livewire` / `flux` / `react-shadcn-ui` |
@@ -37,12 +104,17 @@ Cite this skill when:
37
104
  - Making responsive design decisions
38
105
  - Reviewing UI for accessibility and usability
39
106
 
40
- Do NOT use this skill to:
107
+ **Inside a ticketed run**, do NOT use this skill to:
41
108
 
42
109
  - Implement components — that is the apply-step's stack-dispatched skill
43
110
  - Audit an existing UI — that is `existing-ui-audit`
44
111
  - Drive the full UI flow — that is the `directives/ui/` orchestrator
45
112
 
113
+ Outside a ticketed run, the first and third lines invert: no dispatcher is
114
+ running, so implementation quality and the flow are yours. The second holds in
115
+ both modes — the audit is always `existing-ui-audit`; you invoke it, you do not
116
+ replace it.
117
+
46
118
  > **Resource-first, before taste.** Any request to recreate / redesign / mock /
47
119
  > prototype / improve an existing UI runs the
48
120
  > [resource-first context gate](../existing-ui-audit/SKILL.md#resource-first-context-gate-design-fidelity)
@@ -129,4 +201,4 @@ approach or explicitly invoke the override condition in the design brief.
129
201
  - Do NOT skip mobile viewport testing.
130
202
  - Do NOT use fixed pixel widths for responsive layouts.
131
203
  - Do NOT ignore accessibility requirements.
132
- - Do NOT use this skill as an executor — it is a reference cited by `directives/ui/design.ts`.
204
+ - Do NOT use this skill as an executor INSIDE a ticketed run — there the executor is `directives/ui/design.ts` and this is the reference it cites. Outside one, the ad-hoc loop at the top of this file IS the executor; do NOT read the reference and then write the UI from priors.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "skill": "fe-design",
3
- "last_eval": "2026-07-10",
4
- "description": "5 should-trigger + 5 should-not-trigger queries for the variation & canvas planning layer (road-to-design-artifact-fidelity Phase 4). Should-trigger covers exploration / variation planning and UI-design decisions where fe-design plans BEFORE generating (give me directions, explore, plan the flow, pattern choice). Should-not-trigger covers near-misses that must NOT invoke design planning: pure implementation of an already-selected design (apply-step), inventorying existing UI (existing-ui-audit), a surgical one-line edit, and non-UI/backend work.",
3
+ "last_eval": "2026-08-12",
4
+ "description": "Should-trigger covers BOTH halves of this skill's two modes: planning/exploration (give me directions, explore, plan the flow, pattern choice) AND — new — ad-hoc implementation, because outside a ticketed run no other artefact owns the design quality of a UI write. The implementation query used to be labelled should-not-trigger on the reasoning that it belongs to the apply step; that step only exists as a dispatch target inside the engine, so the label routed ad-hoc implementation prompts to nobody. Should-not-trigger keeps the genuine near-misses: inventorying existing UI (existing-ui-audit owns it in both modes), a ui-trivial one-line edit, and non-UI work.",
5
5
  "queries": [
6
6
  {
7
7
  "q": "give me three directions for the pricing page",
@@ -25,7 +25,15 @@
25
25
  },
26
26
  {
27
27
  "q": "implement the selected checkout design in React exactly as approved",
28
- "trigger": false
28
+ "trigger": true
29
+ },
30
+ {
31
+ "q": "build the settings page",
32
+ "trigger": true
33
+ },
34
+ {
35
+ "q": "improve this form, it feels clunky",
36
+ "trigger": true
29
37
  },
30
38
  {
31
39
  "q": "what form components do we already have in the codebase?",
@@ -94,7 +94,7 @@ biggest publicly-viewable books**, not a single portal:
94
94
  each special/award market a bonus question needs) from several books.
95
95
  Odds-comparison aggregators (Oddschecker, Oddsportal / Betexplorer) show
96
96
  many books at once; supplement with named books. Concrete book list and
97
- the weighting recipe live in [`reference/odds-and-bonus.md`](reference/odds-and-bonus.md).
97
+ the weighting recipe live in [`references/odds-and-bonus.md`](references/odds-and-bonus.md).
98
98
  2. **De-vig each book** independently (remove its margin) → per-book implied
99
99
  probabilities. Raw odds sum to >100%; never treat them as probabilities.
100
100
  3. **Aggregate with a healthy weighting**, not a blind average: weight
@@ -203,7 +203,7 @@ Respect all strategy limits from step 1 (max identical tips, etc.).
203
203
 
204
204
  Walk the **step-1 checklist** and answer **each** entry. Pick the method by
205
205
  question type — full taxonomy + per-type method in
206
- [`reference/odds-and-bonus.md`](reference/odds-and-bonus.md):
206
+ [`references/odds-and-bonus.md`](references/odds-and-bonus.md):
207
207
 
208
208
  - **Tournament structure** (group winners, KO rounds, finalists, champion):
209
209
  use real **outright market odds** ("to win group", "to reach final",
@@ -253,7 +253,7 @@ deadline is the only hard constraint.
253
253
 
254
254
  3. **Group standings and the full bracket** where the event has them.
255
255
  4. **Self-check note** — (a) confirm the tips reconcile with
256
- [`reference/ev-fixtures.md`](reference/ev-fixtures.md) (known pool rules +
256
+ [`references/ev-fixtures.md`](references/ev-fixtures.md) (known pool rules +
257
257
  market odds → a known-good EV tip); (b) confirm the bonus table has the
258
258
  **same number of rows as the step-1 checklist** — a shorter table means a
259
259
  question was dropped. If your method disagrees with a fixture, your method
@@ -310,10 +310,10 @@ gate — the skill never enters or submits anything.
310
310
 
311
311
  - [`/prediction-pool`](../../commands/prediction-pool.md) — the orchestrator (event,
312
312
  persistence, Playwright entry, gates).
313
- - [`reference/odds-and-bonus.md`](reference/odds-and-bonus.md) — the major-book
313
+ - [`references/odds-and-bonus.md`](references/odds-and-bonus.md) — the major-book
314
314
  list + sharpness-weighted consensus recipe, and the bonus / award / special
315
315
  question taxonomy with a per-type method.
316
- - [`reference/ev-fixtures.md`](reference/ev-fixtures.md) — known-good
316
+ - [`references/ev-fixtures.md`](references/ev-fixtures.md) — known-good
317
317
  rules+odds → EV examples.
318
318
  - `node_modules/@event4u/agent-config/src/scripts/prediction-pool/score_ev.ts` —
319
319
  the executed exact-score EV optimiser (step 4a; λ + rule → EV-max scoreline).
@@ -132,7 +132,7 @@ The default path is the bundled `scripts/shadcn_add.ts` CLI wrapper + reading
132
132
  `components.json` — it works on most shadcn projects and stays the default.
133
133
  The modern registry model is an **opt-in enhancement**; do not add round-trips
134
134
  to every component op. Full JSON-schema + namespace detail is lazy-loaded from
135
- [`reference/registry.md`](reference/registry.md) — read it only on this path,
135
+ [`references/registry.md`](references/registry.md) — read it only on this path,
136
136
  not on the vanilla `add`.
137
137
 
138
138
  **`shadcn info --json` handshake** — run it as the grounding step **when** the
@@ -106,6 +106,30 @@ Do NOT create a skill or rule for:
106
106
  * If two skills overlap heavily → merge
107
107
  * If a skill becomes "read the guideline" → it lost its purpose, restore the workflow
108
108
 
109
+ ### Progressive disclosure — the directory is `references/`, plural
110
+
111
+ When a skill needs depth the workflow does not, the depth goes in a
112
+ **`references/`** subdirectory beside `SKILL.md` — that exact name, plural. It
113
+ was two spellings across nine skills until 2026-08-12 because nothing here
114
+ named one; the split cost a rename, not a decision.
115
+
116
+ * **Body:** the trigger, the workflow, the ownership statement, and every
117
+ heuristic that changes a write decision. A skill whose body is a map to its
118
+ own content is loaded and not applied.
119
+ * **`references/`:** corpora, lookup tables, long schemas, worked examples —
120
+ read on the path that needs them, never on every activation.
121
+
122
+ Splitting is not a size escape hatch. The measured estate (n=289, 2026-08-12):
123
+ median 1,077 words, p90 1,867, **only 6 skills above 2,500 and 4 above 3,000**.
124
+ So a skill at 1,500 words is ordinary, and moving prose out of the body to hit
125
+ a number makes it worse. Split when the material is genuinely
126
+ consulted-on-demand; keep the load-bearing part in the body even when that
127
+ leaves the file long.
128
+
129
+ A `references/` split does not change the gates that already apply: a
130
+ `token_budget_class: rich` skill still meets its 3,500-token ceiling on the
131
+ body alone.
132
+
109
133
  ## Procedure
110
134
 
111
135
  ### 0. Inspect, then run the Drafting Protocol
@@ -23,6 +23,35 @@ Every prompt cites the status taxonomy in
23
23
  ends with the **return-envelope** instruction so the subagent's reply
24
24
  validates against that schema.
25
25
 
26
+ ### UI-shaped slices carry the design context, or name its absence
27
+
28
+ A delegated worker's context is whatever the dispatch prompt carries. For a
29
+ slice that writes UI, that has a specific consequence: the audit findings and
30
+ the design brief live in the orchestrator's context, and nothing moves them
31
+ across the dispatch boundary on its own. A worker that receives "build the
32
+ settings page" and nothing else will build it from priors — the same failure
33
+ the design skills exist to prevent, one layer down.
34
+
35
+ So a UI-shaped dispatch prompt carries, in its `CONTEXT FILES:` section:
36
+
37
+ - the **audit findings** from `existing-ui-audit` (components, tokens, layout
38
+ conventions the project already has), and
39
+ - the **design brief** — `layout`, `components`, `states`, `microcopy`, `a11y`,
40
+ with all five states covered.
41
+
42
+ When either is genuinely absent, **say so in the prompt** rather than omitting
43
+ it silently: `no audit findings available — run existing-ui-audit first`. A
44
+ worker told the context is missing can ask for it; a worker told nothing
45
+ assumes there was nothing to know.
46
+
47
+ **This is model-carried, and the reason is worth stating.** An orchestration
48
+ record carries `ts`, `spawn_count`, `token_delta`, `tier_chosen` and
49
+ `task_class` — it has no field able to hold a prompt, deliberately, for the same
50
+ privacy-by-construction reason the telemetry event has none. So no lint can join
51
+ a dispatch prompt against its record to check this clause: the data a check
52
+ would need does not exist and should not be made to exist. The obligation is
53
+ stated here and carried by the orchestrator, not enforced.
54
+
26
55
  ## Prompt-cache discipline
27
56
 
28
57
  When dispatching sibling subagents (e.g. `do-in-parallel` with N independent