@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.
- package/.agent-src/commands/agents/user/accept.md +117 -0
- package/.agent-src/commands/agents/user/init.md +163 -0
- package/.agent-src/commands/agents/user/review.md +107 -0
- package/.agent-src/commands/agents/user/show.md +109 -0
- package/.agent-src/commands/agents/user/update.md +98 -0
- package/.agent-src/commands/agents/user.md +66 -0
- package/.agent-src/commands/agents.md +2 -0
- package/.agent-src/commands/memory/learn-low-impact.md +143 -0
- package/.agent-src/rules/ask-when-uncertain.md +10 -6
- package/.agent-src/rules/copilot-routing.md +1 -1
- package/.agent-src/rules/devcontainer-routing.md +1 -1
- package/.agent-src/rules/external-reference-deep-dive.md +1 -1
- package/.agent-src/rules/fast-path-marker-visibility.md +38 -0
- package/.agent-src/rules/low-impact-corpus-privacy-floor.md +74 -0
- package/.agent-src/rules/symfony-routing.md +1 -1
- package/.agent-src/skills/ai-council/SKILL.md +208 -8
- package/.agent-src/templates/agents/agent-project-settings.example.yml +1 -1
- package/.claude-plugin/marketplace.json +8 -1
- package/CHANGELOG.md +328 -124
- package/README.md +21 -6
- package/config/agent-settings.template.yml +4 -0
- package/config/gitignore-block.txt +17 -0
- package/docs/architecture.md +12 -12
- package/docs/archive/CHANGELOG-pre-2.11.0.md +141 -0
- package/docs/catalog.md +16 -7
- package/docs/contracts/adr-architectural-consensus-mechanism.md +4 -3
- package/docs/contracts/adr-level-6-productization.md +7 -9
- package/docs/contracts/agent-user-schema.md +165 -0
- package/docs/contracts/ai-council-config.md +492 -20
- package/docs/contracts/command-clusters.md +2 -2
- package/docs/contracts/command-surface-tiers.md +3 -2
- package/docs/contracts/cost-profile-defaults.md +5 -0
- package/docs/contracts/decision-engine-gates.md +5 -0
- package/docs/contracts/decision-trace-v1.md +2 -2
- package/docs/contracts/file-ownership-matrix.json +1961 -108
- package/docs/contracts/installed-tools-lockfile.md +2 -1
- package/docs/contracts/low-impact-corpus-format.md +95 -0
- package/docs/contracts/mcp-beta-criteria.md +6 -5
- package/docs/contracts/mcp-cloud-scope.md +5 -4
- package/docs/contracts/multi-tool-projection-fidelity.md +8 -2
- package/docs/contracts/release-trunk-sync.md +4 -3
- package/docs/contracts/tier-3-contrib-plugin.md +5 -6
- package/docs/examples/agent-user.example.md +21 -0
- package/docs/getting-started.md +2 -2
- package/docs/guidelines/agent-infra/installed-tools-manifest.md +2 -1
- package/docs/installation.md +32 -0
- package/package.json +1 -1
- package/scripts/_cli/cmd_doctor.py +134 -0
- package/scripts/ai_council/airgap.py +165 -0
- package/scripts/ai_council/cli_hints.py +123 -0
- package/scripts/ai_council/clients.py +787 -5
- package/scripts/ai_council/compile_corpus.py +178 -0
- package/scripts/ai_council/confidence_gate.py +156 -0
- package/scripts/ai_council/config.py +1007 -11
- package/scripts/ai_council/consensus.py +41 -2
- package/scripts/ai_council/events_log.py +137 -0
- package/scripts/ai_council/learn_low_impact_preview.py +252 -0
- package/scripts/ai_council/low_impact.py +714 -0
- package/scripts/ai_council/low_impact_corpus.py +466 -0
- package/scripts/ai_council/low_impact_intake.py +163 -0
- package/scripts/ai_council/modes.py +6 -1
- package/scripts/ai_council/necessity.py +782 -0
- package/scripts/ai_council/orchestrator.py +252 -14
- package/scripts/ai_council/probation_gate.py +152 -0
- package/scripts/ai_council/redact_low_impact_entry.py +155 -0
- package/scripts/ai_council/replay.py +155 -0
- package/scripts/ai_council/session.py +19 -1
- package/scripts/ai_council/shadow_dispatch.py +235 -0
- package/scripts/ai_council/solo_dispatch.py +226 -0
- package/scripts/audit_cloud_compatibility.py +74 -0
- package/scripts/audit_command_surface.py +363 -0
- package/scripts/check_council_layout.py +11 -0
- package/scripts/council_cli.py +1046 -15
- 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
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
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> #
|
|
51
|
-
mode: <"api" | "manual">
|
|
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.**
|
|
126
|
-
|
|
127
|
-
|
|
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`
|
|
169
|
-
same constraint.
|
|
170
|
-
8. `
|
|
171
|
-
`
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
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
|
|
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
|
-
-
|
|
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.
|
|
@@ -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
|
-
|
|
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`.
|