@windyroad/architect 0.12.1 → 0.12.2-preview.460

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.
@@ -123,5 +123,5 @@
123
123
  }
124
124
  },
125
125
  "name": "wr-architect",
126
- "version": "0.12.1"
126
+ "version": "0.12.2"
127
127
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@windyroad/architect",
3
- "version": "0.12.1",
3
+ "version": "0.12.2-preview.460",
4
4
  "description": "Architecture decision enforcement for AI coding agents",
5
5
  "bin": {
6
6
  "windyroad-architect": "./bin/install.mjs"
@@ -0,0 +1,205 @@
1
+ #!/usr/bin/env bats
2
+
3
+ # ADR-077: generate-decisions-compendium.sh emits a derived `README.md` index
4
+ # of every ADR's chosen option, confirmation criteria, and relationships.
5
+ # Behavioural — exercises the script against fixture trees and against the
6
+ # live committed state, asserts on its exit codes and stdout/file output.
7
+ #
8
+ # Confirmation item (g): CI drift-detection bats — defence-in-depth in case
9
+ # the `architect-compendium-refresh-discipline.sh` PreToolUse hook fails
10
+ # open or is bypassed.
11
+
12
+ setup() {
13
+ REPO_ROOT="$(cd "$(dirname "$BATS_TEST_FILENAME")/../../../.." && pwd)"
14
+ SCRIPT="$REPO_ROOT/packages/architect/scripts/generate-decisions-compendium.sh"
15
+ DIR="$(mktemp -d)"
16
+ mkdir -p "$DIR/docs/decisions"
17
+ }
18
+
19
+ teardown() {
20
+ rm -rf "$DIR"
21
+ }
22
+
23
+ # mk_adr <filename> <status> <title> [extra-frontmatter-lines...]
24
+ # Writes a minimal MADR-shaped ADR with frontmatter + title + the three
25
+ # sections the generator extracts: Decision Outcome, Confirmation, Related.
26
+ mk_adr() {
27
+ local name="$1" status="$2" title="$3"
28
+ shift 3
29
+ {
30
+ echo "---"
31
+ echo "status: \"$status\""
32
+ echo "date: 2026-05-30"
33
+ for line in "$@"; do echo "$line"; done
34
+ echo "---"
35
+ echo ""
36
+ echo "# $title"
37
+ echo ""
38
+ echo "## Decision Outcome"
39
+ echo ""
40
+ echo "Chosen option: **\"$title implementation\"**, because reasons."
41
+ echo ""
42
+ echo "## Confirmation"
43
+ echo ""
44
+ echo "- [ ] (a) First confirmation item for $title."
45
+ echo "- [ ] (b) Second confirmation item for $title."
46
+ echo ""
47
+ echo "## Related"
48
+ echo ""
49
+ echo "- Relates to [ADR-001](001-foo.proposed.md)"
50
+ } > "$DIR/docs/decisions/$name"
51
+ }
52
+
53
+ # --- ADR-077 (g) drift-detection contract on the live committed state -------
54
+
55
+ @test "committed compendium matches generator output (CI drift gate)" {
56
+ # This is the load-bearing assertion from ADR-077 (g): the committed
57
+ # docs/decisions/README.md MUST match what the generator produces from
58
+ # the current docs/decisions/<NNN>-*.md bodies. If this fails in CI, the
59
+ # safety-net hook either failed open or was bypassed.
60
+ cd "$REPO_ROOT"
61
+ run bash "$SCRIPT" --check docs/decisions
62
+ [ "$status" -eq 0 ]
63
+ }
64
+
65
+ # --- Idempotency (ADR-077 (b) re-asserted) ----------------------------------
66
+
67
+ @test "generator is idempotent — two runs produce byte-identical output" {
68
+ mk_adr "010-alpha.proposed.md" "proposed" "Alpha"
69
+ mk_adr "011-beta.accepted.md" "accepted" "Beta"
70
+ run bash "$SCRIPT" "$DIR/docs/decisions"
71
+ [ "$status" -eq 0 ]
72
+ cp "$DIR/docs/decisions/README.md" "$DIR/first.md"
73
+ run bash "$SCRIPT" "$DIR/docs/decisions"
74
+ [ "$status" -eq 0 ]
75
+ run cmp -s "$DIR/first.md" "$DIR/docs/decisions/README.md"
76
+ [ "$status" -eq 0 ]
77
+ }
78
+
79
+ # --- Drift detection on fixture (mutated ADR body) --------------------------
80
+
81
+ @test "--check exits 1 when an ADR body is mutated after generation" {
82
+ mk_adr "010-alpha.proposed.md" "proposed" "Alpha"
83
+ bash "$SCRIPT" "$DIR/docs/decisions" >/dev/null 2>&1
84
+ # Mutate the Decision Outcome — committed compendium now stale.
85
+ sed -i.bak 's/Alpha implementation/Alpha REVISED outcome/' \
86
+ "$DIR/docs/decisions/010-alpha.proposed.md"
87
+ rm "$DIR/docs/decisions/010-alpha.proposed.md.bak"
88
+ run bash "$SCRIPT" --check "$DIR/docs/decisions"
89
+ [ "$status" -eq 1 ]
90
+ # The stderr advice should name the regen command for mechanical recovery.
91
+ [[ "$output" == *"wr-architect-generate-decisions-compendium"* ]]
92
+ }
93
+
94
+ @test "--check exits 1 when compendium is missing entirely" {
95
+ mk_adr "010-alpha.proposed.md" "proposed" "Alpha"
96
+ run bash "$SCRIPT" --check "$DIR/docs/decisions"
97
+ [ "$status" -eq 1 ]
98
+ [[ "$output" == *"MISSING"* || "$output" == *"missing"* || "$output" == *"does not exist"* ]]
99
+ }
100
+
101
+ @test "--check exits 0 on a freshly-generated set (in sync)" {
102
+ mk_adr "010-alpha.proposed.md" "proposed" "Alpha"
103
+ mk_adr "011-beta.accepted.md" "accepted" "Beta"
104
+ bash "$SCRIPT" "$DIR/docs/decisions" >/dev/null 2>&1
105
+ run bash "$SCRIPT" --check "$DIR/docs/decisions"
106
+ [ "$status" -eq 0 ]
107
+ }
108
+
109
+ # --- Section split (ADR-077 amendment 2026-05-30 two-section format) --------
110
+
111
+ @test "compendium splits in-force (proposed+accepted) from historical (superseded+rejected+deprecated)" {
112
+ mk_adr "010-alpha.proposed.md" "proposed" "Alpha In-Force"
113
+ mk_adr "011-beta.accepted.md" "accepted" "Beta In-Force"
114
+ mk_adr "012-gamma.superseded.md" "superseded" "Gamma Historical"
115
+ mk_adr "013-delta.rejected.md" "rejected" "Delta Historical"
116
+ mk_adr "014-eps.deprecated.md" "deprecated" "Eps Historical"
117
+ bash "$SCRIPT" "$DIR/docs/decisions" >/dev/null 2>&1
118
+ local out="$DIR/docs/decisions/README.md"
119
+ grep -q '^## In-force decisions$' "$out"
120
+ grep -q '^## Historical decisions$' "$out"
121
+ # In-force section appears before historical section.
122
+ local in_force_line historical_line
123
+ in_force_line=$(grep -n '^## In-force decisions$' "$out" | cut -d: -f1)
124
+ historical_line=$(grep -n '^## Historical decisions$' "$out" | cut -d: -f1)
125
+ [ "$in_force_line" -lt "$historical_line" ]
126
+ # Header tally reflects the partition.
127
+ grep -q '^\*\*Total ADRs:\*\* 5 (2 in-force, 3 historical)$' "$out"
128
+ }
129
+
130
+ @test "compendium omits historical section when there are no historical ADRs" {
131
+ mk_adr "010-alpha.proposed.md" "proposed" "Alpha"
132
+ bash "$SCRIPT" "$DIR/docs/decisions" >/dev/null 2>&1
133
+ local out="$DIR/docs/decisions/README.md"
134
+ grep -q '^## In-force decisions$' "$out"
135
+ ! grep -q '^## Historical decisions$' "$out"
136
+ grep -q '^\*\*Total ADRs:\*\* 1 (1 in-force, 0 historical)$' "$out"
137
+ }
138
+
139
+ # --- Output deterministic (no timestamp / no date in header) ----------------
140
+
141
+ @test "header carries no timestamp or date — output stays idempotent across days" {
142
+ mk_adr "010-alpha.proposed.md" "proposed" "Alpha"
143
+ bash "$SCRIPT" "$DIR/docs/decisions" >/dev/null 2>&1
144
+ local out="$DIR/docs/decisions/README.md"
145
+ # The README.md never embeds a YYYY-MM-DD or HH:MM stamp at the top —
146
+ # idempotency would break otherwise (drift bats would flag day-by-day
147
+ # churn instead of substance drift).
148
+ ! head -10 "$out" | grep -qE '20[0-9]{2}-[0-9]{2}-[0-9]{2}'
149
+ ! head -10 "$out" | grep -qE '[0-9]{2}:[0-9]{2}'
150
+ }
151
+
152
+ # --- Per-ADR entry shape ----------------------------------------------------
153
+
154
+ @test "each ADR emits ID + Title + Status + Chosen + Confirmation + Related" {
155
+ mk_adr "042-test.accepted.md" "accepted" "Test Entry"
156
+ bash "$SCRIPT" "$DIR/docs/decisions" >/dev/null 2>&1
157
+ local out="$DIR/docs/decisions/README.md"
158
+ grep -q '^### ADR-042 — Test Entry$' "$out"
159
+ grep -q '^\*\*Status:\*\* accepted' "$out"
160
+ grep -q '^\*\*Chosen:\*\* Chosen option: ' "$out"
161
+ grep -q '^\*\*Confirmation:\*\* ' "$out"
162
+ # Related extraction collapses to ADR-NNN ID list.
163
+ grep -q '^\*\*Related:\*\* ADR-001$' "$out"
164
+ }
165
+
166
+ # --- Error handling ---------------------------------------------------------
167
+
168
+ @test "missing decisions dir exits 2 with a clear error" {
169
+ run bash "$SCRIPT" "$DIR/docs/nonexistent"
170
+ [ "$status" -eq 2 ]
171
+ [[ "$output" == *"not found"* || "$output" == *"does not exist"* ]]
172
+ }
173
+
174
+ @test "README.md is excluded from the ADR set (never recurses into itself)" {
175
+ # If README.md were treated as an ADR, the compendium would grow on every
176
+ # run — idempotency would break catastrophically.
177
+ mk_adr "010-alpha.proposed.md" "proposed" "Alpha"
178
+ bash "$SCRIPT" "$DIR/docs/decisions" >/dev/null 2>&1
179
+ cp "$DIR/docs/decisions/README.md" "$DIR/first.md"
180
+ bash "$SCRIPT" "$DIR/docs/decisions" >/dev/null 2>&1
181
+ run cmp -s "$DIR/first.md" "$DIR/docs/decisions/README.md"
182
+ [ "$status" -eq 0 ]
183
+ # The "Total ADRs:" tally must still be 1, not 2.
184
+ grep -q '^\*\*Total ADRs:\*\* 1 ' "$DIR/docs/decisions/README.md"
185
+ }
186
+
187
+ # --- Oversight marker projection (ADR-077 (i) authoritative-state) ----------
188
+
189
+ @test "human-oversight: confirmed surfaces as an Oversight badge" {
190
+ mk_adr "010-conf.proposed.md" "proposed" "Confirmed Entry" \
191
+ "human-oversight: confirmed" \
192
+ "oversight-date: 2026-05-30"
193
+ bash "$SCRIPT" "$DIR/docs/decisions" >/dev/null 2>&1
194
+ grep -q '^\*\*Status:\*\* proposed | \*\*Oversight:\*\* confirmed' \
195
+ "$DIR/docs/decisions/README.md"
196
+ }
197
+
198
+ @test "rejected-pending-supersede surfaces with the supersede ticket in the badge (P316 amendment)" {
199
+ mk_adr "010-rej.proposed.md" "proposed" "Rejected Entry" \
200
+ "human-oversight: rejected-pending-supersede" \
201
+ "supersede-ticket: P297"
202
+ bash "$SCRIPT" "$DIR/docs/decisions" >/dev/null 2>&1
203
+ grep -q 'Oversight:\*\* rejected-pending-supersede (P297)' \
204
+ "$DIR/docs/decisions/README.md"
205
+ }
@@ -70,9 +70,32 @@ This is a genuine human-decision surface (the whole point of P283) — `AskUserQ
70
70
  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).
71
71
  - **Defer**: no write.
72
72
 
73
+ ### Step 4.5: Refresh the decisions compendium (ADR-077)
74
+
75
+ After the batch's Confirm/Amend/Reject writes land in the working tree, regenerate `docs/decisions/README.md` so the architect-agent routine load surface reflects the new substance and badges:
76
+
77
+ ```bash
78
+ wr-architect-generate-decisions-compendium
79
+ ```
80
+
81
+ The compendium is the architect agent's primary load surface per ADR-077; review-decisions owns keeping it fresh through this drain (skills + agent are PRIMARY; the `architect-compendium-refresh-discipline.sh` hook is the safety-net backstop). Why this matters per disposition:
82
+
83
+ - **Confirm** — adds the `human-oversight: confirmed` badge to the entry. Single-line projection; small but load-bearing for the at-a-glance review surface.
84
+ - **Amend** — the Decision Outcome / Confirmation / Related substance changed in the per-ADR body. The compendium entry MUST be refreshed or routine compliance reads the stale prior call. This is the primary drift surface the refresh closes.
85
+ - **Reject / supersede** — adds the `rejected-pending-supersede (P<NNN>)` badge (P316 amendment); surfaces the disposition + tracking ticket without requiring a per-ADR body read.
86
+ - **Defer** — no write, no refresh needed.
87
+
88
+ If the drain's batch contains only Defers, skip the refresh (no diff to stage). Step 5's stage list includes the compendium otherwise.
89
+
73
90
  ### Step 5: Commit + report
74
91
 
75
- Commit the confirmed/amended ADRs per ADR-014 (one commit for the sitting's drained batch is acceptable the unit of work is "this drain sitting"). Report: how many confirmed / amended / rejected / deferred, and the remaining unoversighted count (re-run the detector). The session-start nudge count drops by the number confirmed.
92
+ **Stage list**: the confirmed/amended/rejected ADR files AND the refreshed compendium (ADR-077both move in the same commit so the architect-compendium-refresh-discipline hook passes).
93
+
94
+ ```bash
95
+ git add docs/decisions/<NNN>-*.md docs/decisions/README.md
96
+ ```
97
+
98
+ Commit the drained batch per ADR-014 (one commit for the sitting's drained batch is acceptable — the unit of work is "this drain sitting"). Report: how many confirmed / amended / rejected / deferred, and the remaining unoversighted count (re-run the detector). The session-start nudge count drops by the number confirmed.
76
99
 
77
100
  ## Notes
78
101
 
@@ -86,4 +109,6 @@ Commit the confirmed/amended ADRs per ADR-014 (one commit for the sitting's drai
86
109
  - **ADR-064** — the architect Needs-Direction verdict; the main agent owns `AskUserQuestion` (this skill is that ownership applied to the existing set).
87
110
  - **ADR-009** — never-re-ask persistent-marker principle (the marker, not its TTL/drift lifecycle).
88
111
  - **ADR-013 / ADR-044** — structured user interaction + decision-delegation taxonomy.
112
+ - **ADR-077** — generated decisions compendium as the architect agent's routine load surface; Step 4.5 + Step 5 keep it fresh through this drain (Confirmation item (f)). Mirrors the same regen + stage-with-commit pattern used by `/wr-architect:create-adr` Step 5 and `/wr-architect:capture-adr` Step 4.5.
89
113
  - **P283** — driving problem ticket (prong 2).
114
+ - **P327** — driving problem ticket for the compendium-refresh integration (Confirmation item (f) closure under ADR-077 Slice 3).