@windyroad/architect 0.21.3 → 0.21.4-preview.1105
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
|
@@ -84,14 +84,31 @@ if [ ! -d "$DECISIONS_DIR" ]; then
|
|
|
84
84
|
exit 2
|
|
85
85
|
fi
|
|
86
86
|
|
|
87
|
-
|
|
88
|
-
|
|
87
|
+
if ! command -v iconv >/dev/null 2>&1; then
|
|
88
|
+
echo "generate-decisions-compendium: iconv is required for UTF-8 validation" >&2
|
|
89
|
+
exit 2
|
|
90
|
+
fi
|
|
91
|
+
|
|
92
|
+
# Generate into a temporary file so invalid or incomplete output never
|
|
93
|
+
# replaces the last known-good compendium. Write mode uses the target
|
|
94
|
+
# directory so the final rename is atomic.
|
|
89
95
|
if [ "$CHECK_MODE" = "1" ]; then
|
|
90
|
-
COMPENDIUM=$(mktemp -t architect-compendium-check.XXXXXX)
|
|
91
|
-
|
|
96
|
+
if ! COMPENDIUM=$(mktemp -t architect-compendium-check.XXXXXX); then
|
|
97
|
+
echo "generate-decisions-compendium: could not create check-mode temporary file" >&2
|
|
98
|
+
exit 2
|
|
99
|
+
fi
|
|
92
100
|
else
|
|
93
|
-
COMPENDIUM
|
|
101
|
+
if ! COMPENDIUM=$(mktemp "${TARGET_COMPENDIUM}.tmp.XXXXXX"); then
|
|
102
|
+
echo "generate-decisions-compendium: could not create temporary compendium beside $TARGET_COMPENDIUM" >&2
|
|
103
|
+
exit 2
|
|
104
|
+
fi
|
|
94
105
|
fi
|
|
106
|
+
cleanup_compendium() {
|
|
107
|
+
if [ -n "${COMPENDIUM:-}" ] && [ -f "$COMPENDIUM" ]; then
|
|
108
|
+
rm -f "$COMPENDIUM"
|
|
109
|
+
fi
|
|
110
|
+
}
|
|
111
|
+
trap cleanup_compendium EXIT
|
|
95
112
|
|
|
96
113
|
# --- Field extractors ------------------------------------------------------
|
|
97
114
|
|
|
@@ -130,15 +147,18 @@ get_section() {
|
|
|
130
147
|
' "$file"
|
|
131
148
|
}
|
|
132
149
|
|
|
133
|
-
# Extract the "Chosen option:"
|
|
150
|
+
# Extract the first "Chosen option:" paragraph from the Decision Outcome section.
|
|
134
151
|
# Matches the common MADR shapes:
|
|
135
152
|
# Chosen option: **"X"**, because Y.
|
|
136
153
|
# Chosen option: X, because Y.
|
|
137
154
|
# Chosen: X.
|
|
138
155
|
get_chosen() {
|
|
139
156
|
get_section "$1" "Decision Outcome" \
|
|
140
|
-
| awk '
|
|
141
|
-
|
|
157
|
+
| awk '
|
|
158
|
+
/^Chosen/ { in_chosen = 1 }
|
|
159
|
+
in_chosen && NF == 0 { exit }
|
|
160
|
+
in_chosen { print }
|
|
161
|
+
'
|
|
142
162
|
}
|
|
143
163
|
|
|
144
164
|
# Extract top-level bullet lines (`- ...`) from a section. Skips nested
|
|
@@ -157,7 +177,8 @@ get_bullets() {
|
|
|
157
177
|
# Joins with "; ". Strips markdown emphasis to keep the line scannable.
|
|
158
178
|
compact_join_bullets() {
|
|
159
179
|
local per_item="${1:-120}"
|
|
160
|
-
|
|
180
|
+
local compacted
|
|
181
|
+
if ! compacted=$(awk -v n="$per_item" '
|
|
161
182
|
{
|
|
162
183
|
# Strip leading checkbox markers `[ ]` / `[x]` (from Confirmation).
|
|
163
184
|
sub(/^\[[ x]\] */, "")
|
|
@@ -172,7 +193,11 @@ compact_join_bullets() {
|
|
|
172
193
|
else out = out "; " line
|
|
173
194
|
}
|
|
174
195
|
END { print out }
|
|
175
|
-
'
|
|
196
|
+
' | iconv -f UTF-8 -t UTF-8 -c); then
|
|
197
|
+
echo "generate-decisions-compendium: failed to truncate confirmation text safely" >&2
|
|
198
|
+
return 1
|
|
199
|
+
fi
|
|
200
|
+
printf '%s\n' "$compacted"
|
|
176
201
|
}
|
|
177
202
|
|
|
178
203
|
# Extract ADR-NNN references from Related bullets. Compact "ADR-NNN" listing
|
|
@@ -207,16 +232,27 @@ oneline() {
|
|
|
207
232
|
tr '\n\r' ' ' | tr -s ' ' | sed 's/^ *//; s/ *$//'
|
|
208
233
|
}
|
|
209
234
|
|
|
210
|
-
# Truncate a string to N chars + ellipsis if longer. Avoids slicing
|
|
211
|
-
#
|
|
212
|
-
# inside `**...**`, round back to before the opening pair.
|
|
235
|
+
# Truncate a string to N chars + ellipsis if longer. Avoids slicing a `**`
|
|
236
|
+
# marker in half and closes emphasis when truncation lands inside it.
|
|
213
237
|
truncate_with_ellipsis() {
|
|
214
|
-
local s="$1" n="$2"
|
|
238
|
+
local s="$1" n="$2" truncated remainder pairs=0
|
|
215
239
|
if [ "${#s}" -le "$n" ]; then
|
|
216
240
|
printf '%s' "$s"
|
|
217
241
|
return
|
|
218
242
|
fi
|
|
219
|
-
|
|
243
|
+
truncated="${s:0:n}"
|
|
244
|
+
if [[ "$truncated" == *\* && "${s:n:1}" == "*" ]]; then
|
|
245
|
+
truncated="${truncated%?}"
|
|
246
|
+
fi
|
|
247
|
+
remainder="$truncated"
|
|
248
|
+
while [[ "$remainder" == *"**"* ]]; do
|
|
249
|
+
remainder="${remainder#*"**"}"
|
|
250
|
+
pairs=$((pairs + 1))
|
|
251
|
+
done
|
|
252
|
+
if [ $((pairs % 2)) -ne 0 ]; then
|
|
253
|
+
truncated="${truncated}**"
|
|
254
|
+
fi
|
|
255
|
+
printf '%s' "${truncated}..."
|
|
220
256
|
}
|
|
221
257
|
|
|
222
258
|
# --- Per-ADR entry emitter -------------------------------------------------
|
|
@@ -242,12 +278,18 @@ emit_entry() {
|
|
|
242
278
|
|
|
243
279
|
# Chosen-option line — truncate to a comfortable summary length.
|
|
244
280
|
chosen=$(get_chosen "$file" | strip_links | oneline)
|
|
245
|
-
chosen=$(
|
|
281
|
+
if ! chosen=$(truncate_with_ellipsis "$chosen" 240 \
|
|
282
|
+
| iconv -f UTF-8 -t UTF-8 -c); then
|
|
283
|
+
echo "generate-decisions-compendium: failed to truncate chosen text safely for $file" >&2
|
|
284
|
+
return 1
|
|
285
|
+
fi
|
|
246
286
|
|
|
247
287
|
# Confirmation: cap 5 bullets, ≤ 110 chars each, joined with "; " on one line.
|
|
248
288
|
# This is the routine-compliance scannable view; the full Confirmation list
|
|
249
289
|
# remains in the per-ADR body for deep-dive surfaces.
|
|
250
|
-
confirmation=$(get_bullets "$file" "Confirmation" 5 | strip_links | compact_join_bullets 110)
|
|
290
|
+
if ! confirmation=$(get_bullets "$file" "Confirmation" 5 | strip_links | compact_join_bullets 110); then
|
|
291
|
+
return 1
|
|
292
|
+
fi
|
|
251
293
|
|
|
252
294
|
# Related: extract ADR-NNN graph references only. Full relationship prose
|
|
253
295
|
# (amends / extends / relates / composes) stays in the per-ADR body.
|
|
@@ -311,6 +353,13 @@ done < <(find "$DECISIONS_DIR" -maxdepth 1 -type f -name '*.md' \
|
|
|
311
353
|
! -name '*-summary.md' \
|
|
312
354
|
2>/dev/null | sort)
|
|
313
355
|
|
|
356
|
+
for f in "${all_files[@]}"; do
|
|
357
|
+
if ! iconv -f UTF-8 -t UTF-8 "$f" >/dev/null 2>&1; then
|
|
358
|
+
echo "generate-decisions-compendium: invalid UTF-8 in authoritative ADR: $f" >&2
|
|
359
|
+
exit 1
|
|
360
|
+
fi
|
|
361
|
+
done
|
|
362
|
+
|
|
314
363
|
in_force_files=()
|
|
315
364
|
historical_files=()
|
|
316
365
|
for f in "${all_files[@]}"; do
|
|
@@ -364,7 +413,9 @@ total=$((in_force_total + historical_total))
|
|
|
364
413
|
echo ""
|
|
365
414
|
echo "_${in_force_total} ADRs. These are the current rules. The architect agent reads this section first for routine compliance review._"
|
|
366
415
|
for f in "${in_force_files[@]}"; do
|
|
367
|
-
emit_entry "$f"
|
|
416
|
+
if ! emit_entry "$f"; then
|
|
417
|
+
exit 1
|
|
418
|
+
fi
|
|
368
419
|
done
|
|
369
420
|
if [ "$historical_total" -gt 0 ]; then
|
|
370
421
|
echo ""
|
|
@@ -374,11 +425,40 @@ total=$((in_force_total + historical_total))
|
|
|
374
425
|
echo ""
|
|
375
426
|
echo "_${historical_total} ADRs. These were tried and superseded, rejected, or deprecated. Read them as direction for what NOT to do, or to understand the lineage of an in-force decision. Do not enforce them as current rules._"
|
|
376
427
|
for f in "${historical_files[@]}"; do
|
|
377
|
-
emit_entry "$f"
|
|
428
|
+
if ! emit_entry "$f"; then
|
|
429
|
+
exit 1
|
|
430
|
+
fi
|
|
378
431
|
done
|
|
379
432
|
fi
|
|
380
433
|
} > "$COMPENDIUM"
|
|
381
434
|
|
|
435
|
+
validate_compendium() {
|
|
436
|
+
local file="$1" in_force_count historical_count expected_historical=0
|
|
437
|
+
if ! iconv -f UTF-8 -t UTF-8 "$file" >/dev/null 2>&1; then
|
|
438
|
+
echo "generate-decisions-compendium: generated output is not valid UTF-8" >&2
|
|
439
|
+
return 1
|
|
440
|
+
fi
|
|
441
|
+
if ! in_force_count=$(awk '$0 == "## In-force decisions" { n++ } END { print n + 0 }' "$file"); then
|
|
442
|
+
echo "generate-decisions-compendium: could not validate the in-force section" >&2
|
|
443
|
+
return 1
|
|
444
|
+
fi
|
|
445
|
+
if ! historical_count=$(awk '$0 == "## Historical decisions" { n++ } END { print n + 0 }' "$file"); then
|
|
446
|
+
echo "generate-decisions-compendium: could not validate the historical section" >&2
|
|
447
|
+
return 1
|
|
448
|
+
fi
|
|
449
|
+
if [ "$historical_total" -gt 0 ]; then
|
|
450
|
+
expected_historical=1
|
|
451
|
+
fi
|
|
452
|
+
if [ "$in_force_count" -ne 1 ] || [ "$historical_count" -ne "$expected_historical" ]; then
|
|
453
|
+
echo "generate-decisions-compendium: generated output has invalid section structure (in-force=$in_force_count, historical=$historical_count, expected historical=$expected_historical)" >&2
|
|
454
|
+
return 1
|
|
455
|
+
fi
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
if ! validate_compendium "$COMPENDIUM"; then
|
|
459
|
+
exit 1
|
|
460
|
+
fi
|
|
461
|
+
|
|
382
462
|
if [ "$CHECK_MODE" = "1" ]; then
|
|
383
463
|
# Check mode: diff temp against target. Idempotency contract holds only
|
|
384
464
|
# when both files exist; treat absence as "stale" (drift detected).
|
|
@@ -403,4 +483,14 @@ if [ "$CHECK_MODE" = "1" ]; then
|
|
|
403
483
|
exit 1
|
|
404
484
|
fi
|
|
405
485
|
|
|
406
|
-
|
|
486
|
+
if ! chmod 0644 "$COMPENDIUM"; then
|
|
487
|
+
echo "generate-decisions-compendium: could not set compendium mode to 0644" >&2
|
|
488
|
+
exit 1
|
|
489
|
+
fi
|
|
490
|
+
if ! mv -f "$COMPENDIUM" "$TARGET_COMPENDIUM"; then
|
|
491
|
+
echo "generate-decisions-compendium: could not replace $TARGET_COMPENDIUM" >&2
|
|
492
|
+
exit 1
|
|
493
|
+
fi
|
|
494
|
+
COMPENDIUM=""
|
|
495
|
+
|
|
496
|
+
echo "generate-decisions-compendium: wrote $TARGET_COMPENDIUM (${total} ADRs total — ${in_force_total} in-force, ${historical_total} historical)" >&2
|