@windyroad/architect 0.20.3 → 0.20.4-preview.1058

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,106 +0,0 @@
1
- #!/usr/bin/env bats
2
-
3
- # P095 / ADR-038: architect-detect.sh UserPromptSubmit hook must emit
4
- # the full MANDATORY instruction block only on the first prompt of a
5
- # session (identified by session_id on stdin). Subsequent prompts in
6
- # the same session emit only a terse reminder. New sessions or empty
7
- # session_ids fall back to the full block.
8
-
9
- setup() {
10
- REPO_ROOT="$(cd "$(dirname "$BATS_TEST_FILENAME")/../../../.." && pwd)"
11
- HOOK="$REPO_ROOT/packages/architect/hooks/architect-detect.sh"
12
-
13
- # Stub project working dir so the hook's docs/decisions/ detection fires.
14
- WORKDIR="$(mktemp -d)"
15
- mkdir -p "$WORKDIR/docs/decisions"
16
- : > "$WORKDIR/docs/decisions/.keep"
17
-
18
- SID="arch-detect-test-$$-$RANDOM"
19
- }
20
-
21
- teardown() {
22
- rm -f "/tmp/architect-announced-${SID}"
23
- rm -f "/tmp/architect-announced-${SID}-alt"
24
- rm -rf "$WORKDIR"
25
- }
26
-
27
- # Helper: invoke the hook with a given session_id from stdin, while cd'd
28
- # into WORKDIR so the `docs/decisions` detection picks up the stub.
29
- run_hook() {
30
- local sid="$1"
31
- (cd "$WORKDIR" && echo "{\"session_id\":\"$sid\"}" | bash "$HOOK")
32
- }
33
-
34
- @test "architect-detect: first invocation emits the full MANDATORY block" {
35
- run run_hook "$SID"
36
- [ "$status" -eq 0 ]
37
- [ "${#output}" -gt 500 ]
38
- [[ "$output" == *"MANDATORY ARCHITECTURE CHECK"* ]]
39
- [[ "$output" == *"wr-architect:agent"* ]]
40
- [[ "$output" == *"SCOPE:"* ]]
41
- }
42
-
43
- @test "architect-detect: first invocation writes the announcement marker" {
44
- run run_hook "$SID"
45
- [ "$status" -eq 0 ]
46
- [ -f "/tmp/architect-announced-${SID}" ]
47
- }
48
-
49
- @test "architect-detect: second invocation emits only a terse reminder" {
50
- run_hook "$SID" >/dev/null
51
- run run_hook "$SID"
52
- [ "$status" -eq 0 ]
53
- # Terse reminder is < 250 bytes; full block is > 500 bytes.
54
- [ "${#output}" -lt 250 ]
55
- # Terse reminder names the gate AND the delegation affordance.
56
- [[ "$output" == *"architecture"* ]] || [[ "$output" == *"ARCHITECTURE"* ]]
57
- [[ "$output" == *"wr-architect:agent"* ]]
58
- # Full SCOPE block is NOT re-emitted.
59
- [[ "$output" != *"Does NOT apply to"* ]]
60
- }
61
-
62
- @test "architect-detect: terse reminder preserves the MANDATORY signal word" {
63
- run_hook "$SID" >/dev/null
64
- run run_hook "$SID"
65
- [ "$status" -eq 0 ]
66
- # Per JTBD-lead condition: terse reminder must keep an imperative
67
- # marker (MANDATORY / REQUIRED / NON-OPTIONAL) so the enforcement
68
- # signal does not soften after turn 1.
69
- [[ "$output" == *"MANDATORY"* ]] || [[ "$output" == *"REQUIRED"* ]] || [[ "$output" == *"NON-OPTIONAL"* ]]
70
- }
71
-
72
- @test "architect-detect: terse reminder names the trigger artifact" {
73
- run_hook "$SID" >/dev/null
74
- run run_hook "$SID"
75
- [ "$status" -eq 0 ]
76
- # Per JTBD-lead condition: reason for the gate must stay visible
77
- # on subsequent turns.
78
- [[ "$output" == *"docs/decisions"* ]]
79
- }
80
-
81
- @test "architect-detect: different session_id re-emits the full block" {
82
- run_hook "$SID" >/dev/null
83
- local SID2="${SID}-alt"
84
- run run_hook "$SID2"
85
- [ "$status" -eq 0 ]
86
- [ "${#output}" -gt 500 ]
87
- [[ "$output" == *"MANDATORY ARCHITECTURE CHECK"* ]]
88
- rm -f "/tmp/architect-announced-${SID2}"
89
- }
90
-
91
- @test "architect-detect: empty session_id emits the full block and writes no marker" {
92
- run run_hook ""
93
- [ "$status" -eq 0 ]
94
- [ "${#output}" -gt 500 ]
95
- [[ "$output" == *"MANDATORY ARCHITECTURE CHECK"* ]]
96
- [ ! -f "/tmp/architect-announced-" ]
97
- }
98
-
99
- @test "architect-detect: absent docs/decisions/ emits the no-decisions NOTE (unchanged by this ADR)" {
100
- local NO_DECISIONS="$(mktemp -d)"
101
- run bash -c "cd '$NO_DECISIONS' && echo '{\"session_id\":\"$SID\"}' | bash '$HOOK'"
102
- [ "$status" -eq 0 ]
103
- [[ "$output" == *"no docs/decisions/ directory"* ]]
104
- [[ "$output" != *"MANDATORY ARCHITECTURE CHECK"* ]]
105
- rm -rf "$NO_DECISIONS"
106
- }
@@ -1,56 +0,0 @@
1
- #!/usr/bin/env bats
2
-
3
- # Tests for architect-detect.sh (UserPromptSubmit) — verifies the injected
4
- # scope exclusion text lists governance docs that the PreToolUse gate already
5
- # exempts (P029). Without this, the LLM wastes time delegating to the
6
- # architect for files the edit gate would allow anyway.
7
-
8
- setup() {
9
- SCRIPT_DIR="$(cd "$(dirname "$BATS_TEST_FILENAME")/.." && pwd)"
10
- HOOK="$SCRIPT_DIR/architect-detect.sh"
11
- ORIG_DIR="$PWD"
12
- TEST_DIR=$(mktemp -d)
13
- cd "$TEST_DIR"
14
- mkdir -p docs/decisions
15
- }
16
-
17
- teardown() {
18
- cd "$ORIG_DIR"
19
- rm -rf "$TEST_DIR"
20
- }
21
-
22
- @test "detect: scope text mentions problem files exemption (P029)" {
23
- run bash "$HOOK" </dev/null
24
- [ "$status" -eq 0 ]
25
- [[ "$output" == *"docs/problems/"* ]] || [[ "$output" == *"problem tickets"* ]]
26
- }
27
-
28
- @test "detect: scope text mentions BRIEFING.md exemption (P029)" {
29
- run bash "$HOOK" </dev/null
30
- [ "$status" -eq 0 ]
31
- [[ "$output" == *"BRIEFING"* ]]
32
- }
33
-
34
- @test "detect: scope text mentions docs/briefing/ exemption (P100)" {
35
- run bash "$HOOK" </dev/null
36
- [ "$status" -eq 0 ]
37
- [[ "$output" == *"docs/briefing/"* ]]
38
- }
39
-
40
- @test "detect: scope text mentions RISK-POLICY exemption (P029)" {
41
- run bash "$HOOK" </dev/null
42
- [ "$status" -eq 0 ]
43
- [[ "$output" == *"RISK-POLICY"* ]]
44
- }
45
-
46
- @test "detect: scope text mentions changeset exemption (P029)" {
47
- run bash "$HOOK" </dev/null
48
- [ "$status" -eq 0 ]
49
- [[ "$output" == *"changeset"* ]]
50
- }
51
-
52
- @test "detect: scope text mentions memory files exemption (P029)" {
53
- run bash "$HOOK" </dev/null
54
- [ "$status" -eq 0 ]
55
- [[ "$output" == *"memory"* ]] || [[ "$output" == *"MEMORY"* ]]
56
- }
@@ -1,124 +0,0 @@
1
- #!/usr/bin/env bats
2
-
3
- # Tests for architect-enforce-edit.sh — verifies peer-plugin policy files are
4
- # exempt from the architect gate (P009). Each plugin governs its own policy
5
- # docs via its own enforce hook; the architect should not re-gate them.
6
- #
7
- # All tests are functional (P011): they execute the hook with mock JSON
8
- # input and assert on exit status + BLOCKED output. Source-grep assertions
9
- # were removed because they over-specified the implementation and gave
10
- # false confidence (a literal string can appear in source without the
11
- # matching case branch actually short-circuiting).
12
-
13
- setup() {
14
- SCRIPT_DIR="$(cd "$(dirname "$BATS_TEST_FILENAME")/.." && pwd)"
15
- HOOK="$SCRIPT_DIR/architect-enforce-edit.sh"
16
- ORIG_DIR="$PWD"
17
- TEST_DIR=$(mktemp -d)
18
- cd "$TEST_DIR"
19
- # Engage the gate: architect-enforce only runs when docs/decisions/ exists.
20
- mkdir -p docs/decisions
21
- }
22
-
23
- teardown() {
24
- cd "$ORIG_DIR"
25
- rm -rf "$TEST_DIR"
26
- }
27
-
28
- # Helper: run the hook with a mock JSON input for a given file path.
29
- # Claude Code passes absolute file_path values, so tests use $PWD-prefixed
30
- # paths to match real shape (after the P004 root check).
31
- run_hook_with_file() {
32
- local file_path="$1"
33
- local json="{\"tool_input\":{\"file_path\":\"${file_path}\"},\"session_id\":\"test-session-$$\"}"
34
- echo "$json" | bash "$HOOK"
35
- }
36
-
37
- assert_path_allowed() {
38
- local file_path="$1"
39
- run run_hook_with_file "$file_path"
40
- [ "$status" -eq 0 ]
41
- [[ "$output" != *"BLOCKED"* ]]
42
- }
43
-
44
- @test "architect: does NOT exempt legacy docs/JOBS_TO_BE_DONE.md (ADR-008 Option 3, P019)" {
45
- # Legacy single-file path is no longer a recognised peer-plugin policy
46
- # artefact — ADR-008 Option 3 removes it from runtime consideration.
47
- # The architect gate treats it as ordinary content and applies its
48
- # normal review rules.
49
- run run_hook_with_file "$PWD/docs/JOBS_TO_BE_DONE.md"
50
- [ "$status" -eq 0 ]
51
- [[ "$output" == *"BLOCKED"* ]]
52
- }
53
-
54
- @test "architect: exempts VCS-internal .git/ plumbing files (P458)" {
55
- # A commit-message scratch file under .git/ is not a project artefact. Without
56
- # the exclusion a .txt under the repo root is gated (no extension exemption).
57
- assert_path_allowed "$PWD/.git/cruise-brake-commit-msg.txt"
58
- assert_path_allowed "$PWD/.git/COMMIT_EDITMSG"
59
- }
60
-
61
- @test "architect: exempts JTBD directory file (P009)" {
62
- assert_path_allowed "$PWD/docs/jtbd/solo-developer/persona.md"
63
- }
64
-
65
- @test "architect: exempts PRODUCT_DISCOVERY.md (P009)" {
66
- assert_path_allowed "$PWD/docs/PRODUCT_DISCOVERY.md"
67
- }
68
-
69
- @test "architect: exempts voice-tone policy file (P009)" {
70
- assert_path_allowed "$PWD/docs/VOICE-AND-TONE.md"
71
- }
72
-
73
- @test "architect: exempts style-guide policy file (P009)" {
74
- assert_path_allowed "$PWD/docs/STYLE-GUIDE.md"
75
- }
76
-
77
- # --- Story maps + stories exemptions (P170 Phase 2 Slice 2.5 / ADR-060 amendment 2026-05-12) ---
78
- # ADR-060 § Phase 2 encoding amendment lines 481-496 mandates exempting
79
- # `docs/story-maps/**/*.html` and `docs/stories/**/*.md` from the 5
80
- # enforce-edit gates. Mirrors the docs/problems and docs/jtbd exemptions —
81
- # these are governance-managed surfaces with their own capture/manage skills.
82
-
83
- @test "architect: exempts docs/story-maps/ HTML in per-state subdir (P170 / ADR-060)" {
84
- assert_path_allowed "$PWD/docs/story-maps/draft/STORY-MAP-001-rfc-framework.html"
85
- }
86
-
87
- @test "architect: exempts docs/story-maps/ HTML in completed subdir (P170 / ADR-060)" {
88
- assert_path_allowed "$PWD/docs/story-maps/completed/STORY-MAP-002-foo.html"
89
- }
90
-
91
- @test "architect: exempts docs/story-maps/README.md (P170 / ADR-060)" {
92
- assert_path_allowed "$PWD/docs/story-maps/README.md"
93
- }
94
-
95
- @test "architect: exempts docs/stories/ markdown in per-state subdir (P170 / ADR-060)" {
96
- assert_path_allowed "$PWD/docs/stories/draft/STORY-001-some-slice.md"
97
- }
98
-
99
- @test "architect: exempts docs/stories/ markdown in done subdir (P170 / ADR-060)" {
100
- assert_path_allowed "$PWD/docs/stories/done/STORY-002-other.md"
101
- }
102
-
103
- @test "architect: exempts docs/stories/README.md (P170 / ADR-060)" {
104
- assert_path_allowed "$PWD/docs/stories/README.md"
105
- }
106
-
107
- # --- Retros exemption (P203) ---
108
- # `docs/retros/*` is the ask-hygiene + run-retro narrative trail written
109
- # routinely by `/wr-retrospective:run-retro` Step 2d + Step 5. Routine
110
- # appends should not fire the architect gate — they are not load-bearing
111
- # architecture content. Mirrors the docs/problems and docs/jtbd peer-
112
- # plugin-policy exemptions.
113
-
114
- @test "architect: exempts docs/retros/ ask-hygiene file (P203)" {
115
- assert_path_allowed "$PWD/docs/retros/2026-06-06-some-iter-ask-hygiene.md"
116
- }
117
-
118
- @test "architect: exempts docs/retros/ analyze-context file (P203)" {
119
- assert_path_allowed "$PWD/docs/retros/2026-06-06-context-analysis.md"
120
- }
121
-
122
- @test "architect: exempts docs/retros/ retro narrative file (P203)" {
123
- assert_path_allowed "$PWD/docs/retros/2026-06-06-session-wrap.md"
124
- }
@@ -1,102 +0,0 @@
1
- #!/usr/bin/env bats
2
-
3
- # Tests for architect-gate.sh (TTL, drift, marker)
4
-
5
- setup() {
6
- SCRIPT_DIR="$(cd "$(dirname "$BATS_TEST_FILENAME")/.." && pwd)"
7
- source "$SCRIPT_DIR/lib/architect-gate.sh"
8
- TEST_SESSION="test-$$-$BATS_TEST_NUMBER"
9
- }
10
-
11
- teardown() {
12
- rm -f "/tmp/architect-reviewed-${TEST_SESSION}"
13
- rm -f "/tmp/architect-reviewed-${TEST_SESSION}.hash"
14
- }
15
-
16
- @test "gate denies when no marker exists" {
17
- run check_architect_gate "$TEST_SESSION"
18
- [ "$status" -ne 0 ]
19
- }
20
-
21
- @test "gate allows when marker exists and is fresh" {
22
- touch "/tmp/architect-reviewed-${TEST_SESSION}"
23
- run check_architect_gate "$TEST_SESSION"
24
- [ "$status" -eq 0 ]
25
- }
26
-
27
- @test "gate denies when marker is expired" {
28
- touch "/tmp/architect-reviewed-${TEST_SESSION}"
29
- # Set TTL to 0 to force expiry
30
- ARCHITECT_TTL=0 run check_architect_gate "$TEST_SESSION"
31
- [ "$status" -ne 0 ]
32
- }
33
-
34
- # P215 / RFC-021 — ARCHITECT_GATE_REASON behaviour. The gate must expose a
35
- # differentiated reason per failure mode (no marker / TTL expired / drift
36
- # detected) so the downstream deny message carries a clear recovery directive.
37
- # Mirrors sibling REVIEW_GATE_REASON pattern in jtbd/voice-tone/style-guide
38
- # review-gate.sh.
39
-
40
- @test "ARCHITECT_GATE_REASON names re-delegate directive when no marker" {
41
- ARCHITECT_GATE_REASON=""
42
- check_architect_gate "$TEST_SESSION" || true
43
- [[ "$ARCHITECT_GATE_REASON" == *"wr-architect:agent"* ]]
44
- [[ "$ARCHITECT_GATE_REASON" == *"Agent tool"* ]]
45
- }
46
-
47
- # P400 — the no-marker reason must also document the SendMessage-resume
48
- # recovery path: a verdict upgrade after ISSUES FOUND is a FRESH Agent spawn
49
- # (a SendMessage resume does NOT fire the marker hook), or the manual marker
50
- # assertion. Without this directive the deny message is a dead-end for the
51
- # most common recovery flow.
52
- @test "ARCHITECT_GATE_REASON documents SendMessage-resume recovery when no marker" {
53
- ARCHITECT_GATE_REASON=""
54
- check_architect_gate "$TEST_SESSION" || true
55
- [[ "$ARCHITECT_GATE_REASON" == *"SendMessage"* ]]
56
- [[ "$ARCHITECT_GATE_REASON" == *"FRESH Agent spawn"* ]]
57
- [[ "$ARCHITECT_GATE_REASON" == *"touch /tmp/architect-reviewed-"* ]]
58
- [[ "$ARCHITECT_GATE_REASON" == *"rm -f /tmp/architect-reviewed-"* ]]
59
- }
60
-
61
- @test "ARCHITECT_GATE_REASON names re-delegate directive when TTL expired" {
62
- touch "/tmp/architect-reviewed-${TEST_SESSION}"
63
- ARCHITECT_GATE_REASON=""
64
- ARCHITECT_TTL=0 check_architect_gate "$TEST_SESSION" || true
65
- [[ "$ARCHITECT_GATE_REASON" == *"expired"* ]]
66
- [[ "$ARCHITECT_GATE_REASON" == *"wr-architect:agent"* ]]
67
- [[ "$ARCHITECT_GATE_REASON" == *"refresh the marker"* ]]
68
- }
69
-
70
- @test "ARCHITECT_GATE_REASON names drift directive when stored hash differs" {
71
- # Set up project root with a docs/decisions directory and a stored hash that
72
- # differs from the current substance hash to force the drift branch.
73
- TEST_PROJECT_DIR=$(mktemp -d)
74
- mkdir -p "$TEST_PROJECT_DIR/docs/decisions"
75
- echo "# ADR-001 current content" > "$TEST_PROJECT_DIR/docs/decisions/001-x.md"
76
- touch "/tmp/architect-reviewed-${TEST_SESSION}"
77
- echo "stale-hash-that-will-not-match" > "/tmp/architect-reviewed-${TEST_SESSION}.hash"
78
- ARCHITECT_GATE_REASON=""
79
- CLAUDE_PROJECT_DIR="$TEST_PROJECT_DIR" check_architect_gate "$TEST_SESSION" || true
80
- rm -rf "$TEST_PROJECT_DIR"
81
- [[ "$ARCHITECT_GATE_REASON" == *"drift"* ]] || [[ "$ARCHITECT_GATE_REASON" == *"changed"* ]]
82
- [[ "$ARCHITECT_GATE_REASON" == *"wr-architect:agent"* ]]
83
- [[ "$ARCHITECT_GATE_REASON" == *"refresh the marker"* ]]
84
- }
85
-
86
- @test "architect-enforce-edit deny output includes ARCHITECT_GATE_REASON directive" {
87
- HOOK="$SCRIPT_DIR/architect-enforce-edit.sh"
88
- ORIG_DIR="$PWD"
89
- TEST_DIR=$(mktemp -d)
90
- cd "$TEST_DIR"
91
- mkdir -p docs/decisions
92
- echo "# adr stub" > docs/decisions/001-x.md
93
- json="{\"tool_input\":{\"file_path\":\"$PWD/src/x.ts\"},\"session_id\":\"deny-test-$$\"}"
94
- run bash -c "echo '$json' | bash '$HOOK'"
95
- cd "$ORIG_DIR"
96
- rm -rf "$TEST_DIR"
97
- [[ "$output" == *"BLOCKED"* ]]
98
- [[ "$output" == *"wr-architect:agent"* ]]
99
- # No marker exists for this fresh session — deny reason must explicitly
100
- # name the re-delegate directive (not vague "review required").
101
- [[ "$output" == *"No architect review marker"* ]] || [[ "$output" == *"marker"* ]]
102
- }
@@ -1,113 +0,0 @@
1
- #!/usr/bin/env bats
2
-
3
- # Behavioural tests for architect-mark-reviewed.sh verdict parsing (P181).
4
- # Drives the hook with realistic agent-output payloads and asserts marker
5
- # creation matches the heading-shape contract in
6
- # packages/architect/agents/agent.md "How to Report".
7
- #
8
- # P181 root cause: literal-substring grep `grep -q "ISSUES FOUND"` matches
9
- # anywhere in the response — including prose narrative that mentions the
10
- # verdict string without it being the canonical heading. Anchored heading
11
- # match fixes the false-positive FAIL → silent marker-drop → edit block.
12
-
13
- setup() {
14
- HOOKS_DIR="$(cd "$(dirname "$BATS_TEST_FILENAME")/.." && pwd)"
15
- HOOK="$HOOKS_DIR/architect-mark-reviewed.sh"
16
- TEST_SESSION="bats-arch-verdict-$$-${BATS_TEST_NUMBER}"
17
- REVIEW_MARKER="/tmp/architect-reviewed-${TEST_SESSION}"
18
- HASH_MARKER="/tmp/architect-reviewed-${TEST_SESSION}.hash"
19
- PLAN_MARKER="/tmp/architect-plan-reviewed-${TEST_SESSION}"
20
- rm -f "$REVIEW_MARKER" "$HASH_MARKER" "$PLAN_MARKER"
21
- }
22
-
23
- teardown() {
24
- rm -f "$REVIEW_MARKER" "$HASH_MARKER" "$PLAN_MARKER"
25
- }
26
-
27
- # Build a PostToolUse:Agent input JSON with the given agent-output text.
28
- # Uses python3 (already a hard dep of gate-helpers.sh) for safe escaping.
29
- _make_input() {
30
- local text="$1"
31
- python3 -c "
32
- import json, sys
33
- text = sys.argv[1]
34
- print(json.dumps({
35
- 'session_id': '$TEST_SESSION',
36
- 'tool_name': 'Agent',
37
- 'tool_input': {'subagent_type': 'wr-architect:agent'},
38
- 'tool_response': {'content': [{'type': 'text', 'text': text}]}
39
- }))
40
- " "$text"
41
- }
42
-
43
- # ---------------------------------------------------------------------------
44
- # Sanity: canonical headings classify correctly
45
- # ---------------------------------------------------------------------------
46
-
47
- @test "verdict-grep: marker drops on canonical PASS heading" {
48
- INPUT=$(_make_input "**Architecture Review: PASS**
49
-
50
- No conflicts with existing decisions.")
51
- echo "$INPUT" | "$HOOK"
52
- [ -f "$REVIEW_MARKER" ]
53
- }
54
-
55
- @test "verdict-grep: marker NOT created on canonical ISSUES FOUND heading" {
56
- INPUT=$(_make_input "**Architecture Review: ISSUES FOUND**
57
-
58
- 1. [Decision Conflict] — ADR-009 violation.")
59
- echo "$INPUT" | "$HOOK"
60
- [ ! -f "$REVIEW_MARKER" ]
61
- }
62
-
63
- @test "verdict-grep: marker drops on PASS heading with blockquote prefix" {
64
- INPUT=$(_make_input "> **Architecture Review: PASS**
65
- > No conflicts.")
66
- echo "$INPUT" | "$HOOK"
67
- [ -f "$REVIEW_MARKER" ]
68
- }
69
-
70
- @test "verdict-grep: marker NOT created on ISSUES FOUND heading with blockquote prefix" {
71
- INPUT=$(_make_input "> **Architecture Review: ISSUES FOUND**
72
- > 1. [Conflict] ...")
73
- echo "$INPUT" | "$HOOK"
74
- [ ! -f "$REVIEW_MARKER" ]
75
- }
76
-
77
- # ---------------------------------------------------------------------------
78
- # P181 bug-fix cases: substring-anywhere false-positive FAIL
79
- # ---------------------------------------------------------------------------
80
-
81
- @test "verdict-grep: no marker without a canonical heading" {
82
- INPUT=$(_make_input "I reviewed the change. The previous review surfaced ISSUES FOUND that have since been addressed; the current proposed change is fine.")
83
- echo "$INPUT" | "$HOOK"
84
- [ ! -f "$REVIEW_MARKER" ]
85
- }
86
-
87
- @test "verdict-grep: NEEDS DIRECTION does not unlock edits" {
88
- INPUT=$(_make_input "**Architecture Review: NEEDS DIRECTION**
89
-
90
- A decision must be recorded. This differs from an ISSUES FOUND verdict because the option is not pinned.
91
-
92
- - Option A — ...
93
- - Option B — ...")
94
- echo "$INPUT" | "$HOOK"
95
- [ ! -f "$REVIEW_MARKER" ]
96
- [ ! -f "$PLAN_MARKER" ]
97
- }
98
-
99
- @test "verdict-grep: marker drops when PASS heading present and body also says 'ISSUES FOUND' inline" {
100
- # PASS check runs first and must win even with substring noise downstream.
101
- # This works in both old and new code; sanity-anchors the precedence rule.
102
- INPUT=$(_make_input "**Architecture Review: PASS**
103
-
104
- No conflicts. Note: earlier sessions reported ISSUES FOUND on adjacent files but those are out of scope here.")
105
- echo "$INPUT" | "$HOOK"
106
- [ -f "$REVIEW_MARKER" ]
107
- }
108
-
109
- @test "verdict-grep: prose-only approval does not unlock edits" {
110
- INPUT=$(_make_input "Review complete — no issues found in the diff.")
111
- echo "$INPUT" | "$HOOK"
112
- [ ! -f "$REVIEW_MARKER" ]
113
- }
@@ -1,26 +0,0 @@
1
- #!/usr/bin/env bats
2
-
3
- # Tests for architect verdict parsing from output text
4
-
5
- @test "grep matches 'Architecture Review: PASS'" {
6
- output="Some preamble text\n\n**Architecture Review: PASS**\n\nNo conflicts."
7
- echo -e "$output" | grep -q "Architecture Review: PASS"
8
- }
9
-
10
- @test "grep matches 'ISSUES FOUND'" {
11
- output="**Architecture Review: ISSUES FOUND**\n\n1. [Decision Conflict]"
12
- echo -e "$output" | grep -q "ISSUES FOUND"
13
- }
14
-
15
- @test "grep does NOT match unrelated text" {
16
- output="The review is complete. Everything looks good."
17
- ! echo -e "$output" | grep -q "Architecture Review: PASS"
18
- }
19
-
20
- @test "subagent pattern matches wr-architect:agent" {
21
- SUBAGENT="wr-architect:agent"
22
- case "$SUBAGENT" in
23
- *architect*) true ;;
24
- *) false ;;
25
- esac
26
- }