@windyroad/architect 0.9.2 → 0.10.0-preview.438

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.9.2"
126
+ "version": "0.10.0"
127
127
  }
package/agents/agent.md CHANGED
@@ -17,7 +17,7 @@ You are the Architect. You review proposed changes against the project's archite
17
17
 
18
18
  ## Your Role
19
19
 
20
- 1. Read all existing decisions in `docs/decisions/` (glob for `*.md`, skip `README.md`). If `docs/decisions/` does not exist yet, that is fine. Proceed with the review noting that no prior decisions are recorded.
20
+ 1. Read `docs/decisions/README.md` the generated **Decisions Compendium**. It carries every ADR's chosen option, confirmation criteria, and relationship graph in a compact form (~40 KB vs ~1.6 MB for the full body set; ~40× reduction). **This is the routine load surface for compliance review** (per ADR-077). If `docs/decisions/README.md` does not exist, fall back to globbing `docs/decisions/*.md` (skip the absent README) — the project may predate ADR-077 or be a fresh install. If `docs/decisions/` itself does not exist, that is fine; proceed noting that no prior decisions are recorded. **Load a specific ADR's full body** (`docs/decisions/<NNN>-*.md`) **only when the compendium entry is insufficient for the current review** — deep-dive on a contested change, evolving a decision, ratifying a new ADR via `/wr-architect:create-adr`, or confirming a substance question the compendium summary does not resolve. The per-ADR body remains the authoritative substance (ADR-031); the compendium is a derived view.
21
21
  2. Read the file(s) being edited to understand the current state and proposed change
22
22
  3. Review the proposed change against existing decisions (if any)
23
23
  4. Determine if the change requires a new decision to be documented
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env bash
2
+ # ADR-049 $PATH shim — dispatches to the canonical generate-decisions-compendium script (ADR-077).
3
+ exec "$(dirname "$0")/../scripts/generate-decisions-compendium.sh" "$@"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@windyroad/architect",
3
- "version": "0.9.2",
3
+ "version": "0.10.0-preview.438",
4
4
  "description": "Architecture decision enforcement for AI coding agents",
5
5
  "bin": {
6
6
  "windyroad-architect": "./bin/install.mjs"
@@ -0,0 +1,250 @@
1
+ #!/usr/bin/env bash
2
+ # generate-decisions-compendium.sh — generate docs/decisions/README.md from
3
+ # per-ADR files. Per ADR-077 (Generated decisions compendium as token-cheap
4
+ # load surface for routine architect-agent compliance).
5
+ #
6
+ # Usage: bash packages/architect/scripts/generate-decisions-compendium.sh [decisions_dir]
7
+ # Writes: <decisions_dir>/README.md (idempotent — same input set + bodies
8
+ # produce byte-identical output).
9
+ #
10
+ # Distributed via the ADR-049 $PATH shim at:
11
+ # packages/architect/bin/wr-architect-generate-decisions-compendium
12
+ # Hooks and skills MUST invoke the shim, not this script directly — the
13
+ # shim resolves the canonical body relative to its own location, so it
14
+ # works in adopter installs where the package lives under
15
+ # ~/.claude/plugins/cache/windyroad/wr-architect/<version>/.
16
+ #
17
+ # ADR-031 authoritative-state invariant: per-ADR bodies are the
18
+ # authoritative source of substance; this compendium is a derived/cached
19
+ # view. The compendium is NEVER edited compendium-side first.
20
+ #
21
+ # ADR-077 Confirmation item (b): generator must be idempotent — running
22
+ # it twice produces identical output.
23
+
24
+ set -uo pipefail
25
+
26
+ DECISIONS_DIR="${1:-docs/decisions}"
27
+ COMPENDIUM="$DECISIONS_DIR/README.md"
28
+
29
+ if [ ! -d "$DECISIONS_DIR" ]; then
30
+ echo "generate-decisions-compendium: decisions directory not found: $DECISIONS_DIR" >&2
31
+ exit 2
32
+ fi
33
+
34
+ # --- Field extractors ------------------------------------------------------
35
+
36
+ # Read a frontmatter scalar field (single line `key: value`).
37
+ # Strips surrounding quotes and leading/trailing whitespace.
38
+ get_frontmatter_field() {
39
+ local file="$1" field="$2"
40
+ awk -v f="$field" '
41
+ /^---$/ { fm = !fm; if (!fm) exit; next }
42
+ fm && $0 ~ "^"f":" {
43
+ sub("^"f": *", "")
44
+ gsub(/^["'"'"']|["'"'"']$/, "")
45
+ sub(/^ +/, ""); sub(/ +$/, "")
46
+ print
47
+ exit
48
+ }
49
+ ' "$file"
50
+ }
51
+
52
+ # Read the first "# Title" line after the frontmatter block.
53
+ get_title() {
54
+ awk '
55
+ /^---$/ { fm = !fm; next }
56
+ !fm && /^# / { sub(/^# /, ""); print; exit }
57
+ ' "$1"
58
+ }
59
+
60
+ # Extract a section by its `## Heading` line, up to (but not including) the
61
+ # next `## ` heading or EOF.
62
+ get_section() {
63
+ local file="$1" heading="$2"
64
+ awk -v h="$heading" '
65
+ $0 == "## " h { in_sec = 1; next }
66
+ in_sec && /^## / { exit }
67
+ in_sec { print }
68
+ ' "$file"
69
+ }
70
+
71
+ # Extract the "Chosen option:" line from the Decision Outcome section.
72
+ # Matches the common MADR shapes:
73
+ # Chosen option: **"X"**, because Y.
74
+ # Chosen option: X, because Y.
75
+ # Chosen: X.
76
+ get_chosen() {
77
+ get_section "$1" "Decision Outcome" \
78
+ | awk '/^Chosen/ { print; exit }' \
79
+ | head -1
80
+ }
81
+
82
+ # Extract top-level bullet lines (`- ...`) from a section. Skips nested
83
+ # ` - ...` sub-bullets to keep the compendium dense. Capped at N entries.
84
+ get_bullets() {
85
+ local file="$1" section="$2" cap="${3:-5}"
86
+ get_section "$file" "$section" \
87
+ | awk '/^- / { sub(/^- */, ""); print }' \
88
+ | head -"$cap"
89
+ }
90
+
91
+ # Compact-join bullets onto one line, truncating each to N chars + "…".
92
+ # Joins with "; ". Strips markdown emphasis to keep the line scannable.
93
+ compact_join_bullets() {
94
+ local per_item="${1:-120}"
95
+ awk -v n="$per_item" '
96
+ {
97
+ # Strip leading checkbox markers `[ ]` / `[x]` (from Confirmation).
98
+ sub(/^\[[ x]\] */, "")
99
+ # Strip markdown bold/italic markers for compactness.
100
+ gsub(/\*\*/, "")
101
+ gsub(/`/, "")
102
+ # Drop nested-bullet continuation lines that survived earlier filters.
103
+ if (length($0) == 0) next
104
+ if (length($0) > n) line = substr($0, 1, n) "…"
105
+ else line = $0
106
+ if (out == "") out = line
107
+ else out = out "; " line
108
+ }
109
+ END { print out }
110
+ '
111
+ }
112
+
113
+ # Extract ADR-NNN references from Related bullets. Compact "ADR-NNN" listing
114
+ # is sufficient for routine compliance graph navigation; full relationship
115
+ # prose (amends/extends/relates/composes) is preserved in the per-ADR body.
116
+ extract_related_ids() {
117
+ awk '
118
+ {
119
+ while (match($0, /ADR-[0-9]+/)) {
120
+ ref = substr($0, RSTART, RLENGTH)
121
+ if (!seen[ref]++) {
122
+ if (out == "") out = ref
123
+ else out = out ", " ref
124
+ }
125
+ $0 = substr($0, RSTART + RLENGTH)
126
+ }
127
+ }
128
+ END { print out }
129
+ '
130
+ }
131
+
132
+ # --- Sanitisers ------------------------------------------------------------
133
+
134
+ # Strip markdown links `[text](url)` -> `text`.
135
+ strip_links() {
136
+ sed -E 's/\[([^]]+)\]\([^)]+\)/\1/g'
137
+ }
138
+
139
+ # Collapse to a single line: replace newlines + carriage returns with spaces,
140
+ # squeeze runs of spaces, trim leading/trailing whitespace.
141
+ oneline() {
142
+ tr '\n\r' ' ' | tr -s ' ' | sed 's/^ *//; s/ *$//'
143
+ }
144
+
145
+ # Truncate a string to N chars + ellipsis if longer. Avoids slicing inside
146
+ # a markdown emphasis pair (e.g. `**text**`) — if the truncation would land
147
+ # inside `**...**`, round back to before the opening pair.
148
+ truncate_with_ellipsis() {
149
+ local s="$1" n="$2"
150
+ if [ "${#s}" -le "$n" ]; then
151
+ printf '%s' "$s"
152
+ return
153
+ fi
154
+ printf '%s' "${s:0:n}…"
155
+ }
156
+
157
+ # --- Per-ADR entry emitter -------------------------------------------------
158
+
159
+ emit_entry() {
160
+ local file="$1"
161
+ local id title status oversight superseded
162
+ local chosen drivers confirmation related
163
+
164
+ id=$(basename "$file" | grep -oE '^[0-9]+')
165
+ title=$(get_title "$file")
166
+ status=$(get_frontmatter_field "$file" "status")
167
+ oversight=$(get_frontmatter_field "$file" "human-oversight")
168
+ superseded=$(get_frontmatter_field "$file" "supersedes")
169
+
170
+ # Chosen-option line — truncate to a comfortable summary length.
171
+ chosen=$(get_chosen "$file" | strip_links | oneline)
172
+ chosen=$(printf '%s' "$chosen" | awk -v n=240 '{ if (length($0) > n) print substr($0,1,n) "…"; else print }')
173
+
174
+ # Confirmation: cap 5 bullets, ≤ 110 chars each, joined with "; " on one line.
175
+ # This is the routine-compliance scannable view; the full Confirmation list
176
+ # remains in the per-ADR body for deep-dive surfaces.
177
+ confirmation=$(get_bullets "$file" "Confirmation" 5 | strip_links | compact_join_bullets 110)
178
+
179
+ # Related: extract ADR-NNN graph references only. Full relationship prose
180
+ # (amends / extends / relates / composes) stays in the per-ADR body.
181
+ related=$(get_bullets "$file" "Related" 20 | strip_links | extract_related_ids)
182
+
183
+ # Decision Drivers intentionally NOT emitted in the routine view (per
184
+ # ADR-077 Decision Outcome — drivers belong on the deep-dive surface, not
185
+ # the routine compliance load). If a future iteration needs them, add a
186
+ # `--with-drivers` flag rather than emit by default.
187
+
188
+ # Header line — ID + Title + status badges.
189
+ {
190
+ echo ""
191
+ echo "### ADR-${id} — ${title}"
192
+ # Status / oversight / supersession badges on one compact line.
193
+ local badges="**Status:** ${status:-?}"
194
+ if [ -n "$oversight" ]; then
195
+ badges="${badges} | **Oversight:** ${oversight}"
196
+ fi
197
+ if [ -n "$superseded" ] && [ "$superseded" != "[]" ]; then
198
+ badges="${badges} | **Supersedes:** ${superseded}"
199
+ fi
200
+ echo "${badges}"
201
+ if [ -n "$chosen" ]; then
202
+ echo "**Chosen:** ${chosen}"
203
+ fi
204
+ if [ -n "$confirmation" ]; then
205
+ echo "**Confirmation:** ${confirmation}"
206
+ fi
207
+ if [ -n "$related" ]; then
208
+ echo "**Related:** ${related}"
209
+ fi
210
+ }
211
+ }
212
+
213
+ # --- Compendium emission ---------------------------------------------------
214
+
215
+ # Collect + sort ADR files. README.md and any sibling -history.md / -summary.md
216
+ # style files (future P194 etc.) are excluded.
217
+ files=()
218
+ while IFS= read -r f; do
219
+ files+=("$f")
220
+ done < <(find "$DECISIONS_DIR" -maxdepth 1 -type f -name '*.md' \
221
+ ! -name 'README.md' \
222
+ ! -name '*-history.md' \
223
+ ! -name '*-summary.md' \
224
+ 2>/dev/null | sort)
225
+
226
+ total=${#files[@]}
227
+
228
+ # Header is deterministic — NO timestamp, NO date. The compendium must be
229
+ # idempotent (same input bodies => byte-identical output) so the ADR-077
230
+ # drift-detection bats can compare the committed file against a fresh
231
+ # generator run and detect any divergence as substance drift, not as
232
+ # date-stamp churn.
233
+ {
234
+ echo "# Decisions Compendium"
235
+ echo ""
236
+ echo "<!-- AUTO-GENERATED by packages/architect/scripts/generate-decisions-compendium.sh per ADR-077 — do NOT hand-edit; regenerate via \`wr-architect-generate-decisions-compendium\`. -->"
237
+ echo ""
238
+ echo "Compact rendered index of every ADR's chosen option, confirmation criteria, and relationship graph. **Authoritative substance lives in the per-ADR body** (\`<NNN>-<slug>.<status>.md\`); this compendium is a derived view for routine \`wr-architect:agent\` compliance review."
239
+ echo ""
240
+ echo "For deep-dive — creating, evolving, ratifying, or contesting a decision — open the per-ADR file directly. \`/wr-architect:create-adr\`, \`/wr-architect:capture-adr\`, and \`/wr-architect:review-decisions\` all keep the full body in scope. Decision Drivers, Considered Options bodies, Pros and Cons, Consequences narrative, and Reassessment Criteria are intentionally NOT in this routine view — they live in the per-ADR body."
241
+ echo ""
242
+ echo "**Total ADRs:** ${total}"
243
+ echo ""
244
+ echo "---"
245
+ for f in "${files[@]}"; do
246
+ emit_entry "$f"
247
+ done
248
+ } > "$COMPENDIUM"
249
+
250
+ echo "generate-decisions-compendium: wrote $COMPENDIUM (${total} ADRs)" >&2