@event4u/agent-config 14.4.0 → 14.6.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/.claude-plugin/marketplace.json +1 -1
- package/CHANGELOG.md +132 -291
- package/dist/agent-src/commands/analyze/decision.md +7 -0
- package/dist/agent-src/commands/optimize/project.md +10 -0
- package/dist/agent-src/commands/roadmap/process-full.md +45 -3
- package/dist/agent-src/contexts/execution/roadmap-process-loop.md +130 -1
- package/dist/agent-src/rules/decision-revisit-gate.md +108 -7
- package/dist/agent-src/skills/adr-create/SKILL.md +25 -3
- package/dist/agent-src/skills/decision-review/SKILL.md +18 -0
- package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
- package/dist/cli-delegate/{chunk-ZPSXJTLI.js → chunk-P6LQDIWK.js} +15 -1
- package/dist/cli-delegate/cmd_conformance.js +1 -1
- package/dist/cli-delegate/cmd_handoff.js +1 -1
- package/dist/cli-delegate/cmd_session_recycle.js +1 -1
- package/dist/discovery/deprecation-report.md +1 -1
- package/dist/discovery/discovery-manifest.json +8 -8
- package/dist/discovery/discovery-manifest.json.sha256 +1 -1
- package/dist/discovery/discovery-manifest.summary.md +1 -1
- package/dist/discovery/dormancy-report.md +1 -1
- package/dist/discovery/orphan-report.md +1 -1
- package/dist/discovery/packs.json +2 -2
- package/dist/discovery/trust-report.md +1 -1
- package/dist/discovery/workspaces.json +2 -2
- package/dist/hooks/dispatch.js +419 -94
- package/dist/mcp/registry-manifest.json +2 -2
- package/dist/mcp/server.json +2 -2
- package/dist/router.json +1 -1
- package/docs/guidelines/agent-infra/engineering-memory-data-format.md +49 -0
- package/package.json +1 -1
- package/src/config/estate-count-budget.json +36 -1
- package/src/config/pack-size-budget.json +3 -2
- package/src/scripts/_lib/layer_overlap_notice.ts +85 -0
- package/src/scripts/_lib/scope_guard.sh +62 -3
- package/src/scripts/_lib/subagent_capsule.ts +81 -1
- package/src/scripts/adr/regenerate_index.ts +13 -2
- package/src/scripts/adr_cite_check.ts +424 -0
- package/src/scripts/bench_hook_injection.ts +18 -1
- package/src/scripts/check_adr_frontmatter.ts +187 -1
- package/src/scripts/check_single_delivery.ts +473 -0
- package/src/scripts/hooks/host_semantics.ts +28 -0
- package/src/scripts/hooks/injection_budget.ts +152 -16
- package/src/scripts/hooks/run_continuation_hook.ts +918 -46
- package/src/scripts/install.sh +12 -2
- package/src/scripts/memory_consolidation.ts +189 -0
- package/src/scripts/memory_eviction.ts +384 -0
- package/src/scripts/report_layer_overlap.ts +72 -0
- package/src/scripts/report_memory_pointers.ts +622 -0
- package/src/scripts/session_register_hook.ts +144 -21
|
@@ -93,6 +93,13 @@ suggest deferring or strengthening evidence before proposing.
|
|
|
93
93
|
### Rules
|
|
94
94
|
|
|
95
95
|
- Do NOT commit, push, or open a PR.
|
|
96
|
+
- Do NOT report an ADR as still binding without evaluating it first —
|
|
97
|
+
`./scripts-run src/scripts/adr_cite_check <ADR-NNN>` resolves status,
|
|
98
|
+
`review_trigger` state, amendments, successors, and back-references in one
|
|
99
|
+
call. A `superseded` / `deprecated` ADR is not a live decision
|
|
100
|
+
to audit, and an amended one is not the text its body's top half claims.
|
|
101
|
+
(`rejected` is NOT in that set — on an ADR it records a rejected *proposal*,
|
|
102
|
+
so the rejection is the live decision.)
|
|
96
103
|
- Do NOT auto-promote — `/memory propose` is the intake.
|
|
97
104
|
- Do NOT re-litigate a decision confirmed still valid — acknowledge it,
|
|
98
105
|
note the validation date, stop.
|
|
@@ -166,6 +166,16 @@ Hard rules for this step:
|
|
|
166
166
|
and its recorded revisit-conditions first; if it truly blocks, the
|
|
167
167
|
only flip path offered is option 3 (council re-eval) — "Flip" is not
|
|
168
168
|
offered for locks.
|
|
169
|
+
- **An ADR lock is evaluated before it is reported.** When the lock is a
|
|
170
|
+
numbered ADR, the FIRST action is
|
|
171
|
+
`./scripts-run src/scripts/adr_cite_check <ADR-NNN>` — not "report the
|
|
172
|
+
lock". A `superseded` / `deprecated` status means there is no
|
|
173
|
+
live lock to route at all, and an amended ADR means the cited text may not
|
|
174
|
+
be the operative one. `rejected` stays live — it records a rejected
|
|
175
|
+
*proposal*, so reopening it means showing the premise it rejected on has
|
|
176
|
+
changed. Reporting an unevaluated ADR as a blocker is the
|
|
177
|
+
ordering defect `decision-revisit-gate` exists over: it costs the owner a
|
|
178
|
+
turn to discover what one command answers.
|
|
169
179
|
- **Session budget:** at most 5 interview questions by default
|
|
170
180
|
(`--max-questions=N` to override). Budget exhausted → remaining
|
|
171
181
|
candidates go to Step 4 as "further candidates".
|
|
@@ -46,10 +46,15 @@ with the **scope delta below**.
|
|
|
46
46
|
- **Working set:** every open step across every phase, in document
|
|
47
47
|
order. Phase-internal annotations like `(deferred)` / `(optional)` /
|
|
48
48
|
"gated on Phase N" do not narrow the working set.
|
|
49
|
-
- **Stop after:** the entire roadmap reaches `count_open == 0
|
|
50
|
-
|
|
51
|
-
|
|
49
|
+
- **Stop after:** the entire roadmap reaches `count_open == 0`
|
|
50
|
+
(`complete`), or the runnable set is empty with steps still open
|
|
51
|
+
(`blocked` — see the terminal-outcomes section below), or a halt
|
|
52
|
+
condition fires (Hard-Floor, council-off + ambiguity — the latter only
|
|
53
|
+
outside an accepted contract with council available,
|
|
52
54
|
security-sensitive, scope-out-of-roadmap, test/quality red).
|
|
55
|
+
- **Do not start** when NO open step is runnable: that is
|
|
56
|
+
`blocked-preflight`, and it creates no branch and takes no grant. The
|
|
57
|
+
runnable test is § 3c of the loop.
|
|
53
58
|
- **Phase boundary handling:** at every phase boundary, run the
|
|
54
59
|
per-phase quality pipeline when `quality_cadence: per_phase` (or
|
|
55
60
|
`per_step`) AND `quality.local_auto_run: true` — under the default
|
|
@@ -91,6 +96,8 @@ FORBIDDEN NON-HALT REASONS — NEVER STOP THE RUN FOR ANY OF THESE:
|
|
|
91
96
|
· "avoid a PR pile-up" / "let the open PRs merge first"
|
|
92
97
|
· "this phase is large / touches a deep subsystem"
|
|
93
98
|
· "phase-checkpoints mode, so I'll checkpoint and wait"
|
|
99
|
+
· "this step looks human-gated" — `blocked` is reached by the § 3c
|
|
100
|
+
runnable test over PRE-EXISTING blockers, never by how a step feels
|
|
94
101
|
· any agent-invented caution not in the five halt conditions above.
|
|
95
102
|
INVENTING A HALT REASON IS A VIOLATION OF THE COMMAND AND THE USER'S WILL.
|
|
96
103
|
IF CONTEXT RUNS OUT MID-RUN, KEEP LANDING COMPLETE STEPS UNTIL IT DOES —
|
|
@@ -110,6 +117,41 @@ roadmap carries such phrasing — whether by legacy or by an opt-in
|
|
|
110
117
|
setting — treat it as ordinary prose during execution, never as a
|
|
111
118
|
gate. Phase ordering and explicit dependency gates govern the loop.
|
|
112
119
|
|
|
120
|
+
## Terminal outcomes — and why `blocked` is not a sixth halt
|
|
121
|
+
|
|
122
|
+
The five halt conditions answer *"what interrupts runnable work"*. They never
|
|
123
|
+
answered *"what does the run report when no runnable work is left and the roadmap
|
|
124
|
+
is not finished"*, and the omission was not academic: measured 2026-08-19 across
|
|
125
|
+
the twelve most nearly complete roadmaps in the estate, **zero** could reach
|
|
126
|
+
`count_open == 0` in one PR — every remaining open step needed a human action, a
|
|
127
|
+
repo-admin setting, a paid authorisation, an absent binary, or an elapsed soak
|
|
128
|
+
window. Under the contract as written, such a roadmap had no legal stopping point,
|
|
129
|
+
so the command could be neither obeyed nor honestly declined.
|
|
130
|
+
|
|
131
|
+
| Outcome | When | Success? |
|
|
132
|
+
|---|---|---|
|
|
133
|
+
| `complete` | `count_open == 0` | yes — archival check runs |
|
|
134
|
+
| `blocked` | `count_open > 0` and no open step is runnable per § 3c | **no** — partial progress, labelled as such |
|
|
135
|
+
| `blocked-preflight` | no open step was runnable before the run started | **no** — nothing was attempted; no branch |
|
|
136
|
+
| a halt | one of the five conditions fired | **no** — the halt is reported |
|
|
137
|
+
|
|
138
|
+
```
|
|
139
|
+
`blocked` IS NEVER REPORTED AS COMPLETION. count_open STAYS > 0.
|
|
140
|
+
NEVER FLIP A BOX TO [~] TO REACH IT — THAT LAUNDERS OPEN WORK THROUGH A GLYPH.
|
|
141
|
+
A PR OPENED ON A BLOCKED RUN SAYS PARTIAL PROGRESS IN ITS FIRST LINE.
|
|
142
|
+
REVALIDATE THE RUNNABLE SET BEFORE REPORTING IT. ONE RUNNABLE STEP REJECTS THE CLAIM.
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
**It is a terminal outcome, not a halt, and the distinction is the safety
|
|
146
|
+
property.** The Iron Law above and its forbidden list hold their full authority
|
|
147
|
+
over mid-run stopping: an agent that may not stop for "this phase is large" may
|
|
148
|
+
equally not stop for "this step looks human-gated". Runnability is decided by the
|
|
149
|
+
§ 3c test over blockers that **pre-date the run** — a citation the run wrote
|
|
150
|
+
itself buys nothing. AI council 2026-08-19, 2/2 convergent on exactly this
|
|
151
|
+
separation.
|
|
152
|
+
|
|
153
|
+
Recorded in [`ADR-235`](../../../../docs/decisions/ADR-235-process-full-blocked-terminal-outcome.md).
|
|
154
|
+
|
|
113
155
|
## Iron Law — Real-time dashboard
|
|
114
156
|
|
|
115
157
|
```
|
|
@@ -180,6 +180,83 @@ change in § 3a.
|
|
|
180
180
|
`commit-policy` § NEVER ask about committing holds regardless of
|
|
181
181
|
autonomy).
|
|
182
182
|
|
|
183
|
+
### 3c. Runnable-work precondition — refuse a run with nothing to do
|
|
184
|
+
|
|
185
|
+
```
|
|
186
|
+
A RUN WITH NO RUNNABLE OPEN STEP DOES NOT START.
|
|
187
|
+
NAME THE BLOCKERS. CREATE NO BRANCH. WRITE NOTHING.
|
|
188
|
+
THIS IS A PRECONDITION, NEVER A HALT — IT CANNOT FIRE MID-RUN.
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
Run once, at the end of the pre-scan, before any grant is exercised.
|
|
192
|
+
|
|
193
|
+
**Runnable** is decided from the roadmap's own `## Blockers` section and nothing
|
|
194
|
+
else. An open step is **not** runnable when a blocker satisfies all four:
|
|
195
|
+
|
|
196
|
+
1. it exists in `## Blockers` **before this run started** — a blocker the run
|
|
197
|
+
itself authored does not count (see the gaming note below);
|
|
198
|
+
2. its `Class:` is `2` (consent-once) or `3` (human-only) — classes `0` and `1`
|
|
199
|
+
are agent-runnable by construction and carry a `Run:` the agent executes,
|
|
200
|
+
per [`templates/roadmaps.md`](../../templates/roadmaps.md);
|
|
201
|
+
3. its `Blocks:` field names this step, by phase-and-step identifier or by a
|
|
202
|
+
phase whose scope contains it;
|
|
203
|
+
4. it names what would clear it.
|
|
204
|
+
|
|
205
|
+
Every other open step is runnable. **No blocker citation → runnable**, and the
|
|
206
|
+
default direction is deliberate: the failure this precondition must not have is
|
|
207
|
+
refusing work an agent could have done.
|
|
208
|
+
|
|
209
|
+
- **At least one runnable open step** → the run proceeds. It does *not* matter
|
|
210
|
+
how many steps are blocked; the blocked ones are skipped in place (never
|
|
211
|
+
flipped — see the terminal-outcomes section), and the run works the rest.
|
|
212
|
+
- **Zero runnable open steps** → `blocked-preflight`. Report the roadmap, the
|
|
213
|
+
open-step count, and each blocker with its class and its clearing action.
|
|
214
|
+
Create no branch, write no state, take no grant.
|
|
215
|
+
|
|
216
|
+
**A step this precondition judges non-runnable carries a `blocked-by:` marker on
|
|
217
|
+
its own line.** The obligation is here because two mechanisms read blockedness
|
|
218
|
+
from two different places and only one of them reads this section: `run-continuation`,
|
|
219
|
+
the stop-slot concern that re-engages an autonomous run, decides open-vs-blocked
|
|
220
|
+
from the inline `<!-- blocked-by: <id> -->` marker and never parses `## Blockers`
|
|
221
|
+
at all. Without the marker a step that IS blocked per the four conditions above
|
|
222
|
+
still counts as open work to the concern, which re-engages the agent into it every
|
|
223
|
+
stop fire until the stall rung fires — the mechanism whose job is to detect a
|
|
224
|
+
stall manufacturing one, which is the failure shape this file already records once
|
|
225
|
+
for the roadmap-root split.
|
|
226
|
+
|
|
227
|
+
Round 8 finding 7 caught the two predicates as disjoint. The fix is this coupling
|
|
228
|
+
sentence rather than teaching the concern to parse `## Blockers`: a second
|
|
229
|
+
implementation of the four conditions is a second thing to keep in step, and the
|
|
230
|
+
one-line marker is checkable by eye at the step it belongs to. **The residual is
|
|
231
|
+
real and is not papered over:** nothing enforces the pairing, so a `## Blockers`
|
|
232
|
+
entry authored without its marker is caught by review or not at all.
|
|
233
|
+
|
|
234
|
+
**Why a precondition and not a sixth halt.** The five halt conditions interrupt
|
|
235
|
+
work that is still runnable; this decides whether any runnable work exists at
|
|
236
|
+
all. Keeping them structurally separate is what preserves the forbidden-list's
|
|
237
|
+
authority over mid-run stopping — an agent that may not stop for "this phase is
|
|
238
|
+
large" also may not stop for "this feels human-gated". AI council 2026-08-19,
|
|
239
|
+
2/2 convergent, explicitly on this point: *"keep this structurally separate from
|
|
240
|
+
'five, and only five': the five are exceptional halts; `blocked` is an
|
|
241
|
+
exhaustion-of-runnable-work terminal outcome."*
|
|
242
|
+
|
|
243
|
+
**The gaming vector, named because it is the one thing that would make this
|
|
244
|
+
worse than the gap it closes.** An agent meets a hard step, writes a `## Blockers`
|
|
245
|
+
entry claiming Class 3, cites it, and stops — a comfort-stop wearing a citation.
|
|
246
|
+
Condition 1 is the whole defence: a blocker must pre-date the run, which is
|
|
247
|
+
checkable from git rather than from the agent's word. A blocker discovered
|
|
248
|
+
mid-run is recorded (that is honest and useful) and does **not** make the step
|
|
249
|
+
non-runnable in this run.
|
|
250
|
+
|
|
251
|
+
**One imprecision, recorded rather than smoothed over.** The class taxonomy has
|
|
252
|
+
four rows and none of them says *"waiting on time"* or *"waiting on another
|
|
253
|
+
roadmap"*. A soak window and a cross-roadmap dependency are authored as `3`
|
|
254
|
+
today, because absence of `Class:` already means `3` — so they are decidable but
|
|
255
|
+
mislabelled as human-only. A fifth class is a taxonomy change with a linter
|
|
256
|
+
behind it (`lint_roadmap_blockers`) and is not made here.
|
|
257
|
+
*Revisit-if:* a run is refused on a blocker whose real content is "wait", and
|
|
258
|
+
the report reads wrong to the person holding it.
|
|
259
|
+
|
|
183
260
|
## 4. Resolve cadences — read once, cache for the run
|
|
184
261
|
|
|
185
262
|
Read both keys from `.agent-settings.yml` once and cache for the whole
|
|
@@ -430,6 +507,52 @@ artifact drafting, council enablement), not safety.
|
|
|
430
507
|
|
|
431
508
|
On halt: stop, surface state, do **not** auto-fix outside the failing step.
|
|
432
509
|
|
|
510
|
+
### Terminal outcomes — `complete`, `blocked`, or a halt
|
|
511
|
+
|
|
512
|
+
The halt list above answers *"what interrupts runnable work"*. It does not answer
|
|
513
|
+
*"what does the run report when there is no runnable work left and the roadmap is
|
|
514
|
+
not finished"*, and until this section there was no answer: the wrapper's stop
|
|
515
|
+
condition was `count_open == 0` or a halt, so a roadmap whose remaining steps all
|
|
516
|
+
need a human had **no legal stopping point**.
|
|
517
|
+
|
|
518
|
+
That is not a hypothetical. Measured 2026-08-19 across the twelve most nearly
|
|
519
|
+
complete roadmaps in the estate: **zero** could reach `count_open == 0` in one
|
|
520
|
+
PR. Every remaining open step needed a human action — flip branch protection,
|
|
521
|
+
delete committed evidence, authorise paid spend, install an absent binary, wait
|
|
522
|
+
out a soak window, use the maintainer's own machine, or edit a file an armed host
|
|
523
|
+
hook denies.
|
|
524
|
+
|
|
525
|
+
Three outcomes, and only one of them is success:
|
|
526
|
+
|
|
527
|
+
| Outcome | When | What it reports |
|
|
528
|
+
|---|---|---|
|
|
529
|
+
| `complete` | `count_open == 0` | the roadmap is finished; archival check runs (§ 6) |
|
|
530
|
+
| `blocked` | `count_open > 0` **and** no open step is runnable | the work that DID close, plus every blocker with its clearing action |
|
|
531
|
+
| a halt | one of the five conditions above fired | the halt, its evidence, and what remains |
|
|
532
|
+
|
|
533
|
+
```
|
|
534
|
+
`blocked` IS NEVER PRESENTED AS COMPLETION.
|
|
535
|
+
count_open STAYS > 0. NO CHECKBOX IS FLIPPED TO [~] TO REACH IT.
|
|
536
|
+
A PR OPENED ON A BLOCKED RUN IS LABELLED PARTIAL PROGRESS.
|
|
537
|
+
REVALIDATE IMMEDIATELY BEFORE REPORTING IT — ONE RUNNABLE STEP LEFT REJECTS THE CLAIM.
|
|
538
|
+
```
|
|
539
|
+
|
|
540
|
+
The `[~]` prohibition is load-bearing and was the one point the council split on.
|
|
541
|
+
Deferring a blocked step to `[~]` would let the run reach `count_open == 0` and
|
|
542
|
+
report completion — laundering unfinished work through a glyph, which is exactly
|
|
543
|
+
what Iron Law 3 of [`roadmap-progress-sync`](../../rules/roadmap-progress-sync.md)
|
|
544
|
+
exists to catch on the archival side. So the boxes stay open and the outcome
|
|
545
|
+
carries the truth instead.
|
|
546
|
+
|
|
547
|
+
Revalidation is the second half of the same discipline: the runnable set is
|
|
548
|
+
recomputed at the moment of reporting, because steps unblock during a run (a
|
|
549
|
+
prerequisite closes, a `Run:` succeeds, a peer merges). If any open step is
|
|
550
|
+
runnable, `blocked` is refused and the loop continues.
|
|
551
|
+
|
|
552
|
+
**Runnability is decided by the § 3c test**, unchanged — the same four conditions,
|
|
553
|
+
including that a qualifying blocker must pre-date the run. A blocker the run
|
|
554
|
+
authored is recorded and does not make a step non-runnable in that run.
|
|
555
|
+
|
|
433
556
|
### Forbidden non-halt reasons — agent-invented cautions
|
|
434
557
|
|
|
435
558
|
The halt list above is **exhaustive**. An agent running `process-full` (or any
|
|
@@ -446,9 +569,15 @@ a violation of the command and the user's will:
|
|
|
446
569
|
a phase boundary emits a non-blocking status line and the run **continues**;
|
|
447
570
|
the stop-and-wait reading of `phase-checkpoints` applies only to
|
|
448
571
|
`process-phase`.
|
|
572
|
+
- **"this step looks human-gated"** — the `blocked` outcome above is reached by
|
|
573
|
+
the § 3c test over blockers that pre-date the run, never by a judgement about
|
|
574
|
+
how a step feels. A step with no qualifying blocker citation is runnable, and
|
|
575
|
+
writing the citation yourself does not make it one. This entry exists because
|
|
576
|
+
the terminal outcome is the single most abusable thing on this page.
|
|
449
577
|
|
|
450
578
|
If the work genuinely cannot continue, it will be because one of the five real
|
|
451
|
-
halt conditions fired
|
|
579
|
+
halt conditions fired, or because the runnable set is empty by the § 3c test —
|
|
580
|
+
surface THAT, with the blockers named, not a manufactured caution.
|
|
452
581
|
|
|
453
582
|
### Non-halt — gating notes, "optional" tags
|
|
454
583
|
|
|
@@ -10,8 +10,14 @@ triggers:
|
|
|
10
10
|
- keyword: "already decided"
|
|
11
11
|
- keyword: "locked decision"
|
|
12
12
|
- keyword: "budget blocks"
|
|
13
|
+
- keyword: "adr-"
|
|
14
|
+
- keyword: "adrs"
|
|
15
|
+
- keyword: "superseded"
|
|
16
|
+
- keyword: "review_trigger"
|
|
17
|
+
- phrase: "the adr"
|
|
13
18
|
routes_to:
|
|
14
|
-
- "skill:
|
|
19
|
+
- "skill:ai-council"
|
|
20
|
+
- "skill:decision-record"
|
|
15
21
|
workspaces: [agent-config-maintainer, engineering]
|
|
16
22
|
packs: [meta]
|
|
17
23
|
collision_ok:
|
|
@@ -28,24 +34,99 @@ obligation_frequency: "per-task"
|
|
|
28
34
|
A LOCK IS A DECISION UNDER PAST CONDITIONS, NOT A PERMANENT LAW.
|
|
29
35
|
BENEFIT BLOCKED BY A LOCK → SURFACE + OFFER RE-EVALUATION.
|
|
30
36
|
NEVER SILENT COMPLIANCE. NEVER SILENT DROP OF A GOOD CHANGE.
|
|
37
|
+
RE-EVALUATION GOES TO THE COUNCIL FIRST, NOT TO THE USER FIRST.
|
|
38
|
+
THE USER IS REACHED FOR THE RESERVED SET — AND ALWAYS FOR IT.
|
|
39
|
+
NEVER CITE A LOCK YOU HAVE NOT EVALUATED THIS TURN.
|
|
31
40
|
```
|
|
32
41
|
|
|
33
42
|
An agent that finds a genuinely beneficial change blocked by a recorded
|
|
34
43
|
past decision — an honest-null eval verdict, a "don't relitigate"
|
|
35
44
|
memory or context note, a token-frugality/budget canon line, or an ADR —
|
|
36
|
-
does not quietly comply and drop the change. It surfaces the conflict
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
ceiling on what the package may ever become.
|
|
45
|
+
does not quietly comply and drop the change. It surfaces the conflict and
|
|
46
|
+
re-evaluates the lock — **in the council by default**, reaching the user only
|
|
47
|
+
for the reserved set below. Progress means adaptation: locks encode what was
|
|
48
|
+
true when they were written, not a ceiling on what the package may ever become.
|
|
49
|
+
|
|
50
|
+
**The default venue is the council, and this is a correction, not a new
|
|
51
|
+
policy.** [`ai-council`](../skills/ai-council/SKILL.md) has said since its own
|
|
52
|
+
Iron Law that *"a design decision the agent cannot settle from the tree goes to
|
|
53
|
+
the council first, not to the user first"*, and its class table already lists
|
|
54
|
+
"reopening a recorded decision". This rule used to say "offers the user a path",
|
|
55
|
+
which contradicted that and made every lock an owner interrupt. The measured
|
|
56
|
+
cost of the contradiction: across 26 days of transcripts, the agent reported a
|
|
57
|
+
lock and waited, and the owner had to void the decision retroactively before the
|
|
58
|
+
work resumed — in the two clearest cases the lock report is timestamped **before**
|
|
59
|
+
the override demand. The ordering was the defect; the permission was never
|
|
60
|
+
missing.
|
|
40
61
|
|
|
41
62
|
**Mechanism-match check comes FIRST**: a verdict settles the *mechanism it
|
|
42
63
|
tested*, not every future proposal that resembles it — if the blocked change
|
|
43
64
|
is a different mechanism, the lock does not apply; proceed, noting the
|
|
44
65
|
distinction.
|
|
45
66
|
|
|
46
|
-
|
|
67
|
+
## The five steps, carried here — the depth is elsewhere, the obligation is not
|
|
68
|
+
|
|
69
|
+
1. **Mechanism-match** (above) — different mechanism, no lock, proceed.
|
|
70
|
+
2. **Evaluate the lock before citing it.** An ADR may not be presented as a
|
|
71
|
+
reason not to act until its status, `review_trigger` state, amendments and
|
|
72
|
+
successors have been read. In this repository that is one command —
|
|
73
|
+
`./scripts-run src/scripts/adr_cite_check <ADR-NNN>`; elsewhere it is the
|
|
74
|
+
same four reads done by hand. A `superseded` / `deprecated` status means it is
|
|
75
|
+
not a live lock at all; a fired or indeterminate trigger means it is not an
|
|
76
|
+
**unqualified** one. `rejected` is NOT in the dead set — on an ADR it records
|
|
77
|
+
a rejected *proposal*, so the rejection binds until its premise is shown to
|
|
78
|
+
have changed.
|
|
79
|
+
3. **Surface** the change, the lock, the condition it recorded, and what has
|
|
80
|
+
changed since.
|
|
81
|
+
4. **Route it** — council by default (below), user only for the reserved set.
|
|
82
|
+
5. **Record** the outcome with scope and `revisit-if`.
|
|
83
|
+
|
|
84
|
+
These five are the obligation and they are stated here rather than only behind
|
|
85
|
+
a route, because the route was measurably unreachable: `decision-revisit-gate`
|
|
86
|
+
lives in the always-on `meta` pack while `decision-review` ships
|
|
87
|
+
`install.default: false` in the non-default `analysis-workbench` pack, so a
|
|
88
|
+
pack-legal install received the obligation and not the procedure. `routes_to`
|
|
89
|
+
now names two always-on skills; the backward-audit depth in
|
|
90
|
+
[`decision-review`](../skills/decision-review/SKILL.md) stays optional, which is
|
|
91
|
+
the correct status for depth.
|
|
92
|
+
|
|
93
|
+
Full catalog (honest nulls, don't-relitigate notes, budget-canon lines, ADRs,
|
|
94
|
+
hard structural caps), the per-step detail, when-NOT-to-fire and the failure
|
|
95
|
+
modes remain in [`skill:decision-review` § Decision-revisit gate — mechanics](../skills/decision-review/SKILL.md).
|
|
47
96
|
Trigger-set above activates this routing on demand, independent of the discipline profile (ADR-110).
|
|
48
97
|
|
|
98
|
+
## Who decides — the owner-reserved set
|
|
99
|
+
|
|
100
|
+
The discriminator, in one question: **does the proposed transition weaken an
|
|
101
|
+
owner-reserved invariant, or create an undelegated external, irreversible, or
|
|
102
|
+
destructive commitment?** No, and reversible inside the authorised envelope →
|
|
103
|
+
the council decides. Yes, or not establishable from tree evidence → the owner.
|
|
104
|
+
|
|
105
|
+
The routing unit is the **transition**, never the document: the same ADR may be
|
|
106
|
+
strengthened by the council and weakened only by the owner.
|
|
107
|
+
|
|
108
|
+
| Reserved to the owner | Council-decidable |
|
|
109
|
+
|---|---|
|
|
110
|
+
| Changes the project's purpose or a declared non-negotiable outcome | Any mechanism serving that purpose |
|
|
111
|
+
| **Lowers or removes** a recorded security / privacy / safety / data-handling floor | Strengthening a floor, or an equivalent swap above it |
|
|
112
|
+
| Irreversible or materially destructive | Reversible within the envelope |
|
|
113
|
+
| Spend or liability **above** a delegated threshold | Budgeted, threshold-bounded spend |
|
|
114
|
+
| Creates / removes / weakens a legal, regulatory, contractual, licensing, compatibility, or public commitment | Internal-only commitments |
|
|
115
|
+
| Governance self-amendment — reopening authority, quorum, escalation, this set | What the rules already permit |
|
|
116
|
+
| Cannot be bounded from available evidence | Bounded, with the evidence cited |
|
|
117
|
+
|
|
118
|
+
Council split, abstention, or no quorum is an **escalation condition**, not a
|
|
119
|
+
class: that transition escalates, the ADR does not become owner-reserved
|
|
120
|
+
forever. Availability is `agent-config council:status`, never a project file
|
|
121
|
+
([`council-availability`](council-availability.md)).
|
|
122
|
+
|
|
123
|
+
Field on the ADR: `reopen_policy: directional | owner | unclassified`, absent →
|
|
124
|
+
**`unclassified`**, which permits council investigation and reversible
|
|
125
|
+
experiments and gates only execution of a reserved transition. Absent is
|
|
126
|
+
deliberately not `owner`: with 146 accepted ADRs that default would encode
|
|
127
|
+
today's blockage into the new schema. Full contract, the reopen record, and the
|
|
128
|
+
precedent-creates-no-authority clause: [`adr-layout § Reopen authority`](../docs/contracts/adr-layout.md).
|
|
129
|
+
|
|
49
130
|
## See also
|
|
50
131
|
|
|
51
132
|
- [`decision-review`](../skills/decision-review/SKILL.md) — the backward-audit
|
|
@@ -53,10 +134,30 @@ Trigger-set above activates this routing on demand, independent of the disciplin
|
|
|
53
134
|
- [`ai-council`](../skills/ai-council/SKILL.md) — the re-evaluation mechanism;
|
|
54
135
|
owns the convergence-summary scope + `revisit-if` contract.
|
|
55
136
|
- [`decision-record`](../skills/decision-record/SKILL.md) — forward-flow decision
|
|
56
|
-
locking;
|
|
137
|
+
locking; its **§ 4 Lock the choice + consequences** requires the `Revisit-if:`
|
|
138
|
+
line that names the reopening condition up front. (This pointer used to cite an
|
|
139
|
+
"escalation litmus" heading; `grep` over `src/` + `docs/` returns two hits and
|
|
140
|
+
neither is in that file — the heading never existed, so the pointer led
|
|
141
|
+
nowhere. Corrected to the section that carries the obligation.)
|
|
57
142
|
- [`token-budget-discipline`](token-budget-discipline.md) — the frugality-canon
|
|
58
143
|
value-over-budget escalation this rule's budget-lock case defers to.
|
|
59
144
|
- [`no-cheap-questions`](no-cheap-questions.md) — the question-quality floor a
|
|
60
145
|
revisit-offer must still clear.
|
|
61
146
|
- [`ask-when-uncertain`](ask-when-uncertain.md) — the numbered-options shape
|
|
62
147
|
used to present the revisit offer.
|
|
148
|
+
|
|
149
|
+
## Honest enforcement — `enforced_by: none`
|
|
150
|
+
|
|
151
|
+
`adr_cite_check` is deterministic where it runs, and nothing makes it run. No
|
|
152
|
+
gate can observe an agent citing a decision it never opened, so step 2 above is
|
|
153
|
+
model-carried — the same honesty boundary
|
|
154
|
+
[`security-sensitive-stop`](security-sensitive-stop.md) and
|
|
155
|
+
[`active-remediation`](active-remediation.md) state for their own obligations.
|
|
156
|
+
|
|
157
|
+
Two reach limits, named rather than implied. The tool lives in this repository's
|
|
158
|
+
`src/scripts/` and is **not** exposed as an `agent-config` verb, so a consumer
|
|
159
|
+
install performs step 2 by hand; wiring the verb touches six further surfaces
|
|
160
|
+
plus the curated `dist/agent-src/scripts/` list and is deliberately out of this
|
|
161
|
+
change. And `docs/decisions/` is projected into no agent-visible tree at all —
|
|
162
|
+
the agent sees ADR *numbers* cited across rules and skills, almost never ADR
|
|
163
|
+
*text*. Until that changes, "evaluate before citing" means opening the file.
|
|
@@ -11,6 +11,8 @@ execution:
|
|
|
11
11
|
command:
|
|
12
12
|
- ./scripts-run
|
|
13
13
|
- src/scripts/adr/regenerate_index
|
|
14
|
+
- --dir
|
|
15
|
+
- docs/decisions
|
|
14
16
|
runtime_requires:
|
|
15
17
|
bins:
|
|
16
18
|
- bash
|
|
@@ -59,7 +61,10 @@ Do NOT use when:
|
|
|
59
61
|
governance ADRs, 3-digit numbering (`ADR-NNN-<slug>.md`).
|
|
60
62
|
- **Per-area** — `docs/adrs/<area>/`: sub-area ADRs, 4-digit
|
|
61
63
|
numbering (`NNNN-<slug>.md`); `<area>` must match the canonical
|
|
62
|
-
inventory in
|
|
64
|
+
inventory in `src/scripts/audit_adr_coverage.ts` (`AREAS`). Deliberately not
|
|
65
|
+
a link: this skill is projected into `dist/agent-src/skills/`, whose sibling
|
|
66
|
+
`scripts/` directory carries six curated files and not this one, so any
|
|
67
|
+
relative href that resolves in `src/` is broken in the projection.
|
|
63
68
|
- The decision is **already made** — ADRs record outcomes, they do
|
|
64
69
|
not run the decision process. For unresolved trade-offs, run the
|
|
65
70
|
council or consult `adversarial-review` first.
|
|
@@ -80,7 +85,7 @@ Ask one question only if both are plausible:
|
|
|
80
85
|
`NNNN-<slug>.md` (4-digit, no `ADR-` prefix).
|
|
81
86
|
3. **Unknown area** — `<area>` not in the inventory: refuse with a
|
|
82
87
|
hint to add the area to `AREAS` in
|
|
83
|
-
`scripts/audit_adr_coverage.ts` in the same PR. Do not invent.
|
|
88
|
+
`src/scripts/audit_adr_coverage.ts` in the same PR. Do not invent.
|
|
84
89
|
4. **In doubt** → per-area (cheaper to surface, easier to relocate).
|
|
85
90
|
|
|
86
91
|
### 2. Pick the next ADR number
|
|
@@ -134,8 +139,12 @@ date: YYYY-MM-DD
|
|
|
134
139
|
decision: <slug>
|
|
135
140
|
supersedes: — | ADR-MMM
|
|
136
141
|
superseded_by: — | ADR-MMM
|
|
142
|
+
amends: — | ADR-MMM # optional — this ADR amends that one (reciprocal required)
|
|
143
|
+
amended_by: — | ADR-MMM # optional — reciprocal of `amends`
|
|
137
144
|
phase: <roadmap> · <phase-id>
|
|
138
145
|
review_trigger: <the CONDITION that would reopen this decision>
|
|
146
|
+
protected_dimensions: [...] # optional — purpose | security_floor | privacy_floor | external_commitment | governance | none
|
|
147
|
+
reopen_policy: directional | owner | unclassified # optional; absent → unclassified
|
|
139
148
|
---
|
|
140
149
|
|
|
141
150
|
# ADR-NNN — <Decision Title>
|
|
@@ -204,7 +213,7 @@ completeness check — not on every ADR by default.
|
|
|
204
213
|
- **Per-area numbering is 4-digit** (`NNNN-<slug>.md`); the flat
|
|
205
214
|
surface stays 3-digit (`ADR-NNN-<slug>.md`). Do not mix.
|
|
206
215
|
- **Area inventory is closed** — `<area>` must already exist in
|
|
207
|
-
`AREAS` in `scripts/audit_adr_coverage.ts`. Adding a new area is
|
|
216
|
+
`AREAS` in `src/scripts/audit_adr_coverage.ts`. Adding a new area is
|
|
208
217
|
a separate PR with explicit reviewer sign-off.
|
|
209
218
|
- Frontmatter `adr:` (flat) is the canonical number; the filename
|
|
210
219
|
prefix must match. The flat regenerator fails on mismatch.
|
|
@@ -214,6 +223,19 @@ completeness check — not on every ADR by default.
|
|
|
214
223
|
to `superseded`.
|
|
215
224
|
- Never delete an ADR file — supersede it. Deletion breaks
|
|
216
225
|
historical links and round-trips through git history checks.
|
|
226
|
+
- **Amending is the common case; wire it in both directions.** Most reopens
|
|
227
|
+
correct one decision inside an otherwise sound ADR rather than replacing the
|
|
228
|
+
whole record. Use `## Amendment N (YYYY-MM-DD) — <topic>` plus the reciprocal
|
|
229
|
+
`amends:` / `amended_by:` pair — the validator rejects a one-sided link,
|
|
230
|
+
because a one-sided link is invisible from the stale side, and the stale side
|
|
231
|
+
is the one a reader lands on first. Where the amendment reverses text that is
|
|
232
|
+
still asserted above it, add a one-line banner there too; the frontmatter
|
|
233
|
+
alone does not stop someone quoting the reversed sentence.
|
|
234
|
+
- **Who may reopen it is recorded, not assumed** — `reopen_policy` /
|
|
235
|
+
`protected_dimensions`, both optional, absent meaning `unclassified`
|
|
236
|
+
([`adr-layout § Reopen authority`](../../../docs/contracts/adr-layout.md)).
|
|
237
|
+
Reach for `owner` only when EVERY future transition is genuinely reserved;
|
|
238
|
+
`directional` is the normal answer, and no answer is a fine answer.
|
|
217
239
|
|
|
218
240
|
## Frugality Standards
|
|
219
241
|
|
|
@@ -280,6 +280,24 @@ in the rule; the lock catalog, fire steps, and failure modes live here.
|
|
|
280
280
|
contract — every re-evaluated lock gets a fresh, correctly-scoped
|
|
281
281
|
disposition, not a re-statement of the old one.
|
|
282
282
|
|
|
283
|
+
When the lock is an **ADR**, the disposition carries the five-field reopen
|
|
284
|
+
record from [`adr-layout § Reopen record`](../../../docs/contracts/adr-layout.md):
|
|
285
|
+
the original rationale **addressed** (not cited), what changed with tree
|
|
286
|
+
evidence, dependants and external commitments touched, the rollback path,
|
|
287
|
+
and a blast radius — `narrow | wide | irreversible` — with the evidence for
|
|
288
|
+
that call. Two clauses bind the same record: **precedent creates no
|
|
289
|
+
authority** (that a similar ADR was reopened is never grounds for this one),
|
|
290
|
+
and **one seat must argue the strongest case for keeping the decision**.
|
|
291
|
+
Both answer precedent laundering through correlated self-review, which is
|
|
292
|
+
the failure two independent council seats named for this mechanism on
|
|
293
|
+
2026-08-19: the proposer frames the evidence, correlated seats ratify, and
|
|
294
|
+
the amendment becomes authoritative input for the next review.
|
|
295
|
+
|
|
296
|
+
Proportionally: a narrow, reversible, bounded transition takes the light
|
|
297
|
+
path (record + council); `wide` or `irreversible` adds owner notification.
|
|
298
|
+
Identical ceremony for a typo-level amendment would cost more than asking
|
|
299
|
+
the owner and the mechanism would die of its own weight.
|
|
300
|
+
|
|
283
301
|
### When NOT to fire
|
|
284
302
|
|
|
285
303
|
- The blocked change has no real benefit — the gate is not a lever to
|
|
@@ -39,7 +39,7 @@ schema_version: 1
|
|
|
39
39
|
# CI guard: a release bump of `package.json` must update this value
|
|
40
40
|
# in lockstep — see scripts/check_template_pin_drift.ts (road-to-
|
|
41
41
|
# portable-runtime-and-update-check P3.3).
|
|
42
|
-
agent_config_version: "14.
|
|
42
|
+
agent_config_version: "14.6.0"
|
|
43
43
|
|
|
44
44
|
# --- Project identity ---
|
|
45
45
|
project:
|
|
@@ -213,6 +213,20 @@ var CAPSULE_SCHEMA_VERSION = 3;
|
|
|
213
213
|
function isShortLine(value, max) {
|
|
214
214
|
return typeof value === "string" && value.length > 0 && value.length <= max && !value.includes("\n");
|
|
215
215
|
}
|
|
216
|
+
function isPathRef(value) {
|
|
217
|
+
return typeof value === "string" && value.length > 0 && !/\s/.test(value);
|
|
218
|
+
}
|
|
219
|
+
function checkPathRefList(errors, field, value, max, required) {
|
|
220
|
+
checkList(errors, field, value, max, required);
|
|
221
|
+
if (!Array.isArray(value)) return;
|
|
222
|
+
if (value.some((x) => !isShortLine(x, max))) return;
|
|
223
|
+
const bad = value.filter((x) => !isPathRef(x));
|
|
224
|
+
if (bad.length === 0) return;
|
|
225
|
+
const shown = bad.slice(0, 3).map((x) => JSON.stringify(String(x))).join(", ");
|
|
226
|
+
errors.push(
|
|
227
|
+
`${field} carries ${bad.length} entr${bad.length === 1 ? "y" : "ies"} that are prose, not path refs: ${shown}${bad.length > 3 ? ", \u2026" : ""} \u2014 write the path (src/generated/api.ts, ../other-worktree/) or drop the entry; a sentence naming a file cannot be compared against a write target, and being comparable is the whole reason this is a field rather than a line of prose`
|
|
228
|
+
);
|
|
229
|
+
}
|
|
216
230
|
function checkList(errors, field, value, max, required) {
|
|
217
231
|
if (value === void 0) {
|
|
218
232
|
if (required) errors.push(`${field} is required`);
|
|
@@ -402,7 +416,7 @@ function validateRecycleEnvelope(input) {
|
|
|
402
416
|
checkList(errors, "decisions", e["decisions"], MAX_LINE_CHARS, false);
|
|
403
417
|
errors.push(...decisionTagErrors(e["decisions"]));
|
|
404
418
|
checkList(errors, "constraints", e["constraints"], MAX_LINE_CHARS, false);
|
|
405
|
-
|
|
419
|
+
checkPathRefList(errors, "do_not_touch", e["do_not_touch"], MAX_REF_CHARS, false);
|
|
406
420
|
checkList(errors, "open_worker_envelopes", e["open_worker_envelopes"], MAX_REF_CHARS, false);
|
|
407
421
|
checkList(errors, "artifact_paths", e["artifact_paths"], MAX_REF_CHARS, false);
|
|
408
422
|
checkList(errors, "suggested_skills", e["suggested_skills"], MAX_REF_CHARS, false);
|