@event4u/agent-config 2.13.0 → 2.15.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 (74) hide show
  1. package/.agent-src/commands/agents/user/accept.md +117 -0
  2. package/.agent-src/commands/agents/user/init.md +163 -0
  3. package/.agent-src/commands/agents/user/review.md +107 -0
  4. package/.agent-src/commands/agents/user/show.md +109 -0
  5. package/.agent-src/commands/agents/user/update.md +98 -0
  6. package/.agent-src/commands/agents/user.md +66 -0
  7. package/.agent-src/commands/agents.md +2 -0
  8. package/.agent-src/commands/memory/learn-low-impact.md +143 -0
  9. package/.agent-src/rules/ask-when-uncertain.md +10 -6
  10. package/.agent-src/rules/copilot-routing.md +1 -1
  11. package/.agent-src/rules/devcontainer-routing.md +1 -1
  12. package/.agent-src/rules/external-reference-deep-dive.md +1 -1
  13. package/.agent-src/rules/fast-path-marker-visibility.md +38 -0
  14. package/.agent-src/rules/low-impact-corpus-privacy-floor.md +74 -0
  15. package/.agent-src/rules/symfony-routing.md +1 -1
  16. package/.agent-src/skills/ai-council/SKILL.md +208 -8
  17. package/.agent-src/templates/agents/agent-project-settings.example.yml +1 -1
  18. package/.claude-plugin/marketplace.json +8 -1
  19. package/CHANGELOG.md +328 -124
  20. package/README.md +21 -6
  21. package/config/agent-settings.template.yml +4 -0
  22. package/config/gitignore-block.txt +17 -0
  23. package/docs/architecture.md +12 -12
  24. package/docs/archive/CHANGELOG-pre-2.11.0.md +141 -0
  25. package/docs/catalog.md +16 -7
  26. package/docs/contracts/adr-architectural-consensus-mechanism.md +4 -3
  27. package/docs/contracts/adr-level-6-productization.md +7 -9
  28. package/docs/contracts/agent-user-schema.md +165 -0
  29. package/docs/contracts/ai-council-config.md +492 -20
  30. package/docs/contracts/command-clusters.md +2 -2
  31. package/docs/contracts/command-surface-tiers.md +3 -2
  32. package/docs/contracts/cost-profile-defaults.md +5 -0
  33. package/docs/contracts/decision-engine-gates.md +5 -0
  34. package/docs/contracts/decision-trace-v1.md +2 -2
  35. package/docs/contracts/file-ownership-matrix.json +1961 -108
  36. package/docs/contracts/installed-tools-lockfile.md +2 -1
  37. package/docs/contracts/low-impact-corpus-format.md +95 -0
  38. package/docs/contracts/mcp-beta-criteria.md +6 -5
  39. package/docs/contracts/mcp-cloud-scope.md +5 -4
  40. package/docs/contracts/multi-tool-projection-fidelity.md +8 -2
  41. package/docs/contracts/release-trunk-sync.md +4 -3
  42. package/docs/contracts/tier-3-contrib-plugin.md +5 -6
  43. package/docs/examples/agent-user.example.md +21 -0
  44. package/docs/getting-started.md +2 -2
  45. package/docs/guidelines/agent-infra/installed-tools-manifest.md +2 -1
  46. package/docs/installation.md +32 -0
  47. package/package.json +1 -1
  48. package/scripts/_cli/cmd_doctor.py +134 -0
  49. package/scripts/ai_council/airgap.py +165 -0
  50. package/scripts/ai_council/cli_hints.py +123 -0
  51. package/scripts/ai_council/clients.py +787 -5
  52. package/scripts/ai_council/compile_corpus.py +178 -0
  53. package/scripts/ai_council/confidence_gate.py +156 -0
  54. package/scripts/ai_council/config.py +1007 -11
  55. package/scripts/ai_council/consensus.py +41 -2
  56. package/scripts/ai_council/events_log.py +137 -0
  57. package/scripts/ai_council/learn_low_impact_preview.py +252 -0
  58. package/scripts/ai_council/low_impact.py +714 -0
  59. package/scripts/ai_council/low_impact_corpus.py +466 -0
  60. package/scripts/ai_council/low_impact_intake.py +163 -0
  61. package/scripts/ai_council/modes.py +6 -1
  62. package/scripts/ai_council/necessity.py +782 -0
  63. package/scripts/ai_council/orchestrator.py +252 -14
  64. package/scripts/ai_council/probation_gate.py +152 -0
  65. package/scripts/ai_council/redact_low_impact_entry.py +155 -0
  66. package/scripts/ai_council/replay.py +155 -0
  67. package/scripts/ai_council/session.py +19 -1
  68. package/scripts/ai_council/shadow_dispatch.py +235 -0
  69. package/scripts/ai_council/solo_dispatch.py +226 -0
  70. package/scripts/audit_cloud_compatibility.py +74 -0
  71. package/scripts/audit_command_surface.py +363 -0
  72. package/scripts/check_council_layout.py +11 -0
  73. package/scripts/council_cli.py +1046 -15
  74. package/scripts/install.sh +12 -0
@@ -6,11 +6,10 @@ keep-beta-until: 2026-08-12
6
6
  # AI-Council Config (`agents/.ai-council.yml`)
7
7
 
8
8
  **Purpose.** Lock the schema, validation, and precedence rules for the
9
- centralized council config file. Every later phase of
10
- [`step-2-ai-council-consolidation.md`](../../agents/roadmaps/step-2-ai-council-consolidation.md)
11
- reads from this file; the contract here is the boundary that prevents
12
- drift across the loader, the CLI, the orchestrator, and the
13
- `agents/.ai-council.yml` file itself.
9
+ centralized council config file. Every phase of the AI-Council
10
+ consolidation work reads from this file; the contract here is the
11
+ boundary that prevents drift across the loader, the CLI, the
12
+ orchestrator, and the `agents/.ai-council.yml` file itself.
14
13
 
15
14
  **Audience.** Authors of `scripts/ai_council/config.py`, `council_cli.py`,
16
15
  `scripts/ai_council/orchestrator.py`, and the `agents/.ai-council.yml`
@@ -32,7 +31,7 @@ pointing at this contract.
32
31
  ```yaml
33
32
  enabled: <bool> # master switch, required
34
33
  defaults: # per-invocation defaults, required
35
- mode: <"api" | "manual">
34
+ mode: <"api" | "manual" | "cli">
36
35
  min_rounds: <int >= 1>
37
36
  deep_min_rounds: <int >= min_rounds>
38
37
  max_output_tokens: <int >= 0> # 0 widens to provider ceiling
@@ -42,24 +41,128 @@ cost_budget: # hard caps per /council invocation, required
42
41
  max_input_tokens: <int >= 0> # 0 disables this cap
43
42
  max_output_tokens: <int >= 0> # 0 disables this cap
44
43
  max_calls: <int >= 0> # 0 disables this cap
45
- max_total_usd: <number >= 0> # 0 disables the USD ceiling
44
+ max_total_usd: <number >= 0> # 0 disables the USD ceiling — applies to billable transports: all `mode: api`, plus `mode: cli` for xai/perplexity (community CLIs that still consume the API key). Does NOT apply to vendor-official `mode: cli` (anthropic/openai/gemini) or to `mode: manual`
45
+ cli_call_budget: # optional; per-day call-count guard for mode: cli members
46
+ max_calls_per_day:
47
+ <provider>: <int >= 0> # opt-in per provider; default unset = unlimited
48
+ warn_at: <float in [0.0, 1.0]> # default 0.8 — pre-run summary line prefixes "⚠️" once used/limit >= warn_at (step-8 D4)
46
49
  members: # per-provider blocks, at least one enabled
47
50
  <provider>:
48
51
  enabled: <bool>
49
52
  model: <string>
50
- api_key_ref: <string> # see `api_key_ref` forms below
51
- mode: <"api" | "manual"> # optional override of defaults.mode
53
+ api_key_ref: <string> # required for mode: api; optional for cli/manual
54
+ mode: <"api" | "manual" | "cli"> # optional override of defaults.mode
55
+ binary: <string> # optional; only valid when effective mode == "cli"
52
56
  advisors: # Thinking-style replace-mode advisors
53
57
  <advisor-key>:
54
58
  enabled: <bool>
55
59
  member: <provider> # required; references members.<provider>
56
60
  model: <string> # optional; overrides member.model
57
61
  persona: <path> # optional; defaults to personas/advisors/<advisor-key>.md
62
+ necessity_classifier: # Phase 6 — optional, default enabled+educate
63
+ enabled: <bool> # master switch (default true)
64
+ mode: <"off" | "educate" | "block" | "warn-only"> # default "educate" (agent invocation)
65
+ user_explicit_mode: <"off" | "educate" | "block" | "warn-only"> # default "warn-only" — step-8 D2; applies when invocation=user_explicit
66
+ decision_replay: # Phase 9 — optional, default enabled+full
67
+ enabled: <bool> # master switch (default true)
68
+ include_member_arguments: <bool> # default true; false = redacted view
69
+ lens_overrides: # Phase 6 — optional, per-lens nudges
70
+ necessity_classifier_mode:
71
+ <lens>: <"off" | "educate" | "block">
72
+ lenses: # Phase 9 — optional, per-lens overrides
73
+ <lens>:
74
+ decision_replay:
75
+ enabled: <bool>
76
+ include_member_arguments: <bool>
58
77
  ```
59
78
 
60
79
  Supported `<provider>` keys: `anthropic`, `openai`, `gemini`, `xai`,
61
80
  `perplexity`. Unknown providers fail validation closed.
62
81
 
82
+ ### Transport modes
83
+
84
+ Three first-class transports on the `mode:` axis. Resolution per member:
85
+ `per-member mode > defaults.mode > "api"`.
86
+
87
+ | Mode | Semantics | Billable | Auth | Cost gate |
88
+ |---|---|---|---|---|
89
+ | `manual` | Copy & paste — the human transports prompt + reply between the agent and an external chat surface. | No | None — human-in-the-loop | n/a |
90
+ | `api` | SDK call against a stored key, per-token billing on the provider's API. | Yes | `api_key_ref` (env or 0600 file) | `cost_budget` (full) |
91
+ | `cli` | Shell out to a locally-installed provider CLI. For `anthropic` / `openai` / `gemini` this runs under the user's subscription auth and is `billable=False`. For `xai` / `perplexity` (community wrappers) the CLI consumes the same API key as `mode: api` and remains `billable=True`. | Mixed — see below | CLI-managed OAuth (vendor) or API key in CLI env (community) | Vendor: `cli_call_budget.max_calls_per_day` only · Community: full `cost_budget` |
92
+
93
+ Implications:
94
+
95
+ - **`cost_budget.max_total_usd` applies to `mode: cli` for `xai` and
96
+ `perplexity`.** Their CLIs (`grok`, `perplexity`) are community-built
97
+ wrappers around the same paid API — `mode: cli` is an ergonomic
98
+ shortcut, NOT a billing change. The orchestrator still runs the
99
+ pre-call USD estimate and the budget gate for these two providers.
100
+ - **`cost_budget.max_total_usd` does NOT apply to `mode: cli` for the
101
+ vendor-official CLIs (`anthropic`, `openai`, `gemini`) or to
102
+ `mode: manual`.** All four are `billable=False`; the USD ceiling is
103
+ a token-billing concept they don't participate in.
104
+ - **`api_key_ref` is required for enabled members whose effective mode
105
+ is `api`, AND for `xai` / `perplexity` even in `mode: cli`** — the
106
+ community CLI reads the key from its own env, but the agent still
107
+ surfaces missing-key as a validation error before the call. The
108
+ vendor-official CLIs (`anthropic`, `openai`, `gemini`) authenticate
109
+ via their own login flow and need no `api_key_ref` in `mode: cli`;
110
+ manual members have no key at all.
111
+ - **`binary:` is only valid when the effective mode is `cli`.** Setting it
112
+ on an `api` or `manual` member is a hard validation error — no silent
113
+ ignore, no clutter.
114
+ - **Subscription quotas:** Claude Pro 5h usage windows, ChatGPT Plus
115
+ message caps, Gemini free-tier per-day limits all live outside this
116
+ loader's view. `cli_call_budget.max_calls_per_day.<provider>` lets the
117
+ user opt into a per-day cap; counter state persists at
118
+ `~/.event4u/agent-config/cli-calls.json` with daily UTC reset (wired in
119
+ Phase 1 of the CLI-transport roadmap).
120
+ - **Quota observability (step-8 D1, D4):** every `council run` /
121
+ `council debate` prints a one-line `council:quota · <provider>
122
+ used/limit · …` summary before the first member fires. Only
123
+ providers with a configured `max_calls_per_day` cap appear; uncapped
124
+ providers are omitted (no false sense of metering). When
125
+ `used / max_calls_per_day >= cli_call_budget.warn_at` (default
126
+ `0.8`) the line is prefixed `⚠️` and lists the providers near the
127
+ limit on the next line. The standalone `agent-config council
128
+ quota` subcommand dumps the same state plus the configured caps,
129
+ and `--reset <provider> --confirm` clears today's counter for that
130
+ provider.
131
+
132
+ ### Persistent events log (step-8 D3)
133
+
134
+ The orchestrator appends one JSON line to `agents/council-events.log`
135
+ on every necessity-gate decision (`proceed` / `skip_necessity`) and on
136
+ every `cli_call_budget` block (`block_quota`). The log is gitignored
137
+ by default — it is a local-only audit trail, never part of the
138
+ repository contract.
139
+
140
+ Schema v1:
141
+
142
+ ```json
143
+ {
144
+ "schema_version": 1,
145
+ "ts_utc": "2026-05-15T03:38:45Z",
146
+ "lens": "analysis",
147
+ "invocation": "user_explicit",
148
+ "action": "proceed",
149
+ "verdict": "necessary",
150
+ "provider_caps": {"anthropic": {"mode": "cli", "model": "sonnet-4"}},
151
+ "original_ask_hash": "abc123def456"
152
+ }
153
+ ```
154
+
155
+ - `action` ∈ `proceed | skip_necessity | block_quota`.
156
+ - `original_ask_hash` is `sha256(original_ask)[:12]`. The raw prompt
157
+ is **never** written — privacy floor per
158
+ [`agents/low-impact-decisions.md`](../../agents/low-impact-decisions.md).
159
+ - Diagnostic fields outside the reserved set (`category`, `mode`,
160
+ `cli_calls_used`, …) pass through verbatim. Consumers MUST treat
161
+ unknown keys as forward-compatible.
162
+ - `AGENT_CONFIG_NO_EVENTS_LOG=1` (step-8 D5) disables every write
163
+ in-process — useful for ephemeral worktrees, CI runners that
164
+ shouldn't accumulate state, or sandbox testing.
165
+
63
166
  ### Advisor block (Phase 6, replace-mode)
64
167
 
65
168
  Five built-in advisor keys ship under
@@ -88,6 +191,355 @@ output renders as `Response A (Contrarian)`, never
88
191
  `Response A (Anthropic Opus)`. Plain runs strip identity entirely.
89
192
  Hard-coded behaviour — no flag, no opt-out.
90
193
 
194
+ ### Necessity classifier (Phase 6, pre-flight gate)
195
+
196
+ A heuristic pre-flight that decides whether the request actually
197
+ warrants a full council deliberation. Three verdicts (`necessary`,
198
+ `borderline`, `unnecessary`) drive three exit paths in the dispatcher
199
+ (skip silently, educate + block, or proceed). Implemented in
200
+ [`scripts/ai_council/necessity.py`](../../scripts/ai_council/necessity.py);
201
+ wired into `council_cli.cmd_run` and `cmd_debate` before any member
202
+ is invoked.
203
+
204
+ **Configuration.**
205
+
206
+ - `necessity_classifier.enabled` (bool, default `true`) — master switch.
207
+ `false` short-circuits the gate entirely; legacy "always run" behaviour
208
+ is restored.
209
+ - `necessity_classifier.mode` (`"off" | "educate" | "block" | "warn-only"`,
210
+ default `"educate"`). Governs the **agent** invocation tier.
211
+ - `off` — gate disabled while keeping the classifier module loaded
212
+ (cheap toggle for experiments).
213
+ - `educate` — agent-initiated invocation + `unnecessary` verdict skips
214
+ silently with `skipped_reason: necessity_unnecessary` in
215
+ `session.md`. User-explicit invocation + `unnecessary` prints a
216
+ one-paragraph rationale and exits non-zero; `--proceed-anyway`
217
+ overrides on this single call.
218
+ - `block` — same as `educate` for `necessary` / `borderline`, but
219
+ `unnecessary` is rejected regardless of invocation source. Even
220
+ `--proceed-anyway` is ignored (power-user opt-in for cost-strict
221
+ environments).
222
+ - `warn-only` (step-8 D2) — annotate the verdict on stdout but
223
+ **never** skip. Useful when the user wants observability without
224
+ losing council coverage.
225
+ - `necessity_classifier.user_explicit_mode` (same enum, default
226
+ `"warn-only"`, step-8 D2) — governs the **user_explicit** invocation
227
+ tier. The two-tier split reconciles "Council always active when
228
+ enabled" with "skip trivial agent-side requests": user-typed
229
+ `/council` calls proceed by default, agent-initiated dispatches keep
230
+ `educate` behaviour. Override via `.agent-settings.yml`.
231
+ - `lens_overrides.necessity_classifier_mode.<lens>` — per-lens override.
232
+ Wins over the global `mode` for the matching invocation tier (agent
233
+ vs user_explicit follow the same lens map). Typical use: leave the
234
+ global at `educate` and force `debate` lens to `block` because
235
+ debate is the most expensive transport.
236
+
237
+ **Invocation context (CLI flags).**
238
+
239
+ - `--invocation {agent,user_explicit}` — defaults to `user_explicit`.
240
+ Agent orchestration MUST set `--invocation agent` so silent skips are
241
+ available; user-typed `/council` keeps the default.
242
+ - `--proceed-anyway` — one-shot override of the `educate` block. Does
243
+ NOT lift `block` mode.
244
+
245
+ **Classifier shape.**
246
+
247
+ Word-boundary regex matches against four `necessary` buckets
248
+ (architecture / tradeoff / ambiguity / strategic) and four
249
+ `unnecessary` buckets (bugfix / syntax / single_file / lookup).
250
+ Decision table:
251
+
252
+ | Necessary hits | Unnecessary hits | Lens strict? | Verdict |
253
+ |---|---|---|---|
254
+ | `> unnecessary` | any | n/a | `necessary` |
255
+ | `>= 1` | `== 0` | n/a | `necessary` |
256
+ | `== 0` | `>= 1` | n/a | `unnecessary` |
257
+ | equal `>= 1` each | both `>= 1` | n/a | `borderline` |
258
+ | `== 0` | `== 0` | yes (`debate`) | `unnecessary` |
259
+ | `== 0` | `== 0` | no | `borderline` |
260
+
261
+ Trigger word lists live in
262
+ [`scripts/ai_council/necessity.py`](../../scripts/ai_council/necessity.py)
263
+ as `NECESSARY_TRIGGERS` and `UNNECESSARY_TRIGGERS` — extend there with
264
+ a unit test; never edit downstream copies.
265
+
266
+ ### Decision-replay artefact (Phase 9, audit trail)
267
+
268
+ Per-session `decision-replay.md` written next to `responses.json` whenever
269
+ the consensus round runs. Pure projection of consensus data plus the
270
+ final-round per-member texts — no extra model calls. The artefact
271
+ surfaces, per top finding, the verdict band (Strong/Moderate/Weak), the
272
+ evidence-quality bucket (H/M/L), the agree/dissent member split, and one
273
+ key argument per member. Implementation:
274
+ [`scripts/ai_council/replay.py`](../../scripts/ai_council/replay.py).
275
+
276
+ **Configuration.**
277
+
278
+ - `decision_replay.enabled` (bool, default `true`) — master switch.
279
+ `false` skips the artefact for every lens.
280
+ - `decision_replay.include_member_arguments` (bool, default `true`) —
281
+ when `false`, the artefact emits the redacted view: verdict +
282
+ evidence-quality + counts only, no per-member arguments. Use for
283
+ surfaces where attributing reasoning to a specific model would leak
284
+ vendor preference signal.
285
+ - `lenses.<lens>.decision_replay.enabled` / `include_member_arguments`
286
+ — per-lens overrides that beat the global block. Typical use: keep
287
+ the audit trail on for `analysis` and turn it off for `default` /
288
+ `prompt` where consensus rarely runs.
289
+
290
+ **CLI.**
291
+
292
+ - Written automatically by `council run` when consensus scoring fires.
293
+ - `council replay <responses.json>` re-renders the artefact from a saved
294
+ session. `--output <path>` writes to a file (otherwise stdout);
295
+ `--redact-member-arguments` / `--include-member-arguments` toggle the
296
+ redacted view independent of config.
297
+
298
+ **Decision-replay schema.** The markdown body follows a fixed shape so
299
+ downstream tooling can scrape it:
300
+
301
+ ```
302
+ # Decision Replay
303
+
304
+ > <original-ask truncated to 400 chars>
305
+
306
+ ## <finding-id> — <finding-text truncated to 120 chars>
307
+
308
+ - **Consensus**: Strong|Moderate|Weak (<strength 0.00–1.00>)
309
+ - **Evidence quality**: H|M|L (mean <X>/10)
310
+ - **Agreement**: <concur>/<total> members concur, <dissent> dissent
311
+
312
+ **Agreeing members**: # full mode only
313
+ - _<provider:model>_ — <argument truncated to 200 chars>
314
+
315
+ **Dissenting members**: # full mode only
316
+ - _<provider:model>_ — <argument truncated to 200 chars>
317
+
318
+ **Synthesis verdict**: <band> consensus — <source> sourced.
319
+
320
+ ---
321
+
322
+ _artefact mode: full|redacted (counts only)_
323
+ ```
324
+
325
+ Findings are ranked by `consensus_strength` descending. Empty sessions
326
+ emit the heading plus `*No findings were extracted for this session.*`.
327
+
328
+ ### Decision resolution by impact (Phase 10, ask-user routing)
329
+
330
+ Five-class impact classifier triages every pending agent question
331
+ before it surfaces. Heuristic, shape-based, keyword-driven — no LLM
332
+ call, fully explainable. Lives in `scripts/ai_council/necessity.py`
333
+ (`classify_impact`, `route_decision`).
334
+
335
+ | Class | Trigger shape | Default mode |
336
+ |---|---|---|
337
+ | `trivial` | naming, whitespace, comments, typo, indent | `agent` |
338
+ | `low_impact` | local idioms, DTO vs value object, test extensions | `agent` |
339
+ | `medium_impact` | API shape, contract change, breaking change, module boundary | `council` |
340
+ | `high_impact` | security, auth, tenant boundary, migration, billing, secrets, PII | `user` (**LOCKED**) |
341
+ | `user_required` | user-fence markers — "ask me", "review first", "plan only" | `user` (**LOCKED**) |
342
+
343
+ **Iron Law** — `high_impact` and `user_required` ALWAYS route to the
344
+ user. The schema loader rejects any `decision_resolution.classes.<cls>.mode`
345
+ that maps either class to `agent` or `council`. No override path, no
346
+ config flag, no autonomy setting can lift this lock.
347
+
348
+ **Confidence gate** — each entry carries a `confidence_threshold`
349
+ (default `0.6`). When the classifier's confidence is below the
350
+ threshold, the configured mode is upgraded one rung
351
+ (`agent` → `council` → `user`) so low-certainty calls escalate rather
352
+ than silently auto-resolve.
353
+
354
+ ```yaml
355
+ decision_resolution:
356
+ enabled: true
357
+ classes:
358
+ trivial:
359
+ mode: agent
360
+ confidence_threshold: 0.6
361
+ low_impact:
362
+ mode: agent
363
+ confidence_threshold: 0.6
364
+ medium_impact:
365
+ mode: council
366
+ confidence_threshold: 0.6
367
+ high_impact:
368
+ mode: user # LOCKED — Iron Law
369
+ confidence_threshold: 0.6
370
+ user_required:
371
+ mode: user # LOCKED — Iron Law
372
+ confidence_threshold: 0.6
373
+ ```
374
+
375
+ ### Low-impact council opt-in
376
+
377
+ The default route for `low_impact` is `agent` — the host resolves locally
378
+ and the council is not invoked. Sending `low_impact` to the council is
379
+ an **explicit, two-knob opt-in**; missing either knob keeps the question
380
+ on the host:
381
+
382
+ 1. `decision_resolution.classes.low_impact.mode: council` (default
383
+ `agent`) — flips the class route.
384
+ 2. **At least one** enabled member sets `participate_low_impact: true`
385
+ (default `false`) — names which members run on the fast-path.
386
+
387
+ When both knobs are set, the lightweight-QA fast-path
388
+ (`ai_council.fast_path`) handles the resolution under hard caps:
389
+ `max_members ∈ {1, 2}`, `max_rounds: 1` (LOCKED — Iron Law of the
390
+ fast-path; the loader rejects any other value), `max_tokens: 2500`
391
+ combined input + output budget, `max_cost_usd: 0.05` per resolution.
392
+ No advisors, no peer-review, no consensus scoring run on this path.
393
+
394
+ `high_impact` and `user_required` cannot reach the fast-path at all —
395
+ the impact-class Iron Law above takes precedence.
396
+
397
+ Worked example — opt the Anthropic and OpenAI members in, leave the
398
+ others off, and switch the `low_impact` class to `council`:
399
+
400
+ ```yaml
401
+ ai_council:
402
+ members:
403
+ anthropic:
404
+ enabled: true
405
+ model: claude-sonnet-4-5
406
+ api_key_ref: file:anthropic.key
407
+ participate_low_impact: true # eligible for fast-path
408
+ openai:
409
+ enabled: true
410
+ model: gpt-4o
411
+ api_key_ref: file:openai.key
412
+ participate_low_impact: true # eligible for fast-path
413
+ gemini:
414
+ enabled: false # disabled — opt-in ignored even if set
415
+
416
+ fast_path:
417
+ max_members: 2 # 1 or 2 only
418
+ max_rounds: 1 # LOCKED
419
+ max_tokens: 2500
420
+ max_cost_usd: 0.05
421
+
422
+ decision_resolution:
423
+ classes:
424
+ low_impact:
425
+ mode: council # ← flip from default `agent`
426
+ confidence_threshold: 0.6
427
+ ```
428
+
429
+ Validation behaviour:
430
+
431
+ - `participate_low_impact: true` on a member with `enabled: false`,
432
+ or with the global council disabled, parses but is treated as
433
+ `false` with a one-line loader warning — never silently runs.
434
+ - `fast_path.max_rounds != 1` → `CouncilConfigError` at load time.
435
+ - `fast_path.max_members ∉ {1, 2}` → `CouncilConfigError`.
436
+ - `low_impact.mode: council` with zero opted-in enabled members →
437
+ the resolver falls back to `agent` (or escalates to user when the
438
+ confidence gate trips), with the marker
439
+ `> Low-impact council unavailable (no opted-in members) — escalating to user`.
440
+
441
+ Transparency markers on every fast-path attempt:
442
+
443
+ - **Resolved** — `> Resolved via low-impact council (<member>): <answer>`
444
+ - **Split** — `> Low-impact council split — escalating to user (<m1>: X / <m2>: Y):`
445
+ - **Aborted** — `> Low-impact council aborted (token cap) — escalating to user:`
446
+
447
+ The marker is mandatory; the agent never silently substitutes a
448
+ fast-path verdict for its own answer. See
449
+ [`ai-council § Lightweight-QA fast-path`](../../.agent-src.uncompressed/skills/ai-council/SKILL.md#lightweight-qa-fast-path-phase-11)
450
+ for the orchestration contract and session-artefact format.
451
+
452
+ ### Solo-member dispatch (step-9 P8/P9 · U1 · U2 · U3)
453
+
454
+ Three new top-level settings cooperate to make low-impact decisions
455
+ optionally cheap by routing them to a single member instead of the
456
+ full council. Defaults preserve the prior shape — no behaviour
457
+ changes unless every knob is set explicitly.
458
+
459
+ | Key | Type | Default | Constraint |
460
+ |---|---|---|---|
461
+ | `defaults.member_mode` | `cli` \| `api` | `cli` | Per-member fallback when a member doesn't set `mode`. `manual` is rejected here (manual transport is full-council only). |
462
+ | `routing.solo_member_fallback_chain` | `list[str]` | `[]` | Ordered, unique provider names. Each entry must exist in the `members` block. Empty disables solo dispatch. |
463
+ | `routing.auth_check_timeout_seconds` | `int` | `3` | Range `[1, 30]`. Bounds the lazy auth probe per chain member. |
464
+ | `low_impact.dispatch` | `full` \| `single` | `full` | `single` requires at least one enabled member in the fallback chain — otherwise rejected at load. |
465
+ | `low_impact.shadow_sample_rate` | `float` | `0.1` | Range `[0.0, 1.0]`. Phase 10 shadow-mode sampling probability. |
466
+ | `low_impact.solo_confidence_floor` | `float` | `0.7` | Range `[0.0, 1.0]`. Phase 13 confidence-gate threshold. Solo responses scoring below this floor — or matching split / refusal patterns — auto-escalate to the full council. |
467
+
468
+ **Iron Law (LOCKED).** `decision_resolution.classes.high_impact.dispatch`
469
+ and `decision_resolution.classes.user_required.dispatch` are rejected
470
+ at load time — high-impact and user-required decisions always run the
471
+ full council, with no opt-out. Top-level `high_impact: { dispatch: … }`,
472
+ `high_impact: { solo_confidence_floor: … }`, `user_required: { dispatch: … }`,
473
+ and `user_required: { solo_confidence_floor: … }` are all rejected the
474
+ same way so a mistaken surface choice cannot bypass the lock — these
475
+ classes never run solo, so a solo-specific knob there is incoherent.
476
+
477
+ ```yaml
478
+ defaults:
479
+ mode: api
480
+ member_mode: cli # use CLI binaries by default for per-member calls
481
+
482
+ routing:
483
+ solo_member_fallback_chain: [anthropic, openai] # try anthropic first
484
+ auth_check_timeout_seconds: 3
485
+
486
+ low_impact:
487
+ dispatch: single # route low_impact via solo dispatch
488
+ shadow_sample_rate: 0.1 # 10% shadow to full council for SLO tracking
489
+ solo_confidence_floor: 0.7 # auto-escalate if confidence < 0.7
490
+ ```
491
+
492
+ ### Confidence gate — auto-escalation on uncertain solo runs
493
+
494
+ When `low_impact.dispatch: single` is enabled, every solo response is
495
+ scored before the verdict is returned. Four escalation reasons can
496
+ trigger an automatic fallback to the full council, in priority order:
497
+
498
+ | Reason | Trigger | Source |
499
+ |---|---|---|
500
+ | `refusal` | Empty response or refusal markers (`I cannot decide`, `weiß ich nicht`, …). | `confidence_gate.is_refusal` |
501
+ | `split` | Two competing verdicts without a pick (`Option A … Option B`, `Verdict: ship / Verdict: hold`, `Variante 1 / Variante 2`). | `confidence_gate.is_split_response` |
502
+ | `short_response` | Response shorter than ~20 chars — treated as low-signal. | `confidence_gate.extract_confidence` length floor |
503
+ | `low_confidence` | Explicit `Confidence: 0.X` marker below the floor, or hedge-word density (`maybe`, `perhaps`, `not sure`, `vielleicht`) pulling the implicit score below the floor. | `confidence_gate.extract_confidence` |
504
+
505
+ Escalations are recorded in the shadow-mode log alongside the
506
+ disagreement signal — see the `escalated` and `escalation_reason`
507
+ fields in the `shadow.jsonl` row contract. The shadow SLO banner
508
+ appends a separate auto-escalation rate so a quiet uptick in solo
509
+ uncertainty cannot hide behind a flat disagreement rate.
510
+
511
+ The escalation path uses zero extra LLM calls — heuristics run on the
512
+ solo response text in process. The fallback `run_full()` only fires
513
+ when the gate flags the response.
514
+
515
+ ### Low-impact corpus build pipeline (step-10)
516
+
517
+ [`agents/low-impact-decisions.md`](../../agents/low-impact-decisions.md)
518
+ remains the **human-editable source of truth** — Validated, Probation,
519
+ and Anti-Example phrases stay in Markdown so PR-reviewers diff prose,
520
+ not YAML. A build step compiles it into a generated YAML lockfile
521
+ [`agents/low-impact-decisions.lock.yaml`](../../agents/low-impact-decisions.lock.yaml)
522
+ that is the **runtime source of truth**: the necessity classifier and
523
+ the solo-dispatch fuzzy matcher load phrases from the lockfile, with
524
+ Markdown parsing reserved as fallback.
525
+
526
+ | Component | File | Role |
527
+ |---|---|---|
528
+ | Source | `agents/low-impact-decisions.md` | Hand-edited Markdown. Strict parser (step-9 P4) catches drift. |
529
+ | Build tool | `scripts/ai_council/compile_corpus.py` | `parse_corpus_strict()` → schema-v1 YAML. Deterministic output. |
530
+ | Lockfile | `agents/low-impact-decisions.lock.yaml` | Generated, **committed**. Schema `{schema_version: 1, provenance: {…}, validated, probation, anti_examples}`. |
531
+ | Runtime loader | `low_impact_corpus.load_corpus_lock()` + lenient `load_*` shims | YAML preferred; Markdown fallback when the lockfile is missing or schema-mismatched. |
532
+ | CI gate | `task check-corpus` (used by `task consistency`) | Fails on drift between source and lockfile. |
533
+
534
+ The committed lockfile is the contract: a stale lockfile fails CI the
535
+ same way `.agent-src/` drift does. The Markdown parser stays in the
536
+ repo as a build-time dependency, not a runtime dependency — a parser
537
+ regression breaks `task consistency`, never the live council.
538
+
539
+ `scripts/ai_council/learn_low_impact_preview.py` deliberately stays on
540
+ `parse_corpus_strict` (Markdown). It runs **before** `task sync`
541
+ rebuilds the lockfile, so it must read whatever the user just edited.
542
+
91
543
  ## `api_key_ref` forms
92
544
 
93
545
  Exactly two forms. Raw keys in the yml are a hard validation error.
@@ -122,9 +574,18 @@ as comments and the loader enforces them.
122
574
  identity.
123
575
  - **Autonomy carve-out — no silent spend.** The `/council` command always
124
576
  asks before invoking, even under autonomy: on. Cost is real and paid.
125
- - **Manual vs API transport.** `api` = direct SDK call against the
126
- provider's API (billable). `manual` = copy-paste loop, user is the
127
- transport (free). Per-invocation flag > per-member override > defaults.
577
+ - **Manual vs API vs CLI transport.** Three first-class modes on the
578
+ `mode:` axis:
579
+ - `manual` = copy & paste the human transports prompt + reply between
580
+ the agent and an external chat surface. Free, no key.
581
+ - `api` = direct SDK call against a stored key (per-token billing).
582
+ - `cli` = shell out to a locally-installed provider CLI under the
583
+ user's subscription auth — spend is covered by the flat-rate
584
+ subscription, not per-token. `billable=False` so the `cost_budget`
585
+ USD ceiling does not apply; subscription quotas are guarded by
586
+ `cli_call_budget.max_calls_per_day` instead.
587
+
588
+ Precedence: per-invocation flag > per-member override > defaults > `api`.
128
589
  - **Tokens never stored in this yml.** Keys live in 0600 files
129
590
  (`~/.event4u/agent-config/<provider>.key`, installed via
130
591
  `bash scripts/install_<provider>_key.sh` for providers that ship an
@@ -165,14 +626,25 @@ error) when any of these hold:
165
626
  `sk-`, `pk-`, `xai-`, or matches a provider's key prefix).
166
627
  6. `defaults.deep_min_rounds < defaults.min_rounds` is allowed (clamped
167
628
  by the monotonic rule at runtime) but logged as a warning.
168
- 7. `defaults.mode` not in `{"api", "manual"}`; per-member `mode` override
169
- same constraint.
170
- 8. `advisors.<key>.member` missing, unknown, or pointing at a
171
- `members.<provider>` that does not exist or has `enabled: false`
172
- (when the advisor itself is `enabled: true`). Silent skips are not
173
- allowed a typo never costs the user money on an unintended call
174
- plan.
175
- 9. `advisors.<key>.model` is set but not a string.
629
+ 7. `defaults.mode` not in `{"api", "manual", "cli"}`; per-member `mode`
630
+ override same constraint.
631
+ 8. `members.<provider>.binary` set when the member's effective mode is
632
+ not `cli` explicit error to keep config clutter-free.
633
+ 9. `cli_call_budget` is not a mapping, or
634
+ `cli_call_budget.max_calls_per_day.<key>` is an unknown provider, or
635
+ any value is a negative integer / non-integer.
636
+ 10. `advisors.<key>.member` missing, unknown, or pointing at a
637
+ `members.<provider>` that does not exist or has `enabled: false`
638
+ (when the advisor itself is `enabled: true`). Silent skips are not
639
+ allowed — a typo never costs the user money on an unintended call
640
+ plan.
641
+ 11. `advisors.<key>.model` is set but not a string.
642
+ 12. `necessity_classifier` is not a mapping, or `enabled` is not a bool,
643
+ or `mode` is not one of `{"off", "educate", "block"}`.
644
+ 13. `lens_overrides.necessity_classifier_mode` is not a mapping, or any
645
+ value is not one of `{"off", "educate", "block"}`. Unknown lens keys
646
+ are accepted (forward-compatible) but never silently rewrite the
647
+ global default.
176
648
 
177
649
  ## Migration footprint (Phase 0)
178
650
 
@@ -32,8 +32,8 @@ column 1 of this table.
32
32
  | `optimize` | 1 | `agents-dir` · `augmentignore` · `rtk` · `skills` | `optimize-augmentignore` · `optimize-rtk-filters` · `optimize-skills` · former `/optimize agents` and `/optimize agents-md` moved to the `/agents` file-family cluster 2026-05-09; `/agents prepare/audit/cleanup` collapsed into the single `/optimize agents-dir` (flags or wizard) per the agent-doc consolidation |
33
33
  | `feature` | 1 | `explore` · `plan` · `refactor` · `roadmap` · `dev` | `feature-explore` · `feature-plan` · `feature-refactor` · `feature-roadmap` · `feature-dev` |
34
34
  | `chat-history` | 2 | `show` · `import` · `learn` | `chat-history` (legacy status) — `resume` / `clear` / `checkpoint` removed in `road-to-chat-history-hook-only` (auto-adopt + structural hooks); `import` (verbatim cross-session render) and `learn` (project-improving learning extraction) added in the v4 stateless schema |
35
- | `agents` | 2 | `init` · `optimize` · `audit` | AGENTS.md file family (high-frequency) — repurposed 2026-05-09: `init` (was `/copilot-agents init`) · `optimize` (merger of `/optimize agents-md` + `/copilot-agents optimize`) · `audit` (was `/optimize agents`, collapses old `audit` + `check` verbs); legacy folder ops (`prepare` / `cleanup` / folder-`audit`) moved to `/optimize agents-dir` |
36
- | `memory` | 2 | `add` · `load` · `promote` · `propose` · `mine-session` | `memory-add` · `memory-full` · `memory-promote` · `propose-memory`; `mine-session` added 2026-05-10 — manual transcript-mining sub-command from `road-to-dream-skill-adoption.md`, opt-in via `--confirm-transcript-access` per invocation |
35
+ | `agents` | 2 | `init` · `optimize` · `audit` · `user` | AGENTS.md file family (high-frequency) — repurposed 2026-05-09: `init` (was `/copilot-agents init`) · `optimize` (merger of `/optimize agents-md` + `/copilot-agents optimize`) · `audit` (was `/optimize agents`, collapses old `audit` + `check` verbs); legacy folder ops (`prepare` / `cleanup` / folder-`audit`) moved to `/optimize agents-dir`; `user` sub-cluster added 2026-05-15 — manages the project-root `.agent-user.md` persona file (sub-sub-commands: `init` · `show` · `review` · `accept` · `update`) per [`agent-user-schema`](agent-user-schema.md) |
36
+ | `memory` | 2 | `add` · `load` · `promote` · `propose` · `mine-session` · `learn-low-impact` | `memory-add` · `memory-full` · `memory-promote` · `propose-memory`; `mine-session` added 2026-05-10 — manual transcript-mining sub-command from `road-to-dream-skill-adoption.md`, opt-in via `--confirm-transcript-access` per invocation; `learn-low-impact` added 2026-05-15 — upstreams `## Validated` entries from `agents/low-impact-decisions.md` to the package seed via a DRAFT PR (re-runs the privacy-floor redactor as a second gate per `low-impact-corpus-privacy-floor`) |
37
37
  | `roadmap` | 2 | `create` · `ai-council` · `process-step` · `process-phase` · `process-full` | `roadmap-create` · `roadmap-execute` (replaced — autonomous, no per-step gate; `process-phase` is the default execution scope); `ai-council` added 2026-05-07 — wraps `/council default` with `--input-mode roadmap --depth deep` |
38
38
  | `module` | 2 | `create` · `explore` | `module-create` · `module-explore` |
39
39
  | `tests` | 2 | `create` · `execute` | `tests-create` · `tests-execute` |
@@ -12,7 +12,8 @@ keep-beta-until: 2026-08-12
12
12
  > - **CLI commands** rendered by `./agent-config --help`.
13
13
  > - **Slash commands** under `.agent-src.uncompressed/commands/**`.
14
14
  >
15
- > Per Phase 4 of `agents/roadmaps/road-to-distribution-maturity.md`.
15
+ > Per Phase 4 of the distribution-maturity roadmap (see
16
+ > `agents/roadmaps/` for current status).
16
17
 
17
18
  ## Why tiering
18
19
 
@@ -157,6 +158,6 @@ Hooked into `task lint-skills` so it runs in CI.
157
158
 
158
159
  ## See also
159
160
 
160
- - `agents/roadmaps/road-to-distribution-maturity.md` — Phase 4.
161
+ - The distribution-maturity roadmap — Phase 4 (under `agents/roadmaps/`).
161
162
  - `docs/contracts/command-clusters.md` — orchestrator → child wiring.
162
163
  - `docs/contracts/STABILITY.md` — surface-stability commitments.
@@ -1,3 +1,8 @@
1
+ ---
2
+ stability: beta
3
+ keep-beta-until: 2026-08-13
4
+ ---
5
+
1
6
  # Cost-Profile Defaults — Contract
2
7
 
3
8
  > **Status:** beta · **Owner:** package maintainer · **Last reviewed:** 2026-05-14
@@ -1,3 +1,8 @@
1
+ ---
2
+ stability: beta
3
+ keep-beta-until: 2026-08-13
4
+ ---
5
+
1
6
  # Decision-engine gates (v1)
2
7
 
3
8
  **Status:** beta — landed 2026-05-14 via `road-to-productization.md` Phase 2.
@@ -119,8 +119,8 @@ is `low`.
119
119
  **Purpose.** Bound the surface area where a memory hit can be said
120
120
  to have *changed* an outcome. Closed list, not open — without this
121
121
  bound, every memory call risks the "memory affected everything"
122
- failure mode (Risk register row 2 of
123
- [`agents/roadmaps/road-to-proof-not-features.md`](../../agents/roadmaps/road-to-proof-not-features.md)).
122
+ failure mode (Risk register row 2 of the `road-to-proof-not-features`
123
+ roadmap; see `agents/roadmaps/` or `agents/roadmaps/archive/`).
124
124
 
125
125
  **Closed list (v1).** Exactly four keys. Adding a fifth requires a
126
126
  schema bump + entry under `### Breaking` in `CHANGELOG.md`.