@windyroad/architect 0.20.3 → 0.20.4

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.
Files changed (40) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/.codex-plugin/plugin.json +1 -1
  3. package/package.json +6 -2
  4. package/agents/test/architect-codex-eval-contract.bats +0 -33
  5. package/agents/test/architect-needs-direction-verdict.bats +0 -64
  6. package/agents/test/architect-output-formatting.bats +0 -26
  7. package/agents/test/architect-performance-review.bats +0 -76
  8. package/agents/test/architect-pre-edit-review-mode.bats +0 -50
  9. package/agents/test/architect-unratified-dependency-verdict.bats +0 -55
  10. package/hooks/test/architect-compendium-update-entry.bats +0 -299
  11. package/hooks/test/architect-detect-once-per-session.bats +0 -106
  12. package/hooks/test/architect-detect-scope.bats +0 -56
  13. package/hooks/test/architect-enforce-scope.bats +0 -124
  14. package/hooks/test/architect-gate.bats +0 -102
  15. package/hooks/test/architect-mark-reviewed-verdict-grep.bats +0 -113
  16. package/hooks/test/architect-mark-reviewed.bats +0 -26
  17. package/hooks/test/architect-marker-only-exempt.bats +0 -183
  18. package/hooks/test/architect-no-stop-hook.bats +0 -17
  19. package/hooks/test/architect-oversight-marker-discipline.bats +0 -245
  20. package/hooks/test/architect-oversight-nudge.bats +0 -75
  21. package/hooks/test/architect-project-root.bats +0 -81
  22. package/hooks/test/architect-readme-pairing-check.bats +0 -126
  23. package/hooks/test/architect-slide-marker.bats +0 -92
  24. package/hooks/test/codex-agent-completion.bats +0 -64
  25. package/hooks/test/slide-marker-on-subprocess-return.bats +0 -118
  26. package/hooks/test/substance-aware-drift.bats +0 -243
  27. package/scripts/test/check-amends-backreference.bats +0 -68
  28. package/scripts/test/codex-pack-and-agent.bats +0 -86
  29. package/scripts/test/detect-unoversighted.bats +0 -139
  30. package/scripts/test/generate-decisions-compendium.bats +0 -210
  31. package/scripts/test/is-decision-unconfirmed.bats +0 -143
  32. package/skills/capture-adr/test/capture-adr.bats +0 -362
  33. package/skills/create-adr/test/create-adr-adr-044-contract.bats +0 -185
  34. package/skills/create-adr/test/create-adr-codex-eval-contract.bats +0 -34
  35. package/skills/create-adr/test/create-adr-decision-boundary.bats +0 -49
  36. package/skills/create-adr/test/create-adr-id-collision-guard.bats +0 -35
  37. package/skills/create-adr/test/create-adr-next-id-origin-lookup.bats +0 -45
  38. package/skills/create-adr/test/create-adr-rename-restage-reminder.bats +0 -38
  39. package/skills/create-adr/test/create-adr-substance-confirm-pattern.bats +0 -169
  40. package/skills/review-design/test/review-design-codex-contract.bats +0 -22
@@ -1,68 +0,0 @@
1
- #!/usr/bin/env bats
2
- # Behavioural tests for check-amends-backreference.sh.
3
- # Runs the real script against real fixture trees and asserts on exit status and
4
- # stderr — never on the script's source text (ADR-052 / P081).
5
- #
6
- # @adr ADR-051 ADR-052 @problem P456
7
-
8
- setup() {
9
- CHECK="$(cd "$(dirname "$BATS_TEST_FILENAME")/.." && pwd)/check-amends-backreference.sh"
10
- TMP="$(mktemp -d)"; cd "$TMP"; mkdir -p d
11
- }
12
- teardown() { cd /; rm -rf "$TMP"; }
13
-
14
- amender() { printf -- '---\nstatus: "proposed"\namends: [%s]\n---\n\n# ADR-%s\n' "$2" "$1" > "d/$1-amender.md"; }
15
- amended() { printf -- '---\nstatus: "proposed"\n---\n\n# ADR-%s\n\n%s\n' "$1" "$2" > "d/$1-amended.md"; }
16
-
17
- @test "passes when the amended ADR back-references the amending one" {
18
- amender 101 "ADR-090"
19
- amended 090 "Exercised 2026-07-26 by ADR-101, which coarsens this rule."
20
- run bash "$CHECK" d
21
- [ "$status" -eq 0 ]
22
- }
23
-
24
- @test "fails when the amended ADR never mentions the amending one" {
25
- amender 101 "ADR-090"
26
- amended 090 "This decision stands unqualified."
27
- run bash "$CHECK" d
28
- [ "$status" -eq 1 ]
29
- echo "$output" | grep -q '090'
30
- echo "$output" | grep -q 'ADR-101'
31
- }
32
-
33
- @test "checks EVERY entry in a multi-target amends list, not just the first" {
34
- amender 101 "ADR-090, ADR-095, ADR-096"
35
- amended 090 "Amended by ADR-101."
36
- amended 095 "Amended by ADR-101."
37
- amended 096 "This decision stands unqualified."
38
- run bash "$CHECK" d
39
- [ "$status" -eq 1 ]
40
- echo "$output" | grep -q '096'
41
- ! echo "$output" | grep -q '095'
42
- }
43
-
44
- @test "fails when an amends: target does not resolve at all" {
45
- amender 101 "ADR-777"
46
- run bash "$CHECK" d
47
- [ "$status" -eq 1 ]
48
- echo "$output" | grep -q 'ADR-777'
49
- }
50
-
51
- @test "an ADR with no amends: declaration is not checked" {
52
- amended 090 "A decision that amends nothing."
53
- run bash "$CHECK" d
54
- [ "$status" -eq 0 ]
55
- }
56
-
57
- @test "a body mention of the word amends is not read as a declaration" {
58
- printf -- '---\nstatus: "proposed"\n---\n\n# ADR-101\n\nThis amends: nothing in particular, per ADR-999.\n' > d/101-prose.md
59
- run bash "$CHECK" d
60
- [ "$status" -eq 0 ]
61
- }
62
-
63
- @test "the live corpus satisfies the invariant" {
64
- repo="$(cd "$(dirname "$BATS_TEST_FILENAME")/../../../.." && pwd)"
65
- [ -d "$repo/docs/decisions" ] || skip "not running in the source repo"
66
- run bash "$CHECK" "$repo/docs/decisions"
67
- [ "$status" -eq 0 ]
68
- }
@@ -1,86 +0,0 @@
1
- #!/usr/bin/env bats
2
-
3
- setup() {
4
- unset CODEX_THREAD_ID
5
- PACKAGE="$(cd "$(dirname "$BATS_TEST_FILENAME")/../.." && pwd)"
6
- TMP="$(mktemp -d)"
7
- }
8
-
9
- @test "agent scope selects project or user directory" {
10
- mkdir -p "$TMP/project"
11
- (cd "$TMP/project" && node "$PACKAGE/scripts/codex-agent.mjs" --scope project >/dev/null)
12
- [ -f "$TMP/project/.codex/agents/wr-architect-agent.toml" ]
13
-
14
- CODEX_HOME="$TMP/home" node "$PACKAGE/scripts/codex-agent.mjs" --scope user >/dev/null
15
- [ -f "$TMP/home/agents/wr-architect-agent.toml" ]
16
- }
17
-
18
- @test "SessionStart repairs only Codex user registration" {
19
- CODEX_HOME="$TMP/claude-home" node "$PACKAGE/scripts/codex-agent.mjs" --session-start
20
- [ ! -d "$TMP/claude-home/agents" ]
21
-
22
- run env CODEX_THREAD_ID=test CODEX_HOME="$TMP/codex-home" node "$PACKAGE/scripts/codex-agent.mjs" --session-start
23
- [ "$status" -eq 0 ]
24
- [[ "$output" == *"restart Codex"* ]]
25
- [ -f "$TMP/codex-home/agents/wr-architect-agent.toml" ]
26
-
27
- run env CODEX_THREAD_ID=test CODEX_HOME="$TMP/codex-home" node "$PACKAGE/scripts/codex-agent.mjs" --session-start
28
- [ "$status" -eq 0 ]
29
- [ -z "$output" ]
30
- }
31
-
32
- @test "default installer remains Claude-only" {
33
- run node "$PACKAGE/bin/install.mjs" --dry-run
34
- [ "$status" -eq 0 ]
35
- [[ "$output" == *"claude plugin marketplace add"* ]]
36
- [[ "$output" != *"codex plugin marketplace add"* ]]
37
- }
38
-
39
- @test "packed installer registers plugin and exact agent in isolated Codex home" {
40
- run npm pack "$PACKAGE" --pack-destination "$TMP"
41
- [ "$status" -eq 0 ]
42
- export CODEX_HOME="$TMP/codex-installed"
43
- run npm exec --yes --package "$TMP"/*.tgz -- windyroad-architect --runtime codex --scope user
44
- [ "$status" -eq 0 ]
45
- [ -f "$CODEX_HOME/agents/wr-architect-agent.toml" ]
46
- run codex plugin list
47
- [ "$status" -eq 0 ]
48
- [[ "$output" == *"wr-architect@windyroad-architect-local"* ]]
49
- }
50
-
51
- teardown() {
52
- node "$PACKAGE/scripts/sync-codex-skills.mjs" --restore-pack >/dev/null 2>&1 || true
53
- rm -rf "$TMP"
54
- }
55
-
56
- @test "npm pack emits Codex skills and restores Claude sources" {
57
- before="$(shasum -a 256 "$PACKAGE"/skills/*/SKILL.md)"
58
- run npm pack "$PACKAGE" --pack-destination "$TMP"
59
- [ "$status" -eq 0 ]
60
- [ "$before" = "$(shasum -a 256 "$PACKAGE"/skills/*/SKILL.md)" ]
61
-
62
- tar -xzf "$TMP"/*.tgz -C "$TMP"
63
- run grep -F 'bash "<architect-plugin-root>/scripts/generate-decisions-compendium.sh"' "$TMP/package/skills/review-decisions/SKILL.md"
64
- [ "$status" -eq 0 ]
65
- run grep -F 'use `request_user_input`' "$TMP/package/skills/review-decisions/SKILL.md"
66
- [ "$status" -eq 0 ]
67
- run grep -F 'wr-architect-generate-decisions-compendium' "$TMP/package/skills/review-decisions/SKILL.md"
68
- [ "$status" -ne 0 ]
69
- run grep -F 'packages/architect/' "$TMP/package/skills/create-adr/SKILL.md"
70
- [ "$status" -ne 0 ]
71
- }
72
-
73
- @test "Codex agent install is exact, owned, and removable" {
74
- export CODEX_HOME="$TMP/codex"
75
- run node "$PACKAGE/scripts/codex-agent.mjs" --scope user
76
- [ "$status" -eq 0 ]
77
- target="$CODEX_HOME/agents/wr-architect-agent.toml"
78
- grep -Fq 'name = "wr-architect:agent"' "$target"
79
-
80
- printf '# user managed\n' > "$target"
81
- node "$PACKAGE/scripts/codex-agent.mjs" --scope user >/dev/null
82
- [ "$(cat "$target")" = "# user managed" ]
83
-
84
- node "$PACKAGE/scripts/codex-agent.mjs" --scope user --uninstall
85
- [ -e "$target" ]
86
- }
@@ -1,139 +0,0 @@
1
- #!/usr/bin/env bats
2
-
3
- # ADR-066: detect-unoversighted.sh prints ADRs whose frontmatter lacks the
4
- # `human-oversight: confirmed` marker. Behavioural — exercises the script
5
- # against fixture trees and asserts on its stdout, not its source text.
6
-
7
- setup() {
8
- REPO_ROOT="$(cd "$(dirname "$BATS_TEST_FILENAME")/../../../.." && pwd)"
9
- SCRIPT="$REPO_ROOT/packages/architect/scripts/detect-unoversighted.sh"
10
- DIR="$(mktemp -d)"
11
- mkdir -p "$DIR/docs/decisions"
12
- }
13
-
14
- teardown() {
15
- rm -rf "$DIR"
16
- }
17
-
18
- mk() { # mk <filename> <frontmatter-extra-lines...>
19
- local name="$1"; shift
20
- {
21
- echo "---"
22
- echo "status: \"proposed\""
23
- echo "date: 2026-05-25"
24
- for line in "$@"; do echo "$line"; done
25
- echo "---"
26
- echo "# $name"
27
- } > "$DIR/docs/decisions/$name"
28
- }
29
-
30
- @test "an ADR without the marker is reported" {
31
- mk "010-no-marker.proposed.md"
32
- run bash "$SCRIPT" "$DIR/docs/decisions"
33
- [ "$status" -eq 0 ]
34
- [[ "$output" == *"010-no-marker.proposed.md"* ]]
35
- }
36
-
37
- @test "an ADR carrying human-oversight: confirmed is NOT reported" {
38
- mk "011-confirmed.proposed.md" "human-oversight: confirmed" "oversight-date: 2026-05-25"
39
- run bash "$SCRIPT" "$DIR/docs/decisions"
40
- [ "$status" -eq 0 ]
41
- [[ "$output" != *"011-confirmed.proposed.md"* ]]
42
- }
43
-
44
- @test "marker match is case-insensitive and tolerant of trailing space" {
45
- mk "012-spacey.proposed.md" "human-oversight: confirmed "
46
- run bash "$SCRIPT" "$DIR/docs/decisions"
47
- [[ "$output" != *"012-spacey.proposed.md"* ]]
48
- }
49
-
50
- @test "README.md is never reported" {
51
- echo "# index" > "$DIR/docs/decisions/README.md"
52
- run bash "$SCRIPT" "$DIR/docs/decisions"
53
- [[ "$output" != *"README.md"* ]]
54
- }
55
-
56
- @test "superseded ADRs are excluded even without the marker" {
57
- mk "013-old.superseded.md"
58
- run bash "$SCRIPT" "$DIR/docs/decisions"
59
- [[ "$output" != *"013-old.superseded.md"* ]]
60
- }
61
-
62
- @test "a file with no frontmatter counts as unoversighted" {
63
- echo "# bare ADR, no frontmatter" > "$DIR/docs/decisions/014-bare.proposed.md"
64
- run bash "$SCRIPT" "$DIR/docs/decisions"
65
- [[ "$output" == *"014-bare.proposed.md"* ]]
66
- }
67
-
68
- @test "a body line that looks like the marker does not count (frontmatter only)" {
69
- {
70
- echo "---"
71
- echo "status: \"proposed\""
72
- echo "date: 2026-05-25"
73
- echo "---"
74
- echo "# 015"
75
- echo "human-oversight: confirmed" # in body, not frontmatter
76
- } > "$DIR/docs/decisions/015-body-trick.proposed.md"
77
- run bash "$SCRIPT" "$DIR/docs/decisions"
78
- [[ "$output" == *"015-body-trick.proposed.md"* ]]
79
- }
80
-
81
- @test "accepted ADRs are in scope (oversight is orthogonal to status)" {
82
- mk "016-shipped.accepted.md"
83
- run bash "$SCRIPT" "$DIR/docs/decisions"
84
- [[ "$output" == *"016-shipped.accepted.md"* ]]
85
- }
86
-
87
- @test "missing decisions dir exits 0 with no output" {
88
- run bash "$SCRIPT" "$DIR/docs/nonexistent"
89
- [ "$status" -eq 0 ]
90
- [ -z "$output" ]
91
- }
92
-
93
- @test "fully-confirmed set produces empty output" {
94
- mk "017-a.proposed.md" "human-oversight: confirmed"
95
- mk "018-b.accepted.md" "human-oversight: confirmed"
96
- run bash "$SCRIPT" "$DIR/docs/decisions"
97
- [ "$status" -eq 0 ]
98
- [ -z "$output" ]
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
- }
@@ -1,210 +0,0 @@
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
- # RETIRED per ADR-078 (Option 9) / RFC-014 Story C (test 2145). Under
57
- # architect-on-edit authoring the committed docs/decisions/README.md is
58
- # LLM-authored and intentionally no longer byte-matches programmatic
59
- # generator output, so this idempotency/drift assertion no longer holds.
60
- # Replacement enforcement: the architect-readme-pairing-check.sh pre-commit
61
- # hook (Story B) asserts body↔README pairing at commit time. Removed entirely
62
- # with the generator script after the backstop window (ADR-078 reassessment
63
- # 2026-08-30).
64
- skip "test 2145 retired per ADR-078 Option 9 — compendium is architect-authored, not generator-derived (RFC-014 Story C; pairing enforced by architect-readme-pairing-check.sh)"
65
- cd "$REPO_ROOT"
66
- run bash "$SCRIPT" --check docs/decisions
67
- [ "$status" -eq 0 ]
68
- }
69
-
70
- # --- Idempotency (ADR-077 (b) re-asserted) ----------------------------------
71
-
72
- @test "generator is idempotent — two runs produce byte-identical output" {
73
- mk_adr "010-alpha.proposed.md" "proposed" "Alpha"
74
- mk_adr "011-beta.accepted.md" "accepted" "Beta"
75
- run bash "$SCRIPT" "$DIR/docs/decisions"
76
- [ "$status" -eq 0 ]
77
- cp "$DIR/docs/decisions/README.md" "$DIR/first.md"
78
- run bash "$SCRIPT" "$DIR/docs/decisions"
79
- [ "$status" -eq 0 ]
80
- run cmp -s "$DIR/first.md" "$DIR/docs/decisions/README.md"
81
- [ "$status" -eq 0 ]
82
- }
83
-
84
- # --- Drift detection on fixture (mutated ADR body) --------------------------
85
-
86
- @test "--check exits 1 when an ADR body is mutated after generation" {
87
- mk_adr "010-alpha.proposed.md" "proposed" "Alpha"
88
- bash "$SCRIPT" "$DIR/docs/decisions" >/dev/null 2>&1
89
- # Mutate the Decision Outcome — committed compendium now stale.
90
- sed -i.bak 's/Alpha implementation/Alpha REVISED outcome/' \
91
- "$DIR/docs/decisions/010-alpha.proposed.md"
92
- rm "$DIR/docs/decisions/010-alpha.proposed.md.bak"
93
- run bash "$SCRIPT" --check "$DIR/docs/decisions"
94
- [ "$status" -eq 1 ]
95
- # The stderr advice should name the regen command for mechanical recovery.
96
- [[ "$output" == *"wr-architect-generate-decisions-compendium"* ]]
97
- }
98
-
99
- @test "--check exits 1 when compendium is missing entirely" {
100
- mk_adr "010-alpha.proposed.md" "proposed" "Alpha"
101
- run bash "$SCRIPT" --check "$DIR/docs/decisions"
102
- [ "$status" -eq 1 ]
103
- [[ "$output" == *"MISSING"* || "$output" == *"missing"* || "$output" == *"does not exist"* ]]
104
- }
105
-
106
- @test "--check exits 0 on a freshly-generated set (in sync)" {
107
- mk_adr "010-alpha.proposed.md" "proposed" "Alpha"
108
- mk_adr "011-beta.accepted.md" "accepted" "Beta"
109
- bash "$SCRIPT" "$DIR/docs/decisions" >/dev/null 2>&1
110
- run bash "$SCRIPT" --check "$DIR/docs/decisions"
111
- [ "$status" -eq 0 ]
112
- }
113
-
114
- # --- Section split (ADR-077 amendment 2026-05-30 two-section format) --------
115
-
116
- @test "compendium splits in-force (proposed+accepted) from historical (superseded+rejected+deprecated)" {
117
- mk_adr "010-alpha.proposed.md" "proposed" "Alpha In-Force"
118
- mk_adr "011-beta.accepted.md" "accepted" "Beta In-Force"
119
- mk_adr "012-gamma.superseded.md" "superseded" "Gamma Historical"
120
- mk_adr "013-delta.rejected.md" "rejected" "Delta Historical"
121
- mk_adr "014-eps.deprecated.md" "deprecated" "Eps Historical"
122
- bash "$SCRIPT" "$DIR/docs/decisions" >/dev/null 2>&1
123
- local out="$DIR/docs/decisions/README.md"
124
- grep -q '^## In-force decisions$' "$out"
125
- grep -q '^## Historical decisions$' "$out"
126
- # In-force section appears before historical section.
127
- local in_force_line historical_line
128
- in_force_line=$(grep -n '^## In-force decisions$' "$out" | cut -d: -f1)
129
- historical_line=$(grep -n '^## Historical decisions$' "$out" | cut -d: -f1)
130
- [ "$in_force_line" -lt "$historical_line" ]
131
- # Header tally reflects the partition.
132
- grep -q '^\*\*Total ADRs:\*\* 5 (2 in-force, 3 historical)$' "$out"
133
- }
134
-
135
- @test "compendium omits historical section when there are no historical ADRs" {
136
- mk_adr "010-alpha.proposed.md" "proposed" "Alpha"
137
- bash "$SCRIPT" "$DIR/docs/decisions" >/dev/null 2>&1
138
- local out="$DIR/docs/decisions/README.md"
139
- grep -q '^## In-force decisions$' "$out"
140
- ! grep -q '^## Historical decisions$' "$out"
141
- grep -q '^\*\*Total ADRs:\*\* 1 (1 in-force, 0 historical)$' "$out"
142
- }
143
-
144
- # --- Output deterministic (no timestamp / no date in header) ----------------
145
-
146
- @test "header carries no timestamp or date — output stays idempotent across days" {
147
- mk_adr "010-alpha.proposed.md" "proposed" "Alpha"
148
- bash "$SCRIPT" "$DIR/docs/decisions" >/dev/null 2>&1
149
- local out="$DIR/docs/decisions/README.md"
150
- # The README.md never embeds a YYYY-MM-DD or HH:MM stamp at the top —
151
- # idempotency would break otherwise (drift bats would flag day-by-day
152
- # churn instead of substance drift).
153
- ! head -10 "$out" | grep -qE '20[0-9]{2}-[0-9]{2}-[0-9]{2}'
154
- ! head -10 "$out" | grep -qE '[0-9]{2}:[0-9]{2}'
155
- }
156
-
157
- # --- Per-ADR entry shape ----------------------------------------------------
158
-
159
- @test "each ADR emits ID + Title + Status + Chosen + Confirmation + Related" {
160
- mk_adr "042-test.accepted.md" "accepted" "Test Entry"
161
- bash "$SCRIPT" "$DIR/docs/decisions" >/dev/null 2>&1
162
- local out="$DIR/docs/decisions/README.md"
163
- grep -q '^### ADR-042 — Test Entry$' "$out"
164
- grep -q '^\*\*Status:\*\* accepted' "$out"
165
- grep -q '^\*\*Chosen:\*\* Chosen option: ' "$out"
166
- grep -q '^\*\*Confirmation:\*\* ' "$out"
167
- # Related extraction collapses to ADR-NNN ID list.
168
- grep -q '^\*\*Related:\*\* ADR-001$' "$out"
169
- }
170
-
171
- # --- Error handling ---------------------------------------------------------
172
-
173
- @test "missing decisions dir exits 2 with a clear error" {
174
- run bash "$SCRIPT" "$DIR/docs/nonexistent"
175
- [ "$status" -eq 2 ]
176
- [[ "$output" == *"not found"* || "$output" == *"does not exist"* ]]
177
- }
178
-
179
- @test "README.md is excluded from the ADR set (never recurses into itself)" {
180
- # If README.md were treated as an ADR, the compendium would grow on every
181
- # run — idempotency would break catastrophically.
182
- mk_adr "010-alpha.proposed.md" "proposed" "Alpha"
183
- bash "$SCRIPT" "$DIR/docs/decisions" >/dev/null 2>&1
184
- cp "$DIR/docs/decisions/README.md" "$DIR/first.md"
185
- bash "$SCRIPT" "$DIR/docs/decisions" >/dev/null 2>&1
186
- run cmp -s "$DIR/first.md" "$DIR/docs/decisions/README.md"
187
- [ "$status" -eq 0 ]
188
- # The "Total ADRs:" tally must still be 1, not 2.
189
- grep -q '^\*\*Total ADRs:\*\* 1 ' "$DIR/docs/decisions/README.md"
190
- }
191
-
192
- # --- Oversight marker projection (ADR-077 (i) authoritative-state) ----------
193
-
194
- @test "human-oversight: confirmed surfaces as an Oversight badge" {
195
- mk_adr "010-conf.proposed.md" "proposed" "Confirmed Entry" \
196
- "human-oversight: confirmed" \
197
- "oversight-date: 2026-05-30"
198
- bash "$SCRIPT" "$DIR/docs/decisions" >/dev/null 2>&1
199
- grep -q '^\*\*Status:\*\* proposed | \*\*Oversight:\*\* confirmed' \
200
- "$DIR/docs/decisions/README.md"
201
- }
202
-
203
- @test "rejected-pending-supersede surfaces with the supersede ticket in the badge (P316 amendment)" {
204
- mk_adr "010-rej.proposed.md" "proposed" "Rejected Entry" \
205
- "human-oversight: rejected-pending-supersede" \
206
- "supersede-ticket: P297"
207
- bash "$SCRIPT" "$DIR/docs/decisions" >/dev/null 2>&1
208
- grep -q 'Oversight:\*\* rejected-pending-supersede (P297)' \
209
- "$DIR/docs/decisions/README.md"
210
- }
@@ -1,143 +0,0 @@
1
- #!/usr/bin/env bats
2
-
3
- # ADR-074: is-decision-unconfirmed.sh is the single-ADR predicate for the
4
- # build-upon guard (manage-problem / work-problems propose-fix surface). It
5
- # answers "is this referenced decision unconfirmed?" via its EXIT CODE, where
6
- # "unconfirmed" mirrors detect-unoversighted.sh EXACTLY (frontmatter lacks
7
- # `human-oversight: confirmed`, and the ADR is not superseded).
8
- #
9
- # Behavioural — exercises the script against fixture trees and asserts on its
10
- # exit code + stdout, not its source text (ADR-052).
11
-
12
- setup() {
13
- REPO_ROOT="$(cd "$(dirname "$BATS_TEST_FILENAME")/../../../.." && pwd)"
14
- SCRIPT="$REPO_ROOT/packages/architect/scripts/is-decision-unconfirmed.sh"
15
- DETECT="$REPO_ROOT/packages/architect/scripts/detect-unoversighted.sh"
16
- DIR="$(mktemp -d)"
17
- mkdir -p "$DIR/docs/decisions"
18
- }
19
-
20
- teardown() {
21
- rm -rf "$DIR"
22
- }
23
-
24
- # mk <filename> <status> [extra-frontmatter-lines...]
25
- mk() {
26
- local name="$1"; local status="$2"; shift 2
27
- {
28
- echo "---"
29
- echo "status: \"$status\""
30
- echo "date: 2026-05-27"
31
- for line in "$@"; do echo "$line"; done
32
- echo "---"
33
- echo "# $name"
34
- } > "$DIR/docs/decisions/$name"
35
- }
36
-
37
- @test "proposed ADR without the marker is unconfirmed (exit 0, prints path)" {
38
- mk "100-no-marker.proposed.md" "proposed"
39
- run bash "$SCRIPT" "ADR-100" "$DIR/docs/decisions"
40
- [ "$status" -eq 0 ]
41
- [[ "$output" == *"100-no-marker.proposed.md"* ]]
42
- }
43
-
44
- @test "proposed ADR carrying human-oversight: confirmed is confirmed (exit 1, no stdout)" {
45
- mk "101-confirmed.proposed.md" "proposed" "human-oversight: confirmed" "oversight-date: 2026-05-27"
46
- run bash "$SCRIPT" "ADR-101" "$DIR/docs/decisions"
47
- [ "$status" -eq 1 ]
48
- [ -z "$output" ]
49
- }
50
-
51
- @test "accepted ADR without the marker is still unconfirmed (exit 0)" {
52
- mk "102-accepted-no-marker.accepted.md" "accepted"
53
- run bash "$SCRIPT" "ADR-102" "$DIR/docs/decisions"
54
- [ "$status" -eq 0 ]
55
- }
56
-
57
- @test "superseded ADR (even without marker) does NOT fire the guard (exit 1)" {
58
- mk "103-retired.superseded.md" "superseded"
59
- run bash "$SCRIPT" "ADR-103" "$DIR/docs/decisions"
60
- [ "$status" -eq 1 ]
61
- }
62
-
63
- @test "a bare numeric ref resolves" {
64
- mk "104-bare-num.proposed.md" "proposed"
65
- run bash "$SCRIPT" "104" "$DIR/docs/decisions"
66
- [ "$status" -eq 0 ]
67
- [[ "$output" == *"104-bare-num.proposed.md"* ]]
68
- }
69
-
70
- @test "a direct path ref resolves" {
71
- mk "105-by-path.proposed.md" "proposed"
72
- run bash "$SCRIPT" "$DIR/docs/decisions/105-by-path.proposed.md"
73
- [ "$status" -eq 0 ]
74
- }
75
-
76
- @test "a per-state subdir layout resolves" {
77
- mkdir -p "$DIR/docs/decisions/proposed"
78
- {
79
- echo "---"; echo "status: \"proposed\""; echo "date: 2026-05-27"; echo "---"; echo "# subdir"
80
- } > "$DIR/docs/decisions/proposed/106-subdir.md"
81
- run bash "$SCRIPT" "ADR-106" "$DIR/docs/decisions"
82
- [ "$status" -eq 0 ]
83
- [[ "$output" == *"106-subdir.md"* ]]
84
- }
85
-
86
- @test "an unknown ADR ref exits 2 (not found)" {
87
- run bash "$SCRIPT" "ADR-999" "$DIR/docs/decisions"
88
- [ "$status" -eq 2 ]
89
- }
90
-
91
- @test "an unparseable ref exits 2" {
92
- run bash "$SCRIPT" "not-an-adr" "$DIR/docs/decisions"
93
- [ "$status" -eq 2 ]
94
- }
95
-
96
- @test "agrees with detect-unoversighted on the same fixture (sync guard)" {
97
- # The two scripts share the frontmatter/marker/superseded shape. This guard
98
- # fails if a future edit drifts one from the other.
99
- mk "110-unconfirmed.proposed.md" "proposed"
100
- mk "111-confirmed.proposed.md" "proposed" "human-oversight: confirmed" "oversight-date: 2026-05-27"
101
- detect_out="$(bash "$DETECT" "$DIR/docs/decisions")"
102
- # 110 is in the detector's unoversighted list AND the predicate exits 0.
103
- [[ "$detect_out" == *"110-unconfirmed.proposed.md"* ]]
104
- run bash "$SCRIPT" "ADR-110" "$DIR/docs/decisions"; [ "$status" -eq 0 ]
105
- # 111 is NOT in the detector's list AND the predicate exits 1.
106
- [[ "$detect_out" != *"111-confirmed.proposed.md"* ]]
107
- run bash "$SCRIPT" "ADR-111" "$DIR/docs/decisions"; [ "$status" -eq 1 ]
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
- }