@windyroad/architect 0.11.0 → 0.12.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/plugin.json +1 -1
- package/agents/agent.md +3 -2
- package/package.json +1 -1
- package/scripts/detect-unoversighted.sh +13 -2
- package/scripts/generate-decisions-compendium.sh +11 -2
- package/scripts/is-decision-unconfirmed.sh +10 -0
- package/scripts/test/detect-unoversighted.bats +40 -0
- package/scripts/test/is-decision-unconfirmed.bats +35 -0
- package/skills/review-decisions/SKILL.md +6 -3
package/agents/agent.md
CHANGED
|
@@ -159,12 +159,13 @@ Do NOT emit Needs Direction for the "obvious choice" / only-one-viable-option ca
|
|
|
159
159
|
|
|
160
160
|
### When to flag [Unratified Dependency] (ADR-074 (Confirm a decision's substance before building dependent work) surface 3)
|
|
161
161
|
|
|
162
|
-
When the change or plan under review **explicitly cites or implements** a specific ADR (e.g. the diff/prose says "per ADR-072", a `Refs: ADR-NNN`, or it is authoring the work the ADR governs), check whether that ADR has been **ratified** before letting the change stand. You have Read/Glob/Grep (no Bash), so perform the **read-only equivalent** of `packages/architect/scripts/is-decision-unconfirmed.sh` — mirror
|
|
162
|
+
When the change or plan under review **explicitly cites or implements** a specific ADR (e.g. the diff/prose says "per ADR-072", a `Refs: ADR-NNN`, or it is authoring the work the ADR governs), check whether that ADR has been **ratified** before letting the change stand. You have Read/Glob/Grep (no Bash), so perform the **read-only equivalent** of `packages/architect/scripts/is-decision-unconfirmed.sh` — mirror ALL THREE halves of its "unconfirmed" definition:
|
|
163
163
|
|
|
164
164
|
1. **Frontmatter-scoped marker check.** Read the cited ADR file and inspect ONLY its YAML frontmatter (the block between the leading `---` and the next `---`). The ADR is **ratified** iff that frontmatter contains a line matching `human-oversight: confirmed` — case-insensitive, tolerating trailing whitespace (the canonical predicate greps `-iE '^human-oversight:[[:space:]]*confirmed[[:space:]]*$'`). A body mention of that string does NOT count — it must be in frontmatter.
|
|
165
165
|
2. **Superseded skip.** A `*.superseded.md` ADR is retired — treat it as ratified-equivalent (do NOT flag); a newer ADR replaced it.
|
|
166
|
+
3. **Rejected-pending-supersede skip** (ADR-066 amendment per P316). An ADR whose frontmatter carries BOTH `human-oversight: rejected-pending-supersede` AND `supersede-ticket: P<NNN>` is ratified-equivalent — the user has explicitly rejected the ADR and pinned a supersede in flight. Treat as ratified (do NOT flag). The marker alone (without the `supersede-ticket:` scalar) does NOT skip — it is malformed and surfaces as unratified so the un-tracked case doesn't silently rot.
|
|
166
167
|
|
|
167
|
-
Emit **ISSUES FOUND / [Unratified Dependency]** only when the cited ADR's frontmatter lacks the marker AND it is not superseded — action: "ratify ADR-NNN via `/wr-architect:review-decisions` before this lands."
|
|
168
|
+
Emit **ISSUES FOUND / [Unratified Dependency]** only when the cited ADR's frontmatter lacks the `confirmed` marker AND it is not superseded AND it does not carry the rejected-pending-supersede + supersede-ticket pair — action: "ratify ADR-NNN via `/wr-architect:review-decisions` before this lands."
|
|
168
169
|
|
|
169
170
|
**Key the flag on the oversight marker, NEVER on `status:`.** `status: proposed`/`accepted` and `human-oversight:` are orthogonal axes (ADR-066). Building on a **ratified** ADR is fine even when its `status` is still `proposed` — do NOT flag it. Only the *unratified* (marker-absent, non-superseded) case flags. In steady state almost every ADR is ratified (born-confirmed via `create-adr` + the review-decisions drain), so this fires on essentially nothing — do not over-scan or flag transitive/ambient dependence on governed code, only an explicit cite/implement of a specific unratified ADR (the inverse-P078 / P132 over-fire guard).
|
|
170
171
|
|
package/package.json
CHANGED
|
@@ -45,7 +45,18 @@ for f in "$DECISIONS_DIR"/*.md "$DECISIONS_DIR"/*/*.md; do
|
|
|
45
45
|
{ print }
|
|
46
46
|
' "$f")"
|
|
47
47
|
|
|
48
|
-
if
|
|
49
|
-
|
|
48
|
+
if printf '%s\n' "$fm" | grep -qiE '^human-oversight:[[:space:]]*confirmed[[:space:]]*$'; then
|
|
49
|
+
continue
|
|
50
50
|
fi
|
|
51
|
+
|
|
52
|
+
# ADR-066 amendment (P316): rejected-pending-supersede is a third oversight
|
|
53
|
+
# value. Exclude only when BOTH the marker AND a supersede-ticket: P<NNN>
|
|
54
|
+
# scalar are present in frontmatter — a marker without the ticket is
|
|
55
|
+
# malformed and still surfaces so the drain catches the un-tracked case.
|
|
56
|
+
if printf '%s\n' "$fm" | grep -qiE '^human-oversight:[[:space:]]*rejected-pending-supersede[[:space:]]*$' \
|
|
57
|
+
&& printf '%s\n' "$fm" | grep -qiE '^supersede-ticket:[[:space:]]*P[0-9]+[[:space:]]*$'; then
|
|
58
|
+
continue
|
|
59
|
+
fi
|
|
60
|
+
|
|
61
|
+
echo "$f"
|
|
51
62
|
done | sort
|
|
@@ -195,7 +195,7 @@ truncate_with_ellipsis() {
|
|
|
195
195
|
|
|
196
196
|
emit_entry() {
|
|
197
197
|
local file="$1"
|
|
198
|
-
local id title status oversight superseded
|
|
198
|
+
local id title status oversight superseded supersede_ticket
|
|
199
199
|
local chosen drivers confirmation related
|
|
200
200
|
|
|
201
201
|
id=$(basename "$file" | grep -oE '^[0-9]+')
|
|
@@ -203,6 +203,11 @@ emit_entry() {
|
|
|
203
203
|
status=$(get_frontmatter_field "$file" "status")
|
|
204
204
|
oversight=$(get_frontmatter_field "$file" "human-oversight")
|
|
205
205
|
superseded=$(get_frontmatter_field "$file" "supersedes")
|
|
206
|
+
# ADR-066 amendment (P316): when the oversight value is
|
|
207
|
+
# `rejected-pending-supersede`, surface the tracking ticket parenthetically
|
|
208
|
+
# so the compendium badge shows both the disposition AND the supersede in
|
|
209
|
+
# flight without a per-ADR body read.
|
|
210
|
+
supersede_ticket=$(get_frontmatter_field "$file" "supersede-ticket")
|
|
206
211
|
|
|
207
212
|
# Chosen-option line — truncate to a comfortable summary length.
|
|
208
213
|
chosen=$(get_chosen "$file" | strip_links | oneline)
|
|
@@ -229,7 +234,11 @@ emit_entry() {
|
|
|
229
234
|
# Status / oversight / supersession badges on one compact line.
|
|
230
235
|
local badges="**Status:** ${status:-?}"
|
|
231
236
|
if [ -n "$oversight" ]; then
|
|
232
|
-
|
|
237
|
+
if [ "$oversight" = "rejected-pending-supersede" ] && [ -n "$supersede_ticket" ]; then
|
|
238
|
+
badges="${badges} | **Oversight:** ${oversight} (${supersede_ticket})"
|
|
239
|
+
else
|
|
240
|
+
badges="${badges} | **Oversight:** ${oversight}"
|
|
241
|
+
fi
|
|
233
242
|
fi
|
|
234
243
|
if [ -n "$superseded" ] && [ "$superseded" != "[]" ]; then
|
|
235
244
|
badges="${badges} | **Supersedes:** ${superseded}"
|
|
@@ -76,6 +76,16 @@ if printf '%s\n' "$fm" | grep -qiE '^human-oversight:[[:space:]]*confirmed[[:spa
|
|
|
76
76
|
exit 1 # confirmed — OK to build on
|
|
77
77
|
fi
|
|
78
78
|
|
|
79
|
+
# ADR-066 amendment (P316): rejected-pending-supersede with a tracked
|
|
80
|
+
# supersede-ticket is "ratified-equivalent" for the build-upon guard — the
|
|
81
|
+
# user has explicitly rejected the ADR and pinned a supersede in flight, so
|
|
82
|
+
# the [Unratified Dependency] flag must NOT re-fire on the rejected ADR.
|
|
83
|
+
# Marker without ticket is malformed and still fires (defensive).
|
|
84
|
+
if printf '%s\n' "$fm" | grep -qiE '^human-oversight:[[:space:]]*rejected-pending-supersede[[:space:]]*$' \
|
|
85
|
+
&& printf '%s\n' "$fm" | grep -qiE '^supersede-ticket:[[:space:]]*P[0-9]+[[:space:]]*$'; then
|
|
86
|
+
exit 1
|
|
87
|
+
fi
|
|
88
|
+
|
|
79
89
|
# Unconfirmed — the build-upon guard SHOULD fire. Name the file for the guard.
|
|
80
90
|
echo "$file"
|
|
81
91
|
exit 0
|
|
@@ -97,3 +97,43 @@ mk() { # mk <filename> <frontmatter-extra-lines...>
|
|
|
97
97
|
[ "$status" -eq 0 ]
|
|
98
98
|
[ -z "$output" ]
|
|
99
99
|
}
|
|
100
|
+
|
|
101
|
+
# ADR-066 amendment (P316): rejected-pending-supersede is a third oversight
|
|
102
|
+
# value alongside `confirmed` and absent. It exists so the drain stops crying
|
|
103
|
+
# wolf on ADRs the user has explicitly rejected with a tracked supersede
|
|
104
|
+
# ticket (otherwise they re-surface every drain until the supersede ADR
|
|
105
|
+
# lands). Exclusion is conditional on BOTH the marker AND a supersede-ticket
|
|
106
|
+
# scalar being present — a marker without a ticket is malformed and still
|
|
107
|
+
# surfaces (defensive, JTBD-201/202 audit-trail guard).
|
|
108
|
+
|
|
109
|
+
@test "rejected-pending-supersede WITH supersede-ticket is excluded" {
|
|
110
|
+
mk "020-rejected-tracked.proposed.md" \
|
|
111
|
+
"human-oversight: rejected-pending-supersede" \
|
|
112
|
+
"supersede-ticket: P297"
|
|
113
|
+
run bash "$SCRIPT" "$DIR/docs/decisions"
|
|
114
|
+
[ "$status" -eq 0 ]
|
|
115
|
+
[[ "$output" != *"020-rejected-tracked.proposed.md"* ]]
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
@test "rejected-pending-supersede WITHOUT supersede-ticket still surfaces (defensive)" {
|
|
119
|
+
mk "021-rejected-untracked.proposed.md" \
|
|
120
|
+
"human-oversight: rejected-pending-supersede"
|
|
121
|
+
run bash "$SCRIPT" "$DIR/docs/decisions"
|
|
122
|
+
[ "$status" -eq 0 ]
|
|
123
|
+
[[ "$output" == *"021-rejected-untracked.proposed.md"* ]]
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
@test "supersede-ticket alone (without the rejected marker) does NOT exclude" {
|
|
127
|
+
mk "022-ticket-only.proposed.md" "supersede-ticket: P297"
|
|
128
|
+
run bash "$SCRIPT" "$DIR/docs/decisions"
|
|
129
|
+
[ "$status" -eq 0 ]
|
|
130
|
+
[[ "$output" == *"022-ticket-only.proposed.md"* ]]
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
@test "rejected-pending-supersede match tolerates trailing whitespace" {
|
|
134
|
+
mk "023-spacey.proposed.md" \
|
|
135
|
+
"human-oversight: rejected-pending-supersede " \
|
|
136
|
+
"supersede-ticket: P297 "
|
|
137
|
+
run bash "$SCRIPT" "$DIR/docs/decisions"
|
|
138
|
+
[[ "$output" != *"023-spacey.proposed.md"* ]]
|
|
139
|
+
}
|
|
@@ -106,3 +106,38 @@ mk() {
|
|
|
106
106
|
[[ "$detect_out" != *"111-confirmed.proposed.md"* ]]
|
|
107
107
|
run bash "$SCRIPT" "ADR-111" "$DIR/docs/decisions"; [ "$status" -eq 1 ]
|
|
108
108
|
}
|
|
109
|
+
|
|
110
|
+
# ADR-066 amendment (P316): mirror the rejected-pending-supersede exclusion.
|
|
111
|
+
# The build-upon guard must NOT fire on an ADR the user explicitly rejected
|
|
112
|
+
# with a tracked supersede ticket — otherwise the [Unratified Dependency]
|
|
113
|
+
# verdict re-triggers on every iteration that touches the rejected ADR.
|
|
114
|
+
|
|
115
|
+
@test "rejected-pending-supersede WITH supersede-ticket does NOT fire the guard (exit 1)" {
|
|
116
|
+
mk "120-rejected-tracked.proposed.md" "proposed" \
|
|
117
|
+
"human-oversight: rejected-pending-supersede" \
|
|
118
|
+
"supersede-ticket: P297"
|
|
119
|
+
run bash "$SCRIPT" "ADR-120" "$DIR/docs/decisions"
|
|
120
|
+
[ "$status" -eq 1 ]
|
|
121
|
+
[ -z "$output" ]
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
@test "rejected-pending-supersede WITHOUT supersede-ticket DOES fire the guard (exit 0)" {
|
|
125
|
+
mk "121-rejected-untracked.proposed.md" "proposed" \
|
|
126
|
+
"human-oversight: rejected-pending-supersede"
|
|
127
|
+
run bash "$SCRIPT" "ADR-121" "$DIR/docs/decisions"
|
|
128
|
+
[ "$status" -eq 0 ]
|
|
129
|
+
[[ "$output" == *"121-rejected-untracked.proposed.md"* ]]
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
@test "agrees with detect-unoversighted on rejected-pending-supersede (sync guard)" {
|
|
133
|
+
mk "130-rejected.proposed.md" "proposed" \
|
|
134
|
+
"human-oversight: rejected-pending-supersede" \
|
|
135
|
+
"supersede-ticket: P298"
|
|
136
|
+
mk "131-rejected-untracked.proposed.md" "proposed" \
|
|
137
|
+
"human-oversight: rejected-pending-supersede"
|
|
138
|
+
detect_out="$(bash "$DETECT" "$DIR/docs/decisions")"
|
|
139
|
+
[[ "$detect_out" != *"130-rejected.proposed.md"* ]]
|
|
140
|
+
run bash "$SCRIPT" "ADR-130" "$DIR/docs/decisions"; [ "$status" -eq 1 ]
|
|
141
|
+
[[ "$detect_out" == *"131-rejected-untracked.proposed.md"* ]]
|
|
142
|
+
run bash "$SCRIPT" "ADR-131" "$DIR/docs/decisions"; [ "$status" -eq 0 ]
|
|
143
|
+
}
|
|
@@ -44,7 +44,7 @@ For each ADR in the ordered queue, surface the decision as an `AskUserQuestion`
|
|
|
44
44
|
- **Options** (per ADR):
|
|
45
45
|
- **Confirm** — the recorded decision is correct; write the marker.
|
|
46
46
|
- **Amend** — the decision is mostly right but needs a change; capture the change, apply it to the ADR body, then write the marker.
|
|
47
|
-
- **Reject / supersede** — the auto-made pick is wrong;
|
|
47
|
+
- **Reject / supersede** — the auto-made pick is wrong; capture the supersede ticket (see Step 4) and write the **rejected-pending-supersede** marker so the drain stops re-asking.
|
|
48
48
|
- **Defer** — skip this sitting; leave unoversighted for a later run.
|
|
49
49
|
|
|
50
50
|
This is a genuine human-decision surface (the whole point of P283) — `AskUserQuestion` is correct here and is NOT over-asking. Do not auto-confirm; do not prose-ask.
|
|
@@ -52,7 +52,10 @@ This is a genuine human-decision surface (the whole point of P283) — `AskUserQ
|
|
|
52
52
|
### Step 4: Apply the outcome
|
|
53
53
|
|
|
54
54
|
- **Confirm / Amend**: write `human-oversight: confirmed` + `oversight-date: <today, YYYY-MM-DD>` into the ADR's frontmatter (insert after the `date:` line if absent; never duplicate). For Amend, apply the directed body change first. Both edits go through the standard architect / JTBD edit gate per ADR-014.
|
|
55
|
-
- **Reject / supersede
|
|
55
|
+
- **Reject / supersede** (ADR-066 amendment per P316):
|
|
56
|
+
1. Capture the supersede ticket via a follow-up `AskUserQuestion`: "Which problem ticket tracks the supersede?" — options: existing `P<NNN>` IDs surfaced from `docs/problems/`, **Capture a new ticket** (delegate to `/wr-itil:capture-problem`), or **Defer (leave un-tracked for now)**.
|
|
57
|
+
2. If a ticket ID is captured, write `human-oversight: rejected-pending-supersede` + `supersede-ticket: P<NNN>` into the ADR's frontmatter. The detector excludes ADRs carrying both, so the drain stops re-asking until either the supersede ADR lands (status flips to `superseded`) or the rejection is revisited.
|
|
58
|
+
3. If the user defers ticket capture, leave the marker absent — the ADR re-surfaces next drain (the un-tracked case is intentionally re-asked so it doesn't silently rot).
|
|
56
59
|
- **Defer**: no write.
|
|
57
60
|
|
|
58
61
|
### Step 5: Commit + report
|
|
@@ -61,7 +64,7 @@ Commit the confirmed/amended ADRs per ADR-014 (one commit for the sitting's drai
|
|
|
61
64
|
|
|
62
65
|
## Notes
|
|
63
66
|
|
|
64
|
-
- **Never re-ask** — a confirmed ADR carries the marker permanently and is excluded from future runs (ADR-009 never-re-ask principle). The marker is write-once **except** when an ADR is materially amended after confirmation (the Decision Outcome is rewritten) — a supersede/amend clears it for re-confirmation per ADR-066 Reassessment.
|
|
67
|
+
- **Never re-ask** — a confirmed ADR carries the marker permanently and is excluded from future runs (ADR-009 never-re-ask principle). The same write-once-permanence applies to the `rejected-pending-supersede` value (P316 amendment): once the user rejects with a tracked ticket, the drain stops asking. The marker is write-once **except** when an ADR is materially amended after confirmation (the Decision Outcome is rewritten) — a supersede/amend clears it for re-confirmation per ADR-066 Reassessment. When the supersede ADR eventually lands and the original flips to `*.superseded.md`, the existing superseded-status skip takes over; the `rejected-pending-supersede` lines become historical residue (no active clearance required).
|
|
65
68
|
- **AFK** — this skill is interactive by construction (the confirm IS the human decision). It is not dispatched inside AFK iteration subprocesses; the session-start nudge self-suppresses there (`WR_SUPPRESS_OVERSIGHT_NUDGE=1`) so the drain is never half-run by an absent user.
|
|
66
69
|
- **Born-confirmed going forward** — `/wr-architect:create-adr` writes the marker at its Step 5 confirm, so new ADRs enter the set already oversighted and the unoversighted count only shrinks.
|
|
67
70
|
|