@windyroad/architect 0.18.6 → 0.19.0-preview.927
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/plugin.json +1 -1
- package/.codex-plugin/plugin.json +1 -1
- package/hooks/hooks.json +4 -0
- package/hooks/quota-pace-throttle.sh +88 -0
- package/package.json +1 -1
- package/skills/capture-adr/REFERENCE.md +40 -56
- package/skills/capture-adr/SKILL.md +57 -50
- package/skills/capture-adr/test/capture-adr.bats +79 -55
- package/skills/create-adr/SKILL.md +2 -2
package/hooks/hooks.json
CHANGED
|
@@ -8,6 +8,10 @@
|
|
|
8
8
|
{ "hooks": [{ "type": "command", "command": "${CLAUDE_PLUGIN_ROOT}/hooks/staleness-check.sh" }] }
|
|
9
9
|
],
|
|
10
10
|
"PreToolUse": [
|
|
11
|
+
{
|
|
12
|
+
"matcher": "Bash|Write|Edit|Read|Glob|Grep|Task|WebFetch|WebSearch",
|
|
13
|
+
"hooks": [{ "type": "command", "command": "${CLAUDE_PLUGIN_ROOT}/hooks/quota-pace-throttle.sh" }]
|
|
14
|
+
},
|
|
11
15
|
{ "matcher": "Edit|Write", "hooks": [{ "type": "command", "command": "${CLAUDE_PLUGIN_ROOT}/hooks/architect-enforce-edit.sh" }] },
|
|
12
16
|
{ "matcher": "Edit|Write", "hooks": [{ "type": "command", "command": "${CLAUDE_PLUGIN_ROOT}/hooks/architect-oversight-marker-discipline.sh" }] },
|
|
13
17
|
{ "matcher": "ExitPlanMode", "hooks": [{ "type": "command", "command": "${CLAUDE_PLUGIN_ROOT}/hooks/architect-plan-enforce.sh" }] },
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# quota-pace-throttle.sh — PreToolUse hook (P160 / ADR-093 / RFC-046).
|
|
3
|
+
#
|
|
4
|
+
# Fires before every tool call across ALL work (interactive + AFK). Mechanically
|
|
5
|
+
# paces token burn so it never sprints into a mid-flight quota hard-stop: it
|
|
6
|
+
# compares cumulative window usage% against elapsed% and, when AHEAD of pace,
|
|
7
|
+
# sleeps the calculated catch-up time (capped per firing). When behind/on pace
|
|
8
|
+
# it is a fast no-op. It NEVER blocks, NEVER asks, and fails OPEN on every
|
|
9
|
+
# abnormal path — a broken throttle must not break the session.
|
|
10
|
+
#
|
|
11
|
+
# Data source: ~/.claude/quota-state.json, written by the statusline (the only
|
|
12
|
+
# surface Claude Code passes `.rate_limits` to). Fields:
|
|
13
|
+
# { "five_used_pct": N, "five_resets_at": <unix>,
|
|
14
|
+
# "week_used_pct": N, "week_resets_at": <unix> }
|
|
15
|
+
# If the cache is missing/stale/malformed, the hook no-ops (fail-open) — the
|
|
16
|
+
# statusline-writer install is a separate, user-consented step.
|
|
17
|
+
#
|
|
18
|
+
# ponytail: pure arithmetic + one sleep; the 60s/firing cap + fail-open keep the
|
|
19
|
+
# blast radius to "at worst a slightly-slower tool call", never a stall or block.
|
|
20
|
+
|
|
21
|
+
set +e
|
|
22
|
+
CACHE="${WR_QUOTA_CACHE:-${HOME}/.claude/quota-state.json}"
|
|
23
|
+
MARKER="${WR_QUOTA_MARKER:-${TMPDIR:-/tmp}/wr-quota-throttle-last}"
|
|
24
|
+
FIVE_WINDOW=18000 # 5h in seconds
|
|
25
|
+
WEEK_WINDOW=604800 # 7d in seconds
|
|
26
|
+
WEEK_HEADROOM_PP=5 # leave 5pp weekly headroom for non-Claude-Code surfaces
|
|
27
|
+
CAP_SECONDS=60 # max sleep per firing
|
|
28
|
+
|
|
29
|
+
emit_ok() { exit 0; } # PreToolUse: silent allow, never a permissionDecision deny
|
|
30
|
+
|
|
31
|
+
# Fail-open preconditions.
|
|
32
|
+
command -v jq >/dev/null 2>&1 || emit_ok
|
|
33
|
+
[ -r "$CACHE" ] || emit_ok
|
|
34
|
+
|
|
35
|
+
now=$(date +%s 2>/dev/null) || emit_ok
|
|
36
|
+
case "$now" in ''|*[!0-9]*) emit_ok;; esac
|
|
37
|
+
|
|
38
|
+
# Recent-check no-op: if we checked <5s ago, skip recompute (keeps per-call
|
|
39
|
+
# latency negligible when we're firing on every tool call).
|
|
40
|
+
if [ -f "$MARKER" ]; then
|
|
41
|
+
last=$(cat "$MARKER" 2>/dev/null)
|
|
42
|
+
case "$last" in
|
|
43
|
+
''|*[!0-9]*) : ;;
|
|
44
|
+
*) [ $(( now - last )) -lt 5 ] && emit_ok ;;
|
|
45
|
+
esac
|
|
46
|
+
fi
|
|
47
|
+
printf '%s' "$now" > "$MARKER" 2>/dev/null
|
|
48
|
+
|
|
49
|
+
# Read the cache; fail-open on any parse miss.
|
|
50
|
+
read -r five_used five_reset week_used week_reset < <(
|
|
51
|
+
jq -r '[.five_used_pct, .five_resets_at, .week_used_pct, .week_resets_at]
|
|
52
|
+
| map(tostring) | join(" ")' "$CACHE" 2>/dev/null
|
|
53
|
+
) || emit_ok
|
|
54
|
+
for v in "$five_used" "$five_reset" "$week_used" "$week_reset"; do
|
|
55
|
+
case "$v" in ''|null|*[!0-9.]*) emit_ok;; esac
|
|
56
|
+
done
|
|
57
|
+
|
|
58
|
+
# elapsed% of a window = (window - remaining) / window * 100, clamped [0,100].
|
|
59
|
+
elapsed_pct() { # resets_at window_secs
|
|
60
|
+
local remaining=$(( $1 - now )) win="$2" e
|
|
61
|
+
[ "$remaining" -lt 0 ] && remaining=0
|
|
62
|
+
[ "$remaining" -gt "$win" ] && remaining="$win"
|
|
63
|
+
e=$(( (win - remaining) * 100 / win ))
|
|
64
|
+
printf '%s' "$e"
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
# integer-truncate the used% (cache may carry a decimal)
|
|
68
|
+
five_used_i=${five_used%%.*}; week_used_i=${week_used%%.*}
|
|
69
|
+
five_elapsed=$(elapsed_pct "$five_reset" "$FIVE_WINDOW")
|
|
70
|
+
week_elapsed=$(elapsed_pct "$week_reset" "$WEEK_WINDOW")
|
|
71
|
+
|
|
72
|
+
# pace gap per window (positive = ahead of pace = burning too fast).
|
|
73
|
+
# weekly target leaves headroom, so effective gap = used - (elapsed - headroom).
|
|
74
|
+
gap5=$(( five_used_i - five_elapsed ))
|
|
75
|
+
gap7=$(( week_used_i - (week_elapsed - WEEK_HEADROOM_PP) ))
|
|
76
|
+
|
|
77
|
+
# Take the tighter (larger-gap) window and its window length.
|
|
78
|
+
if [ "$gap5" -ge "$gap7" ]; then gap="$gap5"; win="$FIVE_WINDOW"; else gap="$gap7"; win="$WEEK_WINDOW"; fi
|
|
79
|
+
[ "$gap" -le 0 ] && emit_ok # behind/on pace → fast no-op
|
|
80
|
+
|
|
81
|
+
# Catch-up sleep: elapsed% rises at 100/win per second, so closing `gap` pp of
|
|
82
|
+
# lead needs gap/100 * win seconds. Cap per firing so no single call stalls long.
|
|
83
|
+
sleep_secs=$(( gap * win / 100 ))
|
|
84
|
+
[ "$sleep_secs" -gt "$CAP_SECONDS" ] && sleep_secs="$CAP_SECONDS"
|
|
85
|
+
[ "$sleep_secs" -le 0 ] && emit_ok
|
|
86
|
+
|
|
87
|
+
sleep "$sleep_secs" 2>/dev/null
|
|
88
|
+
emit_ok
|
package/package.json
CHANGED
|
@@ -14,72 +14,55 @@ It is wrong for the **aside-invocation** use case. P156 surfaced three repeating
|
|
|
14
14
|
2. **Architect-review verdict capture**: a `wr-architect:agent` review yields a substantive verdict (PASS-WITH-NOTES / ISSUES-FOUND) whose rationale deserves an ADR-shaped record. Today the verdict + rationale lands in commit messages and rots — future readers grep history but lose the structured trace.
|
|
15
15
|
3. **User-driven design conversations**: user resolves options (a)/(b)/(c) during conversational work; the settlement currently lives in a problem-ticket RCA section instead of a discoverable ADR.
|
|
16
16
|
|
|
17
|
-
`/wr-architect:capture-adr` is the source-side fix: a lightweight skill
|
|
17
|
+
`/wr-architect:capture-adr` is the source-side fix: a lightweight skill that captures the decision in ~5-6 turns with full derived substance. "Lightweight" means zero-interaction + single-commit — not skimpy content.
|
|
18
18
|
|
|
19
|
-
##
|
|
20
|
-
|
|
21
|
-
### Skeleton-MADR validity at status `proposed`
|
|
22
|
-
|
|
23
|
-
Architect Q1 verdict (P156 review): the architect-agent's review prompt tolerates skeleton ADRs at `status: proposed`. It checks "does the proposed change conflict with the decision's outcome?" not "does every section have prose?" The deferred-flag pattern is the load-bearing signal that downstream tooling and any future canonical-expansion auto-detect path keys off (mirrors capture-problem's `(deferred — re-rate at next /wr-itil:review-problems)` literal).
|
|
19
|
+
## Derived-substance amendment (RFC-045 / P375, 2026-07-06)
|
|
24
20
|
|
|
25
|
-
|
|
21
|
+
The skill originally shipped (P156, 2026-05-03) with a **deferred-placeholder pattern**: every section the capture didn't fill carried the literal pointer string `(deferred to /wr-architect:create-adr canonical review)`, on the theory that a later canonical-expansion pass would fill them. That pattern failed the P375 rot test — a named re-entry point is not a self-firing cadence; nothing ever triggered the canonical review, the anticipated auto-detect-and-expand tooling was never built, and the sections rotted. User direction 2026-07-05: "It should capture it properly."
|
|
26
22
|
|
|
27
|
-
|
|
23
|
+
The amended contract (ADR-032 derived-substance amendment; same correction class as ADR-067's silent derivation of capture-problem ratings): **every MADR section is derived for real at capture time** — genuine Decision Drivers, ≥2 real Considered Options (chosen + actually-rejected alternatives), real Good/Neutral/Bad Consequences, testable Confirmation criteria, real Reassessment Criteria, derived decision-makers. No placeholder, pointer, or sentinel strings of any kind. The capturing agent has more decision context in-session than any later pass would; capture is the cheapest moment to write the substance down.
|
|
28
24
|
|
|
29
|
-
|
|
25
|
+
The derived substance is provisional. `human-oversight: unconfirmed` states that honestly, and the SessionStart oversight nudge → `/wr-architect:review-decisions` drain is the **self-firing** surface where a human ratifies or amends it (ADR-066). That is the only deferral the skill retains.
|
|
30
26
|
|
|
31
|
-
|
|
32
|
-
1. **Option A (chosen)** — <one-line summary>
|
|
33
|
-
2. (deferred — see /wr-architect:create-adr canonical review)
|
|
34
|
-
```
|
|
27
|
+
The architect Q-verdict subsections below record the original P156 trade-off analysis for history; where they describe placeholder/sentinel mechanics they are superseded by this amendment.
|
|
35
28
|
|
|
36
|
-
|
|
29
|
+
## Contract trade-offs
|
|
37
30
|
|
|
38
|
-
###
|
|
31
|
+
### Skeleton-MADR validity at status `proposed`
|
|
39
32
|
|
|
40
|
-
Architect
|
|
33
|
+
Architect Q1 verdict (P156 review, superseded 2026-07-06): the review prompt tolerates not-yet-accepted ADRs at `status: proposed` — it checks "does the proposed change conflict with the decision's outcome?". Under the derived-substance amendment the MADR ≥2-options requirement is satisfied at capture (real options are derived), so there is no skeleton state to cover; `status: proposed` now signals only "derived substance awaiting human ratification + acceptance review".
|
|
41
34
|
|
|
42
|
-
|
|
43
|
-
---
|
|
44
|
-
status: "proposed"
|
|
45
|
-
date: <YYYY-MM-DD>
|
|
46
|
-
decision-makers: [unspecified — fill at canonical review]
|
|
47
|
-
consulted: []
|
|
48
|
-
informed: []
|
|
49
|
-
reassessment-date: <YYYY-MM-DD + 3 months>
|
|
50
|
-
---
|
|
51
|
-
```
|
|
35
|
+
### Considered Options — real alternatives, derived
|
|
52
36
|
|
|
53
|
-
|
|
37
|
+
Architect Q2 verdict (P156, superseded 2026-07-06): the original numbered-placeholder sibling (`2. (deferred — see ...)`) existed only to satisfy ≥2-options lint. Under the derived-substance amendment the capture writes the chosen option PLUS every alternative actually weighed and rejected in the decision context — real options with one-line summaries. If the context genuinely weighed only one option, the capture derives the strongest status-quo/do-nothing alternative and says why it lost. Lint is satisfied by substance, not by a placeholder. Still no AskUserQuestion.
|
|
54
38
|
|
|
55
|
-
|
|
39
|
+
### Frontmatter sentinel values vs. truly minimal
|
|
56
40
|
|
|
57
|
-
|
|
41
|
+
Architect Q5 verdict (P156, superseded 2026-07-06): the original sentinel `decision-makers: [unspecified — fill at canonical review]` was another deferral marker. Under the derived-substance amendment frontmatter is derived: `decision-makers: [<git config user.name>]` plus any decision-owner named in `$ARGUMENTS`; `consulted`/`informed` from context or `[]`. `reassessment-date` defaults to 3 months from today (matches `create-adr` Step 4); the Reassessment Criteria body section carries real reopen conditions derived at capture.
|
|
58
42
|
|
|
59
|
-
|
|
43
|
+
### Deferred-ratification contract (was: deferred-canonical-expansion)
|
|
60
44
|
|
|
61
|
-
|
|
62
|
-
|---------|-------------------------------|----------------------------------|
|
|
63
|
-
| Architect-agent review at write-time | Yes (Step 5 confirm pass) | No (deferred to canonical expansion) |
|
|
64
|
-
| Capture-time turn cost | ~10-15 turns | ~3-4 turns |
|
|
65
|
-
| MADR conformance at write-time | Full | Skeleton (status: proposed covers) |
|
|
66
|
-
| Audit trail (commit) | One commit covers full ADR | One commit covers skeleton |
|
|
67
|
-
| Acceptance window | Same session | Bounded by next canonical-expansion invocation |
|
|
45
|
+
Capture-adr skips the interactive confirm-with-user pass that `/wr-architect:create-adr` Step 5 performs. The trade-off under the derived-substance amendment:
|
|
68
46
|
|
|
69
|
-
|
|
47
|
+
| Surface | Inline intake (create-adr) | Capture (capture-adr) |
|
|
48
|
+
|---------|----------------------------|------------------------|
|
|
49
|
+
| Section substance at write-time | User-authored via AskUserQuestion | Agent-derived from decision context |
|
|
50
|
+
| Human ratification | Step 5 confirm pass, same session | `/wr-architect:review-decisions` drain, next interactive session |
|
|
51
|
+
| Ratification trigger | In-flow | Self-firing SessionStart oversight nudge (`human-oversight: unconfirmed`) |
|
|
52
|
+
| Capture-time turn cost | ~10-15 turns | ~5-6 turns |
|
|
53
|
+
| MADR conformance at write-time | Full | Full (derived) |
|
|
54
|
+
| Audit trail (commit) | One commit covers full ADR | One commit covers full derived ADR |
|
|
70
55
|
|
|
71
|
-
|
|
72
|
-
2. **The trailing pointer in Step 6 is the user-visible signal** that canonical expansion is needed. The user has explicit instructions for how to reconcile.
|
|
73
|
-
3. **Auto-detect-and-expand is a follow-up** (Q3 verdict — out of scope for P156). When `/wr-architect:create-adr` is later invoked on a captured `<NNN>`, it can detect the existing skeleton and expand the deferred sections rather than writing a new ADR. P156 does not ship this auto-detect path; the manual workflow is `/wr-architect:create-adr` invoked with the captured ID + body context.
|
|
56
|
+
The contract passes the P375 rot test because the ratification path starts from a self-firing trigger (the SessionStart nudge), not a named on-demand skill. The pre-amendment version failed that test: expansion depended on someone remembering to run `/wr-architect:create-adr <NNN>`, and nothing ever fired it.
|
|
74
57
|
|
|
75
58
|
### No AskUserQuestion at all
|
|
76
59
|
|
|
77
60
|
Architect Q4 + JTBD review confirmed: capture-adr is a **mechanical-stage skill** per ADR-044's framework-resolution boundary. Every potentially-interactive decision is framework-mediated:
|
|
78
61
|
|
|
79
|
-
- **Considered Options**:
|
|
80
|
-
- **Decision Drivers / Consequences / Confirmation**:
|
|
81
|
-
- **Reassessment date**: framework-policy default 3 months from today.
|
|
82
|
-
- **Decision-makers / consulted / informed**:
|
|
62
|
+
- **Considered Options**: silent derivation of chosen + actually-rejected alternatives (≥2 real options at capture).
|
|
63
|
+
- **Decision Drivers / Consequences / Confirmation**: silent derivation of real content from the decision context.
|
|
64
|
+
- **Reassessment date**: framework-policy default 3 months from today; criteria derived.
|
|
65
|
+
- **Decision-makers / consulted / informed**: derived from git `user.name` + context — never a sentinel.
|
|
83
66
|
- **Multi-decision split**: out of scope. The user invoking capture-adr with a multi-decision payload gets one ADR with the full payload; they re-route to `/wr-architect:create-adr` for the structured Step 2b decision-boundary split.
|
|
84
67
|
|
|
85
68
|
This mirrors the mechanical-stage carve-out pattern documented in CLAUDE.md (P132 / inverse-P078 trap): when a SKILL contract names a stage as mechanical, do not ask. Per-action consent gates re-ask decisions the user already made and silently undo the load-bearing UX investment.
|
|
@@ -94,9 +77,9 @@ AFK orchestrators MUST NOT invoke capture-adr with empty arguments — caller-si
|
|
|
94
77
|
|
|
95
78
|
### Partial `$ARGUMENTS` (Title only / Title + Decision)
|
|
96
79
|
|
|
97
|
-
If only Title is supplied,
|
|
80
|
+
If only Title is supplied, derive Context + Decision from the invoking session's decision context. If Title + Decision (no Context), derive Context. Derivation is real prose from the context at hand — never a placeholder (RFC-045).
|
|
98
81
|
|
|
99
|
-
This is a graceful-degradation case — real captures carry Title + Context + Decision — but the partial-payload path prevents a halt when only some
|
|
82
|
+
This is a graceful-degradation case — real captures carry Title + Context + Decision — but the partial-payload path prevents a halt when only some of the payload is spelled out.
|
|
100
83
|
|
|
101
84
|
### Title slug collision
|
|
102
85
|
|
|
@@ -110,34 +93,34 @@ If the local session has not fetched recently and origin has captures the local
|
|
|
110
93
|
|
|
111
94
|
`--name-only` is required (P056): without it, default `git ls-tree` output carries the 40-char blob SHA which can contain three-digit runs that the digit-extraction regex false-matches. Same fix as create-adr Step 3 / manage-problem Step 3.
|
|
112
95
|
|
|
113
|
-
### Captured ADR never
|
|
96
|
+
### Captured ADR never ratified
|
|
114
97
|
|
|
115
|
-
If the user captures and never
|
|
98
|
+
If the user captures and never ratifies, the `.proposed.md` ADR remains `human-oversight: unconfirmed` — and the SessionStart oversight nudge re-surfaces it every session until the `/wr-architect:review-decisions` drain handles it. Unlike the pre-RFC-045 never-expanded failure mode, this state cannot rot silently: the nudge is self-firing, and the sections already carry real (if unratified) substance.
|
|
116
99
|
|
|
117
100
|
### Architect-review verdict capture
|
|
118
101
|
|
|
119
102
|
Use case: a `wr-architect:agent` review yields PASS-WITH-NOTES with substantive rationale. Pattern:
|
|
120
103
|
|
|
121
104
|
1. User invokes capture-adr with `$ARGUMENTS = "Title from review topic\nContext: review of <change>\nDecision: <one-line verdict + rationale>"`.
|
|
122
|
-
2.
|
|
123
|
-
3. Trailing pointer
|
|
124
|
-
4.
|
|
105
|
+
2. The ADR lands at `docs/decisions/<NNN>-<kebab-title>.proposed.md` with status `proposed`, all sections derived — Considered Options carries the alternatives the architect actually weighed, Consequences the trade-offs from the verdict rationale.
|
|
106
|
+
3. Trailing pointer notes the ADR awaits ratification at the oversight drain.
|
|
107
|
+
4. The SessionStart nudge surfaces it; the user ratifies or amends at `/wr-architect:review-decisions`.
|
|
125
108
|
|
|
126
109
|
This pattern preserves architect-review verdicts as first-class ADR-shaped records instead of letting them rot in commit-message bodies.
|
|
127
110
|
|
|
128
111
|
### Cross-namespace consistency with capture-problem
|
|
129
112
|
|
|
130
|
-
The `capture-` verb is consistent across `/wr-itil:capture-problem` and `/wr-architect:capture-adr`. Same dispatch shape (
|
|
113
|
+
The `capture-` verb is consistent across `/wr-itil:capture-problem` and `/wr-architect:capture-adr`. Same dispatch shape, same derive-real-values-at-capture discipline (ADR-067 for capture-problem ratings; RFC-045 for capture-adr sections), same single-commit-per-capture grain, same trailing-pointer signal. Users learn one mental model that spans both. ADR-032 amendment names this symmetry.
|
|
131
114
|
|
|
132
115
|
## Composition with the rest of the suite
|
|
133
116
|
|
|
134
117
|
### `/wr-architect:create-adr`
|
|
135
118
|
|
|
136
|
-
Heavyweight intake counterpart. The two skills share the `docs/decisions/*.proposed.md` directory and the next-ID formula. Cross-skill ordering: capture-adr writes a
|
|
119
|
+
Heavyweight intake counterpart. The two skills share the `docs/decisions/*.proposed.md` directory and the next-ID formula. Cross-skill ordering: capture-adr writes a fully-derived ADR at `<NNN>`; `/wr-architect:create-adr <NNN>` (or `/wr-architect:review-decisions`) is the human-ratification/acceptance surface, not an expansion surface — there are no deferred sections to expand post-RFC-045.
|
|
137
120
|
|
|
138
121
|
### `wr-architect:agent`
|
|
139
122
|
|
|
140
|
-
The review surface that processes ADR review delegations. capture-adr does not invoke the architect-agent inline;
|
|
123
|
+
The review surface that processes ADR review delegations. capture-adr does not invoke the architect-agent inline; review fires at acceptance (via `/wr-architect:create-adr`'s Step 5 confirm pass or direct delegation). The architect-agent reviewing a captured `.proposed.md` sees `status: proposed` + `human-oversight: unconfirmed` and treats it as a not-yet-accepted, not-yet-ratified ADR; reviews focus on whether the captured Decision conflicts with existing accepted ADRs.
|
|
141
124
|
|
|
142
125
|
### `/wr-itil:manage-problem` / `/wr-itil:capture-problem`
|
|
143
126
|
|
|
@@ -157,7 +140,7 @@ The intended invocation surface is `/wr-architect:capture-adr <Title>\n<Context>
|
|
|
157
140
|
- **ADR-013** — structured user interaction (Rule 6 fail-safe; capture-adr has no AskUserQuestion branches so Rule 6 is trivially satisfied).
|
|
158
141
|
- **ADR-014** — governance skills commit their own work (capture-adr owns its commit).
|
|
159
142
|
- **ADR-019** — AFK orchestrator preflight (next-ID formula uses origin-tracking ref per ADR-019 confirmation criterion 2).
|
|
160
|
-
- **ADR-032** — governance skill invocation patterns (this skill's parent ADR; foreground-lightweight-capture variant amendment 2026-05-03
|
|
143
|
+
- **ADR-032** — governance skill invocation patterns (this skill's parent ADR; foreground-lightweight-capture variant amendment 2026-05-03; derived-substance amendment 2026-07-06 per RFC-045).
|
|
161
144
|
- **ADR-038** — progressive disclosure (SKILL.md + REFERENCE.md split shape).
|
|
162
145
|
- **ADR-044** — decision-delegation contract (framework-mediated mechanical-stage carve-outs).
|
|
163
146
|
- **ADR-049** — bin/ on PATH (capture-adr is self-contained; no new shim required, same as create-adr).
|
|
@@ -173,3 +156,4 @@ The intended invocation surface is `/wr-architect:capture-adr <Title>\n<Context>
|
|
|
173
156
|
- **P157** — sibling pending-questions-surface hook.
|
|
174
157
|
- **P056** — ticket-creator next-ID lookup blob-SHA false-match (capture-adr's next-ID formula uses the `--name-only` fix).
|
|
175
158
|
- **P040** — origin-collision incident referenced in Edge cases.
|
|
159
|
+
- **P375** — named re-entry point is not a self-firing cadence; drove the RFC-045 derived-substance amendment.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: wr-architect:capture-adr
|
|
3
|
-
description: Lightweight ADR-capture skill for aside-invocation during foreground work —
|
|
3
|
+
description: Lightweight ADR-capture skill for aside-invocation during foreground work — derives full MADR substance (Considered Options / Decision Drivers / Consequences / Confirmation / Reassessment) silently from the in-session decision context, single commit, no inline architect-review handoff, no AskUserQuestion. Lightweight means zero-interaction + single-commit, not skimpy content (RFC-045). Use this when the user (or agent mid-iter) wants to record a decision quickly without the ~10-15 turn ceremony of /wr-architect:create-adr. For interactive full-intake new ADR creation where the user authors the options + drivers + consequences + confirmation, use /wr-architect:create-adr.
|
|
4
4
|
allowed-tools: Read, Write, Edit, Bash, Grep, Glob
|
|
5
5
|
---
|
|
6
6
|
|
|
@@ -17,9 +17,9 @@ This skill is the foreground-lightweight-capture variant of `/wr-architect:creat
|
|
|
17
17
|
- **User-driven design conversations**: user resolves options (a)/(b)/(c) during conversational work; today the settlement gets buried in a problem-ticket RCA section instead of codified.
|
|
18
18
|
|
|
19
19
|
**Use `/wr-architect:create-adr` instead** when:
|
|
20
|
-
- The user wants to walk the full intake flow (Considered Options ≥2, Decision Drivers, full Consequences, Confirmation criteria, Pros/Cons of Options).
|
|
21
|
-
- The decision is
|
|
22
|
-
- The decision needs immediate architect review + acceptance (capture-adr writes `.proposed.md`;
|
|
20
|
+
- The user wants to walk the full interactive intake flow and author the sections themselves (Considered Options ≥2, Decision Drivers, full Consequences, Confirmation criteria, Pros/Cons of Options via AskUserQuestion).
|
|
21
|
+
- The decision is contested or under-specified in-session — silent derivation needs a real decision context to derive FROM; if the options were never actually weighed, create-adr's interactive intake is the honest surface.
|
|
22
|
+
- The decision needs immediate architect review + acceptance (capture-adr writes `.proposed.md`; acceptance review is a follow-up via `/wr-architect:create-adr` or direct architect-agent review).
|
|
23
23
|
|
|
24
24
|
## Rule 6 audit (per ADR-032 + ADR-013)
|
|
25
25
|
|
|
@@ -27,14 +27,16 @@ This skill has **zero AskUserQuestion branches** by design. Each potentially-int
|
|
|
27
27
|
|
|
28
28
|
| Decision | Resolution |
|
|
29
29
|
|----------|-----------|
|
|
30
|
-
| Considered Options ≥2 |
|
|
31
|
-
| Decision drivers |
|
|
32
|
-
| Consequences |
|
|
33
|
-
| Confirmation criteria |
|
|
34
|
-
| Reassessment criteria |
|
|
35
|
-
| Decision-makers / consulted / informed |
|
|
30
|
+
| Considered Options ≥2 | Silent derivation (ADR-044 category-4): write the chosen option PLUS every alternative that was actually weighed and rejected in the decision context (`$ARGUMENTS` + the invoking session). Real options with one-line summaries — never a placeholder sibling. If the context genuinely weighed only one option, derive the strongest do-nothing / status-quo alternative and say why it lost. |
|
|
31
|
+
| Decision drivers | Silent derivation: extract the forces that actually drove the decision from the context (the problem's symptoms, the constraint that ruled options out, the user's stated priorities). |
|
|
32
|
+
| Consequences | Silent derivation: real Good/Neutral/Bad trade-off analysis of the chosen option. The invoking agent performs the analysis at capture — it has more decision context in-session than any later expansion pass would. |
|
|
33
|
+
| Confirmation criteria | Silent derivation: testable criteria (a command, an observable behaviour, a hook that fires) confirming the decision is implemented and holding. |
|
|
34
|
+
| Reassessment criteria | Silent derivation: real conditions that would reopen the decision, plus `reassessment-date` default 3 months from today (matches `create-adr` Step 4 default). |
|
|
35
|
+
| Decision-makers / consulted / informed | Silent derivation: `decision-makers: [<git config user.name>]` plus any decision-owner named in `$ARGUMENTS`; `consulted`/`informed` from context or `[]`. Never a sentinel string. |
|
|
36
36
|
| Empty `$ARGUMENTS` | Halt-with-stderr-directive: print "capture-adr requires Title + 1-line Context + 1-line Decision in $ARGUMENTS — invoke /wr-architect:create-adr instead for the full intake flow" and exit. AFK orchestrators MUST NOT invoke capture-adr with empty arguments — caller-side contract. |
|
|
37
37
|
|
|
38
|
+
**No placeholder, pointer, or sentinel strings of any kind** (RFC-045 / P375, derived-substance amendment to ADR-032 2026-07-06). The previous deferred-placeholder pattern (`(deferred to /wr-architect:create-adr canonical review)`) named a re-entry point nothing self-firing ever triggered — the sections rotted. Same correction class as ADR-067's silent derivation of capture-problem ratings: derive a real value, always. The derived substance is provisional — that is what `human-oversight: unconfirmed` states honestly, and the SessionStart oversight nudge → `/wr-architect:review-decisions` drain is the self-firing surface where a human ratifies or amends it (ADR-066).
|
|
39
|
+
|
|
38
40
|
Per ADR-013 Rule 6 fail-safe: every branch above resolves without user input, so AFK and interactive contexts behave identically.
|
|
39
41
|
|
|
40
42
|
## Steps
|
|
@@ -43,7 +45,7 @@ Per ADR-013 Rule 6 fail-safe: every branch above resolves without user input, so
|
|
|
43
45
|
|
|
44
46
|
The expected `$ARGUMENTS` shape is free-text describing **(a) Title**, **(b) one-line Context** (the problem being solved), and **(c) one-line Decision** (the chosen option in one sentence).
|
|
45
47
|
|
|
46
|
-
Parsing heuristic: split on the first newline (Title) and second newline (Context); the rest is Decision. If only one line is supplied, treat it as Title and
|
|
48
|
+
Parsing heuristic: split on the first newline (Title) and second newline (Context); the rest is Decision. If only one line is supplied, treat it as Title and derive Context + Decision from the invoking session's decision context. If two lines, treat as Title + Decision and derive Context. Derivation is real prose from the context at hand — never a placeholder.
|
|
47
49
|
|
|
48
50
|
Empty `$ARGUMENTS` halts per the Rule 6 audit above.
|
|
49
51
|
|
|
@@ -60,10 +62,10 @@ ADR titles must name the **decision outcome** as a short noun phrase, not the qu
|
|
|
60
62
|
In `capture-adr` the chosen Decision is pinned in `$ARGUMENTS` at invocation, so the caller SHOULD supply a Title already in outcome shape — the framework does not retitle here (the canonical-outcome short-name is the caller's to author, not the framework's to derive). If the parsed Title slug matches a question-shape pattern (`-vs-`, `should-`, `whether-`, `-or-`), emit an advisory in the I2-isomorphic shape via the shared `emit_stderr_advisory` helper:
|
|
61
63
|
|
|
62
64
|
```
|
|
63
|
-
capture-adr: derived title='<slug>' from $ARGUMENTS — slug appears question-shaped (matched <pattern>); the Decision is pinned in $ARGUMENTS so an outcome-shaped Title is recommended; re-invoke with an outcome-shaped Title or rename the file
|
|
65
|
+
capture-adr: derived title='<slug>' from $ARGUMENTS — slug appears question-shaped (matched <pattern>); the Decision is pinned in $ARGUMENTS so an outcome-shaped Title is recommended; re-invoke with an outcome-shaped Title or rename the file before acceptance review.
|
|
64
66
|
```
|
|
65
67
|
|
|
66
|
-
The advisory is **advisory-only** (no halt, no retitle). The caller may proceed with the question-shaped slug if they choose;
|
|
68
|
+
The advisory is **advisory-only** (no halt, no retitle). The caller may proceed with the question-shaped slug if they choose; a subsequent `/wr-architect:create-adr <NNN>` acceptance pass picks up the retitle in its Step 5a mechanical retitle-after-decision check.
|
|
67
69
|
|
|
68
70
|
(Serves JTBD-001 — skimmable titles on the on-disk record; ADR-044 category-4 silent-framework — advisory, not ask.)
|
|
69
71
|
|
|
@@ -81,76 +83,81 @@ next=$(printf '%03d' $(( 10#$(echo -e "${local_max:-0}\n${origin_max:-0}" | sort
|
|
|
81
83
|
|
|
82
84
|
Log the renumber decision in the operation report if origin and local diverged.
|
|
83
85
|
|
|
84
|
-
### 3.
|
|
86
|
+
### 3. Derive-fill the MADR template
|
|
85
87
|
|
|
86
88
|
**File path**: `docs/decisions/<NNN>-<kebab-title>.proposed.md`
|
|
87
89
|
|
|
88
|
-
**Template** (
|
|
90
|
+
**Template** (derived-substance pattern per RFC-045 — every section carries real content derived from `$ARGUMENTS` + the invoking session's decision context; no placeholder, pointer, or sentinel strings of any kind):
|
|
89
91
|
|
|
90
92
|
```markdown
|
|
91
93
|
---
|
|
92
94
|
status: "proposed"
|
|
93
95
|
date: <YYYY-MM-DD>
|
|
94
96
|
human-oversight: unconfirmed
|
|
95
|
-
decision-makers: [
|
|
96
|
-
consulted: []
|
|
97
|
-
informed: []
|
|
97
|
+
decision-makers: [<git config user.name>, <any decision-owner named in $ARGUMENTS>]
|
|
98
|
+
consulted: [<from context, or empty>]
|
|
99
|
+
informed: [<from context, or empty>]
|
|
98
100
|
reassessment-date: <YYYY-MM-DD + 3 months>
|
|
99
101
|
---
|
|
100
102
|
|
|
101
103
|
# <Title>
|
|
102
104
|
|
|
103
|
-
> Captured via /wr-architect:capture-adr (foreground-lightweight aside-invocation per ADR-032
|
|
105
|
+
> Captured via /wr-architect:capture-adr (foreground-lightweight aside-invocation per ADR-032, derived-substance amendment 2026-07-06 / RFC-045). Section content was derived by the capturing agent from the in-session decision context; human-oversight: unconfirmed until ratified at the /wr-architect:review-decisions drain.
|
|
104
106
|
|
|
105
107
|
## Context and Problem Statement
|
|
106
108
|
|
|
107
|
-
<
|
|
109
|
+
<Context from $ARGUMENTS, expanded with the problem the session was actually solving when the decision arose>
|
|
108
110
|
|
|
109
111
|
## Decision Drivers
|
|
110
112
|
|
|
111
|
-
-
|
|
113
|
+
- <the real forces that drove the decision: symptoms, constraints, stated priorities — one bullet each>
|
|
112
114
|
|
|
113
115
|
## Considered Options
|
|
114
116
|
|
|
115
|
-
1.
|
|
116
|
-
2.
|
|
117
|
+
1. **<Chosen option> (chosen)** — <one-line summary>
|
|
118
|
+
2. **<Rejected alternative actually weighed in context>** — <one-line summary>
|
|
119
|
+
<further alternatives if the context weighed them; if only one option was ever on the table, the status-quo/do-nothing alternative and why it lost>
|
|
117
120
|
|
|
118
121
|
## Decision Outcome
|
|
119
122
|
|
|
120
|
-
Chosen option: **"
|
|
123
|
+
Chosen option: **"<Chosen option>"**, because <the real reason from $ARGUMENTS/context>.
|
|
121
124
|
|
|
122
125
|
## Consequences
|
|
123
126
|
|
|
124
127
|
### Good
|
|
125
128
|
|
|
126
|
-
-
|
|
129
|
+
- <real benefits of the chosen option>
|
|
127
130
|
|
|
128
131
|
### Neutral
|
|
129
132
|
|
|
130
|
-
-
|
|
133
|
+
- <real neutral effects, or omit bullets that would be filler>
|
|
131
134
|
|
|
132
135
|
### Bad
|
|
133
136
|
|
|
134
|
-
-
|
|
137
|
+
- <real costs/risks accepted by choosing this option>
|
|
135
138
|
|
|
136
139
|
## Confirmation
|
|
137
140
|
|
|
138
|
-
|
|
141
|
+
<testable criteria: a command, an observable behaviour, a gate that fires — how a future reader verifies the decision is implemented and holding>
|
|
139
142
|
|
|
140
143
|
## Pros and Cons of the Options
|
|
141
144
|
|
|
142
|
-
###
|
|
145
|
+
### <Chosen option>
|
|
146
|
+
|
|
147
|
+
- Good, because <...>
|
|
148
|
+
- Bad, because <...>
|
|
143
149
|
|
|
144
|
-
|
|
150
|
+
### <Rejected alternative>
|
|
151
|
+
|
|
152
|
+
- Good, because <...>
|
|
153
|
+
- Bad, because <...>
|
|
145
154
|
|
|
146
155
|
## Reassessment Criteria
|
|
147
156
|
|
|
148
|
-
|
|
157
|
+
<real conditions that would reopen this decision — e.g. the constraint that forced it lifts, the chosen mechanism's false-positive rate exceeds tolerance>
|
|
149
158
|
```
|
|
150
159
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
The numbered-options placeholder (`1. Option A (chosen) ...` + `2. (deferred ...)`) preserves the MADR ≥2-options surface for any doc-lint that asserts numbered-option presence; status `proposed` covers the skeleton state for canonical-acceptance review.
|
|
160
|
+
Content-quality bar: each section must be true to the decision context at hand, not boilerplate. A section the context genuinely gives nothing for gets the honest one-liner saying so in real prose (e.g. "No neutral consequences identified at capture") — never a deferral marker. The capturing agent has more decision context in-session than any later pass will; capture is the cheapest moment to write the substance down.
|
|
154
161
|
|
|
155
162
|
### 4. Write the file
|
|
156
163
|
|
|
@@ -158,7 +165,7 @@ Single `Write` to `docs/decisions/<NNN>-<kebab-title>.proposed.md`.
|
|
|
158
165
|
|
|
159
166
|
### 4.5. Refresh the decisions compendium (ADR-077)
|
|
160
167
|
|
|
161
|
-
After the ADR
|
|
168
|
+
After the ADR lands, regenerate `docs/decisions/README.md` so the architect-agent routine load surface includes the new entry:
|
|
162
169
|
|
|
163
170
|
```bash
|
|
164
171
|
wr-architect-generate-decisions-compendium
|
|
@@ -185,39 +192,39 @@ Commit message:
|
|
|
185
192
|
docs(decisions): capture ADR-<NNN> <title>
|
|
186
193
|
```
|
|
187
194
|
|
|
188
|
-
The `capture` verb is the audit signal that this ADR landed via the lightweight aside path (vs. `add` / `accept` for canonical create-adr's full intake). The status remains `proposed` until
|
|
195
|
+
The `capture` verb is the audit signal that this ADR landed via the lightweight aside path (vs. `add` / `accept` for canonical create-adr's full intake). The status remains `proposed` until acceptance review (with human substance-ratification per ADR-064) promotes it.
|
|
189
196
|
|
|
190
197
|
### 6. Report
|
|
191
198
|
|
|
192
199
|
After the commit, report:
|
|
193
200
|
|
|
194
201
|
- The new ADR file path and ID.
|
|
195
|
-
- Trailing pointer: `
|
|
202
|
+
- Trailing pointer: `ADR-<NNN> was captured with derived substance and is human-oversight: unconfirmed — the SessionStart oversight nudge will surface it for ratification at /wr-architect:review-decisions (or ratify now if interactive).`
|
|
196
203
|
- Note any renumber-from-origin-collision log line from Step 2.
|
|
197
204
|
|
|
198
|
-
The trailing pointer is **not optional** — it is the user-visible signal that the
|
|
205
|
+
The trailing pointer is **not optional** — it is the user-visible signal that the derived substance awaits human ratification. Unlike the pre-RFC-045 contract there is no expansion step: the sections are already real; the drain confirms or amends them.
|
|
199
206
|
|
|
200
|
-
**Confirm-every-ADR gate (ADR-064):** a capture-adr
|
|
207
|
+
**Confirm-every-ADR gate (ADR-064):** a capture-adr ADR is recorded `proposed` with derived substance but WITHOUT human review of that substance. It must NOT be promoted to `accepted` until a human has ratified the derived content via `/wr-architect:review-decisions` (or a `/wr-architect:create-adr` review-and-confirm pass). Capture records the decision quickly; the ratification — not the capture — is what gives it human oversight. This is prong 1 of P283 (lift auto-/quick-recorded decisions to human-confirmed before they stand).
|
|
201
208
|
|
|
202
|
-
**Oversight marker discipline (ADR-066 amendment 2026-06-02 / P348).** A capture-adr
|
|
209
|
+
**Oversight marker discipline (ADR-066 amendment 2026-06-02 / P348).** A capture-adr ADR MUST be born `human-oversight: unconfirmed` — NOT `confirmed`. Capture is the AFK-friendly aside surface; there is no substance-confirm `AskUserQuestion` pass in this flow, so `confirmed` would be a hollow marker (the P348 bug class). The `architect-oversight-marker-discipline.sh` PreToolUse hook will DENY any Edit/Write that introduces `human-oversight: confirmed` without a matching session-scoped evidence marker. The frontmatter (Step 3 above) MUST include `human-oversight: unconfirmed` so the ADR enters the world honestly self-identified as needing user confirmation. The drain (`/wr-architect:review-decisions`) and a `/wr-architect:create-adr <NNN>` review pass are the surfaces that legitimately promote it to `confirmed` via `wr-architect-mark-oversight-confirmed` + the gated marker write.
|
|
203
210
|
|
|
204
211
|
## Composition with create-adr
|
|
205
212
|
|
|
206
213
|
| Concern | create-adr | capture-adr |
|
|
207
214
|
|---------|------------|-------------|
|
|
208
|
-
| Considered Options | AskUserQuestion gathering ≥2 options + pros/cons |
|
|
209
|
-
| Decision Drivers | AskUserQuestion gathering |
|
|
210
|
-
| Consequences | AskUserQuestion gathering Good/Neutral/Bad |
|
|
211
|
-
| Confirmation | AskUserQuestion gathering testable criteria |
|
|
212
|
-
| Reassessment criteria | AskUserQuestion gathering | 3-month default date
|
|
213
|
-
| Frontmatter | Full populated frontmatter |
|
|
215
|
+
| Considered Options | AskUserQuestion gathering ≥2 options + pros/cons (user authors) | Silent derivation of chosen + actually-rejected alternatives (agent authors, human ratifies at drain) |
|
|
216
|
+
| Decision Drivers | AskUserQuestion gathering | Silent derivation from decision context |
|
|
217
|
+
| Consequences | AskUserQuestion gathering Good/Neutral/Bad | Silent derivation — real Good/Neutral/Bad trade-off analysis at capture |
|
|
218
|
+
| Confirmation | AskUserQuestion gathering testable criteria | Silent derivation of testable criteria |
|
|
219
|
+
| Reassessment criteria | AskUserQuestion gathering | Silent derivation + 3-month default date |
|
|
220
|
+
| Frontmatter | Full populated frontmatter | Derived values (git user.name etc.) — no sentinels |
|
|
214
221
|
| Decision-boundary check (Step 2b) | Multi-decision split via AskUserQuestion | Out of scope (one ADR per invocation) |
|
|
215
222
|
| Supersession (Step 6) | Handles `git mv .accepted.md → .superseded.md` | Out of scope (capture is creation only) |
|
|
216
|
-
| Confirm-with-user (Step 5) | AskUserQuestion review pass |
|
|
223
|
+
| Confirm-with-user (Step 5) | AskUserQuestion review pass at intake | Deferred to the self-firing oversight drain (`human-oversight: unconfirmed` → SessionStart nudge → `/wr-architect:review-decisions`) |
|
|
217
224
|
| Commit grain | One commit per intake | One commit per capture |
|
|
218
|
-
| Use case | Full-intake new ADR; user wants to
|
|
225
|
+
| Use case | Full-intake new ADR; user wants to author the flow interactively | Aside-invocation; capture-and-continue |
|
|
219
226
|
|
|
220
|
-
The two skills share the `docs/decisions/*.proposed.md` directory and the next-ID formula.
|
|
227
|
+
The two skills share the `docs/decisions/*.proposed.md` directory and the next-ID formula. Category-1 reconciliation (per the ADR-032 derived-substance amendment): create-adr's Step 2 dispatch table classifies Drivers/Options/Consequences/Confirmation as category-1 "only the user knows" — that governs the interactive intake surface. On the capture surface the same fields are derived-provisional substance under `human-oversight: unconfirmed`, ratified at the ADR-066 drain (the ADR-067 lift-auto-decisions-to-human pattern). Capture derives-then-ratifies; create-adr asks-then-records.
|
|
221
228
|
|
|
222
229
|
## Related
|
|
223
230
|
|
|
@@ -231,6 +238,6 @@ The two skills share the `docs/decisions/*.proposed.md` directory and the next-I
|
|
|
231
238
|
- **ADR-049** — bin/ on PATH (capture-adr is self-contained; no shim required, same as create-adr).
|
|
232
239
|
- **ADR-052** — behavioural-tests-default for skill testing.
|
|
233
240
|
- `packages/architect/skills/create-adr/SKILL.md` — heavyweight intake counterpart.
|
|
234
|
-
- `packages/architect/agents/agent.md` — wr-architect:agent review surface; reviews `.proposed.md`
|
|
241
|
+
- `packages/architect/agents/agent.md` — wr-architect:agent review surface; reviews `.proposed.md` ADRs at acceptance delegation.
|
|
235
242
|
|
|
236
243
|
$ARGUMENTS
|
|
@@ -11,15 +11,16 @@
|
|
|
11
11
|
# fixture decisions directory and asserts the computed next ID
|
|
12
12
|
# matches the expected zero-padded value (including the empty-dir
|
|
13
13
|
# first-ADR base case).
|
|
14
|
-
# 2.
|
|
15
|
-
#
|
|
16
|
-
#
|
|
17
|
-
#
|
|
18
|
-
#
|
|
14
|
+
# 2. Derive-fill MADR shape (RFC-045 derived-substance amendment) —
|
|
15
|
+
# captured ADR has Title + status proposed + human-oversight:
|
|
16
|
+
# unconfirmed + real derived content in every section, and matches
|
|
17
|
+
# NOTHING in the shared deferral-marker vocabulary
|
|
18
|
+
# (DEFERRAL_MARKER_RE, packages/retrospective/hooks/lib/
|
|
19
|
+
# deferral-markers.sh) — no placeholder/pointer/sentinel of any kind.
|
|
19
20
|
# 3. Default reassessment-date — 3 months from today is computed
|
|
20
21
|
# correctly and lands in frontmatter.
|
|
21
|
-
# 4. Frontmatter
|
|
22
|
-
#
|
|
22
|
+
# 4. Frontmatter derived values — decision-makers carries a real name
|
|
23
|
+
# (git user.name), never a sentinel.
|
|
23
24
|
#
|
|
24
25
|
# Structural assertions are limited to existence/wiring (file presence +
|
|
25
26
|
# frontmatter name + allowed-tools surface) per the precedent set by the
|
|
@@ -143,22 +144,28 @@ teardown() {
|
|
|
143
144
|
}
|
|
144
145
|
|
|
145
146
|
# ---------------------------------------------------------------------------
|
|
146
|
-
#
|
|
147
|
-
# at status: proposed. Load-bearing primitives:
|
|
147
|
+
# Derive-fill MADR shape (RFC-045 / P375) — capture-adr writes a fully-
|
|
148
|
+
# derived ADR at status: proposed. Load-bearing primitives:
|
|
148
149
|
# - Title at H1
|
|
149
|
-
# - status: proposed in frontmatter
|
|
150
|
-
# - decision-makers sentinel
|
|
150
|
+
# - status: proposed + human-oversight: unconfirmed in frontmatter
|
|
151
|
+
# - decision-makers derived (real name, no sentinel)
|
|
151
152
|
# - reassessment-date 3 months from today
|
|
152
|
-
# -
|
|
153
|
-
#
|
|
154
|
-
#
|
|
155
|
-
# canonical review)` — this is the canonical-expansion detection key.
|
|
153
|
+
# - >=2 REAL numbered options (chosen + actually-rejected alternative)
|
|
154
|
+
# - Every section carries real derived prose; the file matches NOTHING
|
|
155
|
+
# in the shared deferral-marker vocabulary (DEFERRAL_MARKER_RE).
|
|
156
156
|
# ---------------------------------------------------------------------------
|
|
157
157
|
|
|
158
|
-
@test "capture-adr:
|
|
159
|
-
#
|
|
160
|
-
#
|
|
161
|
-
#
|
|
158
|
+
@test "capture-adr: derived-substance ADR matches nothing in the deferral-marker vocabulary" {
|
|
159
|
+
# RFC-045: no placeholder, pointer, or sentinel strings of any kind.
|
|
160
|
+
# Asserted against the single source of truth for the deferred-work
|
|
161
|
+
# vocabulary (P375 census) so "no placeholder of any kind" is the
|
|
162
|
+
# behavioural contract, not just "not the one old literal string".
|
|
163
|
+
MARKERS_LIB="${REPO_ROOT}/packages/retrospective/hooks/lib/deferral-markers.sh"
|
|
164
|
+
[ -f "$MARKERS_LIB" ]
|
|
165
|
+
# shellcheck disable=SC1090
|
|
166
|
+
source "$MARKERS_LIB"
|
|
167
|
+
[ -n "$DEFERRAL_MARKER_RE" ]
|
|
168
|
+
|
|
162
169
|
mkdir -p "$TMPROOT/docs/decisions"
|
|
163
170
|
TITLE="example-mid-iter-decision"
|
|
164
171
|
ID="200"
|
|
@@ -167,12 +174,13 @@ teardown() {
|
|
|
167
174
|
CONTEXT_LINE="Iter-bound design choice that needs codification."
|
|
168
175
|
DECISION_LINE="Adopt Option A because it preserves invariants X and Y."
|
|
169
176
|
|
|
170
|
-
# Mirror the SKILL.md
|
|
177
|
+
# Mirror the SKILL.md derive-fill template with real derived content.
|
|
171
178
|
cat > "$TMPROOT/docs/decisions/${ID}-${TITLE}.proposed.md" <<EOF
|
|
172
179
|
---
|
|
173
180
|
status: "proposed"
|
|
174
181
|
date: ${TODAY}
|
|
175
|
-
|
|
182
|
+
human-oversight: unconfirmed
|
|
183
|
+
decision-makers: [Test User]
|
|
176
184
|
consulted: []
|
|
177
185
|
informed: []
|
|
178
186
|
reassessment-date: ${REASSESS}
|
|
@@ -180,7 +188,7 @@ reassessment-date: ${REASSESS}
|
|
|
180
188
|
|
|
181
189
|
# ${TITLE}
|
|
182
190
|
|
|
183
|
-
> Captured via /wr-architect:capture-adr (foreground-lightweight aside-invocation per ADR-032
|
|
191
|
+
> Captured via /wr-architect:capture-adr (foreground-lightweight aside-invocation per ADR-032, derived-substance amendment 2026-07-06 / RFC-045). Section content was derived by the capturing agent from the in-session decision context; human-oversight: unconfirmed until ratified at the /wr-architect:review-decisions drain.
|
|
184
192
|
|
|
185
193
|
## Context and Problem Statement
|
|
186
194
|
|
|
@@ -188,12 +196,13 @@ ${CONTEXT_LINE}
|
|
|
188
196
|
|
|
189
197
|
## Decision Drivers
|
|
190
198
|
|
|
191
|
-
-
|
|
199
|
+
- Invariant X must survive iter restarts.
|
|
200
|
+
- Option B would couple the loop to session state.
|
|
192
201
|
|
|
193
202
|
## Considered Options
|
|
194
203
|
|
|
195
204
|
1. **Option A (chosen)** — ${DECISION_LINE}
|
|
196
|
-
2. (
|
|
205
|
+
2. **Option B (session-state coupling)** — rejected: couples the loop to session state.
|
|
197
206
|
|
|
198
207
|
## Decision Outcome
|
|
199
208
|
|
|
@@ -203,58 +212,69 @@ Chosen option: **"Option A"**, because ${DECISION_LINE}
|
|
|
203
212
|
|
|
204
213
|
### Good
|
|
205
214
|
|
|
206
|
-
-
|
|
215
|
+
- Invariants X and Y hold across iters.
|
|
207
216
|
|
|
208
217
|
### Neutral
|
|
209
218
|
|
|
210
|
-
-
|
|
219
|
+
- No neutral consequences identified at capture.
|
|
211
220
|
|
|
212
221
|
### Bad
|
|
213
222
|
|
|
214
|
-
-
|
|
223
|
+
- One extra lookup per iter.
|
|
215
224
|
|
|
216
225
|
## Confirmation
|
|
217
226
|
|
|
218
|
-
|
|
227
|
+
Run the iter loop twice; invariant X holds on the second run.
|
|
219
228
|
|
|
220
229
|
## Pros and Cons of the Options
|
|
221
230
|
|
|
222
231
|
### Option A
|
|
223
232
|
|
|
224
|
-
-
|
|
233
|
+
- Good, because invariants survive restarts.
|
|
234
|
+
- Bad, because of the extra lookup.
|
|
235
|
+
|
|
236
|
+
### Option B
|
|
237
|
+
|
|
238
|
+
- Good, because no extra lookup.
|
|
239
|
+
- Bad, because session-state coupling breaks AFK iters.
|
|
225
240
|
|
|
226
241
|
## Reassessment Criteria
|
|
227
242
|
|
|
228
|
-
|
|
243
|
+
Reopen if the extra-lookup cost exceeds one turn per iter.
|
|
229
244
|
EOF
|
|
230
245
|
|
|
231
246
|
ADR="$TMPROOT/docs/decisions/${ID}-${TITLE}.proposed.md"
|
|
232
247
|
[ -f "$ADR" ]
|
|
233
248
|
|
|
234
|
-
#
|
|
249
|
+
# Load-bearing fields present.
|
|
235
250
|
run grep -F 'status: "proposed"' "$ADR"
|
|
236
251
|
[ "$status" -eq 0 ]
|
|
237
|
-
|
|
238
|
-
|
|
252
|
+
run grep -F 'human-oversight: unconfirmed' "$ADR"
|
|
253
|
+
[ "$status" -eq 0 ]
|
|
254
|
+
# Decision-makers carries a real name — no sentinel.
|
|
255
|
+
run grep -F 'decision-makers: [Test User]' "$ADR"
|
|
239
256
|
[ "$status" -eq 0 ]
|
|
240
257
|
# Title from input lands at H1.
|
|
241
258
|
run grep -F "# ${TITLE}" "$ADR"
|
|
242
259
|
[ "$status" -eq 0 ]
|
|
243
|
-
# Context
|
|
260
|
+
# Context + Decision survive verbatim from input.
|
|
244
261
|
run grep -F "$CONTEXT_LINE" "$ADR"
|
|
245
262
|
[ "$status" -eq 0 ]
|
|
246
|
-
# Decision survives verbatim from input.
|
|
247
263
|
run grep -F "$DECISION_LINE" "$ADR"
|
|
248
264
|
[ "$status" -eq 0 ]
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
265
|
+
|
|
266
|
+
# THE contract: nothing in the file matches the deferral vocabulary.
|
|
267
|
+
run grep -Eic "$DEFERRAL_MARKER_RE" "$ADR"
|
|
268
|
+
[ "$output" = "0" ]
|
|
269
|
+
# And the legacy sentinel shape is gone too.
|
|
270
|
+
run grep -F 'unspecified — fill at canonical review' "$ADR"
|
|
271
|
+
[ "$status" -ne 0 ]
|
|
252
272
|
}
|
|
253
273
|
|
|
254
|
-
@test "capture-adr:
|
|
255
|
-
# MADR
|
|
256
|
-
#
|
|
257
|
-
#
|
|
274
|
+
@test "capture-adr: derived ADR carries >=2 REAL numbered options (no placeholder sibling)" {
|
|
275
|
+
# RFC-045: MADR >=2-options is satisfied by substance — the chosen
|
|
276
|
+
# option plus an actually-weighed alternative — never by a numbered
|
|
277
|
+
# placeholder.
|
|
258
278
|
mkdir -p "$TMPROOT/docs/decisions"
|
|
259
279
|
ID="201"
|
|
260
280
|
TITLE="another-decision"
|
|
@@ -263,16 +283,18 @@ EOF
|
|
|
263
283
|
## Considered Options
|
|
264
284
|
|
|
265
285
|
1. **Option A (chosen)** — One-line summary
|
|
266
|
-
2. (
|
|
286
|
+
2. **Status quo (do nothing)** — rejected: the symptom recurs every session
|
|
267
287
|
EOF
|
|
268
288
|
|
|
269
289
|
ADR="$TMPROOT/docs/decisions/${ID}-${TITLE}.proposed.md"
|
|
270
290
|
# Numbered option 1 with chosen marker.
|
|
271
291
|
run grep -F '1. **Option A (chosen)**' "$ADR"
|
|
272
292
|
[ "$status" -eq 0 ]
|
|
273
|
-
# Numbered option 2
|
|
274
|
-
run grep -
|
|
293
|
+
# Numbered option 2 is real content, not a deferral.
|
|
294
|
+
run grep -E '^2\. \*\*' "$ADR"
|
|
275
295
|
[ "$status" -eq 0 ]
|
|
296
|
+
run grep -F '(deferred' "$ADR"
|
|
297
|
+
[ "$status" -ne 0 ]
|
|
276
298
|
}
|
|
277
299
|
|
|
278
300
|
@test "capture-adr: default reassessment-date is 3 months from today (matches create-adr)" {
|
|
@@ -320,19 +342,21 @@ EOF
|
|
|
320
342
|
}
|
|
321
343
|
|
|
322
344
|
# ---------------------------------------------------------------------------
|
|
323
|
-
# Deferred-
|
|
345
|
+
# Deferred-ratification contract — distinguishing capture-adr from
|
|
324
346
|
# create-adr. capture-adr must NOT invoke the architect-agent inline; it
|
|
325
|
-
# writes status: proposed
|
|
326
|
-
#
|
|
347
|
+
# writes status: proposed + human-oversight: unconfirmed and defers human
|
|
348
|
+
# ratification to the self-firing oversight drain (RFC-045).
|
|
327
349
|
# ---------------------------------------------------------------------------
|
|
328
350
|
|
|
329
|
-
@test "capture-adr: SKILL.md
|
|
351
|
+
@test "capture-adr: SKILL.md routes ratification to the review-decisions drain (no inline review handoff)" {
|
|
330
352
|
# The contract distinction from create-adr: capture-adr does NOT invoke
|
|
331
|
-
# the wr-architect:agent review inline;
|
|
332
|
-
#
|
|
333
|
-
#
|
|
334
|
-
# would break the
|
|
335
|
-
#
|
|
336
|
-
run grep -F '/wr-architect:
|
|
353
|
+
# the wr-architect:agent review inline; the derived substance is
|
|
354
|
+
# ratified at the /wr-architect:review-decisions drain surfaced by the
|
|
355
|
+
# SessionStart oversight nudge. A future maintainer who copies
|
|
356
|
+
# create-adr's Step 5 confirm pass into capture-adr would break the
|
|
357
|
+
# zero-interaction promise.
|
|
358
|
+
run grep -F '/wr-architect:review-decisions' "$SKILL_FILE"
|
|
359
|
+
[ "$status" -eq 0 ]
|
|
360
|
+
run grep -F 'human-oversight: unconfirmed' "$SKILL_FILE"
|
|
337
361
|
[ "$status" -eq 0 ]
|
|
338
362
|
}
|
|
@@ -10,7 +10,7 @@ Create a new ADR in `docs/decisions/` following MADR 4.0 format. The wr-architec
|
|
|
10
10
|
|
|
11
11
|
## Needs-Direction handoff + confirm-every-ADR (ADR-064)
|
|
12
12
|
|
|
13
|
-
When a `wr-architect:agent` review returns a **NEEDS DIRECTION** verdict (a new decision with 2+ viable options and no pinned direction, per ADR-064), the option choice is the user's, not the agent's — this skill is the translation surface. The architect's named question + options become the Step 2 cat-1 `AskUserQuestion` calls (Considered Options / Decision Outcome), and the Step 5 confirm is the load-bearing **review-and-confirm-every-ADR** gate: an ADR must not stand as a human-oversighted decision (reach `accepted`) without that confirm pass. A `/wr-architect:capture-adr`
|
|
13
|
+
When a `wr-architect:agent` review returns a **NEEDS DIRECTION** verdict (a new decision with 2+ viable options and no pinned direction, per ADR-064), the option choice is the user's, not the agent's — this skill is the translation surface. The architect's named question + options become the Step 2 cat-1 `AskUserQuestion` calls (Considered Options / Decision Outcome), and the Step 5 confirm is the load-bearing **review-and-confirm-every-ADR** gate: an ADR must not stand as a human-oversighted decision (reach `accepted`) without that confirm pass. A `/wr-architect:capture-adr` ADR — zero-ask, with its decision pre-pinned in `$ARGUMENTS` and its remaining sections silently DERIVED at capture per the ADR-032 derived-substance amendment (RFC-045) — must still have its derived substance human-ratified (via `/wr-architect:review-decisions` or this skill's confirm) before promotion to `accepted`. When direction IS already pinned (same-turn / same-session / accepted ADR / RISK-POLICY.md / CLAUDE.md mandatory rule), act on it — do not re-ask (P132 inverse-P078 guard).
|
|
14
14
|
|
|
15
15
|
## Steps
|
|
16
16
|
|
|
@@ -61,7 +61,7 @@ The advisory text shape is I2-isomorphic — same sentence structure across all
|
|
|
61
61
|
|
|
62
62
|
**ADR-026 cost-source grounding**: each derived field cites its source in the advisory (problem-statement token sequence for Title; today's date for date / reassessment-date; default convention for status). The `re-invoke or update if mis-rated` clause carries the reversibility marker ADR-026 mandates for ungrounded outputs.
|
|
63
63
|
|
|
64
|
-
**AFK fail-safe (ADR-013 Rule 6)**: under AFK orchestration, derivable fields (Title / status / date / reassessment-date / Context-when-prose-present) resolve without interactive input. The 6 retained cat-1 AskUserQuestion surfaces (decision-makers / Decision Drivers / Considered Options / Decision Outcome / Consequences / Confirmation) WILL halt AFK execution — that is **correct behaviour** because ADR creation is genuinely user-judgment-bound (the user authors the decision; the framework cannot). JTBD-006 protection: AFK orchestrators that need ADR creation should call `/wr-architect:capture-adr` (the lightweight aside surface)
|
|
64
|
+
**AFK fail-safe (ADR-013 Rule 6)**: under AFK orchestration, derivable fields (Title / status / date / reassessment-date / Context-when-prose-present) resolve without interactive input. The 6 retained cat-1 AskUserQuestion surfaces (decision-makers / Decision Drivers / Considered Options / Decision Outcome / Consequences / Confirmation) WILL halt AFK execution — that is **correct behaviour** because ADR creation is genuinely user-judgment-bound (the user authors the decision; the framework cannot). JTBD-006 protection: AFK orchestrators that need ADR creation should call `/wr-architect:capture-adr` (the lightweight aside surface), which derives ALL section content silently at capture (ADR-032 derived-substance amendment, RFC-045) and records it `human-oversight: unconfirmed`; the user ratifies or amends the derived substance at the next interactive `/wr-architect:review-decisions` drain (surfaced by the self-firing SessionStart oversight nudge). The cat-1 classification above governs THIS interactive intake surface — on the capture surface the same fields are derived-provisional-then-ratified, not asked (the ADR-067 pattern); the two surfaces do not conflict.
|
|
65
65
|
|
|
66
66
|
**Cross-skill consistency note**: this is the **fourth declaration-skill surface** to ship the derive-first dispatch (after `/wr-itil:capture-problem` Step 1.5, `/wr-itil:manage-incident` Step 4, and `/wr-itil:manage-problem` Step 4 in commits b7cc645 / 43255d2 / 30fd22b). Phase 2a-iii-B (2026-05-16) closes Phase 2a's full 4-surface scope — the I2-isomorphic stderr advisory format is now locked-in across `capture-problem`, `manage-incident`, `manage-problem`, AND `create-adr` via the shared helper at `packages/shared/derive-first-dispatch.sh` with synced per-package lib/ copies. Per ADR-017, drift between copies is caught by `npm run check:derive-first-dispatch` in CI.
|
|
67
67
|
|