@windyroad/architect 0.21.4-preview.1104 → 0.21.4-preview.1106

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@windyroad/architect",
3
- "version": "0.21.4-preview.1104",
3
+ "version": "0.21.4-preview.1106",
4
4
  "description": "Architecture decision enforcement for AI coding agents",
5
5
  "bin": {
6
6
  "windyroad-architect": "./bin/install.mjs"
@@ -112,19 +112,41 @@ trap cleanup_compendium EXIT
112
112
 
113
113
  # --- Field extractors ------------------------------------------------------
114
114
 
115
- # Read a frontmatter scalar field (single line `key: value`).
115
+ # Read a frontmatter scalar or block-list field. Scalars are returned as-is;
116
+ # block lists are compacted to the same `[item, item]` shape as inline lists.
116
117
  # Strips surrounding quotes and leading/trailing whitespace.
117
118
  get_frontmatter_field() {
118
119
  local file="$1" field="$2"
119
120
  awk -v f="$field" '
121
+ function emit_list( i) {
122
+ if (!list_count || list_emitted) return
123
+ printf "["
124
+ for (i = 1; i <= list_count; i++) {
125
+ if (i > 1) printf ", "
126
+ printf "%s", list[i]
127
+ }
128
+ print "]"
129
+ list_emitted = 1
130
+ }
120
131
  /^---$/ { fm = !fm; if (!fm) exit; next }
132
+ in_list && /^ - / {
133
+ item = $0
134
+ sub(/^ - */, "", item)
135
+ list[++list_count] = item
136
+ next
137
+ }
138
+ in_list { emit_list(); exit }
121
139
  fm && $0 ~ "^"f":" {
122
140
  sub("^"f": *", "")
123
141
  gsub(/^["'"'"']|["'"'"']$/, "")
124
142
  sub(/^ +/, ""); sub(/ +$/, "")
125
- print
126
- exit
143
+ if (length($0)) {
144
+ print
145
+ exit
146
+ }
147
+ in_list = 1
127
148
  }
149
+ END { emit_list() }
128
150
  ' "$file"
129
151
  }
130
152
 
@@ -147,24 +169,32 @@ get_section() {
147
169
  ' "$file"
148
170
  }
149
171
 
150
- # Extract the "Chosen option:" line from the Decision Outcome section.
172
+ # Extract the first "Chosen option:" paragraph from the Decision Outcome section.
151
173
  # Matches the common MADR shapes:
152
174
  # Chosen option: **"X"**, because Y.
153
175
  # Chosen option: X, because Y.
154
176
  # Chosen: X.
155
177
  get_chosen() {
156
178
  get_section "$1" "Decision Outcome" \
157
- | awk '/^Chosen/ { print; exit }' \
158
- | head -1
179
+ | awk '
180
+ /^Chosen/ { in_chosen = 1 }
181
+ in_chosen && NF == 0 { exit }
182
+ in_chosen { print }
183
+ '
159
184
  }
160
185
 
161
186
  # Extract top-level bullet lines (`- ...`) from a section. Skips nested
162
- # ` - ...` sub-bullets to keep the compendium dense. Capped at N entries.
187
+ # ` - ...` sub-bullets to keep the compendium dense. Capped at N entries;
188
+ # a cap of 0 preserves every item.
163
189
  get_bullets() {
164
190
  local file="$1" section="$2" cap="${3:-5}"
165
191
  get_section "$file" "$section" \
166
- | awk '/^- / { sub(/^- */, ""); print }' \
167
- | head -"$cap"
192
+ | awk -v cap="$cap" '/^- / {
193
+ sub(/^- */, "")
194
+ print
195
+ count++
196
+ if (cap > 0 && count >= cap) exit
197
+ }'
168
198
  }
169
199
 
170
200
  # Compact-join bullets onto one line, truncating each to N chars + "...".
@@ -229,24 +259,35 @@ oneline() {
229
259
  tr '\n\r' ' ' | tr -s ' ' | sed 's/^ *//; s/ *$//'
230
260
  }
231
261
 
232
- # Truncate a string to N chars + ellipsis if longer. Avoids slicing inside
233
- # a markdown emphasis pair (e.g. `**text**`) if the truncation would land
234
- # inside `**...**`, round back to before the opening pair.
262
+ # Truncate a string to N chars + ellipsis if longer. Avoids slicing a `**`
263
+ # marker in half and closes emphasis when truncation lands inside it.
235
264
  truncate_with_ellipsis() {
236
- local s="$1" n="$2"
265
+ local s="$1" n="$2" truncated remainder pairs=0
237
266
  if [ "${#s}" -le "$n" ]; then
238
267
  printf '%s' "$s"
239
268
  return
240
269
  fi
241
- printf '%s' "${s:0:n}..."
270
+ truncated="${s:0:n}"
271
+ if [[ "$truncated" == *\* && "${s:n:1}" == "*" ]]; then
272
+ truncated="${truncated%?}"
273
+ fi
274
+ remainder="$truncated"
275
+ while [[ "$remainder" == *"**"* ]]; do
276
+ remainder="${remainder#*"**"}"
277
+ pairs=$((pairs + 1))
278
+ done
279
+ if [ $((pairs % 2)) -ne 0 ]; then
280
+ truncated="${truncated}**"
281
+ fi
282
+ printf '%s' "${truncated}..."
242
283
  }
243
284
 
244
285
  # --- Per-ADR entry emitter -------------------------------------------------
245
286
 
246
287
  emit_entry() {
247
288
  local file="$1"
248
- local id title status oversight superseded supersede_ticket
249
- local chosen drivers confirmation related
289
+ local id title status oversight superseded superseded_by supersede_ticket
290
+ local chosen drivers confirmation related amends supplements
250
291
 
251
292
  id=$(basename "$file" | grep -oE '^[0-9]+')
252
293
  title=$(get_title "$file")
@@ -256,6 +297,9 @@ emit_entry() {
256
297
  esac
257
298
  oversight=$(get_frontmatter_field "$file" "human-oversight")
258
299
  superseded=$(get_frontmatter_field "$file" "supersedes")
300
+ superseded_by=$(get_frontmatter_field "$file" "superseded-by")
301
+ amends=$(get_frontmatter_field "$file" "amends")
302
+ supplements=$(get_frontmatter_field "$file" "supplements")
259
303
  # ADR-066 amendment (P316): when the oversight value is
260
304
  # `rejected-pending-supersede`, surface the tracking ticket parenthetically
261
305
  # so the compendium badge shows both the disposition AND the supersede in
@@ -264,17 +308,15 @@ emit_entry() {
264
308
 
265
309
  # Chosen-option line — truncate to a comfortable summary length.
266
310
  chosen=$(get_chosen "$file" | strip_links | oneline)
267
- if ! chosen=$(printf '%s' "$chosen" \
268
- | awk -v n=240 '{ if (length($0) > n) print substr($0,1,n) "..."; else print }' \
311
+ if ! chosen=$(truncate_with_ellipsis "$chosen" 240 \
269
312
  | iconv -f UTF-8 -t UTF-8 -c); then
270
313
  echo "generate-decisions-compendium: failed to truncate chosen text safely for $file" >&2
271
314
  return 1
272
315
  fi
273
316
 
274
- # Confirmation: cap 5 bullets, ≤ 110 chars each, joined with "; " on one line.
275
- # This is the routine-compliance scannable view; the full Confirmation list
276
- # remains in the per-ADR body for deep-dive surfaces.
277
- if ! confirmation=$(get_bullets "$file" "Confirmation" 5 | strip_links | compact_join_bullets 110); then
317
+ # Confirmation: preserve every top-level item, ≤ 110 chars each, joined
318
+ # with "; " on one line for the routine-compliance scannable view.
319
+ if ! confirmation=$(get_bullets "$file" "Confirmation" 0 | strip_links | compact_join_bullets 110); then
278
320
  return 1
279
321
  fi
280
322
 
@@ -304,6 +346,15 @@ emit_entry() {
304
346
  badges="${badges} | **Supersedes:** ${superseded}"
305
347
  fi
306
348
  echo "${badges}"
349
+ if [ -n "$superseded_by" ] && [ "$superseded_by" != "[]" ]; then
350
+ echo "**Superseded-by:** ${superseded_by}"
351
+ fi
352
+ if [ -n "$amends" ] && [ "$amends" != "[]" ]; then
353
+ echo "**Amends:** ${amends}"
354
+ fi
355
+ if [ -n "$supplements" ] && [ "$supplements" != "[]" ]; then
356
+ echo "**Supplements:** ${supplements}"
357
+ fi
307
358
  if [ -n "$chosen" ]; then
308
359
  echo "**Chosen:** ${chosen}"
309
360
  fi