@event4u/agent-config 8.7.0 → 8.8.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 (83) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/CHANGELOG.md +52 -0
  3. package/CONTRIBUTING.md +25 -0
  4. package/README.md +7 -4
  5. package/dist/agent-src/contexts/execution/auto-orchestration-activation.md +1 -1
  6. package/dist/agent-src/contexts/execution/orchestration-benchmark-gate.md +28 -16
  7. package/dist/agent-src/contexts/execution/orchestration-telemetry.md +15 -0
  8. package/dist/agent-src/rules/active-remediation.md +92 -0
  9. package/dist/agent-src/rules/broken-access-control.md +106 -0
  10. package/dist/agent-src/rules/communication-through-line.md +42 -0
  11. package/dist/agent-src/rules/decision-revisit-gate.md +8 -0
  12. package/dist/agent-src/rules/minimal-safe-diff.md +18 -1
  13. package/dist/agent-src/rules/prefer-enums-over-literals.md +65 -0
  14. package/dist/agent-src/rules/senior-engineering-discipline.md +82 -0
  15. package/dist/agent-src/skills/ai-code-blindspots/SKILL.md +105 -0
  16. package/dist/agent-src/skills/ai-code-blindspots/evals/triggers.json +52 -0
  17. package/dist/agent-src/skills/authz-review/SKILL.md +21 -4
  18. package/dist/agent-src/skills/dependency-upgrade/SKILL.md +20 -0
  19. package/dist/agent-src/skills/frontend-render-security/SKILL.md +104 -0
  20. package/dist/agent-src/skills/frontend-render-security/evals/triggers.json +52 -0
  21. package/dist/agent-src/skills/roadmap-writing/SKILL.md +7 -0
  22. package/dist/agent-src/skills/supply-chain-intake/SKILL.md +100 -0
  23. package/dist/agent-src/skills/supply-chain-intake/evals/triggers.json +52 -0
  24. package/dist/agent-src/skills/testing-anti-patterns/SKILL.md +43 -2
  25. package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
  26. package/dist/agent-src/templates/roadmaps.md +24 -0
  27. package/dist/discovery/deprecation-report.md +1 -1
  28. package/dist/discovery/discovery-manifest.json +195 -15
  29. package/dist/discovery/discovery-manifest.json.sha256 +1 -1
  30. package/dist/discovery/discovery-manifest.summary.md +3 -3
  31. package/dist/discovery/orphan-report.md +1 -1
  32. package/dist/discovery/packs.json +13 -5
  33. package/dist/discovery/trust-report.md +11 -11
  34. package/dist/discovery/workspaces.json +12 -4
  35. package/dist/install/install.mjs +5 -2
  36. package/dist/mcp/awesome-mcp-servers.row.md +1 -1
  37. package/dist/mcp/mcp-cloudflare-catalogue.json +1 -1
  38. package/dist/mcp/registry-manifest.json +4 -4
  39. package/dist/router.json +1 -1
  40. package/dist/server/schemas/settings.js +2 -1
  41. package/dist/server/schemas/settings.js.map +1 -1
  42. package/docs/CLAIMS.md +30 -2
  43. package/docs/architecture.md +2 -2
  44. package/docs/catalog.md +14 -0
  45. package/docs/contracts/mcp-tool-inventory.md +31 -30
  46. package/docs/decisions/ADR-105-automatic-subagent-orchestration.md +6 -0
  47. package/docs/decisions/ADR-116-memory-tripwire-activation-path.md +2 -2
  48. package/docs/decisions/ADR-117-subagents-auto-default-on.md +88 -0
  49. package/docs/decisions/INDEX.md +1 -0
  50. package/docs/experiences/content_creator.md +2 -0
  51. package/docs/experiences/finance.md +2 -0
  52. package/docs/experiences/founder.md +2 -0
  53. package/docs/experiences/ops.md +2 -0
  54. package/docs/featured-skills.md +2 -2
  55. package/docs/getting-started-by-role.md +1 -1
  56. package/docs/getting-started.md +1 -1
  57. package/docs/governance-advantage.md +1 -1
  58. package/docs/guidelines/agent-infra/memory-access.md +12 -0
  59. package/docs/proof.md +45 -3
  60. package/docs/release-runbook.md +138 -0
  61. package/docs/second-brain-scope.md +73 -0
  62. package/docs/succession.md +80 -0
  63. package/package.json +2 -2
  64. package/src/config/agent-settings.template.yml +25 -6
  65. package/src/scripts/_lib/orchestration_gate.ts +12 -8
  66. package/src/scripts/_lib/orchestration_savings.ts +127 -0
  67. package/src/scripts/build_proof.ts +38 -0
  68. package/src/scripts/chat_history.ts +79 -7
  69. package/src/scripts/domain_soundness_status.ts +236 -0
  70. package/src/scripts/generate_index.ts +14 -0
  71. package/src/scripts/hot_context_hook.ts +47 -2
  72. package/src/scripts/lint_behavioural_eval_freshness.ts +140 -0
  73. package/src/scripts/mcp_server/consumer_tool_catalog.json +22 -3
  74. package/src/scripts/mcp_server/tools.ts +159 -1
  75. package/src/scripts/memory_lookup.ts +184 -6
  76. package/src/scripts/memory_replay.ts +285 -0
  77. package/src/scripts/orchestration_savings_report.ts +100 -0
  78. package/src/scripts/schemas/domain-truth.schema.json +94 -0
  79. package/src/scripts/schemas/evals.schema.json +113 -0
  80. package/src/scripts/second_brain_score.ts +186 -0
  81. package/src/scripts/session_memory_index.ts +94 -0
  82. package/src/scripts/skill_eval_coverage.ts +318 -0
  83. package/src/server/schemas/settings.ts +5 -2
@@ -6,7 +6,7 @@
6
6
  },
7
7
  "metadata": {
8
8
  "description": "Bootstrap shim for the event4u agent-config suite. This plugin ships ONLY the dispatcher hooks plus one install-pointer skill — ALL content (skills, rules, commands) is installed via `npx -y @event4u/agent-config init`, which writes the file projection AND registers the same hooks in ~/.claude/settings.json. DEPRECATED as a content channel; kept so the marketplace listing stays discoverable and existing installs keep working hooks.",
9
- "version": "8.7.0",
9
+ "version": "8.8.0",
10
10
  "keywords": [
11
11
  "agent-config",
12
12
  "skills",
package/CHANGELOG.md CHANGED
@@ -989,6 +989,58 @@ our recommendation order, not its support status.
989
989
  > that forces a new era split (`# Era: 8.2.x`, etc.) — see
990
990
  > [`docs/contracts/CHANGELOG-conventions.md § Era splits`](docs/contracts/CHANGELOG-conventions.md).
991
991
 
992
+ ## [8.8.0](https://github.com/event4u-app/agent-config/compare/8.7.0...8.8.0) (2026-07-09)
993
+
994
+ ### Features
995
+
996
+ * **rules:** security & quality hardening governance layer ([2574ce8](https://github.com/event4u-app/agent-config/commit/2574ce8f243763e06e62ad82e58413d182964a50))
997
+ * **orchestration:** token-savings report over orchestration telemetry ([60b90a2](https://github.com/event4u-app/agent-config/commit/60b90a253efe26810a349a930d8fd6fdf02a3d37))
998
+ * **subagents:** default subagents.auto to `on` on subagent-capable hosts ([bc3eea5](https://github.com/event4u-app/agent-config/commit/bc3eea554e06cbcc9013145143fce93a1a1adb53))
999
+ * **bus-factor:** CODEOWNERS + self-imposed-gate CONTRIBUTING section ([ec9b93e](https://github.com/event4u-app/agent-config/commit/ec9b93ebe94f33aecf6ddb00b72dbfb60b2e0c37))
1000
+ * **second-brain:** deterministic multi-session recall corpus + scorer ([5f09fff](https://github.com/event4u-app/agent-config/commit/5f09ffff2d4090ed6761f37d55a8bb0a73b99333))
1001
+ * **domain-soundness:** domain-truth fixture schema + validation-status ratchet ([bd67c4d](https://github.com/event4u-app/agent-config/commit/bd67c4dec9de9691eea2b1367807a4e190db5db8))
1002
+ * **evals:** staleness lint + wire the coverage ratchet into CI ([886bf25](https://github.com/event4u-app/agent-config/commit/886bf25fedcaface708ee3f8be17135d8be2e742))
1003
+ * **evals:** behavioural-eval schema + per-tier coverage metric + ratchet floor ([2e303f6](https://github.com/event4u-app/agent-config/commit/2e303f66d076355434ff720b6d81ac3f3021b22e))
1004
+ * **memory:** opt-in session-start index + FTS5/AST backlog ledger ([bb15b21](https://github.com/event4u-app/agent-config/commit/bb15b21fb334d7c44896b7ef4c340c0396f0c6c4))
1005
+ * **memory:** catalog discipline, history timeline anchor, chunk index flags ([3cce078](https://github.com/event4u-app/agent-config/commit/3cce0789f48966fe31e8fad1ad63ae2477c53259))
1006
+ * **memory:** index/detail split + memory_get batch fetch (Phase 1) ([36547c8](https://github.com/event4u-app/agent-config/commit/36547c88452c99eb02d7c2db61ce9556701db80d))
1007
+ * **memory:** Phase 0 replay substrate for the retrieval-economy gate ([6d05daf](https://github.com/event4u-app/agent-config/commit/6d05dafa35ae38b4c4d97cd17a155c1e4bbc801e))
1008
+
1009
+ ### Bug Fixes
1010
+
1011
+ * **rules:** apply council-adjudicated review fixes ([d6d818a](https://github.com/event4u-app/agent-config/commit/d6d818a8bfb79c6862488eb367f4f3e92f7d7aa6))
1012
+ * **rules:** satisfy skill-lint, schema, and proof-check gates ([ea551cc](https://github.com/event4u-app/agent-config/commit/ea551cc5c9efb21ab6453948f97848557d98ae1f))
1013
+ * **orchestration:** guard undefined argv in savings-report parseArgs ([cf2539e](https://github.com/event4u-app/agent-config/commit/cf2539e73ac7e7edbaee206bcd09e03032986618))
1014
+ * **mcp:** cast validated depth args to number for tsc ([cc5a8a4](https://github.com/event4u-app/agent-config/commit/cc5a8a4ac80ac245ec2b30fa4223d1888788e475))
1015
+
1016
+ ### Documentation
1017
+
1018
+ * **orchestration:** document the savings report + the no-percentage limit ([d29a5d1](https://github.com/event4u-app/agent-config/commit/d29a5d10411c3507d6fb035ac44959ee2a738435))
1019
+ * **adoption:** link the deployed docs site + surface the 30-second wedge ([eb74680](https://github.com/event4u-app/agent-config/commit/eb74680bec665c422e226ac8c5e8458c8279539e))
1020
+ * **adr:** ADR-117 flip subagents.auto default to on; amend ADR-105, supersede verdict ([0b5879c](https://github.com/event4u-app/agent-config/commit/0b5879c3f25014c54779e959e16282ac66d4ae4d))
1021
+ * **bus-factor:** bind the honest bus-factor to CLAIMS ([6bbbda4](https://github.com/event4u-app/agent-config/commit/6bbbda4d841f084c27c5a8f34bd10171429bc684))
1022
+ * **bus-factor:** inheritable release runbook + succession doc ([7d05577](https://github.com/event4u-app/agent-config/commit/7d05577f32c154332fb8bdf90833e8e7acf7f704))
1023
+ * **second-brain:** honest scope vs human-PKM + unproven-lift CLAIMS gate ([b9dc934](https://github.com/event4u-app/agent-config/commit/b9dc9347661237c24cdadd5ca454ed07acbf9409))
1024
+ * **domain-soundness:** label the domains unvalidated + gate the provenance claim ([7221662](https://github.com/event4u-app/agent-config/commit/72216625a559818196441349501af3b65c4be3ce))
1025
+ * **evals:** publish the honest coverage baseline + label the long tail ([299a7fe](https://github.com/event4u-app/agent-config/commit/299a7fe74360a8df5b7d46c026be65b501685ff0))
1026
+ * **memory:** Phase 1b honest null — default flip falsified at current scale ([938aab2](https://github.com/event4u-app/agent-config/commit/938aab2f5d6dcf43fd82738025fe309d5cb7ad91))
1027
+
1028
+ ### CI
1029
+
1030
+ * **second-brain:** wire the recall-scorer dry-run into ci + ci-strict ([7da6d01](https://github.com/event4u-app/agent-config/commit/7da6d010f40793e31a9b3f891a5e08aa2afe184c))
1031
+ * **domain-soundness:** wire the validation-status ratchet into ci + ci-strict ([7ae10c2](https://github.com/event4u-app/agent-config/commit/7ae10c2cd4d36e65db3a52c8c68b824200c3f9c6))
1032
+
1033
+ ### Chores
1034
+
1035
+ * **generated:** regenerate counts, router, and pack manifest ([f141060](https://github.com/event4u-app/agent-config/commit/f141060e9da2f4f0a97a257d2786dd42fdf2ee0f))
1036
+ * **roadmap:** adoption Phase-0 site-link quick-win landed; rest positioning/human-gated ([de27276](https://github.com/event4u-app/agent-config/commit/de27276b06b3969a8ea8a22ed5327385aa3ff582))
1037
+ * **roadmap:** bus-factor inheritability slice landed; gate + admin deferred ([8bf79f5](https://github.com/event4u-app/agent-config/commit/8bf79f5dd3e9dcd940acce7c56eaad978d2525f7))
1038
+ * **roadmap:** second-brain-delta-proof P1 rig + P3 gate landed; P2 run deferred ([e01ec9e](https://github.com/event4u-app/agent-config/commit/e01ec9e9691fa5d19390fa3b187bafbef3e34618))
1039
+ * **roadmap:** domain-soundness P1/P3-gate/P4-labeling landed; P2 open on domain competence ([9a786d4](https://github.com/event4u-app/agent-config/commit/9a786d4cb2b0dc469c3b2967e8bc360e9877ec79))
1040
+ * **roadmap:** skill-eval-coverage P1/P3/P4 landed; P2 open on human gate ([a4c526d](https://github.com/event4u-app/agent-config/commit/a4c526d43a459bb86d78dcf2d07e3dc2d44b6828))
1041
+
1042
+ Tests: 7027 (+78 since 8.7.0)
1043
+
992
1044
  ## [8.7.0](https://github.com/event4u-app/agent-config/compare/8.6.0...8.7.0) (2026-07-08)
993
1045
 
994
1046
  ### Features
package/CONTRIBUTING.md CHANGED
@@ -93,6 +93,25 @@ task lint-skills # ./scripts-run src/scripts/skill_linter --all
93
93
  - Open the PR against `main` with a short description of the change,
94
94
  motivation, and any notes for reviewers.
95
95
 
96
+ ## Reviewability and the self-imposed gate
97
+
98
+ This is a governance standard, so the maintainer holds themselves to the same
99
+ gate as any contributor — the point is that even a solo merge goes **through**
100
+ the review floor, not around it.
101
+
102
+ - [`.github/CODEOWNERS`](.github/CODEOWNERS) routes review on the sensitive
103
+ surfaces (kernel rules, router compiler, install, hooks, claims/proof
104
+ generators, release pipeline, schemas). Branch protection is configured to
105
+ require Code-Owner review on those paths (maintainer, in GitHub → Settings →
106
+ Rules; source of truth: [`docs/contracts/branch-protection-policy.md`](docs/contracts/branch-protection-policy.md)).
107
+ - **Honest bound:** an AI adversarial-review / security gate (when enabled)
108
+ raises the floor and catches regressions and claim-drift, but it is **not**
109
+ independent human review. The bus-factor is tracked truthfully in
110
+ [`docs/succession.md`](docs/succession.md) — a bus-factor of 1 stated plainly
111
+ beats one implied to be more.
112
+ - A realistic second-reviewer on-ramp starts small (docs/claims or a single
113
+ pack), never "co-maintain the kernel on day one".
114
+
96
115
  ## Adding or editing skills, rules, and commands
97
116
 
98
117
  - Skills and rules live in `src/skills/` and `src/rules/`; commands in
@@ -276,6 +295,12 @@ which never reach a consumer.
276
295
 
277
296
  ### Release process
278
297
 
298
+ > **Inheritable runbook:** the exact, reproducible steps a second maintainer
299
+ > can follow cold — both entry points, the manual approval checkpoint, and
300
+ > recovery — live in [`docs/release-runbook.md`](docs/release-runbook.md), with
301
+ > the secret/operator-gated dependencies in [`docs/succession.md`](docs/succession.md).
302
+ > The summary below is orientation; the runbook is the source of truth.
303
+
279
304
  Releases are driven by a single command that owns the entire pipeline from
280
305
  version bump to npm publish:
281
306
 
package/README.md CHANGED
@@ -4,19 +4,21 @@
4
4
 
5
5
  [![Smoke](https://github.com/event4u-app/agent-config/actions/workflows/smoke.yml/badge.svg)](https://github.com/event4u-app/agent-config/actions/workflows/smoke.yml) [![Public install smoke (3 OS × 2 Node)](https://github.com/event4u-app/agent-config/actions/workflows/smoke-public-install.yml/badge.svg)](https://github.com/event4u-app/agent-config/actions/workflows/smoke-public-install.yml) [![npm](https://img.shields.io/npm/v/@event4u/agent-config?style=flat-square&label=npm&color=orange)](https://www.npmjs.com/package/@event4u/agent-config) [![agent-config MCP server](https://glama.ai/mcp/servers/event4u-app/agent-config/badges/score.svg)](https://glama.ai/mcp/servers/event4u-app/agent-config)
6
6
 
7
- [![Skills](https://img.shields.io/badge/Skills-264-orange?style=flat-square)](dist/agent-src/skills/) [![Rules](https://img.shields.io/badge/Rules-95-orange?style=flat-square)](dist/agent-src/rules/) [![Commands](https://img.shields.io/badge/Commands-172-orange?style=flat-square)](dist/agent-src/commands/) [![Guidelines](https://img.shields.io/badge/Guidelines-87-orange?style=flat-square)](docs/guidelines/) [![Personas](https://img.shields.io/badge/Personas-29-orange?style=flat-square)](dist/agent-src/personas/) [![Advisors](https://img.shields.io/badge/Advisors-5-orange?style=flat-square)](dist/agent-src/personas/advisors/)
7
+ [![Skills](https://img.shields.io/badge/Skills-267-orange?style=flat-square)](dist/agent-src/skills/) [![Rules](https://img.shields.io/badge/Rules-100-orange?style=flat-square)](dist/agent-src/rules/) [![Commands](https://img.shields.io/badge/Commands-172-orange?style=flat-square)](dist/agent-src/commands/) [![Guidelines](https://img.shields.io/badge/Guidelines-87-orange?style=flat-square)](docs/guidelines/) [![Personas](https://img.shields.io/badge/Personas-29-orange?style=flat-square)](dist/agent-src/personas/) [![Advisors](https://img.shields.io/badge/Advisors-5-orange?style=flat-square)](dist/agent-src/personas/advisors/)
8
8
 
9
9
  > **Choose your experience — developer · founder · content · agency · finance · ops. Add packs. Get a focused command set, not a 500-artefact dump.** Bring your own AI provider.
10
10
 
11
+ **Try it in 30 seconds** — drop one read-only subagent into any repo and watch it gate "done": `@production-validator check this branch is actually done`. No wizard, no lock-in, nothing else installed — the [30-second wedge ↓](#quickstart) is the whole first step. Full docs: **[event4u-app.github.io/agent-config](https://event4u-app.github.io/agent-config)**.
12
+
11
13
  **Every public claim in this README is machine-checked — [verify it yourself](docs/proof.md).** In a market that runs on unbacked headline numbers, this one binds each claim to resolvable evidence or fails its own build.
12
14
 
13
- **264 skills, 172 commands, 95 governed rules** — plus a capability router that loads the right skill on intent, and multi-agent orchestration with consensus review. The whole layer is compiled into 7+ host agents (Claude Code, Cursor, Augment, Cline, Windsurf, Copilot, Gemini) with **zero runtime daemon**.<!-- claim:no-runtime-daemon --> Six role-shaped entry paths sit on top, so any host becomes a reliable team member — without locking you to a single model or vendor.
15
+ **267 skills, 172 commands, 100 governed rules** — plus a capability router that loads the right skill on intent, and multi-agent orchestration with consensus review. The whole layer is compiled into 7+ host agents (Claude Code, Cursor, Augment, Cline, Windsurf, Copilot, Gemini) with **zero runtime daemon**.<!-- claim:no-runtime-daemon --> Six role-shaped entry paths sit on top, so any host becomes a reliable team member — without locking you to a single model or vendor.
14
16
 
15
17
  ### What's different
16
18
 
17
19
  It is both deep **and** disciplined — and honest about what it deliberately is not:
18
20
 
19
- - **Depth that routes itself** — 264 skills + 172 commands, with a capability router that loads the right one on intent, not a 500-artefact context dump.
21
+ - **Depth that routes itself** — 267 skills + 172 commands, with a capability router that loads the right one on intent, not a 500-artefact context dump.
20
22
  - **Governance on every host** — rules compiled into each tool's native format at projection time; deterministic runtime hooks added on hook-capable hosts. This config-space, host-agnostic governance is the moat ([the governance advantage](docs/governance-advantage.md) · [enforcement by host](docs/enforcement-by-host.md)).
21
23
  - **Surgical uninstall** — removes only its own keys from a shared host config (matched by JSON-pointer + SHA-256), never a neighbour tool's entries.<!-- claim:surgical-uninstall -->
22
24
  - **Pack-scoped install** — writes the active pack only, not a 500-artefact dump.
@@ -118,7 +120,8 @@ is generated from source: a claim→evidence table (every public claim binds to
118
120
  resolvable pointer, or CI fails), honest-null benchmarks *including the runs where
119
121
  the package changed nothing*, and a "verify it yourself" block you run on a fresh
120
122
  checkout. The proof page fails CI if it drifts from its sources — reproducibility
121
- is the proof.
123
+ is the proof. Browse it on the deployed docs site:
124
+ [event4u-app.github.io/agent-config](https://event4u-app.github.io/agent-config).
122
125
 
123
126
  Audit-disciplined by construction — every memory consult, decision
124
127
  key, and hook concern lands in `agents/runtime/state/` so you can replay it.
@@ -12,7 +12,7 @@ whether dispatch is physically possible on the host).
12
12
  | Key | Default | Purpose |
13
13
  |---|---|---|
14
14
  | `subagents.enabled` | `true` | Global master switch. `false` = the whole layer is off — the canonical kill-switch. No auto-dispatch, no routing, everything runs in-session. |
15
- | `subagents.auto` | `ask` | Auto-dispatch mode: `off` \| `ask` \| `on`. |
15
+ | `subagents.auto` | `on` | Auto-dispatch mode: `off` \| `ask` \| `on`. Default `on` on subagent-capable hosts (flipped from `ask` 2026-07-09, ADR-117), `off` elsewhere. |
16
16
  | `subagents.downshift` | `true` | Route delegable sub-tasks to the lowest-capable tier (Phase 2). |
17
17
  | `subagents.quota_arbitrage` | `true` | Prefer a separate-quota-pool model where the host manifest allows it (Phase 2 bonus). |
18
18
  | `subagents.model_map` | `{}` | Optional per-tier model override for downshift routing. |
@@ -1,9 +1,19 @@
1
- # Orchestration Benchmark & Default-Flip Gate (Phase 6)
2
-
3
- The falsification gate the whole auto-orchestration roadmap is built around.
4
- Reaching the user's "default `on`" is a **measured milestone**, not an
5
- assumption: the shipped default for `subagents.auto` flips toward `on` only
6
- when a real benchmark proves a net win at held quality.
1
+ # Orchestration Benchmark & Demotion Gate (Phase 6 → telemetry-demotion)
2
+
3
+ Originally the falsification gate the auto-orchestration roadmap was built
4
+ around: shipped default for `subagents.auto` would flip toward `on` only on a
5
+ benchmark proving a net win at held quality.
6
+
7
+ **As of 2026-07-09 the shipped default IS `on`** on subagent-capable hosts
8
+ (ADR-117; [`orchestration-default-flip-verdict`](../../../../agents/settings/contexts/orchestration-default-flip-verdict.md)
9
+ § 2026-07-09). Flipped on a **bounded-downside re-evaluation**, not a passed
10
+ benchmark: rigorous paired bench:ab not producible here (no runtime executing
11
+ model `Task`/`Agent` calls), win-validation deadlocked (telemetry needs `on`,
12
+ `on` gated on telemetry). Re-eval broke the deadlock — downside of `on` is
13
+ structurally small (delegable slices cost-routed to cheapest tier,
14
+ classification fires only on real structural signals, every return verified) →
15
+ `on` gathers realized telemetry responsibly. This gate now governs
16
+ **demotion**: measured regression flips default back to `ask`.
7
17
 
8
18
  ## Measurement — pin the method
9
19
 
@@ -30,18 +40,20 @@ HONEST-NULL EXIT: no win, or quality regressed → KEEP the conservative default
30
40
 
31
41
  `gateVerdict()` encodes pass = `net_win && quality_held`.
32
42
 
33
- ## Default flip — one line, host-gated
43
+ ## Default state + demotion — host-gated
34
44
 
35
- On a **passing** gate, the shipped default for `subagents.auto` becomes `on`
36
- **only** on hosts whose capability manifest reports `subagent_spawn: true`
37
- (`off` elsewhere). `resolveShippedDefault()` encodes this. The flip itself is a
38
- one-line edit to the `subagents.auto` default in
39
- `src/config/agent-settings.template.yml` — no code change, host-gated by the
40
- manifest at runtime.
45
+ Shipped default for `subagents.auto` is `on` on hosts whose manifest reports
46
+ `subagent_spawn: true` (`off` elsewhere) — set directly in
47
+ `src/config/agent-settings.template.yml`. `resolveShippedDefault()` remains the
48
+ host-gated resolver, now serving **demotion**: fed a `fail` verdict from
49
+ accumulated real-world orchestration telemetry, resolves back to `ask`
50
+ (conservative). A `pass` keeps `on`. Demotion path is a one-line edit,
51
+ host-gated by the manifest at runtime.
41
52
 
42
- Until the gate passes, the shipped default stays `ask` (conservative). This is
43
- the reconciliation of the user's "default on" goal with the council's
44
- "prove-first": `on` is the destination, reached by evidence, not assumed.
53
+ The 2026-07-09 flip reconciles the user's "default on" goal with the council's
54
+ "prove-first": rather than wait on a benchmark that cannot be produced here,
55
+ `on` ships because its downside is bounded and reversible, and realized
56
+ telemetry is now free to accrue and drive demotion if it regresses.
45
57
 
46
58
  ## Reproducibility
47
59
 
@@ -87,6 +87,21 @@ capture path (decided 2026-06-30, maintainer decision; see
87
87
  response length (chars / 4 ≈ tokens). Set `token_delta_provenance:
88
88
  "estimated"`. Lossy; measured always preferred.
89
89
 
90
+ ## Savings report
91
+
92
+ Aggregate accumulated telemetry into a token-savings report:
93
+
94
+ ```bash
95
+ ./scripts-run src/scripts/orchestration_savings_report [--dir <path>] [--format text|json]
96
+ ```
97
+
98
+ Sums `token_delta` across dispatches (negative = net saved); splits by
99
+ provenance (measured vs estimated), `tier_chosen`, `task_class`. Reports
100
+ **ABSOLUTE net tokens saved, never a percentage**: telemetry records net delta,
101
+ not the absolute in-session baseline → "% of session saved" not derivable. A
102
+ percentage needs an additive absolute-baseline field (deferred follow-up).
103
+ Reader: [`src/scripts/_lib/orchestration_savings.ts`](../../../../src/scripts/_lib/orchestration_savings.ts).
104
+
90
105
  ## Related
91
106
 
92
107
  - [`audit-log-v1`](../../../../docs/contracts/audit-log-v1.md) — the frozen
@@ -0,0 +1,92 @@
1
+ ---
2
+ type: "auto"
3
+ tier: "2b"
4
+ alwaysApply: false
5
+ description: "Spotted an issue while working (security gap, missing test, bad code, duplication, stale idiom) — never ignore; fix small+aligned inline, ask on bigger, propose a follow-up PR for many"
6
+ triggers:
7
+ - intent: "spotted an unrelated issue while working"
8
+ - intent: "found legacy or duplicated code"
9
+ - keyword: "refactor"
10
+ - keyword: "duplicate"
11
+ - keyword: "legacy"
12
+ - keyword: "deprecated"
13
+ - keyword: "modernize"
14
+ - keyword: "upgrade"
15
+ - keyword: "cleanup"
16
+ - keyword: "technical debt"
17
+ workspaces: [engineering]
18
+ packs: [engineering-base]
19
+ ---
20
+
21
+ # Active Remediation
22
+
23
+ The user doesn't see the code at the spot you're editing — you do. So this package guides the agent to **actively improve** what it touches: don't accept legacy, debt, security gaps, or duplication as given. But active improvement is bounded — never the scope creep `minimal-safe-diff` exists to stop. The rule is a **ladder**: act by size and alignment, and **never silently ignore** a real issue.
24
+
25
+ ## The Iron Law
26
+
27
+ ```
28
+ NEVER IGNORE A REAL ISSUE YOU SPOT — AT MINIMUM, NOTE IT.
29
+ SMALL + TASK-ALIGNED → FIX IT NOW, IN THE SAME CHANGE, WITH ITS TEST.
30
+ BIGGER OR DIVERGENT → NOTE IT, FINISH THE TASK, THEN ASK — NEVER MID-FLOW.
31
+ TOO MANY → PROPOSE ONE OR MORE SEPARATE FOLLOW-UP PRs THE USER REVIEWS.
32
+ A LIVE SECURITY / DATA-EXPOSURE GAP IS NEVER MERELY "NOTED FOR LATER".
33
+ ```
34
+
35
+ ## What to remediate
36
+
37
+ Security gaps (broken access control, injection, secrets, missing authz — see `broken-access-control`) · missing/weak test coverage · bad code (dead code, silent error-swallow, unguarded edge cases) · **duplication** (same/near-same logic repeated instead of one method/abstraction) · **stale version-idioms** (code for an older language/framework version than the project runs).
38
+
39
+ ## The ladder — classify, then act
40
+
41
+ ### Fix now (autonomous, inline) — a bounded amendment to `minimal-safe-diff`
42
+
43
+ Allowed **only** when ALL hold — the testable "small + task-aligned" definition:
44
+ - **Same request path / module** as the current task (not an unrelated feature).
45
+ - **≤ ~10 changed lines** in one production file plus its test file — no wider cross-file ripple.
46
+ - **No public-API / response-shape change**, no new parameter.
47
+ - **No dependency bump, no migration, no data change.**
48
+ - **Its verification ships in the same commit** — the security fix's negative test, the correctness fix's case.
49
+
50
+ Under those constraints the fix is auditable in the same diff and is *not* scope creep — it corrects a boundary the agent touched. Anything outside them → **note + ask**, never auto. (`minimal-safe-diff` still governs the rest: no reformatting, no opportunistic refactor, no drive-by rename.)
51
+
52
+ ### Note + ask (batched)
53
+
54
+ Bigger, or diverges from the task → do **not** refactor inline, do **not** interrupt the flow. Note the site (file:line + issue). Surface the batch as **one** numbered-options prompt (`user-interaction`, one recommendation line) **after the task is delivered** — or mid-work only when the flow makes it natural. Each option carries a real trade-off (`no-cheap-questions`). Refactor only on an explicit yes, as a separate scoped change (`scope-control`, `downstream-changes`).
55
+
56
+ ### Propose a follow-up PR (many spots)
57
+
58
+ Too many to fold in without blowing the current diff's scope → propose **one or more separate follow-up PRs** (or a roadmap under `agents/roadmaps/`, using the shared-prefix convention) so the user reviews before merge. Creating the PR/roadmap is **permission-gated** — propose, get a yes, then create (`scope-control`, `commit-policy`).
59
+
60
+ ## Version-gated modernization
61
+
62
+ Update stale idioms to the version the project **actually runs** — only when that version is **verified**:
63
+ - **Establish the version first** from the manifest constraint (`composer.json` `require`, `package.json` `engines`, `.tool-versions`, lockfile) — use the **lowest** bound; on an ambiguous/monorepo range, ask once. **Unknown version → do not touch** (must fit the project structure — the `source-discovery` gate).
64
+ - **Syntax-only, behavior-preserving idioms** (e.g. `array()` → `[]`, `isset($x)?$x:$d` → `$x ?? $d`, concat → template) provably equivalent → treat as a small fix (auto per the ladder).
65
+ - **Behavioral changes** (`readonly`/typed properties, new semantics) and **any dependency/version bump** → **ask only**, never auto (a version bump stays under `minimal-safe-diff`'s no-dependency-bump prohibition; a pure syntax idiom is categorically different).
66
+
67
+ ## Live-security carve-out (priority)
68
+
69
+ A **live cross-user / cross-tenant data exposure** (`broken-access-control`) is not "note for later": a potential GDPR-notifiable breach (Art. 33, 72 h from discovery). Meets the fix-now bar → fix now with its negative test. Otherwise → **stop and surface it immediately** (a safety surface, visible even under an autonomous mandate — like the `no-pr-progress-comments` safety carve-out). Never defer silently, never look away.
70
+
71
+ ## Guardrails — don't become a nagging machine
72
+
73
+ - Subordinate to `no-cheap-questions` (self-check items 3 & 14 — real trade-off, not a disguised continuation/commit ask), `autonomous-execution` (the end-of-session batch must not read as "shall I continue?"), `user-interaction`, `ask-when-uncertain` (one batched prompt = one question).
74
+ - Threshold to surface at all: a **real, nameable** improvement with concrete benefit. Cosmetic nitpicks with no trade-off → drop silently. The live-security carve-out is the only case that interrupts or overrides autonomy.
75
+
76
+ ## When it fires
77
+
78
+ While implementing/modifying code you pass an issue outside the literal task. Also on explicit "clean this up / modernize / dedupe".
79
+
80
+ ## When NOT to fire
81
+
82
+ - No issue spotted. Prose/docs-only. User fenced the scope ("just this one line").
83
+ - The issue is the task itself — then it's just the task (no ladder needed).
84
+
85
+ ## See also
86
+
87
+ - [`minimal-safe-diff`](minimal-safe-diff.md) — the carve-out above is bounded by it; everything outside the five conditions stays note+ask.
88
+ - [`broken-access-control`](broken-access-control.md) — the security category + the live-exposure carve-out.
89
+ - [`prefer-enums-over-literals`](prefer-enums-over-literals.md) — an instance of this ladder (enum literals).
90
+ - [`source-discovery-gate`](source-discovery-gate.md) — establishing the verified project version before modernizing.
91
+ - [`scope-control`](scope-control.md), [`commit-policy`](commit-policy.md) — PR/commit creation stays permission-gated.
92
+ - [`improve-before-implement`](improve-before-implement.md) — the pre-work challenge (before); this is the during/after remediation.
@@ -0,0 +1,106 @@
1
+ ---
2
+ type: "auto"
3
+ tier: "2a"
4
+ alwaysApply: false
5
+ description: "Endpoint/query returning user or tenant data — authenticated ≠ authorized; enforce a server-derived ownership/tenant/role check + the three negative tests (401/non-owner/cross-tenant)"
6
+ triggers:
7
+ - intent: "returning user or tenant data"
8
+ - intent: "adding an endpoint that reads a record by id"
9
+ - keyword: "endpoint"
10
+ - keyword: "route"
11
+ - keyword: "authorization"
12
+ - keyword: "tenant"
13
+ - keyword: "IDOR"
14
+ - keyword: "session"
15
+ - keyword: "login"
16
+ - keyword: "controller"
17
+ - keyword: "findById"
18
+ routes_to:
19
+ - "skill:authz-review"
20
+ - "skill:threat-modeling"
21
+ - "skill:multi-tenancy"
22
+ workspaces: [engineering]
23
+ packs: [engineering-base]
24
+ ---
25
+
26
+ # Broken Access Control
27
+
28
+ The most common — and most damaging — failure in real systems and AI-written code: log in as one user, see another user's data. **Authenticated ≠ authorized.** The login check passes so the endpoint *feels* protected; the per-object ownership/tenant check is a separate line devs and AI omit — especially when the object id comes from the request. OWASP Web #1 (A01:2021) and API #1 (BOLA/IDOR), trivially scriptable, and a recurring real-world breach class — e.g. First American Financial (885M documents exposed via sequential ids with no ownership check).
29
+
30
+ ## The Iron Law
31
+
32
+ ```
33
+ AUTHENTICATED IS NOT AUTHORIZED. EVERY REQUEST-SUPPLIED ID/FILTER GETS A
34
+ SERVER-DERIVED OWNERSHIP/TENANT CHECK BEFORE ANY DATA IS RETURNED.
35
+ NO DATA-RETURNING ENDPOINT IS DONE WITHOUT THREE NEGATIVE TESTS:
36
+ UNAUTHENTICATED → 401 · NOT-OWNER → 403/404 · CROSS-TENANT → 403/404 (404 HIDES EXISTENCE).
37
+ ROLE DECIDES WHICH FIELDS — THE SERVER STRIPS THEM. THE FRONTEND HIDING A FIELD IS NOT PROTECTION.
38
+ A LIVE CROSS-USER EXPOSURE IS A NOTIFIABLE BREACH — NEVER SILENTLY PATCH IT.
39
+ ```
40
+
41
+ ## Non-optional controls (every data-returning surface)
42
+
43
+ - **Server-derived principal.** Ownership/tenant derived from the session/token, **never** from a request header/param/body the caller controls. A matching id in the request is not authorization.
44
+ - **Ownership/tenant check on every request id** before returning data. `findById(params.id)` with no `where owner/tenant = currentPrincipal` is the canonical bug.
45
+ - **Tenant-scoped by construction.** Every query on a tenant table carries the tenant predicate; prefer a base scope that injects it so a forgotten clause can't leak. (DSGVO: Mandantentrennung / Trennungskontrolle is a required TOM — a missing `tenant_id` filter is a compliance failure, not just a bug.)
46
+ - **Response minimization.** Return only entitled fields — no `SELECT *` / full-object serialization leaking PII "because the model has it" (Art. 25(2)).
47
+ - **Property-level authz (BOPLA / mass assignment).** Reject `role` / `user_id` / `tenant_id` / `is_admin` from the request body; explicit field allow-list, never whole-body binding.
48
+ - **Role/field-level output filtering (vertical BOPLA).** *Which* fields a principal receives depends on role + business rules — a driver role must not receive `price`; an office role must not receive the boss's `offer`. Serialize via a **role-scoped output DTO**, never the raw model / `to_json()`; sensitive fields (`price`/`cost`/`margin`/`salary`/`discount`/`offer`/internal notes) default-deny per role.
49
+ - **No guessable public ids** on sensitive resources — UUID/ULID, not sequential integers (turns one bug into full-DB enumeration).
50
+
51
+ ## The three negative tests — the completion gate
52
+
53
+ A protected endpoint is **not complete** until these exist and pass (happy-path 200 alone is the tautology this rule stops):
54
+
55
+ 1. **unauthenticated → 401** (no token).
56
+ 2. **authenticated but not the owner → 403/404** (user A requests user B's object with A's token).
57
+ 3. **cross-tenant → 403/404** (a principal in tenant A requests tenant B's resource; prefer 404 on sensitive resources — a hard 403 confirms the id exists in another tenant).
58
+
59
+ Small + task-aligned gap in code you touched → add the check **and** its negative test in the same commit (auto-fix boundary in `active-remediation`). Bigger → surface it.
60
+
61
+ ## Role-based field-level access — both directions (vertical BOPLA + BFLA)
62
+
63
+ Object-ownership (above) is *horizontal* (may this principal touch this record). This is *vertical*: which **fields** and which **functions** a role may reach — the maintainer's case (driver ≠ price, office worker ≠ boss's offer). Depends on role + business rules, and **the server is the only boundary** — the frontend hiding a field does not protect it: the raw JSON on the wire is readable via `curl`/DevTools/a proxy (3Fun leak: a privacy toggle filtered only in the app; the server returned every user's location to a direct query).
64
+
65
+ - **Read side (CWE-213 / Excessive Data Exposure).** Build the response from a **role-scoped output DTO** — never serialize the ORM model directly. Sensitive fields default-deny per role. A client `?fields=` selection is ergonomics, not authorization — intersect it server-side with the role's read-allowlist.
66
+ - **Write side (CWE-915).** Bind input through a **role-scoped write-allowlist** — never the raw body. Read-allowed ≠ write-allowed: a role may *see* `status` but not *set* `status:"approved"`; two independent sets.
67
+ - **Function level (BFLA, OWASP API #5).** A role must not reach a function/verb reserved for a higher role. Gate **every mutating verb** (POST/PUT/PATCH/DELETE), deny-by-default — not just the GET you were asked about. The admin button being hidden is not a control.
68
+ - **Nested / GraphQL.** Field-level checks propagate to nested selections; a low-role token selecting a restricted field gets null/error, not data.
69
+ - **Negative tests, per (role × sensitive field), both directions:** lower-role token → the field is **absent from the raw response body** (assert the body, not the UI); lower-role setting it on input → rejected/ignored, **verified by re-reading the persisted record**; every privileged verb → 403 for lower roles (403/404 on sensitive object reads).
70
+
71
+ ## Defense-in-depth (so one miss can't leak)
72
+
73
+ Stack ≥2 independent layers on sensitive data — query-level ownership scoping, a centralized default-deny policy layer (a route with no policy is denied, not silently open), and DB row-level security as the backstop for a forgotten `WHERE tenant_id`. The RLS / policy-layer configuration, the per-role authz matrix, and the audit greps live once in [`authz-review`](../skills/authz-review/SKILL.md).
74
+
75
+ ## GDPR / DSGVO — data protection by design
76
+
77
+ A cross-user leak violates **Art. 5(1)(f)** (integrity & confidentiality — a principle → higher fine tier), **Art. 25** (by design/default: default-deny + least privilege + response minimization), and **Art. 32** — which also **mandates a process for regularly testing effectiveness** of these controls (untested authz is a direct Art. 32 gap; hence the three negative tests are non-optional). A discovered live exposure is a **notifiable breach** (Art. 33, 72 h from discovery) — surface as "notify + remediate", never a silent patch. Data-protection context, not legal advice → `privacy-review`, `domain-safety-pii`.
78
+
79
+ ## Backstop greps (authoring-time)
80
+
81
+ ```bash
82
+ # Record fetched by request id with no owner/tenant predicate nearby (high-noise — a hit means read the line, not auto-fix)
83
+ rg -n '(findById|find|findOne|get)\(\s*(req\.|request\.|params\.|\$request|\$id)'
84
+ # Client-supplied tenant/user hint used as the scope (should come from the session)
85
+ rg -n '(tenant|tenantId|user_id|userId)\s*=\s*(req|request|params|headers|query)\.'
86
+ ```
87
+
88
+ The full authoring-time grep set (mass-assignment, raw-serialization, secrets) lives once in [`ai-code-blindspots`](../skills/ai-code-blindspots/SKILL.md).
89
+
90
+ A hit means read that line — is the ownership/tenant check present? Some are safe (already scoped); none should ship unchecked.
91
+
92
+ ## When it fires
93
+
94
+ Writing/modifying any endpoint, route, query, or serializer returning user- or tenant-owned data, or any auth/session/login/tenant path.
95
+
96
+ ## When NOT to fire
97
+
98
+ - Public, non-user-specific data with no principal concept (public docs page, health check with no data).
99
+ - Prose/docs/config-only edits.
100
+
101
+ ## See also
102
+
103
+ - [`authz-review`](../skills/authz-review/SKILL.md) — the 6-stage per-entrypoint chain, defense-in-depth, authz matrix, negative-test contract.
104
+ - [`security-sensitive-stop`](security-sensitive-stop.md) — threat-model BEFORE editing an auth/tenant path (fires first).
105
+ - [`active-remediation`](active-remediation.md) — the fix-now/ask/follow-up ladder + the live-exposure safety carve-out.
106
+ - [`threat-modeling`](../skills/threat-modeling/SKILL.md), [`multi-tenancy`](../skills/multi-tenancy/SKILL.md), [`ai-code-blindspots`](../skills/ai-code-blindspots/SKILL.md), [`privacy-review`](../skills/privacy-review/SKILL.md).
@@ -0,0 +1,42 @@
1
+ ---
2
+ type: "auto"
3
+ tier: "2b"
4
+ alwaysApply: false
5
+ description: "Multi-step or continuation replies carry a red thread — state the goal once, tie each turn back to it, name what changed since last turn, close the loop with one end-summary"
6
+ triggers:
7
+ - intent: "producing a multi-step or continuation reply"
8
+ - intent: "reporting progress on ongoing work"
9
+ workspaces: [agent-config-maintainer, construction, engineering, finance, founder, gtm, legal-review-prep, ops, product, small-business]
10
+ packs: [engineering-base]
11
+ ---
12
+
13
+ # Communication Through-Line
14
+
15
+ The user follows the work through your replies. A multi-step task whose replies jump between sub-topics with no visible thread forces the user to re-derive where things stand every turn. Give the work a **red thread**: a followable narrative from goal to result. Session-spanning coherence — distinct from single-reply brevity (`direct-answers`) and option formatting (`user-interaction`), which it never overrides.
16
+
17
+ ## The Iron Law
18
+
19
+ ```
20
+ STATE THE GOAL ONCE. TIE EACH TURN BACK TO IT. NAME WHAT CHANGED SINCE THE LAST TURN.
21
+ CLOSE THE LOOP WITH ONE END-SUMMARY. NEVER MAKE THE USER RE-DERIVE WHERE THINGS STAND.
22
+ ```
23
+
24
+ ## Checkable behaviors
25
+
26
+ - **Anchor once.** At the start of a multi-step task, state the goal/plan in one line (not re-stated every turn).
27
+ - **Each turn locates itself.** A mid-task reply says, in a few words, where it sits in the plan ("step 3/5", "now the downstream wiring") — so the user never scrolls up to orient.
28
+ - **Name the delta.** On a continuation, say what changed since the last turn (what landed, what's next) rather than restating everything.
29
+ - **Close the loop.** A work reply ends with ONE end-summary mapping back to the stated goal — reached or not, what remains. (Same single end-summary `direct-answers` requires; do not add a second.)
30
+ - **Signal pivots.** If the work changes direction (new sub-task, interrupt, blocker), say so explicitly — a silent topic switch breaks the thread.
31
+
32
+ ## When NOT to over-apply
33
+
34
+ - A one-shot answer or trivial reply needs no plan-anchor or end-summary (`direct-answers` brevity wins).
35
+ - Don't narrate for its own sake — the thread is orientation, not play-by-play (respect `personal.play_by_play`).
36
+ - Never let the through-line inflate a reply past what the task needs; brevity and the one-recommendation rule win on conflict.
37
+
38
+ ## See also
39
+
40
+ - [`direct-answers`](direct-answers.md) — brevity + the single end-summary this closes the loop with.
41
+ - [`user-interaction`](user-interaction.md) — the one-recommendation format; through-line never adds a competing summary.
42
+ - [`language-and-tone`](language-and-tone.md) — the thread is in the user's language.
@@ -45,6 +45,14 @@ ceiling on what the package may ever become.
45
45
  `telegraph-speak`, thin-projector trimming) that rejects a change on
46
46
  cost grounds alone.
47
47
  - An ADR whose decision is being cited as a blocker for the current change.
48
+ - A **hard structural cap** (kernel size, per-domain persona cap, rich-skill
49
+ ratio). A change that genuinely *qualifies* to cross the cap — e.g. a new
50
+ rule that truly meets the kernel-membership inclusion criteria — triggers a
51
+ **cap-raise proposal + maintainer/council approval**, never a silent
52
+ downgrade of the rule to fit the old number (e.g. demoting a must-always-fire
53
+ rule to `auto` just because the kernel says 9). Fitting the cap is not a
54
+ reason to weaken a qualifying rule; but a rule that does **not** meet the
55
+ criteria stays out — the cap is not the reason, the criteria are.
48
56
 
49
57
  ## What to do when it fires
50
58
 
@@ -98,4 +98,21 @@ After the incident, open a **follow-up PR** for any scope that was
98
98
  intentionally deferred and reference the break-glass commit in its
99
99
  description.
100
100
 
101
- See also: `scope-control` · `downstream-changes` · `think-before-action` · `preservation-guard` · `verify-before-complete`.
101
+ ## Bounded remediation carve-out
102
+
103
+ The default stands: no drive-by edits, no opportunistic refactors. The one
104
+ bounded exception — a **small, task-aligned security/correctness fix in code
105
+ the current task already touches** — is governed by
106
+ [`active-remediation`](active-remediation.md). Permitted inline **only** when
107
+ ALL hold: same request path / module, ≤ ~10 changed lines in one production file (plus its test file),
108
+ no public-API / response-shape change, no dependency bump or migration, and
109
+ its verification (e.g. the negative test) ships in the same commit. Anything
110
+ outside those five conditions is **not** this carve-out — it is note + ask per
111
+ `active-remediation`. This never licenses reformatting, renames, dependency
112
+ bumps, or version upgrades (a syntax-idiom modernization is allowed only under
113
+ `active-remediation`'s version-gated, behavior-preserving clause; a
114
+ dependency/version bump stays ask-only). The sole case that may interrupt is a
115
+ **live cross-user/tenant data exposure** ([`broken-access-control`](broken-access-control.md)) —
116
+ fix-now-if-small, else stop and surface; never defer it silently.
117
+
118
+ See also: `scope-control` · `downstream-changes` · `think-before-action` · `preservation-guard` · `verify-before-complete` · [`active-remediation`](active-remediation.md) · [`broken-access-control`](broken-access-control.md).
@@ -0,0 +1,65 @@
1
+ ---
2
+ type: "auto"
3
+ tier: "2b"
4
+ alwaysApply: false
5
+ description: "Modeling a field/property with multiple non-boolean states — prefer an enum over bare string/numeric literals; found old-style literals where an enum fits → note, finish the task, ask after"
6
+ triggers:
7
+ - intent: "modeling a field with multiple states"
8
+ - intent: "adding a status or type field"
9
+ - keyword: "enum"
10
+ - keyword: "status"
11
+ - keyword: "kind"
12
+ - keyword: "category"
13
+ - keyword: "migration"
14
+ workspaces: [engineering]
15
+ packs: [engineering-base]
16
+ ---
17
+
18
+ # Prefer Enums Over Literal Values
19
+
20
+ When a field/property holds **two or more non-boolean states** — strings (`'active'`, `'pending'`, `'archived'`) or meaningful numeric codes (priority `1`/`2`/`3`, tier levels) — model it as an **enum**, not a bare string/int. An enum names the value in code (readable), makes it greppable + safe to rename, gives exhaustiveness/typo safety, and makes filtering + editing relations far easier than raw literals scattered across the codebase.
21
+
22
+ ## The Iron Law
23
+
24
+ ```
25
+ A FIELD WITH MULTIPLE NON-BOOLEAN STATES IS AN ENUM, NOT A BARE STRING OR MAGIC NUMBER.
26
+ NAME THE VALUE IN CODE — NEVER SCATTER RAW LITERALS ('active', 2, 'PENDING') ACROSS THE CODEBASE.
27
+ FOUND OLD-STYLE LITERALS WHERE AN ENUM FITS? NOTE IT, FINISH THE TASK, THEN ASK — NEVER REFACTOR MID-FLOW.
28
+ ```
29
+
30
+ ## Prefer an enum when
31
+
32
+ - A multi-state **string** field — `status`, `type`, `role`, `kind`, `category`, `state` — with 2+ distinct values.
33
+ - A **numeric code that is a label, not a quantity** — priority `1/2/3`, severity levels, tiers.
34
+ - Any **closed set of values the code branches on** (`match` / `switch` / `if ===`).
35
+
36
+ ## Do NOT force an enum when
37
+
38
+ - A boolean (`true`/`false`) — a flag, not an enum.
39
+ - Open/unbounded free text — name, description, URL, comment.
40
+ - A genuine numeric **quantity** used in arithmetic — price, count, age, bytes.
41
+ - A set owned entirely by a volatile external system — unless you map it to your own enum at the boundary.
42
+
43
+ ## How (framework-neutral)
44
+
45
+ - Use the language's native construct: backed enum (PHP `enum Status: string`), string-literal union or `enum` (TypeScript), `enum.Enum` (Python), an enum/`CHECK`-constrained column or lookup table + FK (database).
46
+ - Persist the enum's backing value; reference the **named case** in code, never the raw literal.
47
+
48
+ ## Found old-style literals where an enum fits — defer, do not disrupt
49
+
50
+ Apply exactly this order so the current task's flow is never interrupted:
51
+
52
+ 1. **While doing the actual task**, if you pass code using raw string/numeric literals for a multi-state field where an enum fits, do **NOT** refactor inline (a drive-by edit — see `minimal-safe-diff`) and do **NOT** stop to ask mid-flow.
53
+ 2. **Note the site** — file:line + the literal set — in your working notes.
54
+ 3. **After the actual task is delivered**, surface the noted candidates as **one** numbered-options prompt (per `user-interaction`): list each enum-candidate site, ask whether to replace — all / selected / none.
55
+ 4. **Only on an explicit yes** do you refactor, as a **separate change** with its own scope (`downstream-changes` applies — update every caller, migration, test, serializer).
56
+
57
+ If the task itself is *adding* the field, apply the enum from the start — no deferral; the defer-and-ask flow is only for pre-existing literals you encounter.
58
+
59
+ ## See also
60
+
61
+ - [`improve-before-implement`](improve-before-implement.md) — the Strategy sniff test when a second branch on the same enum/string discriminator signals the enum should become a Strategy (`docs/guidelines/php/patterns/strategy.md`).
62
+ - [`minimal-safe-diff`](minimal-safe-diff.md) — why you don't drive-by-refactor the literals you find.
63
+ - [`scope-control`](scope-control.md) — replacing an existing pattern needs the user's yes first.
64
+ - [`active-remediation`](active-remediation.md) — the general fix-now / note-and-ask / follow-up-PR ladder; the enum defer-and-ask above is one instance of it.
65
+ - [`senior-engineering-discipline`](senior-engineering-discipline.md) — the "generalize, don't overfit" anchor this extends.