@event4u/agent-config 2.19.0 → 2.20.1

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 (94) hide show
  1. package/.agent-src/commands/agent-status.md +29 -0
  2. package/.agent-src/commands/onboard.md +221 -81
  3. package/.agent-src/packs/README.md +49 -0
  4. package/.agent-src/packs/agency-delivery.yml +63 -0
  5. package/.agent-src/packs/content-engine.yml +53 -0
  6. package/.agent-src/packs/founder-mvp.yml +51 -0
  7. package/.agent-src/presets/README.md +26 -0
  8. package/.agent-src/presets/balanced.yml +34 -0
  9. package/.agent-src/presets/fast.yml +31 -0
  10. package/.agent-src/presets/strict.yml +38 -0
  11. package/.agent-src/profiles/README.md +29 -0
  12. package/.agent-src/profiles/agency.yml +27 -0
  13. package/.agent-src/profiles/content_creator.yml +25 -0
  14. package/.agent-src/profiles/developer.yml +26 -0
  15. package/.agent-src/profiles/finance.yml +24 -0
  16. package/.agent-src/profiles/founder.yml +25 -0
  17. package/.agent-src/profiles/ops.yml +25 -0
  18. package/.agent-src/rules/no-cheap-questions.md +25 -17
  19. package/.agent-src/skills/adr-create/SKILL.md +78 -68
  20. package/.agent-src/skills/subagent-orchestration/SKILL.md +33 -0
  21. package/.agent-src/templates/agents/agent-project-settings.example.yml +1 -1
  22. package/.agent-src/templates/skill-archive-note.md +101 -0
  23. package/.claude-plugin/marketplace.json +1 -1
  24. package/CHANGELOG.md +73 -70
  25. package/README.md +68 -72
  26. package/config/agent-settings.template.yml +22 -0
  27. package/docs/adrs/caveman/0001-default-off-until-bench.md +93 -0
  28. package/docs/adrs/caveman/README.md +9 -0
  29. package/docs/adrs/cost/0001-hard-stop-hook.md +114 -0
  30. package/docs/adrs/cost/README.md +9 -0
  31. package/docs/adrs/memory/0001-consumer-side-snapshot.md +111 -0
  32. package/docs/adrs/memory/README.md +9 -0
  33. package/docs/adrs/router/0001-three-tier-routing.md +119 -0
  34. package/docs/adrs/router/README.md +9 -0
  35. package/docs/adrs/schema/0001-json-schema-frontmatter.md +102 -0
  36. package/docs/adrs/schema/README.md +9 -0
  37. package/docs/adrs/smoke/0001-per-tier-smoke-scripts.md +99 -0
  38. package/docs/adrs/smoke/README.md +9 -0
  39. package/docs/architecture/current-onboard-baseline.md +126 -0
  40. package/docs/architecture/current-safety-behavior.md +137 -0
  41. package/docs/archive/CHANGELOG-pre-2.16.0.md +48 -0
  42. package/docs/archive/CHANGELOG-pre-2.17.0.md +63 -0
  43. package/docs/contracts/adr-layout.md +108 -0
  44. package/docs/contracts/benchmark-corpus-spec.md +97 -0
  45. package/docs/contracts/benchmark-report-schema.md +111 -0
  46. package/docs/contracts/command-clusters.md +1 -0
  47. package/docs/contracts/command-taxonomy.md +137 -0
  48. package/docs/contracts/compression-default-kill-criterion.md +69 -0
  49. package/docs/contracts/config-presets.md +144 -0
  50. package/docs/contracts/cost-dashboard.md +143 -0
  51. package/docs/contracts/cost-enforcement.md +134 -0
  52. package/docs/contracts/file-ownership-matrix.json +0 -7
  53. package/docs/contracts/mcp-tool-inventory.md +53 -0
  54. package/docs/contracts/measurement-baseline.md +102 -0
  55. package/docs/contracts/namespace.md +125 -0
  56. package/docs/contracts/profile-system.md +142 -0
  57. package/docs/contracts/safety-model.md +129 -0
  58. package/docs/contracts/smoke-contracts.md +144 -0
  59. package/docs/contracts/workflow-packs.md +121 -0
  60. package/docs/decisions/ADR-010-profile-pack-preset-boundary.md +132 -0
  61. package/docs/decisions/INDEX.md +1 -0
  62. package/docs/featured-commands.md +27 -0
  63. package/docs/parity/bench-ruflo.json +58 -0
  64. package/docs/parity/bench.json +41 -0
  65. package/docs/parity/ruflo.md +46 -0
  66. package/docs/profiles.md +91 -0
  67. package/package.json +1 -1
  68. package/scripts/_cli/cmd_explain.py +250 -0
  69. package/scripts/_lib/bench_cost.py +138 -0
  70. package/scripts/_lib/bench_quality.py +118 -0
  71. package/scripts/_lib/bench_report.py +150 -0
  72. package/scripts/agent-config +13 -0
  73. package/scripts/audit_adr_coverage.py +175 -0
  74. package/scripts/audit_mcp_tools.py +146 -0
  75. package/scripts/bench_baseline_ready.py +108 -0
  76. package/scripts/bench_drift_check.py +151 -0
  77. package/scripts/bench_per_tool.py +216 -0
  78. package/scripts/bench_run.py +155 -0
  79. package/scripts/config/__init__.py +9 -0
  80. package/scripts/config/presets.py +206 -0
  81. package/scripts/config/profiles.py +173 -0
  82. package/scripts/cost/budget.mjs +73 -12
  83. package/scripts/cost/preflight.mjs +89 -0
  84. package/scripts/lint_archived_skills.py +143 -0
  85. package/scripts/lint_bench_corpus.py +161 -0
  86. package/scripts/lint_namespace.py +135 -0
  87. package/scripts/lint_roadmap_complexity.py +3 -2
  88. package/scripts/skill_overlap.py +204 -0
  89. package/scripts/skill_usage_collect.py +191 -0
  90. package/scripts/skill_usage_report.py +162 -0
  91. package/scripts/smoke/kernel.sh +101 -0
  92. package/scripts/smoke/router.sh +129 -0
  93. package/scripts/smoke/schema.sh +71 -0
  94. package/scripts/smoke/skills.sh +101 -0
@@ -0,0 +1,93 @@
1
+ # ADR 0001 — Caveman compression default stays OFF until `task bench`
2
+
3
+ > Area: `caveman` · Status: accepted · Date: 2026-05-16 · Type: retrospective
4
+ > Roadmap: `agents/roadmaps/step-11-ruflo-parity.md` Phase 4 Step 3
5
+ > Supersedes: —
6
+
7
+ ## Context
8
+
9
+ Caveman-speak is a turn-time compression dialect that trades English
10
+ grammar for tokens. The dialect is documented in
11
+ [`caveman-speak`](../../../.agent-src.uncompressed/rules/caveman-speak.md);
12
+ the question this ADR records is **whether the dialect should default
13
+ ON for all consumers**.
14
+
15
+ The North-Star council ([`council-synthesis.md` § 7](../../../agents/audit-2026-05-14-north-star/council-synthesis.md))
16
+ landed split: two voices (token-efficiency, ops) argued default-ON
17
+ saves 40 %+ tokens on long sessions; two voices (UX, governance)
18
+ argued default-ON degrades novice readability and locks in a dialect
19
+ nobody has measured against the real benchmark corpus.
20
+
21
+ ## Decision
22
+
23
+ **`caveman.speak_scope` defaults `off`**. Carve-outs (security ·
24
+ destructive · multi-step · code blocks · paths · numbered options ·
25
+ Iron-Law markers) stay in the rule body so the dialect is **defined**
26
+ even when **inactive**. The default flips only when `task bench`
27
+ produces measured win/loss numbers against the locked 25-prompt corpus.
28
+
29
+ Authoritative kill-criterion:
30
+ [`docs/contracts/compression-default-kill-criterion.md`](../../contracts/compression-default-kill-criterion.md)
31
+ § Rule lines 8–11.
32
+
33
+ ### Decision owner
34
+
35
+ `step-4-measurement-and-benchmark.md` closeout phase. Not this doc.
36
+ Not `step-99`. Not the North-Star council. The closeout reads
37
+ `docs/parity/bench.json` and applies exactly one of three branches
38
+ defined in the kill-criterion.
39
+
40
+ ### Default-ON gates (all required)
41
+
42
+ 1. ≥ 30 % token reduction averaged across the 25-prompt corpus.
43
+ 2. ≤ 5 % regression on the readability score from the same run.
44
+ 3. Reversibility check passes (caveman → English round-trip ≥ 95 %
45
+ semantic preservation per the bench scorer).
46
+
47
+ Any gate fails → default stays OFF. Re-bench after the next dialect
48
+ edit.
49
+
50
+ ## Considered alternatives
51
+
52
+ ### Alt 1 — Default ON now (rejected)
53
+
54
+ Flip the default immediately; let consumers opt out.
55
+
56
+ **Why rejected:** no measured baseline exists. The two pro voices'
57
+ 40 % claim is from informal turn-snippet comparisons, not the locked
58
+ corpus. Default-on without measurement is exactly the Ruflo-style
59
+ "trust me it's faster" pattern the audit calls out.
60
+
61
+ ### Alt 2 — Default ON for cost-profile `lean`, OFF for `default` (rejected)
62
+
63
+ Couple the default to the cost profile.
64
+
65
+ **Why rejected:** cost profiles already carry profile-specific budget
66
+ ladders and smoke contracts. Adding a dialect coupling makes the
67
+ matrix `2 × N` instead of `1 × N`. The kill-criterion measurement is
68
+ the same regardless of profile — make the decision once.
69
+
70
+ ### Alt 3 — Keep default OFF, measure first (accepted)
71
+
72
+ The chosen path. Dialect ships, stays dormant, gets benched, then
73
+ either ships default-on or gets deprecated. No middle state.
74
+
75
+ ## Consequences
76
+
77
+ - **Positive:** new consumers get standard English by default; the
78
+ novice-onboarding path is unaffected; the dialect is auditable and
79
+ measurable before it becomes the default cost vector.
80
+ - **Negative:** the 40 %+ token savings are leave-on-table for any
81
+ consumer who doesn't explicitly opt in. Mitigated by surfacing the
82
+ flag in `/onboard` once `task bench` lands.
83
+ - **Reversal cost:** flip the default in
84
+ [`.agent-settings.yml`](../../../.agent-settings.yml.example)
85
+ template; existing user settings unaffected by template change.
86
+
87
+ ## References
88
+
89
+ - [`docs/contracts/compression-default-kill-criterion.md`](../../contracts/compression-default-kill-criterion.md) — kill-criterion contract.
90
+ - [`.agent-src.uncompressed/rules/caveman-speak.md`](../../../.agent-src.uncompressed/rules/caveman-speak.md) — dialect definition.
91
+ - [`agents/roadmaps/step-4-measurement-and-benchmark.md`](../../../agents/roadmaps/step-4-measurement-and-benchmark.md) — bench owner.
92
+ - [`agents/audit-2026-05-14-north-star/council-synthesis.md`](../../../agents/audit-2026-05-14-north-star/council-synthesis.md) § 7 — council split.
93
+ - [`agents/roadmaps/step-11-ruflo-parity.md`](../../../agents/roadmaps/step-11-ruflo-parity.md) Phase 4 Step 3 — origin.
@@ -0,0 +1,9 @@
1
+ # ADRs — `caveman`
2
+
3
+ > Caveman-speak compression, decompression, reversibility guards.
4
+
5
+ Contract: [`docs/contracts/compression-default-kill-criterion.md`](../../../docs/contracts/compression-default-kill-criterion.md).
6
+
7
+ | # | Title | Status | Date | Supersedes |
8
+ |---|---|---|---|---|
9
+ | [0001](0001-default-off-until-bench.md) | Default Off Until Bench | — | — | — |
@@ -0,0 +1,114 @@
1
+ # ADR 0001 — Hard-stop hook surface
2
+
3
+ > Area: `cost` · Status: accepted · Date: 2026-05-16 · Type: retrospective
4
+ > Roadmap: `agents/roadmaps/step-11-ruflo-parity.md` Phase 2 Step 3
5
+
6
+ ## Context
7
+
8
+ `step-11-measurement-governance-parity` Phase 2 lands a 5-tier budget
9
+ ladder (`OK / INFO / WARNING / CRITICAL / HARD_STOP`) evaluated by
10
+ [`scripts/cost/budget.mjs`](../../../scripts/cost/budget.mjs). The
11
+ roadmap requires that the `HARD_STOP` tier fail closed when the user
12
+ opts into `cost.enforcement: hard-stop` — without changing the
13
+ default-on advisory experience.
14
+
15
+ This package is a governance layer, not a runtime orchestrator (see
16
+ [`step-11`](../../../agents/roadmaps/step-11-ruflo-parity.md) scope
17
+ boundary). The hook must therefore live at a **process-entry seam**,
18
+ not inside the rule-loader, agent dispatcher, or per-tool-call
19
+ interceptor — none of those exist in this codebase by design.
20
+
21
+ ## Decision
22
+
23
+ Land a single shell-entry preflight script:
24
+ [`scripts/cost/preflight.mjs`](../../../scripts/cost/preflight.mjs).
25
+
26
+ ### Surface
27
+
28
+ - **Where it fires:** any shell or CI wrapper that opts in by calling
29
+ `task cost:preflight` (or `node scripts/cost/preflight.mjs`) before
30
+ composing a turn. Not auto-injected anywhere.
31
+ - **What it does:** wraps `budget.mjs check`. Reads `cost.enforcement`
32
+ from `.agent-settings.yml`. Exits non-zero **only** when
33
+ `enforcement: hard-stop` **and** `level: HARD_STOP`.
34
+ - **Refusal output:** human-readable refusal block citing
35
+ [`docs/contracts/cost-enforcement.md`](../../contracts/cost-enforcement.md)
36
+ with the three bypass paths (raise budget · reset ledger · disable
37
+ enforcement). Machine-readable equivalent on `PREFLIGHT_QUIET=1`.
38
+
39
+ ### Default behaviour without a budget
40
+
41
+ When `cost.budgets.{daily,weekly,monthly}` are all `0`:
42
+
43
+ - `preflight.mjs` exits `0`. Always.
44
+ - This is the **fail-open default**. Unconfigured projects never get
45
+ blocked.
46
+
47
+ ### Bypass
48
+
49
+ Three options, all documented in the refusal block and the contract:
50
+
51
+ 1. **Raise the budget** — edit `.agent-settings.yml § cost.budgets`.
52
+ 2. **Reset the ledger** — `node scripts/cost/track.mjs reset` (drops
53
+ historical spend from utilization).
54
+ 3. **Disable enforcement** — set `cost.enforcement: advisory`.
55
+
56
+ No env-var override. Bypass is durable and auditable.
57
+
58
+ ## Considered alternatives
59
+
60
+ ### Alt 1 — Rule-loader hook (rejected)
61
+
62
+ Refuse to compose the turn-start preamble in the kernel-rule loader.
63
+
64
+ **Why rejected:** the kernel-rule loader is a static-projection step
65
+ (`scripts/sync.py` writes `.agent-src/rules/` from
66
+ `.agent-src.uncompressed/`). There is no live loader at turn-start to
67
+ hook — Claude / Augment / etc. read the projected files directly. A
68
+ "refusal" would have to be baked into the file content, which mixes
69
+ policy enforcement with documentation. Out of scope.
70
+
71
+ ### Alt 2 — `/onboard` boot path (rejected)
72
+
73
+ Block `/onboard` when over budget.
74
+
75
+ **Why rejected:** `/onboard` is a one-shot install path. Blocking it
76
+ when the **historical** spend exceeds the **new** budget creates a
77
+ chicken-and-egg lock-out. Onboarding must never fail-closed.
78
+
79
+ ### Alt 3 — Per-tool-call interceptor (rejected)
80
+
81
+ Wrap every tool call with a budget check.
82
+
83
+ **Why rejected:** this package doesn't intercept tool calls. Building
84
+ that would require a runtime engine — the explicit Non-goal of
85
+ `step-11` (see scope boundary). This is the Ruflo-style runtime
86
+ absorption we ruled out.
87
+
88
+ ### Alt 4 — Single-shell-entry preflight (accepted)
89
+
90
+ The chosen surface. Opt-in by wrapper invocation; predictable; no
91
+ runtime, no projection coupling, no chicken-and-egg edges. Matches the
92
+ governance-layer charter.
93
+
94
+ ## Consequences
95
+
96
+ - **Positive:** consumers who want fail-closed behaviour get it with
97
+ one settings flip + one task invocation. The default-on experience
98
+ is unchanged. No runtime is introduced. The hook composes with `task
99
+ ci`, `task work:*`, or any shell wrapper the consumer chooses.
100
+ - **Negative:** the hook is **opt-in by wrapper**. A consumer who sets
101
+ `enforcement: hard-stop` but never invokes `task cost:preflight`
102
+ gets no enforcement. Documented as a known limitation; mitigated by
103
+ surfacing the gap in `agent-status`.
104
+ - **Reversal cost:** flip `cost.enforcement: advisory` globally, or
105
+ remove the `task cost:preflight` call from wrappers. Hook becomes
106
+ inert with no code change.
107
+
108
+ ## References
109
+
110
+ - [`docs/contracts/cost-enforcement.md`](../../contracts/cost-enforcement.md) — contract surface.
111
+ - [`scripts/cost/budget.mjs`](../../../scripts/cost/budget.mjs) — evaluator.
112
+ - [`scripts/cost/preflight.mjs`](../../../scripts/cost/preflight.mjs) — this hook.
113
+ - [`agents/roadmaps/step-11-ruflo-parity.md`](../../../agents/roadmaps/step-11-ruflo-parity.md) Phase 2 Step 3 — origin.
114
+ - [`agents/audit-2026-05-14-north-star/external-findings.md`](../../../agents/audit-2026-05-14-north-star/external-findings.md) § 2 row "hard stop" — upstream Ruflo pattern this absorbs.
@@ -0,0 +1,9 @@
1
+ # ADRs — `cost`
2
+
3
+ > Budget ladder, hard-stop hook, cost reporting and dashboards.
4
+
5
+ Contract: [`docs/contracts/cost-enforcement.md`](../../../docs/contracts/cost-enforcement.md).
6
+
7
+ | # | Title | Status | Date | Supersedes |
8
+ |---|---|---|---|---|
9
+ | [0001](0001-hard-stop-hook.md) | Hard Stop Hook | — | — | — |
@@ -0,0 +1,111 @@
1
+ # ADR 0001 — Consumer-side snapshot of the agent-memory contract
2
+
3
+ > Area: `memory` · Status: accepted · Date: 2026-05-16 · Type: retrospective
4
+ > Roadmap: `agents/roadmaps/step-11-ruflo-parity.md` Phase 4 Step 3
5
+ > Supersedes: —
6
+
7
+ ## Context
8
+
9
+ `@event4u/agent-memory` is a sibling package owned by this team. It
10
+ provides MCP-server semantic-retrieval plus a v1 retrieval envelope
11
+ (`id`, `type`, `source`, `confidence`, `body`, …). The spec-side
12
+ source-of-truth lives in
13
+ [`agents/roadmaps/archive/agent-memory/`](../../../agents/roadmaps/archive/agent-memory/);
14
+ the implementation lives in the sibling repo.
15
+
16
+ The question this ADR records is: **what surface does `agent-config`
17
+ freeze internally**, given the package ships independently and the
18
+ two repos can drift between releases?
19
+
20
+ ## Decision
21
+
22
+ **Maintain a consumer-side snapshot at
23
+ [`docs/contracts/agent-memory-contract.md`](../../contracts/agent-memory-contract.md)** —
24
+ a point-in-time pin of the interface `agent-config`'s wired code
25
+ **currently assumes**.
26
+
27
+ ### Three expected backend states
28
+
29
+ Probed by [`scripts/memory_status.py`](../../../scripts/memory_status.py):
30
+
31
+ | Status | Meaning | Agent-config behaviour |
32
+ |---|---|---|
33
+ | `absent` | Package not installed / CLI not on PATH | File fallback only |
34
+ | `misconfigured` | Installed; `health()` fails within 2 s | Warn once per session, fall back to file |
35
+ | `present` | Installed; healthy within 2 s | Route retrieval through package |
36
+
37
+ Detection is **bounded** (≤ 2 s cold probe), **cached per process**,
38
+ **non-raising** on probe failure.
39
+
40
+ ### CLI candidates
41
+
42
+ Probed in `_CLI_CANDIDATES`: `memory` (canonical, v1.1+) ·
43
+ `agent-memory` (planned alias) · `agentmem` (legacy). If the released
44
+ package diverges from these names, this file updates — never the
45
+ other way round.
46
+
47
+ ### Retrieval envelope (v1)
48
+
49
+ Mandatory per entry: `id`, `type`, `source ∈ {repo, operational}`,
50
+ `confidence`, `body`. Optional: `trust`, `last_validated`,
51
+ `shadowed_by`. Envelope: `contract_version`, `status ∈ {ok, partial,
52
+ error}`, `entries`, `slices`, `errors`. Source-of-truth:
53
+ [`scripts/memory_lookup.py`](../../../scripts/memory_lookup.py)
54
+ lines 320–345.
55
+
56
+ ### Refresh policy
57
+
58
+ The snapshot doc carries a `Last refreshed:` line. On every sibling
59
+ package release that touches the CLI or envelope shape, the
60
+ maintainer:
61
+
62
+ 1. Runs the sibling package's CLI against `agent-config`'s test
63
+ fixtures.
64
+ 2. Updates this contract doc with the new shape, bumps
65
+ `Last refreshed:`, files the diff as a PR.
66
+ 3. Updates `_CLI_CANDIDATES` and any consumer skills only if the
67
+ shape diff is breaking.
68
+
69
+ ## Considered alternatives
70
+
71
+ ### Alt 1 — No internal pin; trust the sibling spec (rejected)
72
+
73
+ Read the sibling package's spec at runtime / docs time.
74
+
75
+ **Why rejected:** the two repos release on independent cadences. A
76
+ mid-iteration sibling-package change would silently break consumer
77
+ code. The internal pin makes the divergence point a diffable doc.
78
+
79
+ ### Alt 2 — Submodule the sibling spec (rejected)
80
+
81
+ Git submodule pulls `agent-memory/docs/` into `agent-config/`.
82
+
83
+ **Why rejected:** submodules complicate consumer installs (npx /
84
+ shell installer must handle nested clones); the spec is fast-moving
85
+ and submodule churn dwarfs the consumer-side surface this package
86
+ actually wires.
87
+
88
+ ### Alt 3 — Consumer-side snapshot (accepted)
89
+
90
+ The chosen path. One readable doc, refreshed on sibling-package
91
+ release, citable from every consumer skill that touches memory.
92
+
93
+ ## Consequences
94
+
95
+ - **Positive:** drift between sibling spec and consumer wiring lands
96
+ in one diffable file; the `present` / `misconfigured` / `absent`
97
+ contract is one citation away; new consumer skills don't have to
98
+ re-derive the envelope shape.
99
+ - **Negative:** the snapshot can lag the sibling package. Mitigated
100
+ by the explicit `Last refreshed:` line and the refresh policy in §
101
+ "Refresh policy" above.
102
+ - **Reversal cost:** delete the contract doc; consumers fall back to
103
+ reading the sibling spec directly. No code change required.
104
+
105
+ ## References
106
+
107
+ - [`docs/contracts/agent-memory-contract.md`](../../contracts/agent-memory-contract.md) — the consumer-side snapshot.
108
+ - [`scripts/memory_status.py`](../../../scripts/memory_status.py) — three-state probe.
109
+ - [`scripts/memory_lookup.py`](../../../scripts/memory_lookup.py) — retrieval envelope source.
110
+ - [`agents/roadmaps/archive/agent-memory/`](../../../agents/roadmaps/archive/agent-memory/) — sibling-package spec.
111
+ - [`agents/roadmaps/step-11-ruflo-parity.md`](../../../agents/roadmaps/step-11-ruflo-parity.md) Phase 4 Step 3 — origin.
@@ -0,0 +1,9 @@
1
+ # ADRs — `memory`
2
+
3
+ > Memory MCP, propose / promote / poison flow, runtime-trust scoring.
4
+
5
+ Contract: [`docs/contracts/agent-memory-contract.md`](../../../docs/contracts/agent-memory-contract.md).
6
+
7
+ | # | Title | Status | Date | Supersedes |
8
+ |---|---|---|---|---|
9
+ | [0001](0001-consumer-side-snapshot.md) | Consumer Side Snapshot | — | — | — |
@@ -0,0 +1,119 @@
1
+ # ADR 0001 — Three-tier rule router (kernel · tier-1 · tier-2)
2
+
3
+ > Area: `router` · Status: accepted · Date: 2026-05-16 · Type: retrospective
4
+ > Roadmap: `agents/roadmaps/step-11-ruflo-parity.md` Phase 4 Step 3
5
+ > Supersedes: —
6
+
7
+ ## Context
8
+
9
+ Every rule in `.agent-src.uncompressed/rules/` loads into a host
10
+ agent's system prompt. Loading them all is wasteful (the workspace
11
+ budget is finite) and noisy (irrelevant rules degrade attention).
12
+ The kernel-membership work
13
+ ([`docs/contracts/kernel-membership.md`](../../contracts/kernel-membership.md))
14
+ proved 9 rules are unconditionally relevant; the rest are
15
+ situational.
16
+
17
+ The router question is: **how does a non-kernel rule declare when it
18
+ fires, what artefacts carry its body, and how does the host agent
19
+ look that up cheaply at session start**.
20
+
21
+ ## Decision
22
+
23
+ **Three tiers, declared in rule frontmatter, compiled into a single
24
+ [`router.json`](../../../router.json) at the repo root.** Host agents
25
+ read `router.json` once per session.
26
+
27
+ ### Tier semantics
28
+
29
+ | Tier | Load behaviour | Workspace cost |
30
+ |---|---|---|
31
+ | `kernel` | Body injected verbatim every turn | High (capped per ADR-002 · ≤ 26k chars) |
32
+ | `tier-1` | Description stub injected; body loaded on trigger | Low (stub only) |
33
+ | `tier-2` | No injection; loaded only on explicit trigger match | Zero (until activated) |
34
+
35
+ The tier value sits in the rule's `tier:` frontmatter key; the
36
+ schema enforces the enum (`scripts/schemas/rule.schema.json`).
37
+
38
+ ### Trigger vocabulary
39
+
40
+ Six match kinds, OR-combined across entries, AND-combined within an
41
+ entry's qualifier object (full grammar:
42
+ [`rule-router.md`](../../contracts/rule-router.md) § `triggers:` shape):
43
+
44
+ `keyword` · `phrase` · `intent` · `file_pattern` · `path_prefix` ·
45
+ `command`. Kernel rules **forbid** `triggers:` — they are
46
+ unconditional by definition.
47
+
48
+ ### Compilation pipeline
49
+
50
+ 1. `scripts/sync.py` projects `.agent-src.uncompressed/` → `.agent-src/`.
51
+ 2. `scripts/compile_router.py` walks rule frontmatter, validates the
52
+ `routes_to:` targets exist on disk, writes `router.json`.
53
+ 3. `scripts/skill_linter.py` runs the bidirectional check: every
54
+ `routes_to:` target has a matching `triggered_by:` back-ref.
55
+
56
+ ### `routes_to:` resolution
57
+
58
+ Plain `<kind>:<id>` strings; kinds are `skill`, `guideline`,
59
+ `command`, `contract`. Resolution paths are pinned in
60
+ [`rule-router.md`](../../contracts/rule-router.md) lines 73–77 and
61
+ enforced by the linter.
62
+
63
+ ## Considered alternatives
64
+
65
+ ### Alt 1 — Single flat list (rejected)
66
+
67
+ All rules `always`-load.
68
+
69
+ **Why rejected:** the rules tree is ~70 entries; even with caveman
70
+ compression the workspace budget blows past the kernel cap. Static
71
+ loading wastes attention on rules irrelevant to the current turn.
72
+
73
+ ### Alt 2 — Pure-runtime semantic search (rejected)
74
+
75
+ Embed every rule, semantic-match the turn against the index, load
76
+ top-k.
77
+
78
+ **Why rejected:** introduces a runtime (this package is a governance
79
+ layer, not a runtime — see step-11 scope boundary); index drift adds
80
+ a non-trivial maintenance surface; first-turn cold-start is slow.
81
+
82
+ ### Alt 3 — Two tiers (kernel + load-on-trigger, rejected)
83
+
84
+ Skip the `tier-1` description-stub layer.
85
+
86
+ **Why rejected:** the stub layer carries 70 % of the "does this turn
87
+ need this rule?" signal at < 5 % of the body cost. Removing it
88
+ forces the host agent to either over-load (cost spike) or
89
+ mis-trigger (correctness loss).
90
+
91
+ ### Alt 4 — Three-tier with frontmatter declaration (accepted)
92
+
93
+ The chosen path. Kernel + tier-1 stubs (balanced + full profiles) +
94
+ tier-2 (full profile only). Cost stays bounded; trigger declarations
95
+ diff cleanly in PRs.
96
+
97
+ ## Consequences
98
+
99
+ - **Positive:** every rule declares its activation cost; the kernel
100
+ cap is enforceable; the host agent's session-start cost is one
101
+ JSON read; the `routes_to:` link makes "where does this rule's
102
+ body live?" answerable without grep.
103
+ - **Negative:** two layers of indirection (rule → stub → body)
104
+ raise the bar for new contributors. Mitigated by
105
+ [`rule-router.md`](../../contracts/rule-router.md) being the
106
+ single citable contract and the linter catching shape errors.
107
+ - **Reversal cost:** flatten by removing tier-1 / tier-2 distinction
108
+ in the compiler — `router.json` becomes a list of all rules. The
109
+ rule frontmatter stays valid (extra fields are tolerated by
110
+ Draft-07 once `additionalProperties` is relaxed).
111
+
112
+ ## References
113
+
114
+ - [`docs/contracts/rule-router.md`](../../contracts/rule-router.md) — frontmatter contract.
115
+ - [`docs/contracts/kernel-membership.md`](../../contracts/kernel-membership.md) — kernel cap.
116
+ - [`router.json`](../../../router.json) — compiled output.
117
+ - [`scripts/compile_router.py`](../../../scripts/compile_router.py) — compiler.
118
+ - [`scripts/schemas/rule.schema.json`](../../../scripts/schemas/rule.schema.json) — schema.
119
+ - [`agents/roadmaps/step-11-ruflo-parity.md`](../../../agents/roadmaps/step-11-ruflo-parity.md) Phase 4 Step 3 — origin.
@@ -0,0 +1,9 @@
1
+ # ADRs — `router`
2
+
3
+ > router.json shape, tier semantics, dispatch precedence.
4
+
5
+ Contract: [`docs/contracts/rule-router.md`](../../../docs/contracts/rule-router.md).
6
+
7
+ | # | Title | Status | Date | Supersedes |
8
+ |---|---|---|---|---|
9
+ | [0001](0001-three-tier-routing.md) | Three Tier Routing | — | — | — |
@@ -0,0 +1,102 @@
1
+ # ADR 0001 — JSON Schema as the frontmatter source of truth
2
+
3
+ > Area: `schema` · Status: accepted · Date: 2026-05-16 · Type: retrospective
4
+ > Roadmap: `agents/roadmaps/step-11-ruflo-parity.md` Phase 4 Step 3
5
+ > Supersedes: —
6
+
7
+ ## Context
8
+
9
+ Skills, rules, commands, and personas all declare YAML frontmatter
10
+ with required and optional keys. Validation needs to be:
11
+
12
+ 1. Machine-checkable on every PR (no "guideline" enforcement).
13
+ 2. Diff-readable so the contract drift is visible.
14
+ 3. Tool-agnostic — the same artefacts ship to Claude, Cursor, Cline,
15
+ Windsurf, and Augment, all of which parse the same YAML.
16
+
17
+ The contract is documented for humans in
18
+ [`agents/docs/frontmatter-contract.md`](../../../agents/docs/frontmatter-contract.md);
19
+ the question this ADR records is **how that contract is enforced**.
20
+
21
+ ## Decision
22
+
23
+ **JSON Schema (Draft-07) files in
24
+ [`scripts/schemas/`](../../../scripts/schemas/) are the
25
+ machine-readable source of truth.** The human contract document
26
+ defers to the schema (`$comment` in each schema file pins
27
+ `agents/docs/frontmatter-contract.md` as the source).
28
+
29
+ ### Schema files
30
+
31
+ | Type | Schema | Validator entry-point |
32
+ |---|---|---|
33
+ | Skill | `scripts/schemas/skill.schema.json` | `scripts/skill_linter.py` |
34
+ | Rule | `scripts/schemas/rule.schema.json` | `scripts/skill_linter.py` |
35
+ | Command | `scripts/schemas/command.schema.json` | `scripts/validate_frontmatter.py` |
36
+ | Persona | `scripts/schemas/persona.schema.json` | `scripts/validate_frontmatter.py` |
37
+
38
+ ### Required-key threshold
39
+
40
+ A key is **required** (in the schema's `required:` array) if ≥ 95 %
41
+ of files in the type declare it. Everything else is optional. The
42
+ threshold is documented inline in
43
+ [`frontmatter-contract.md`](../../../agents/docs/frontmatter-contract.md)
44
+ § "Definition of required". Re-derive counts with
45
+ `python3 scripts/inventory_frontmatter.py`.
46
+
47
+ ### Validation hooks
48
+
49
+ - `task lint-skills` — runs `skill_linter.py` against all skills + rules.
50
+ - `task ci` — wraps `lint-skills` plus router + smoke smokes.
51
+ - Smoke contract `scripts/smoke/schema.sh` — fast random-sample check
52
+ for PRs touching `.agent-src.uncompressed/**` (see
53
+ [`docs/contracts/smoke-contracts.md`](../../contracts/smoke-contracts.md)).
54
+
55
+ ## Considered alternatives
56
+
57
+ ### Alt 1 — Inline regex in the linter (rejected)
58
+
59
+ Hard-code shape checks in `skill_linter.py`.
60
+
61
+ **Why rejected:** the linter becomes the contract, the contract is
62
+ hidden in Python, and the artefacts can't be validated by external
63
+ tools (CI, editors, IDE plugins). Cross-tool portability is the
64
+ whole point of YAML frontmatter — the schema must be portable too.
65
+
66
+ ### Alt 2 — Pydantic / dataclass models (rejected)
67
+
68
+ Validate via typed Python models.
69
+
70
+ **Why rejected:** ties validation to Python runtime, prevents
71
+ JS/TS / Node tooling from reusing the same contract, and forces a
72
+ non-trivial dependency on every consumer that wants to validate
73
+ their own artefacts. JSON Schema is the lingua franca.
74
+
75
+ ### Alt 3 — JSON Schema Draft-07 (accepted)
76
+
77
+ The chosen path. One source-of-truth file per artefact type,
78
+ referenced by Python linter today, reusable by Node / VS Code /
79
+ JetBrains / any JSON-Schema-aware tool tomorrow.
80
+
81
+ ## Consequences
82
+
83
+ - **Positive:** schemas are diffable; new keys land via PR with
84
+ reviewable shape changes; non-Python tools can validate the same
85
+ contract; the linter stays thin.
86
+ - **Negative:** Draft-07 is older than Draft-2020-12; some keyword
87
+ niceties (`unevaluatedProperties`, conditional `then`/`else`) are
88
+ awkward. Mitigated by `additionalProperties: false` plus
89
+ `oneOf` / `if`-then composition where needed.
90
+ - **Reversal cost:** the linter is the only consumer today; replacing
91
+ the schema with Pydantic models would be a one-PR swap. The choice
92
+ is reversible without contract churn.
93
+
94
+ ## References
95
+
96
+ - [`scripts/schemas/skill.schema.json`](../../../scripts/schemas/skill.schema.json) — skill contract.
97
+ - [`scripts/schemas/rule.schema.json`](../../../scripts/schemas/rule.schema.json) — rule contract.
98
+ - [`scripts/schemas/command.schema.json`](../../../scripts/schemas/command.schema.json) — command contract.
99
+ - [`scripts/schemas/persona.schema.json`](../../../scripts/schemas/persona.schema.json) — persona contract.
100
+ - [`agents/docs/frontmatter-contract.md`](../../../agents/docs/frontmatter-contract.md) — human-readable contract.
101
+ - [`scripts/skill_linter.py`](../../../scripts/skill_linter.py) — primary validator.
102
+ - [`agents/roadmaps/step-11-ruflo-parity.md`](../../../agents/roadmaps/step-11-ruflo-parity.md) Phase 4 Step 3 — origin.
@@ -0,0 +1,9 @@
1
+ # ADRs — `schema`
2
+
3
+ > Frontmatter schemas, v2 rigor, lint behaviour for skills / rules / commands.
4
+
5
+ Contract: [`agents/docs/frontmatter-contract.md`](../../../agents/docs/frontmatter-contract.md).
6
+
7
+ | # | Title | Status | Date | Supersedes |
8
+ |---|---|---|---|---|
9
+ | [0001](0001-json-schema-frontmatter.md) | Json Schema Frontmatter | — | — | — |