@event4u/agent-config 12.1.0 → 13.0.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 (105) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/CHANGELOG.md +151 -0
  3. package/README.md +1 -1
  4. package/dist/agent-src/commands/cost/report.md +23 -0
  5. package/dist/agent-src/contexts/execution/subagent-routing.md +148 -33
  6. package/dist/agent-src/contexts/subagent-configuration.md +69 -7
  7. package/dist/agent-src/rules/autonomous-execution.md +1 -1
  8. package/dist/agent-src/rules/design-fidelity.md +1 -1
  9. package/dist/agent-src/rules/design-review-after-ui-write.md +10 -5
  10. package/dist/agent-src/rules/evaluator-independence.md +27 -5
  11. package/dist/agent-src/rules/git-history-discipline.md +5 -1
  12. package/dist/agent-src/rules/missing-skill-recovery.md +89 -0
  13. package/dist/agent-src/rules/ui-audit-gate.md +9 -4
  14. package/dist/agent-src/scripts/archive_completed_roadmaps.ts +33 -0
  15. package/dist/agent-src/scripts/update_roadmap_progress.ts +17 -2
  16. package/dist/agent-src/skills/authz-review/SKILL.md +4 -0
  17. package/dist/agent-src/skills/design-system-capture/SKILL.md +4 -0
  18. package/dist/agent-src/skills/design-system-capture/references/snapshot-preference-order.md +77 -0
  19. package/dist/agent-src/skills/merge-conflicts/SKILL.md +4 -0
  20. package/dist/agent-src/skills/prompt-optimizer/SKILL.md +17 -1
  21. package/dist/agent-src/skills/roadmap-writing/SKILL.md +4 -4
  22. package/dist/agent-src/skills/systematic-debugging/SKILL.md +4 -0
  23. package/dist/agent-src/skills/threat-modeling/SKILL.md +3 -0
  24. package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
  25. package/dist/cli-delegate/{chunk-O52B4F5S.js → chunk-3B72LSZL.js} +1 -1
  26. package/dist/cli-delegate/{chunk-55WZN5VF.js → chunk-EVTY7ISR.js} +2 -2
  27. package/dist/cli-delegate/{chunk-K34QTSD5.js → chunk-N7FEST6D.js} +17 -17
  28. package/dist/cli-delegate/{chunk-LKZ5DOYW.js → chunk-XURJ4AQC.js} +11 -3
  29. package/dist/cli-delegate/cmd_conformance.js +2 -2
  30. package/dist/cli-delegate/cmd_doctor.js +2 -2
  31. package/dist/cli-delegate/cmd_export.js +2 -2
  32. package/dist/cli-delegate/cmd_handoff.js +1 -1
  33. package/dist/cli-delegate/cmd_prune.js +2 -2
  34. package/dist/cli-delegate/cmd_settings_migrate.js +2 -2
  35. package/dist/cli-delegate/cmd_settings_set.js +1 -1
  36. package/dist/cli-delegate/cmd_sync.js +2 -2
  37. package/dist/cli-delegate/cmd_uninstall.js +2 -2
  38. package/dist/cli-delegate/cmd_validate.js +2 -2
  39. package/dist/discovery/deprecation-report.md +1 -1
  40. package/dist/discovery/discovery-manifest.json +43 -22
  41. package/dist/discovery/discovery-manifest.json.sha256 +1 -1
  42. package/dist/discovery/discovery-manifest.summary.md +3 -3
  43. package/dist/discovery/dormancy-report.md +1 -1
  44. package/dist/discovery/orphan-report.md +1 -1
  45. package/dist/discovery/packs.json +6 -5
  46. package/dist/discovery/trust-report.md +2 -2
  47. package/dist/discovery/workspaces.json +5 -4
  48. package/dist/hooks/dispatch.js +936 -346
  49. package/dist/install/install.mjs +12 -4
  50. package/dist/mcp/registry-manifest.json +3 -3
  51. package/dist/mcp/server.json +2 -2
  52. package/dist/mcp/server.mjs +1192 -160
  53. package/dist/router.json +1 -1
  54. package/dist/server/schemas/settings.js +2 -2
  55. package/dist/server/schemas/settings.js.map +1 -1
  56. package/docs/guidelines/design-fidelity-mechanics.md +21 -0
  57. package/docs/guidelines/design-handover-extraction.md +87 -0
  58. package/docs/guidelines/prompt-templates.md +24 -1
  59. package/package.json +1 -1
  60. package/src/agent-src/scripts/archive_completed_roadmaps.ts +33 -0
  61. package/src/agent-src/scripts/update_roadmap_progress.ts +17 -2
  62. package/src/config/evaluator-budgets.json +5 -4
  63. package/src/config/gate-coverage.yml +33 -11
  64. package/src/config/gate-violation-baselines.json +9 -3
  65. package/src/config/hook-token-budget.json +8 -1
  66. package/src/scripts/_lib/bench_ab_complexity.ts +439 -0
  67. package/src/scripts/_lib/bench_ab_safety_tier.ts +142 -0
  68. package/src/scripts/_lib/bench_ab_scoring_v2.ts +28 -3
  69. package/src/scripts/_lib/bench_ab_search_adherence.ts +423 -0
  70. package/src/scripts/_lib/bench_ab_size_claim.ts +206 -0
  71. package/src/scripts/_lib/bench_ab_workspace.ts +111 -0
  72. package/src/scripts/_lib/skill_catalogue.ts +258 -5
  73. package/src/scripts/_lib/tier_budget_routing.ts +31 -331
  74. package/src/scripts/ai_council/clients.ts +21 -21
  75. package/src/scripts/bench_ab_v2_complexity.ts +252 -0
  76. package/src/scripts/bench_ab_v2_run.ts +3 -42
  77. package/src/scripts/bench_ab_v2_safety.ts +296 -0
  78. package/src/scripts/bench_ab_v2_search.ts +343 -0
  79. package/src/scripts/bench_ab_v2_stats.ts +191 -0
  80. package/src/scripts/build_archive_index.ts +383 -0
  81. package/src/scripts/capture_skill_catalogue.ts +107 -2
  82. package/src/scripts/check_source_size_budget.ts +428 -0
  83. package/src/scripts/check_suppression_hygiene.ts +5 -1
  84. package/src/scripts/cost/backfill_rates.mjs +298 -0
  85. package/src/scripts/cost/budget.mjs +12 -32
  86. package/src/scripts/cost_summary.ts +25 -0
  87. package/src/scripts/external_sources_denylist.json +1 -0
  88. package/src/scripts/hook_manifest.yaml +28 -2
  89. package/src/scripts/hooks/concern_registry.ts +2 -0
  90. package/src/scripts/hooks/skill_route_hook.ts +226 -0
  91. package/src/scripts/hooks/ui_route_nudge_hook.ts +6 -3
  92. package/src/scripts/lint_design_antipattern_parity.ts +40 -6
  93. package/src/scripts/lint_harvest_provenance.ts +35 -1
  94. package/src/scripts/lint_rule_skill_pack_reach.ts +43 -0
  95. package/src/scripts/lint_skill_router_head.ts +21 -0
  96. package/src/scripts/mcp_server/consumer_tool_catalog.json +26 -3
  97. package/src/scripts/mcp_server/tool_catalog_source.ts +6 -24
  98. package/src/scripts/mcp_server/tools.ts +87 -0
  99. package/src/scripts/release.ts +5 -5
  100. package/src/scripts/routing_doctor.ts +50 -13
  101. package/src/scripts/schemas/command.schema.json +1 -1
  102. package/src/scripts/schemas/skill.schema.json +18 -1
  103. package/src/scripts/trigger_coverage.ts +224 -3
  104. package/src/server/schemas/settings.ts +2 -2
  105. package/src/config/budget-routing.json +0 -7
@@ -95,13 +95,35 @@ Three limits, stated because they were measured. The pre-loaded-verdict list is
95
95
  a **phrase list**, so a paraphrase evades it — it catches recurrences of known
96
96
  steering wording, not steering as such. The turn boundary is the
97
97
  authorization ledger's `detected_at` stamp, because the envelope carries no turn
98
- id; with no ledger yet, the counter falls back to session scope. And the slot
99
- itself is not universal: `pre_tool_use` exists on **three** hosts — augment,
100
- claude, cowork. On cursor, cline, windsurf, gemini and copilot this guard has
101
- nowhere to bind, so items 1 and 4 join 2 and 3 as model-carried there. The
102
- frequency join in `check_enforcement_coverage.ts` reports exactly that set, and
98
+ id; with no ledger yet, the counter falls back to session scope. And the guard
99
+ does not deny everywhere it exists: `pre_tool_use` is **bound** on three hosts —
100
+ augment, claude, cowork — and only `claude` honours the deny. Everywhere else,
101
+ augment and cowork included, items 1 and 4 join 2 and 3 as model-carried.
103
102
  `agent-config hooks:status` answers it for the host you are on right now.
104
103
 
104
+ **Corrected 2026-08-17 — this paragraph was wrong on both sides of the line it
105
+ drew.** It said `pre_tool_use` "exists on three hosts" and that the guard has
106
+ "nowhere to bind" on the other five. Neither half survived a re-read of the
107
+ tree. **Downward:** the manifest's own `native_event_aliases` table already maps
108
+ `preToolUse` (cursor), `PreToolUse` (cline) and `BeforeTool` (gemini) onto
109
+ `pre_tool_use`, so there the guard is **unbound, not unbindable**; only windsurf
110
+ and copilot carry no pre-tool surface at all. **Upward, and worse:**
111
+ `host_semantics.ts` certifies **claude alone**, and the augment and cowork
112
+ trampolines discard dispatcher output and `exit 0` unconditionally — so on two
113
+ of the three "enforcing" hosts this guard runs and is then ignored. Nor may the
114
+ inverse be asserted downward: nothing records whether an unbound host's
115
+ pre-tool event can *deny*, and `severity: blocking` is a property of the concern
116
+ rather than of the host. The four states are tabulated once in
117
+ [`hook-architecture-v1 § Which hosts carry pre_tool_use`](../docs/contracts/hook-architecture-v1.md).
118
+
119
+ Stated at this length because this rule exists over a case where a process that
120
+ *looked* followed produced fabricated evidence. An unbacked reason for a real
121
+ gap and an unmeasured claim of enforcement are the same failure in a smaller
122
+ font, and this paragraph had shipped one of each. Note also what the frequency
123
+ join actually reports: `check_enforcement_coverage.ts` skips `fallback_only`
124
+ platforms, so its gap set is **four** — cursor, cline, windsurf, gemini — never
125
+ copilot, which is excluded by declaration rather than measured as a gap.
126
+
105
127
  Items 2 and 3 — an honestly chosen scope, and recording the prompt with the
106
128
  verdict — are **not** enforced by anything. A narrowed scope is not decidable
107
129
  from the prompt alone, and no validator reads the artefacts a verdict lands in.
@@ -84,4 +84,8 @@ Trigger-set above activates this routing on demand, independent of the disciplin
84
84
  - [`commit-policy`](commit-policy.md) — commits are the user's call; rewriting them is a stronger version of the same restriction.
85
85
  - [`token-efficiency`](token-efficiency.md) — Iron Law on burning the user's tokens for cosmetic gain.
86
86
  - [`skill:git-workflow`](../skills/git-workflow/SKILL.md) — Safe Squash-After-Push protocol and Divergent-State Recovery decision tree.
87
- - `git … --no-verify` / `core.hooksPath` overrides — deterministically blocked by the `block-no-verify` PreToolUse guard (`src/scripts/hooks/block_no_verify.ts`, registered in `hook_manifest.yaml`) **on the three hosts that have a `pre_tool_use` slot at all** — augment, claude, cowork. On cursor, cline, windsurf, gemini and copilot the guard has nowhere to bind, so on those hosts the Iron Laws above are model-carried and "deterministically blocked" is not a claim this rule can make. Surfaced by the frequency join in `check_enforcement_coverage.ts`, which reports the per-commit obligation as uncovered on exactly those platforms. To check the host you are on rather than trusting this sentence, run `agent-config hooks:status`.
87
+ - `git … --no-verify` / `core.hooksPath` overrides — deterministically blocked by the `block-no-verify` PreToolUse guard (`src/scripts/hooks/block_no_verify.ts`, registered in `hook_manifest.yaml`) **on `claude`, the one host that both binds `pre_tool_use` and honours a deny**. It is *bound* on augment and cowork too, and ignored there. Everywhere else the Iron Laws above are model-carried and "deterministically blocked" is not a claim this rule can make. To check the host you are on rather than trusting this sentence, run `agent-config hooks:status`.
88
+
89
+ **Corrected 2026-08-17, in both directions, because this line was wrong on each side.** It read "the guard has nowhere to bind" on cursor, cline, windsurf, gemini and copilot — refuted for three of the five by the manifest's own `native_event_aliases` table, which already maps `preToolUse`, `PreToolUse` and `BeforeTool` onto `pre_tool_use`: there the guard is **unbound, not unbindable**, and only windsurf and copilot carry no pre-tool surface. And it certified augment, claude and cowork as the enforcing set — `host_semantics.ts` verifies **claude alone**, while the augment and cowork trampolines discard dispatcher output and `exit 0` unconditionally, so a guard bound there runs and is then ignored. Both halves are the same failure: a host-capability claim this tree never established. The four states are tabulated once in [`hook-architecture-v1 § Which hosts carry pre_tool_use`](../docs/contracts/hook-architecture-v1.md).
90
+
91
+ The frequency join in `check_enforcement_coverage.ts` reports this obligation uncovered on **four** platforms — cursor, cline, windsurf, gemini. It skips `fallback_only` platforms before computing, so copilot never appears in its output and must not be quoted as if it did; copilot is uncovered for the separate reason that it has no hook surface at all.
@@ -0,0 +1,89 @@
1
+ ---
2
+ type: "auto"
3
+ tier: "2a"
4
+ description: "A skill exists in the tree but not in the host's catalogue — ask for it by TASK via suggest_skill_for_task, never conclude it does not exist"
5
+ triggers:
6
+ - phrase: "skill not found"
7
+ - phrase: "no such skill"
8
+ - phrase: "skill does not exist"
9
+ - phrase: "kein passendes skill"
10
+ - phrase: "which skill"
11
+ - phrase: "welches skill"
12
+ applies_to_user_types:
13
+ - "developer"
14
+ - "maintainer"
15
+ self_contained: true
16
+ workspaces: [engineering]
17
+ packs: [meta]
18
+ # obligation: line 41
19
+ obligation_frequency: "per-turn"
20
+ ---
21
+
22
+ # Missing-Skill Recovery
23
+
24
+ The catalogue a host shows you is not the catalogue that exists. Hosts truncate,
25
+ and at least one truncates hard: measured 2026-08-16 on a default install, the
26
+ host published its own budget event stating it had stripped every description
27
+ and dropped **402 entries** from the model-visible skills list
28
+ (`agents/evidence/analysis/scoped-projection-host-delivery.md`). Scoping the
29
+ projection moved that to 330 and stripped the descriptions anyway.
30
+
31
+ 402 is the **host's own count against the host's own denominator**, which is not
32
+ this package's: the same install projects 297 skills, so the figure is a
33
+ magnitude, never a subtraction anyone can perform. What it establishes is that
34
+ most of the catalogue is not model-visible — which is all this rule needs.
35
+
36
+ So "I do not see a skill for this" is evidence about the **delivery**, never
37
+ about the tree.
38
+
39
+ ## The Iron Law
40
+
41
+ ```
42
+ A SKILL MISSING FROM THE HOST CATALOGUE IS NOT A SKILL THAT DOES NOT EXIST.
43
+ NEVER CONCLUDE "NO SKILL COVERS THIS" FROM THE LIST YOU WERE SHOWN.
44
+ ASK BY TASK — `suggest_skill_for_task` RANKS THE TREE, NOT THE CATALOGUE.
45
+ NEVER REBUILD A CAPABILITY BECAUSE ITS SKILL WAS NOT DELIVERED.
46
+ ```
47
+
48
+ ## What to do
49
+
50
+ 1. **Ask by task, not by name.** A name you cannot see is a name you cannot
51
+ type; a task description always works. The `suggest_skill_for_task` MCP tool
52
+ ranks `SKILL.md` frontmatter over the source tree and returns names, scores
53
+ and personas — no bodies, so a wrong rank costs one read.
54
+ 2. **Open the winner and check it yourself.** The ranker is deterministic
55
+ keyword scoring, not judgement. A high score is a pointer, not a verdict.
56
+ 3. **Say which source answered.** "The catalogue had no entry, the ranker
57
+ found `X`" is a different claim from "there is no skill for this", and a
58
+ reader cannot reconstruct which one you meant from silence.
59
+ 4. **If nothing scores, say that** — and proceed without a skill rather than
60
+ inventing one. A ranked empty list and an unreachable catalogue are
61
+ different answers; the tool distinguishes them (`status: no_catalogue`) and
62
+ so should you.
63
+
64
+ ## When NOT to fire
65
+
66
+ - The skill you need was delivered and is in front of you. This rule is for the
67
+ gap, not for routine skill selection.
68
+ - The task genuinely has no skill and you already checked by task. Checking
69
+ twice is the loop, not the discipline.
70
+ - A host that delivers its catalogue whole — the rule costs nothing there and
71
+ simply never has a gap to close.
72
+
73
+ ## Honest enforcement — `enforced_by: none`
74
+
75
+ Nothing can observe an agent concluding "no skill exists". The `skill-route`
76
+ concern injects ranked pointers on `user_prompt_submit` when its floor is
77
+ cleared, which covers the prompts where the ranker is confident and by
78
+ construction covers no others: it is silent on ~86 % of prompts by design. This
79
+ rule is the model-carried half for the rest, and it is stated as such rather
80
+ than implied to be caught by the concern.
81
+
82
+ ## See also
83
+
84
+ - [`code-intelligence`](../skills/code-intelligence/SKILL.md) — the same
85
+ shape for source code: query the index first, fall back, name the source.
86
+ - [`external-code-graph-interop`](external-code-graph-interop.md) — its rule-side
87
+ twin, and the model this rule's trigger set follows.
88
+ - `src/scripts/skill_tools/score_skill_relevance.ts` — the ranker itself, and
89
+ the one place its scoring is defined.
@@ -121,10 +121,15 @@ every `file_pattern` declared here is accepted by that predicate. The predicate
121
121
  is deliberately wider (it also covers `.css`, `.scss`, `.astro`), because a
122
122
  measurement denominator and a routing trigger are not the same population.
123
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.
124
+ Warn-only, capped at two per session, default-OFF, and bound only in the three
125
+ `platforms:` rows that carry a `pre_tool_use` key — augment, claude, cowork
126
+ (**clarified 2026-08-17**: "where a slot exists" read as a host property, and it
127
+ is a manifest one; three further hosts alias a native pre-tool event without a
128
+ binding, and only claude honours a deny at all — the four states are tabulated
129
+ in [`hook-architecture-v1 § Which hosts carry pre_tool_use`](../docs/contracts/hook-architecture-v1.md)).
130
+ None of that changes the verdict here: a reminder that can be ignored is not a
131
+ gate, so `enforced_by:` stays `none`. Run `agent-config hooks:status` for the
132
+ host you are on rather than trusting this sentence.
128
133
 
129
134
  ## Failure modes
130
135
 
@@ -403,6 +403,38 @@ function _regen_dashboard(root: string, dry_run: boolean): void {
403
403
  }
404
404
  }
405
405
 
406
+ /**
407
+ * Rebuild the archive index after a move, when the builder is present.
408
+ *
409
+ * The sweep is the one operation that changes what `agents/roadmaps/archive/`
410
+ * contains, so it is the one operation that can leave the generated index
411
+ * stale — and the drift gate on that index runs in the same PR the sweep runs
412
+ * in. Regenerating here keeps the two from disagreeing by construction rather
413
+ * than by remembering.
414
+ *
415
+ * `build_archive_index` is package-internal (`src/scripts/`), while this sweep
416
+ * is projected into consumer installs that carry no such script. Absent
417
+ * builder → nothing to regenerate, and that is a normal consumer state, not a
418
+ * failure. The spawn is best-effort for the same reason `_regen_dashboard` is:
419
+ * a sweep that archived correctly must not report failure because a
420
+ * regeneration could not run.
421
+ */
422
+ function _regen_archive_index(root: string, dry_run: boolean): void {
423
+ if (dry_run) {
424
+ return;
425
+ }
426
+ const script = path.join(root, 'src', 'scripts', 'build_archive_index.ts');
427
+ if (!_isFile(script)) {
428
+ return;
429
+ }
430
+ _runTwin(root, script);
431
+ for (const rel of ['agents/roadmaps/archive/INDEX.md', 'agents/roadmaps/archive/index.json']) {
432
+ if (_isFile(path.join(root, rel))) {
433
+ _run(['git', 'add', '--', rel], root);
434
+ }
435
+ }
436
+ }
437
+
406
438
  /** Spawn the dashboard generator twin (tsx) with cwd=root, mirroring `[sys.executable, script]`. */
407
439
  function _runTwin(root: string, script: string): void {
408
440
  // Python uses `sys.executable` — the interpreter already running this
@@ -491,6 +523,7 @@ function main(argv?: readonly string[]): number {
491
523
  return 0;
492
524
  }
493
525
  _regen_dashboard(root, ns.dry_run);
526
+ _regen_archive_index(root, ns.dry_run);
494
527
  const verb = ns.dry_run ? 'Would archive' : 'Archived';
495
528
  for (const rec of archived) {
496
529
  process.stdout.write(
@@ -380,8 +380,23 @@ function _stripFencedCode(text: string): string {
380
380
  return text.replace(FENCED_CODE_RE, (m) => '\n'.repeat(_splitlines(m).length));
381
381
  }
382
382
 
383
- const BLOCKER_FIELD_RE =
384
- /^-[ \t]*\*\*(Status|Owner|Blocks|Resolved when|What to do|Recommendation|If you do nothing|Question):\*\*/i;
383
+ /**
384
+ * Terminator for a blocker field's continuation lines — ANY `- **Label:**`
385
+ * bullet, not a closed list of known labels.
386
+ *
387
+ * It used to enumerate eight names, which silently diverged from the contract
388
+ * the two call sites below already document ("up to the next `- **Field:**`
389
+ * marker"): a bullet whose label was not on the list did not terminate, so the
390
+ * PREVIOUS field kept absorbing it and the dashboard rendered the two as one
391
+ * run-on sentence. Measured when `- **Options:**` and `- **Side finding:**`
392
+ * first appeared in a blocker — a reader saw the side finding as part of
393
+ * `Resolved when`, i.e. the generator changed what a field meant.
394
+ *
395
+ * A closed list is the wrong shape for a terminator: every future field name
396
+ * would have to be added here to avoid corrupting the field above it, and the
397
+ * failure is silent in generated output nobody edits by hand.
398
+ */
399
+ const BLOCKER_FIELD_RE = /^-[ \t]*\*\*[^*]+:\*\*/;
385
400
 
386
401
  /** Strip an inline `<!-- comment -->` and trim. */
387
402
  function _stripComment(s: string): string {
@@ -11,6 +11,10 @@ workspaces:
11
11
  - engineering
12
12
  packs:
13
13
  - engineering-base
14
+ triggers:
15
+ - phrase: "authorization check"
16
+ - phrase: "permission check"
17
+ - keyword: "authz"
14
18
  ---
15
19
 
16
20
  # authz-review
@@ -306,6 +306,10 @@ handoff shape emitted alongside `design-system.json`
306
306
  — when the artifact is a mock — an explicit "prototype, not production code"
307
307
  warning so a consumer never ships the reference verbatim.
308
308
 
309
+ **What to capture FROM** a running project: sources first, SSR fetch second,
310
+ rendered snapshot only with consumer tooling; record the git SHA. Rationale:
311
+ [snapshot preference order](references/snapshot-preference-order.md).
312
+
309
313
  ## Gotcha
310
314
 
311
315
  - **Over-writing instead of diff-updating** — the most common failure.
@@ -0,0 +1,77 @@
1
+ # Snapshot preference order — what to capture a design system FROM
2
+
3
+ When a design system has to be captured out of a running project — for a
4
+ handoff, an external design pass, or a reference bundle — there is more than one
5
+ thing you could point at, and they are not equally good. This is the preference
6
+ order, highest fidelity per unit of effort first.
7
+
8
+ **Take the first level the project supports. Do not skip down the list for
9
+ convenience, and do not skip UP it for thoroughness** — Level C is additional
10
+ evidence, never a replacement for Level A.
11
+
12
+ ## Level A — source-level (always available, the default)
13
+
14
+ The involved component sources, plus the token layer (DTCG / CSS custom
15
+ properties), plus the page composition that assembles them.
16
+
17
+ **Why this is the default and not the fallback**, which is the part that reads
18
+ backwards until you have seen the alternative: in a component project, a
19
+ rendered DOM dump is *worse* input than the sources it came from. Hydrated
20
+ markup is utility-class soup with the component boundaries erased — the very
21
+ structure a design system is made of is the structure rendering destroys. A
22
+ reader (human or tool) reworks clean sources far better than expanded markup.
23
+
24
+ Level A needs no dev server, no browser, and no network. It is available in a
25
+ bare checkout, which is why it is also the only level that always works.
26
+
27
+ ## Level B — SSR fetch (no browser)
28
+
29
+ When the stack renders server-side and a dev server is running, an ordinary
30
+ HTTP fetch of the rendered page.
31
+
32
+ Use it to answer what Level A cannot: what the composition actually resolves to
33
+ for a given route — conditional branches taken, data-dependent states, the real
34
+ class strings after server-side composition. Zero browser dependency, so it
35
+ stays cheap.
36
+
37
+ Level B **supplements** Level A. Capturing B alone leaves you with the soup
38
+ problem above and no component boundaries.
39
+
40
+ ## Level C — rendered snapshot (consumer tooling only)
41
+
42
+ If the project already carries a browser-automation tool, or a DevTools bridge
43
+ is available in the session, additionally capture a screenshot plus a
44
+ computed-style extract as visual ground truth.
45
+
46
+ **Capability-detect, never hard-depend.** Level C is available when the consumer
47
+ already has the tooling; it is never a reason to install a browser stack, add a
48
+ dependency, or fail a capture. A project without it captures at A (and B where
49
+ applicable) and says so.
50
+
51
+ Of the two artefacts, **the screenshot is the valuable part** — it is the only
52
+ one that records what a person actually sees. The DOM dump is supplementary and
53
+ carries the erasure problem Level A exists to avoid.
54
+
55
+ ## Every snapshot records the source revision
56
+
57
+ Whatever level was captured, record the git SHA the capture was taken at.
58
+
59
+ This is the anchor that makes a return leg possible: without it, reworked output
60
+ cannot be diffed against what was sent, and a capture from an unknown state is
61
+ evidence about nothing in particular. It costs one line and it is the difference
62
+ between a snapshot and a screenshot.
63
+
64
+ ## State the level in the output
65
+
66
+ A capture says which level it reached and why it stopped there — "Level A + B;
67
+ no browser tooling detected, so no visual ground truth". A consumer reading the
68
+ bundle can then tell *absent because unavailable* from *absent because
69
+ overlooked*, which is the same distinction the extraction floor draws for
70
+ not-extractable values.
71
+
72
+ ## See also
73
+
74
+ - [`design-system-json.md`](design-system-json.md) — the import contract the
75
+ captured artefact is emitted against.
76
+ - [`../SKILL.md`](../SKILL.md) § Design-system extraction floor — what a
77
+ complete extraction carries once the source has been chosen.
@@ -7,6 +7,10 @@ workspaces:
7
7
  - engineering
8
8
  packs:
9
9
  - engineering-base
10
+ triggers:
11
+ - phrase: "merge conflict"
12
+ - phrase: "rebase conflict"
13
+ - phrase: "resolve conflicts"
10
14
  ---
11
15
 
12
16
  # merge-conflicts
@@ -89,6 +89,18 @@ Apply the table above. State: *"Running in BASIC — say `DETAIL` to switch."* (
89
89
 
90
90
  Identify each slot in the rough prompt: intent · entities · output shape · constraints · target AI · tone · audience. List every missing slot. Check for ambiguity, unstated assumptions, and contradictory requirements.
91
91
 
92
+ #### 3b. De-inflate before you sharpen
93
+
94
+ Diagnose asks what is *missing*. The de-inflation pass asks what must **go** — because sharpening a premise the target model will confabulate around makes the output worse, not better. Strip exactly these three, and nothing else:
95
+
96
+ | Pattern | What it looks like | What it costs |
97
+ |---|---|---|
98
+ | **Grandiose persona** | "You are the world's foremost / legendary / 10× expert in X" | Tokens without behaviour change — a superlative is not a constraint, and the model cannot act on it |
99
+ | **Presupposed canon** | "Apply the 7 laws of X", "use the standard X framework" for a body of knowledge that may not exist | The model invents a canon to satisfy the presupposition, and the confabulation reads as authority |
100
+ | **Scope stuffing** | One prompt demanding analysis *and* strategy *and* copy *and* a checklist | An answer to a question nobody asked, at the cost of the one that was |
101
+
102
+ Strip only these three. Anything else the author wrote is a requirement, not inflation — see § Gotcha for the failure mode on both sides. A stripped persona is replaced by the concrete role the task actually needs ("a senior X" is a role; "the world's foremost X" is decoration), and a presupposed canon becomes a question in DETAIL mode or a stated assumption in BASIC. **This is own analysis, not adopted material** — the three patterns are named here rather than cited from an external collection.
103
+
92
104
  ### 4a. BASIC path
93
105
 
94
106
  Fill missing slots with safe defaults (general audience, neutral tone, the named AI or "any modern LLM"). Skip to step 5.
@@ -120,7 +132,11 @@ Format per § Output format. Do **not** execute the optimized prompt yourself un
120
132
  - The model tends to over-engineer BASIC mode — for a one-line ask, the optimized prompt should still be short. No 800-word system prompts for "help with my resume".
121
133
  - Don't drift into German welcome text. The optimized prompt mirrors the user's source-language preference; the skill's own scaffolding stays English (per `language-and-tone` for `.md`).
122
134
  - The model tends to **mix languages** in the optimized prompt when the user wrote in German but named an English-speaking target audience — pick one language for the whole optimized prompt body (default: source-language of the rough prompt unless the user explicitly named the target audience's language).
123
- - The model tends to inherit upstream dogma that "only 5 techniques are safe" (few-shot, role, structured-output, constraint-based, chain-of-thought). That claim travels with `nidhinjs/prompt-master` and is **rejected here** — CO-STAR, RISEN, CRISPE, ReAct, and the image-AI templates land in [`docs/guidelines/prompt-templates.md`](../../../docs/guidelines/prompt-templates.md) and are first-class. Pick by request type, not by upstream whitelist.
135
+ - The model tends to **polish a grandiose persona instead of removing it** — "you are the world's foremost expert in X" gets tightened into a better-written superlative. It is decoration either way: replace it with the concrete role the task needs, or drop it.
136
+ - The model tends to **preserve a presupposed canon while sharpening around it** — asked to improve "apply the 7 laws of X", it produces a cleaner prompt that still presupposes seven laws, and the target model then invents them. Turn the presupposition into a DETAIL question or a stated BASIC assumption; never carry it through polished.
137
+ - The model tends to **keep every demand in a scope-stuffed prompt** because each one came from the author — four deliverables in one prompt produce four shallow answers. Name the primary deliverable, and surface the rest as a numbered option rather than silently answering all of them.
138
+ - The model tends to **over-apply de-inflation once it has the concept** — stripping a constraint the author meant, because it read as verbose. Exactly three patterns are strippable (§ 3b); everything else the author wrote is a requirement. Removing a requirement is the worse failure of the two, and it is silent.
139
+ - The model tends to inherit upstream dogma that "only 5 techniques are safe" (few-shot, role, structured-output, constraint-based, chain-of-thought). That claim travels with an external prompt-collection and is **rejected here** — CO-STAR, RISEN, CRISPE, ReAct, and the image-AI templates land in [`docs/guidelines/prompt-templates.md`](../../../docs/guidelines/prompt-templates.md) and are first-class. Pick by request type, not by upstream whitelist.
124
140
 
125
141
  ## Do NOT
126
142
 
@@ -393,8 +393,8 @@ to every roadmap you author.
393
393
  reads as open questions. For source-derived/contested plans, council
394
394
  *first* (§ 8.B), then author the verdicts.
395
395
 
396
- ## Examples
396
+ ## Examples & the "already tried?" check
397
397
 
398
- Browse `agents/roadmaps/` (active set) and `agents/roadmaps/archive/`
399
- (closed work) for canonical structural / tactical / structural-with-council
400
- examples.
398
+ Browse `agents/roadmaps/` (active set) for canonical structural / tactical examples. For closed
399
+ work — and for every already-tried / closed / refuted question — read the generated `INDEX.md` in
400
+ `agents/roadmaps/archive/` first; open an archived file only for a row it marks `not-extractable`.
@@ -8,6 +8,10 @@ workspaces:
8
8
  - engineering
9
9
  packs:
10
10
  - engineering-base
11
+ triggers:
12
+ - phrase: "root cause"
13
+ - phrase: "reproduce the bug"
14
+ - keyword: "flaky"
11
15
  ---
12
16
 
13
17
  # systematic-debugging
@@ -10,6 +10,9 @@ workspaces:
10
10
  - engineering
11
11
  packs:
12
12
  - engineering-base
13
+ triggers:
14
+ - phrase: "threat model"
15
+ - phrase: "abuse case"
13
16
  ---
14
17
 
15
18
  # threat-modeling
@@ -39,7 +39,7 @@ schema_version: 1
39
39
  # CI guard: a release bump of `package.json` must update this value
40
40
  # in lockstep — see scripts/check_template_pin_drift.ts (road-to-
41
41
  # portable-runtime-and-update-check P3.3).
42
- agent_config_version: "12.1.0"
42
+ agent_config_version: "13.0.0"
43
43
 
44
44
  # --- Project identity ---
45
45
  project:
@@ -21,7 +21,7 @@ import {
21
21
  projectStoreSlug,
22
22
  scanTranscripts,
23
23
  weightedInputUnits
24
- } from "./chunk-K34QTSD5.js";
24
+ } from "./chunk-N7FEST6D.js";
25
25
  import {
26
26
  git_common_dir
27
27
  } from "./chunk-PZWRPRWP.js";
@@ -4111,13 +4111,13 @@ var settingsSchema = external_exports.object({
4111
4111
  ),
4112
4112
  per_tier: external_exports.object({
4113
4113
  cheap: external_exports.number().min(0).nullable().default(null).describe(
4114
- "USD ceiling for the cheap model tier under budget-aware delegation (docs/contracts/budget-routing.md). null = no separate tier cap; global ceilings still apply."
4114
+ "USD ceiling for the cheap model tier. Budget-aware delegation was ARCHIVED 2026-08-16 (docs/contracts/budget-routing.md), so no code routes on this today; the cap is still summed and reported by `budget.mjs tier`. null = no separate tier cap; global ceilings still apply."
4115
4115
  ),
4116
4116
  medium: external_exports.number().min(0).nullable().default(null).describe(
4117
4117
  "USD ceiling for the medium model tier. null = no separate tier cap."
4118
4118
  ),
4119
4119
  strong: external_exports.number().min(0).nullable().default(null).describe(
4120
- "USD ceiling for the strong model tier. null = no separate tier cap \u2014 with cheap exhausted and strong funded, budget routing uses the strong tier rather than blocking work."
4120
+ "USD ceiling for the strong model tier. null = no separate tier cap. The never-block-to-save-money relation this used to describe went with the archived budget-routing layer (docs/contracts/budget-routing.md); nothing routes between tiers today."
4121
4121
  )
4122
4122
  }).default({ cheap: null, medium: null, strong: null })
4123
4123
  }),
@@ -1451,23 +1451,23 @@ var AnthropicCliClient = class extends CliClient {
1451
1451
  // documented "disable all tools" value.
1452
1452
  //
1453
1453
  // It also repairs a live failure, which is how the grant was found
1454
- // rather than reasoned about. The spawn inherits the caller's cwd
1455
- // (`_runSubprocess` sets none, deliberately — the worker-role marker
1456
- // below depends on the vendor CLI loading this repo's hook chain),
1457
- // so invoking the council from inside a repository made the child
1458
- // load that project's own instructions AND every tool definition.
1459
- // In this package that overflowed the model's context window
1460
- // outright: `is_error: true`, `"the request is ~214331 tokens
1454
+ // rather than reasoned about. AT THE TIME (2026-08-12) the spawn
1455
+ // inherited the caller's cwd, so invoking the council from inside a
1456
+ // repository made the child load that project's own instructions AND
1457
+ // every tool definition. In this package that overflowed the context
1458
+ // window outright: `is_error: true`, `"the request is ~214331 tokens
1461
1459
  // (limit 200000) … the rest is system prompt, tool definitions"`,
1462
- // exit 1, empty stderr — so the member reported as unavailable with
1463
- // no diagnosable cause. Measured 2026-08-12: identical call from
1464
- // `/tmp` exit 0, from the worktree exit 1, and adding this one flag
1465
- // returns the worktree call to exit 0.
1460
+ // exit 1, empty stderr — the member reported unavailable with no
1461
+ // diagnosable cause. Identical call from `/tmp` exit 0, from the
1462
+ // worktree exit 1, and this one flag returns the worktree to exit 0.
1466
1463
  //
1467
- // Dropping the tools rather than neutralising the cwd is the
1468
- // narrower fix of the two: it leaves the worker-role hook chain
1469
- // intact and removes only the capability the role never had a use
1470
- // for. The context saving is a consequence, not the justification.
1464
+ // TENSE CORRECTED 2026-08-16. This block read "`_runSubprocess` sets
1465
+ // none, deliberately" and "dropping the tools RATHER THAN
1466
+ // neutralising the cwd" — both false since `_runSubprocess` set
1467
+ // `cwd: os.tmpdir()` for a DIFFERENT contamination (a member
1468
+ // answering this repo's `end-review-nudge`). Two repairs, one
1469
+ // cause, neither aware of the other, so a reader here concluded
1470
+ // the cwd is inherited. Both are live; this flag bounds what a member may DO.
1471
1471
  "--tools",
1472
1472
  "",
1473
1473
  "--append-system-prompt",
@@ -1557,7 +1557,7 @@ var OpenAICliClient = class extends CliClient {
1557
1557
  if (measuredOn === void 0) return null;
1558
1558
  return {
1559
1559
  code: "model_unsupported_on_transport",
1560
- detail: `openai member pins model \`${this.model}\`, which the codex CLI (subscription transport) refused when measured on ${measuredOn}: "The '${this.model}' model is not supported when using Codex with a ChatGPT account." The CLI publishes no list of models it DOES accept, so there is no supported set to offer here \u2014 the one configuration measured to work is no pin at all. Fix: remove the \`model:\` line from the openai member (or set it to \`codex-default\`) to use the CLI's own default. A pinned model is only reachable on the API transport (\`api_key_ref:\` without \`mode: cli\`).`
1560
+ detail: `openai member pins model \`${this.model}\`, which the codex CLI (subscription transport) refused when measured on ${measuredOn}: "The '${this.model}' model is not supported when using Codex with a ChatGPT account." The CLI publishes no list of models it DOES accept, so there is no supported set to offer here \u2014 the one configuration measured to work is no pin at all. Fix: set \`model: codex-default\` on the openai member. Do NOT delete the \`model:\` line \u2014 an enabled member with no model fails config load, so that takes EVERY provider to NOT CONFIGURED. A pin is reachable only on the API transport.`
1561
1561
  };
1562
1562
  }
1563
1563
  /**
@@ -1672,7 +1672,7 @@ var OpenAICliClient = class extends CliClient {
1672
1672
  // needs and neither of which the error code carries.
1673
1673
  detail: failure.slice(0, 400),
1674
1674
  ...unsupported ? {
1675
- hint: "the codex subscription transport serves no explicitly pinned model measured so far \u2014 remove the `model:` line from the openai member to use the CLI default"
1675
+ hint: "the codex subscription transport serves no explicitly pinned model measured so far \u2014 set `model: codex-default` on the openai member; deleting the line fails config load"
1676
1676
  } : {}
1677
1677
  }
1678
1678
  });
@@ -2,7 +2,7 @@ import { createRequire as __acCreateRequire } from 'node:module'; const require
2
2
  import {
3
3
  ZodFirstPartyTypeKind,
4
4
  settingsSchema
5
- } from "./chunk-55WZN5VF.js";
5
+ } from "./chunk-EVTY7ISR.js";
6
6
  import {
7
7
  main
8
8
  } from "./chunk-PPQO4NGR.js";
@@ -705,14 +705,22 @@ function measureCatalogueVolume(host, root) {
705
705
  descriptionBytes: skills.reduce((sum, e) => sum + Buffer.byteLength(e.description), 0)
706
706
  };
707
707
  }
708
+ function _supersedes(candidate, incumbent) {
709
+ if (candidate.observed_at !== incumbent.observed_at) {
710
+ return candidate.observed_at > incumbent.observed_at;
711
+ }
712
+ return (candidate.dropped_count ?? 0) > (incumbent.dropped_count ?? 0);
713
+ }
708
714
  function knownHostLimits(records) {
709
715
  const out = /* @__PURE__ */ new Map();
716
+ const chosen = /* @__PURE__ */ new Map();
710
717
  for (const record of records) {
711
718
  if (truncationModeOf(record) !== "budget-strip-and-drop") continue;
712
719
  if (typeof record.dropped_count !== "number") continue;
713
720
  if (record.dropped_count <= 0) continue;
714
- const previous = out.get(record.host);
715
- if (previous !== void 0 && previous.observedAt > record.observed_at) continue;
721
+ const previous = chosen.get(record.host);
722
+ if (previous !== void 0 && !_supersedes(record, previous)) continue;
723
+ chosen.set(record.host, record);
716
724
  out.set(record.host, {
717
725
  host: record.host,
718
726
  droppedEntries: record.dropped_count,
@@ -22,7 +22,7 @@ import {
22
22
  frontmatterPaths,
23
23
  proseEqual,
24
24
  splitFrontmatter
25
- } from "./chunk-O52B4F5S.js";
25
+ } from "./chunk-3B72LSZL.js";
26
26
  import "./chunk-FTAISGDQ.js";
27
27
  import {
28
28
  atomic_write_json,
@@ -33,7 +33,7 @@ import "./chunk-Q26HT4QT.js";
33
33
  import {
34
34
  projectStoreSlug,
35
35
  resolveSessionRole
36
- } from "./chunk-K34QTSD5.js";
36
+ } from "./chunk-N7FEST6D.js";
37
37
  import "./chunk-PZWRPRWP.js";
38
38
  import "./chunk-M2DPGFZ4.js";
39
39
  import {
@@ -66,11 +66,11 @@ import {
66
66
  _wizard_state_path,
67
67
  main,
68
68
  shutilWhich
69
- } from "./chunk-O52B4F5S.js";
69
+ } from "./chunk-3B72LSZL.js";
70
70
  import "./chunk-FTAISGDQ.js";
71
71
  import "./chunk-EJITJBJX.js";
72
72
  import "./chunk-Q26HT4QT.js";
73
- import "./chunk-K34QTSD5.js";
73
+ import "./chunk-N7FEST6D.js";
74
74
  import "./chunk-PZWRPRWP.js";
75
75
  import "./chunk-M2DPGFZ4.js";
76
76
  import "./chunk-63WSP6YZ.js";
@@ -9,8 +9,8 @@ import {
9
9
  KIRO_MARKER,
10
10
  ROOCODE_MARKER,
11
11
  ZED_MARKER
12
- } from "./chunk-LKZ5DOYW.js";
13
- import "./chunk-55WZN5VF.js";
12
+ } from "./chunk-XURJ4AQC.js";
13
+ import "./chunk-EVTY7ISR.js";
14
14
  import "./chunk-PPQO4NGR.js";
15
15
  import "./chunk-BKO2AZ7Z.js";
16
16
  import "./chunk-63WSP6YZ.js";
@@ -26,7 +26,7 @@ import {
26
26
  _RAW_KEY_PREFIXES,
27
27
  hardenedSpawnEnv,
28
28
  projectStoreSlug
29
- } from "./chunk-K34QTSD5.js";
29
+ } from "./chunk-N7FEST6D.js";
30
30
  import "./chunk-PZWRPRWP.js";
31
31
  import "./chunk-NLP54U3J.js";
32
32
  import "./chunk-NDJLNC6Q.js";
@@ -1,8 +1,8 @@
1
1
  import { createRequire as __acCreateRequire } from 'node:module'; const require = globalThis.require ?? __acCreateRequire(import.meta.url);
2
2
  import {
3
3
  PROJECT_BRIDGE_MARKERS
4
- } from "./chunk-LKZ5DOYW.js";
5
- import "./chunk-55WZN5VF.js";
4
+ } from "./chunk-XURJ4AQC.js";
5
+ import "./chunk-EVTY7ISR.js";
6
6
  import "./chunk-PPQO4NGR.js";
7
7
  import "./chunk-BKO2AZ7Z.js";
8
8
  import {