@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.
- package/.claude-plugin/plugin.json +1 -1
- package/.codex-plugin/plugin.json +1 -1
- package/package.json +6 -2
- package/agents/test/architect-codex-eval-contract.bats +0 -33
- package/agents/test/architect-needs-direction-verdict.bats +0 -64
- package/agents/test/architect-output-formatting.bats +0 -26
- package/agents/test/architect-performance-review.bats +0 -76
- package/agents/test/architect-pre-edit-review-mode.bats +0 -50
- package/agents/test/architect-unratified-dependency-verdict.bats +0 -55
- package/hooks/test/architect-compendium-update-entry.bats +0 -299
- package/hooks/test/architect-detect-once-per-session.bats +0 -106
- package/hooks/test/architect-detect-scope.bats +0 -56
- package/hooks/test/architect-enforce-scope.bats +0 -124
- package/hooks/test/architect-gate.bats +0 -102
- package/hooks/test/architect-mark-reviewed-verdict-grep.bats +0 -113
- package/hooks/test/architect-mark-reviewed.bats +0 -26
- package/hooks/test/architect-marker-only-exempt.bats +0 -183
- package/hooks/test/architect-no-stop-hook.bats +0 -17
- package/hooks/test/architect-oversight-marker-discipline.bats +0 -245
- package/hooks/test/architect-oversight-nudge.bats +0 -75
- package/hooks/test/architect-project-root.bats +0 -81
- package/hooks/test/architect-readme-pairing-check.bats +0 -126
- package/hooks/test/architect-slide-marker.bats +0 -92
- package/hooks/test/codex-agent-completion.bats +0 -64
- package/hooks/test/slide-marker-on-subprocess-return.bats +0 -118
- package/hooks/test/substance-aware-drift.bats +0 -243
- package/scripts/test/check-amends-backreference.bats +0 -68
- package/scripts/test/codex-pack-and-agent.bats +0 -86
- package/scripts/test/detect-unoversighted.bats +0 -139
- package/scripts/test/generate-decisions-compendium.bats +0 -210
- package/scripts/test/is-decision-unconfirmed.bats +0 -143
- package/skills/capture-adr/test/capture-adr.bats +0 -362
- package/skills/create-adr/test/create-adr-adr-044-contract.bats +0 -185
- package/skills/create-adr/test/create-adr-codex-eval-contract.bats +0 -34
- package/skills/create-adr/test/create-adr-decision-boundary.bats +0 -49
- package/skills/create-adr/test/create-adr-id-collision-guard.bats +0 -35
- package/skills/create-adr/test/create-adr-next-id-origin-lookup.bats +0 -45
- package/skills/create-adr/test/create-adr-rename-restage-reminder.bats +0 -38
- package/skills/create-adr/test/create-adr-substance-confirm-pattern.bats +0 -169
- package/skills/review-design/test/review-design-codex-contract.bats +0 -22
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@windyroad/architect",
|
|
3
|
-
"version": "0.20.
|
|
3
|
+
"version": "0.20.4-preview.1058",
|
|
4
4
|
"description": "Architecture decision enforcement for AI coding agents",
|
|
5
5
|
"bin": {
|
|
6
6
|
"windyroad-architect": "./bin/install.mjs"
|
|
@@ -33,6 +33,10 @@
|
|
|
33
33
|
".codex-plugin/",
|
|
34
34
|
"lib/",
|
|
35
35
|
"!skills/*/eval/",
|
|
36
|
-
"!agents/eval/"
|
|
36
|
+
"!agents/eval/",
|
|
37
|
+
"!agents/test/",
|
|
38
|
+
"!hooks/test/",
|
|
39
|
+
"!scripts/test/",
|
|
40
|
+
"!skills/*/test/"
|
|
37
41
|
]
|
|
38
42
|
}
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bats
|
|
2
|
-
|
|
3
|
-
setup() {
|
|
4
|
-
REPO_ROOT="$(cd "$(dirname "$BATS_TEST_FILENAME")/../../../.." && pwd)"
|
|
5
|
-
EVAL_DIR="$REPO_ROOT/packages/architect/agents/eval"
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
@test "architect Codex agent eval covers required verdict classes" {
|
|
9
|
-
[ -f "$EVAL_DIR/promptfooconfig.codex.yaml" ]
|
|
10
|
-
run grep -n "PASS" "$EVAL_DIR/promptfooconfig.codex.yaml"
|
|
11
|
-
[ "$status" -eq 0 ]
|
|
12
|
-
run grep -n "ISSUES FOUND" "$EVAL_DIR/promptfooconfig.codex.yaml"
|
|
13
|
-
[ "$status" -eq 0 ]
|
|
14
|
-
run grep -n "NEEDS DIRECTION" "$EVAL_DIR/promptfooconfig.codex.yaml"
|
|
15
|
-
[ "$status" -eq 0 ]
|
|
16
|
-
run grep -n "Unratified Dependency" "$EVAL_DIR/promptfooconfig.codex.yaml"
|
|
17
|
-
[ "$status" -eq 0 ]
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
@test "architect Codex agent eval runner uses packed install and exact custom agent" {
|
|
21
|
-
run grep -n 'npm pack' "$EVAL_DIR/run-codex-agent-eval.sh"
|
|
22
|
-
[ "$status" -eq 0 ]
|
|
23
|
-
run grep -n 'windyroad-architect --runtime codex --scope user' "$EVAL_DIR/run-codex-agent-eval.sh"
|
|
24
|
-
[ "$status" -eq 0 ]
|
|
25
|
-
run grep -n "codex exec" "$EVAL_DIR/run-codex-agent-eval.sh"
|
|
26
|
-
[ "$status" -eq 0 ]
|
|
27
|
-
run grep -n 'agents/wr-architect-agent.toml' "$EVAL_DIR/run-codex-agent-eval.sh"
|
|
28
|
-
[ "$status" -eq 0 ]
|
|
29
|
-
run grep -n 'wr-architect:agent' "$EVAL_DIR/run-codex-agent-eval.sh"
|
|
30
|
-
[ "$status" -eq 0 ]
|
|
31
|
-
run grep -n 'git -C "$TMP_REPO" init' "$EVAL_DIR/run-codex-agent-eval.sh"
|
|
32
|
-
[ "$status" -eq 0 ]
|
|
33
|
-
}
|
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bats
|
|
2
|
-
# Doc-lint guard: architect agent.md must carry the NEEDS DIRECTION verdict
|
|
3
|
-
# type (ADR-064) so the architect names the question + viable options for an
|
|
4
|
-
# unpinned 2+-option decision instead of auto-picking or prose-asking, and the
|
|
5
|
-
# main agent translates it into an AskUserQuestion.
|
|
6
|
-
#
|
|
7
|
-
# tdd-review: structural-permitted (justification: P176 — agent behaviour is
|
|
8
|
-
# prompt-driven with no skill-invocation harness to exercise the verdict
|
|
9
|
-
# behaviourally; ADR-052 Surface 2 structural-justified case, NOT an ADR-005
|
|
10
|
-
# Permitted Exception — ADR-052 narrows ADR-005 to exclude prose-doc greps).
|
|
11
|
-
# When P176 lands, upgrade to a behavioural test per ADR-064 Confirmation item 2.
|
|
12
|
-
#
|
|
13
|
-
# Cross-reference:
|
|
14
|
-
# P283 (architect should AskUserQuestion when recording a new decision)
|
|
15
|
-
# ADR-064 (Architect Needs-Direction verdict; main agent owns the AskUserQuestion)
|
|
16
|
-
# ADR-052 Surface 2 (structural-justified verdict) + P176 (harness gap)
|
|
17
|
-
# @jtbd JTBD-001 (enforce governance without slowing down)
|
|
18
|
-
|
|
19
|
-
setup() {
|
|
20
|
-
AGENT_DIR="$(cd "$(dirname "$BATS_TEST_FILENAME")/.." && pwd)"
|
|
21
|
-
AGENT_FILE="${AGENT_DIR}/agent.md"
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
@test "agent.md carries a NEEDS DIRECTION report verdict (ADR-064)" {
|
|
25
|
-
run grep -n "Architecture Review: NEEDS DIRECTION" "$AGENT_FILE"
|
|
26
|
-
[ "$status" -eq 0 ]
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
@test "agent.md lists [Needs Direction] as an issue/verdict type" {
|
|
30
|
-
run grep -n "\[Needs Direction\]" "$AGENT_FILE"
|
|
31
|
-
[ "$status" -eq 0 ]
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
@test "agent.md has a 'When to emit Needs Direction' section citing ADR-064" {
|
|
35
|
-
run grep -n "When to emit Needs Direction" "$AGENT_FILE"
|
|
36
|
-
[ "$status" -eq 0 ]
|
|
37
|
-
run grep -n "ADR-064" "$AGENT_FILE"
|
|
38
|
-
[ "$status" -eq 0 ]
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
@test "agent.md requires the main agent to translate the verdict into AskUserQuestion (not prose)" {
|
|
42
|
-
run grep -n "AskUserQuestion" "$AGENT_FILE"
|
|
43
|
-
[ "$status" -eq 0 ]
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
@test "agent.md guards the negative bound: do NOT emit Needs Direction on obvious/single-option choices" {
|
|
47
|
-
# inverse-P078 over-ask guard — the verdict must not fire when only one viable option exists
|
|
48
|
-
run grep -niE "Do NOT emit Needs Direction.*(obvious|one-viable|only-one)" "$AGENT_FILE"
|
|
49
|
-
[ "$status" -eq 0 ]
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
@test "agent.md requires Needs-Direction to name the SUBSTANCE not the grain (ADR-074)" {
|
|
53
|
-
# ADR-074 surface 1: the verdict must name the substantive choice, not a
|
|
54
|
-
# meta/grain framing question (e.g. "one ADR or two?"). Closes the P315 vector.
|
|
55
|
-
run grep -niE "Name the SUBSTANCE, not the grain" "$AGENT_FILE"
|
|
56
|
-
[ "$status" -eq 0 ]
|
|
57
|
-
run grep -n "ADR-074" "$AGENT_FILE"
|
|
58
|
-
[ "$status" -eq 0 ]
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
@test "agent.md performance-review section cites ADR-026 as parent (ADR-026 Confirmation item 1)" {
|
|
62
|
-
run grep -nE "Runtime-Path Performance Review \(per ADR-026" "$AGENT_FILE"
|
|
63
|
-
[ "$status" -eq 0 ]
|
|
64
|
-
}
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bats
|
|
2
|
-
# Doc-lint guard: architect agent.md must include the output formatting rule
|
|
3
|
-
# requiring human-readable titles alongside bare IDs (P032).
|
|
4
|
-
#
|
|
5
|
-
# Structural assertion — Permitted Exception to the source-grep ban (ADR-005 / P011).
|
|
6
|
-
#
|
|
7
|
-
# Cross-reference:
|
|
8
|
-
# P032 (agent output uses opaque IDs without titles)
|
|
9
|
-
# @jtbd JTBD-001 (enforce governance without slowing down)
|
|
10
|
-
|
|
11
|
-
setup() {
|
|
12
|
-
AGENT_DIR="$(cd "$(dirname "$BATS_TEST_FILENAME")/.." && pwd)"
|
|
13
|
-
AGENT_FILE="${AGENT_DIR}/agent.md"
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
@test "agent.md contains output formatting section" {
|
|
17
|
-
run grep -n "## Output Formatting" "$AGENT_FILE"
|
|
18
|
-
[ "$status" -eq 0 ]
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
@test "agent.md output formatting rule requires titles with IDs (P032)" {
|
|
22
|
-
run grep -n "title" "$AGENT_FILE"
|
|
23
|
-
[ "$status" -eq 0 ]
|
|
24
|
-
run grep -n "Output Formatting" "$AGENT_FILE"
|
|
25
|
-
[ "$status" -eq 0 ]
|
|
26
|
-
}
|
|
@@ -1,76 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bats
|
|
2
|
-
# Doc-lint guard: architect agent.md must include the runtime-path
|
|
3
|
-
# performance-review step per ADR-023 (wr-architect agent performance
|
|
4
|
-
# review scope). Closes P046 (wr-architect agent misses performance
|
|
5
|
-
# implications on high-traffic endpoints).
|
|
6
|
-
#
|
|
7
|
-
# Structural assertion — Permitted Exception to the source-grep ban
|
|
8
|
-
# (ADR-005 / P011).
|
|
9
|
-
#
|
|
10
|
-
# Cross-reference:
|
|
11
|
-
# P046 (wr-architect agent misses per-request performance implications)
|
|
12
|
-
# ADR-023 (wr-architect agent performance review scope)
|
|
13
|
-
# @jtbd JTBD-002 (ship with confidence)
|
|
14
|
-
# @jtbd JTBD-101 (extend the suite with clear patterns)
|
|
15
|
-
|
|
16
|
-
setup() {
|
|
17
|
-
AGENT_DIR="$(cd "$(dirname "$BATS_TEST_FILENAME")/.." && pwd)"
|
|
18
|
-
AGENT_FILE="${AGENT_DIR}/agent.md"
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
@test "agent.md contains a runtime-path performance review section (ADR-023)" {
|
|
22
|
-
run grep -nE "[Pp]erformance [Rr]eview|[Rr]untime-[Pp]ath" "$AGENT_FILE"
|
|
23
|
-
[ "$status" -eq 0 ]
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
@test "agent.md lists the runtime-path trigger categories (cache / throttle / rate-limit / per-request handler)" {
|
|
27
|
-
# Cache directives
|
|
28
|
-
run grep -niE "cache-control|etag|last-modified" "$AGENT_FILE"
|
|
29
|
-
[ "$status" -eq 0 ]
|
|
30
|
-
# Rate limiting / throttle
|
|
31
|
-
run grep -niE "rate[- ]limit|throttl|quota" "$AGENT_FILE"
|
|
32
|
-
[ "$status" -eq 0 ]
|
|
33
|
-
# Per-request handler
|
|
34
|
-
run grep -niE "per-request|per request handler" "$AGENT_FILE"
|
|
35
|
-
[ "$status" -eq 0 ]
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
@test "agent.md requires quantification: per-request cost delta (ADR-023 Confirmation criterion 2a)" {
|
|
39
|
-
run grep -niE "per-request cost delta|cost delta per request" "$AGENT_FILE"
|
|
40
|
-
[ "$status" -eq 0 ]
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
@test "agent.md requires request-frequency estimate with cited source (ADR-023)" {
|
|
44
|
-
run grep -niE "[Rr]equest[- ][Ff]requency" "$AGENT_FILE"
|
|
45
|
-
[ "$status" -eq 0 ]
|
|
46
|
-
# Must require citing a source (ADR/JTBD/telemetry/worst-case)
|
|
47
|
-
run grep -niE "cite|source" "$AGENT_FILE"
|
|
48
|
-
[ "$status" -eq 0 ]
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
@test "agent.md requires product (cost x frequency) computation" {
|
|
52
|
-
run grep -niE "(cost.*frequency|frequency.*cost|aggregate load|product)" "$AGENT_FILE"
|
|
53
|
-
[ "$status" -eq 0 ]
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
@test "agent.md bans qualitative-only performance claims (ADR-023 Confirmation criterion 2b)" {
|
|
57
|
-
# The prompt must forbid phrases like "load is negligible", "microseconds only"
|
|
58
|
-
run grep -niE "(must not|MUST NOT).*(qualitative|negligible|microsecond)" "$AGENT_FILE"
|
|
59
|
-
[ "$status" -eq 0 ]
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
@test "agent.md references performance-budget ADR convention by name (ADR-023 Confirmation criterion 2c)" {
|
|
63
|
-
run grep -niE "performance[- ]budget" "$AGENT_FILE"
|
|
64
|
-
[ "$status" -eq 0 ]
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
@test "agent.md instructs a verdict against any in-scope performance-budget ADR" {
|
|
68
|
-
# Must describe what to do when a budget is present vs missing
|
|
69
|
-
run grep -niE "verdict|recommend creating|no performance budget" "$AGENT_FILE"
|
|
70
|
-
[ "$status" -eq 0 ]
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
@test "agent.md cross-references ADR-023 so readers can trace the rule's origin" {
|
|
74
|
-
run grep -n "ADR-023" "$AGENT_FILE"
|
|
75
|
-
[ "$status" -eq 0 ]
|
|
76
|
-
}
|
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bats
|
|
2
|
-
# Doc-lint guard: architect agent.md must carry an explicit pre-edit /
|
|
3
|
-
# proposed-change review-mode carve-out so the agent classifies alignment
|
|
4
|
-
# of the PROPOSAL when the calling prompt describes a not-yet-applied
|
|
5
|
-
# change, instead of mis-classifying not-yet-applied state as ISSUES FOUND
|
|
6
|
-
# (P313 catch-22: gate blocks edits; reviewer wants edits done first).
|
|
7
|
-
#
|
|
8
|
-
# tdd-review: structural-permitted (justification: P176 — agent behaviour is
|
|
9
|
-
# prompt-driven with no skill-invocation harness to exercise the verdict
|
|
10
|
-
# behaviourally; ADR-052 Surface 2 structural-justified case, NOT an ADR-005
|
|
11
|
-
# Permitted Exception — ADR-052 narrows ADR-005 to exclude prose-doc greps).
|
|
12
|
-
# When P176 lands, upgrade to a behavioural test that feeds the agent a
|
|
13
|
-
# pre-edit proposal and asserts the PASS verdict.
|
|
14
|
-
#
|
|
15
|
-
# Cross-reference:
|
|
16
|
-
# P313 (Pre-edit governance-gate catch-22 — pass withheld pending edits)
|
|
17
|
-
# ADR-052 Surface 2 (structural-justified verdict) + P176 (harness gap)
|
|
18
|
-
# @jtbd JTBD-001 (Enforce Governance Without Slowing Down — preserves the
|
|
19
|
-
# under-60-second review outcome by removing the redundant
|
|
20
|
-
# re-delegation the catch-22 currently forces)
|
|
21
|
-
|
|
22
|
-
setup() {
|
|
23
|
-
AGENT_DIR="$(cd "$(dirname "$BATS_TEST_FILENAME")/.." && pwd)"
|
|
24
|
-
AGENT_FILE="${AGENT_DIR}/agent.md"
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
@test "agent.md carries a Review Mode section distinguishing pre-edit from post-edit (P313)" {
|
|
28
|
-
run grep -nE "^## Review Mode: Pre-edit / proposed-change vs\. Post-edit / applied" "$AGENT_FILE"
|
|
29
|
-
[ "$status" -eq 0 ]
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
@test "agent.md classifies alignment of the PROPOSAL in pre-edit mode (P313 verbatim core sentence)" {
|
|
33
|
-
run grep -nE "classify alignment of the PROPOSAL itself" "$AGENT_FILE"
|
|
34
|
-
[ "$status" -eq 0 ]
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
@test "agent.md treats not-yet-applied state as the EXPECTED baseline of a pre-edit gate (P313)" {
|
|
38
|
-
run grep -nE "EXPECTED baseline of a pre-edit gate" "$AGENT_FILE"
|
|
39
|
-
[ "$status" -eq 0 ]
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
@test "agent.md explicitly rejects 'edits aren't applied yet' as a valid ISSUES FOUND substance (P313)" {
|
|
43
|
-
run grep -nE "Do NOT treat .edits aren't applied yet" "$AGENT_FILE"
|
|
44
|
-
[ "$status" -eq 0 ]
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
@test "agent.md cites P313 as the catch-22 closure (audit-trail)" {
|
|
48
|
-
run grep -nE "P313" "$AGENT_FILE"
|
|
49
|
-
[ "$status" -eq 0 ]
|
|
50
|
-
}
|
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bats
|
|
2
|
-
# Doc-lint guard: architect agent.md must carry the [Unratified Dependency]
|
|
3
|
-
# verdict (ADR-074 enforcement surface 3 / RFC-010 / P318) — flag a change or
|
|
4
|
-
# plan that explicitly cites/implements an ADR lacking `human-oversight:
|
|
5
|
-
# confirmed` (unratified, non-superseded), keyed on the oversight marker NOT
|
|
6
|
-
# `status:`. Closes the residual P315 foreground gap at the architect-review
|
|
7
|
-
# surface (the ITIL propose-fix surface is covered by RFC-008).
|
|
8
|
-
#
|
|
9
|
-
# tdd-review: structural-permitted (justification: P176 — agent behaviour is
|
|
10
|
-
# prompt-driven with no skill-invocation harness to exercise the verdict
|
|
11
|
-
# behaviourally; ADR-052 Surface 2 structural-justified case, NOT an ADR-005
|
|
12
|
-
# Permitted Exception). When P176 lands, upgrade to a behavioural test that
|
|
13
|
-
# feeds the agent a change citing an unratified ADR and asserts the verdict.
|
|
14
|
-
#
|
|
15
|
-
# Cross-reference:
|
|
16
|
-
# ADR-074 (Confirm a decision's substance before building dependent work — surface 3)
|
|
17
|
-
# ADR-066 (oversight marker; orthogonal status/oversight axes; the "unconfirmed" definition)
|
|
18
|
-
# RFC-010 / P318 (this enforcement surface)
|
|
19
|
-
# ADR-052 Surface 2 (structural-justified verdict) + P176 (harness gap)
|
|
20
|
-
# @jtbd JTBD-002 (ship with confidence) / JTBD-001 (enforce governance without slowing down)
|
|
21
|
-
|
|
22
|
-
setup() {
|
|
23
|
-
AGENT_DIR="$(cd "$(dirname "$BATS_TEST_FILENAME")/.." && pwd)"
|
|
24
|
-
AGENT_FILE="${AGENT_DIR}/agent.md"
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
@test "agent.md lists [Unratified Dependency] as an issue/verdict type (ADR-074 surface 3)" {
|
|
28
|
-
run grep -n '\[Unratified Dependency\]' "$AGENT_FILE"
|
|
29
|
-
[ "$status" -eq 0 ]
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
@test "agent.md has a 'When to flag [Unratified Dependency]' section citing ADR-074" {
|
|
33
|
-
run grep -niE "When to flag \[Unratified Dependency\]" "$AGENT_FILE"
|
|
34
|
-
[ "$status" -eq 0 ]
|
|
35
|
-
run grep -n "ADR-074" "$AGENT_FILE"
|
|
36
|
-
[ "$status" -eq 0 ]
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
@test "agent.md keys the flag on the oversight marker, NOT on status (orthogonal axes)" {
|
|
40
|
-
# ADR-066 / user correction 2026-05-27: building on a ratified-but-proposed ADR is fine.
|
|
41
|
-
run grep -niE "NEVER on .?status|not .?\`?status|orthogonal" "$AGENT_FILE"
|
|
42
|
-
[ "$status" -eq 0 ]
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
@test "agent.md scopes the marker check to frontmatter + skips superseded (mirrors is-decision-unconfirmed.sh)" {
|
|
46
|
-
run grep -niE "frontmatter" "$AGENT_FILE"
|
|
47
|
-
[ "$status" -eq 0 ]
|
|
48
|
-
run grep -niE "superseded" "$AGENT_FILE"
|
|
49
|
-
[ "$status" -eq 0 ]
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
@test "agent.md guards against over-firing on transitive/ambient dependence (inverse-P078)" {
|
|
53
|
-
run grep -niE "explicit(ly)? cite|over-?(fire|scan)|transitive" "$AGENT_FILE"
|
|
54
|
-
[ "$status" -eq 0 ]
|
|
55
|
-
}
|
|
@@ -1,299 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bats
|
|
2
|
-
|
|
3
|
-
# Behavioural tests for architect-compendium-update-entry.sh (RFC-014 Story A,
|
|
4
|
-
# ADR-078 Phase 1 / Option 9). Exercises the hook against fixture compendium
|
|
5
|
-
# trees; asserts on its side-effects (README mutation, staging, exit code) and
|
|
6
|
-
# stderr signals — never on hook source content (feedback_behavioural_tests).
|
|
7
|
-
#
|
|
8
|
-
# The `claude -p` subprocess is stubbed with a PATH-priority fake-claude shim
|
|
9
|
-
# (RFC-014 SQ-014-1) that emits a fixed-shape `{"result": "<entry>"}` envelope
|
|
10
|
-
# for whichever ADR-ID appears in the prompt. Real-subprocess integration is
|
|
11
|
-
# out of scope for Phase 1.
|
|
12
|
-
|
|
13
|
-
setup() {
|
|
14
|
-
HOOK="$(cd "$(dirname "$BATS_TEST_FILENAME")/.." && pwd)/architect-compendium-update-entry.sh"
|
|
15
|
-
PROJ="$(mktemp -d)"
|
|
16
|
-
mkdir -p "$PROJ/docs/decisions"
|
|
17
|
-
( cd "$PROJ" && git init -q && git config user.email t@e.x && git config user.name t )
|
|
18
|
-
|
|
19
|
-
# PATH-priority fake-claude shim: emits a {"result": "<entry>"} envelope with
|
|
20
|
-
# an entry block for the ADR-ID found in the prompt. Marker text STUBBED-<id>
|
|
21
|
-
# makes the emitted Decides line assertable.
|
|
22
|
-
SHIMDIR="$(mktemp -d)"
|
|
23
|
-
cat > "$SHIMDIR/claude" <<'SHIM'
|
|
24
|
-
#!/usr/bin/env bash
|
|
25
|
-
prompt=$(cat)
|
|
26
|
-
id=$(printf '%s' "$prompt" | grep -oE 'ADR-[0-9]+' | head -1 | sed 's/ADR-//')
|
|
27
|
-
entry="### ADR-${id} — Stub Title
|
|
28
|
-
**Status:** proposed | **Oversight:** confirmed
|
|
29
|
-
**Decides:** STUBBED-${id} decision body.
|
|
30
|
-
**Confirmation:** stub crit a; stub crit b
|
|
31
|
-
**Related:** ADR-001"
|
|
32
|
-
jq -cn --arg r "$entry" '{result:$r}'
|
|
33
|
-
SHIM
|
|
34
|
-
chmod +x "$SHIMDIR/claude"
|
|
35
|
-
|
|
36
|
-
# Failing shim variant: exits non-zero, emits nothing (degraded-mode tests).
|
|
37
|
-
FAILSHIMDIR="$(mktemp -d)"
|
|
38
|
-
cat > "$FAILSHIMDIR/claude" <<'SHIM'
|
|
39
|
-
#!/usr/bin/env bash
|
|
40
|
-
cat >/dev/null
|
|
41
|
-
exit 7
|
|
42
|
-
SHIM
|
|
43
|
-
chmod +x "$FAILSHIMDIR/claude"
|
|
44
|
-
|
|
45
|
-
ORIG_PATH="$PATH"
|
|
46
|
-
export PATH="$SHIMDIR:$PATH"
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
teardown() {
|
|
50
|
-
export PATH="$ORIG_PATH"
|
|
51
|
-
rm -rf "$PROJ" "$SHIMDIR" "$FAILSHIMDIR"
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
# Writes a minimal compendium README with one in-force section (ADRs 003, 049,
|
|
55
|
-
# 051) and one historical section (ADR 010). Stable fixture for placement tests.
|
|
56
|
-
mk_readme() {
|
|
57
|
-
cat > "$PROJ/docs/decisions/README.md" <<'EOF'
|
|
58
|
-
# Decisions Compendium
|
|
59
|
-
|
|
60
|
-
Intro prose.
|
|
61
|
-
|
|
62
|
-
---
|
|
63
|
-
|
|
64
|
-
## In-force decisions
|
|
65
|
-
|
|
66
|
-
_3 ADRs._
|
|
67
|
-
|
|
68
|
-
### ADR-003 — Three
|
|
69
|
-
**Status:** proposed | **Oversight:** confirmed
|
|
70
|
-
**Decides:** Decides three.
|
|
71
|
-
|
|
72
|
-
### ADR-049 — FortyNine
|
|
73
|
-
**Status:** accepted | **Oversight:** confirmed
|
|
74
|
-
**Decides:** Decides forty-nine.
|
|
75
|
-
|
|
76
|
-
### ADR-051 — FiftyOne
|
|
77
|
-
**Status:** proposed | **Oversight:** confirmed
|
|
78
|
-
**Decides:** Decides fifty-one.
|
|
79
|
-
|
|
80
|
-
---
|
|
81
|
-
|
|
82
|
-
## Historical decisions
|
|
83
|
-
|
|
84
|
-
_1 ADR._
|
|
85
|
-
|
|
86
|
-
### ADR-010 — Ten
|
|
87
|
-
**Status:** superseded | **Oversight:** confirmed
|
|
88
|
-
**Decides:** Decides ten.
|
|
89
|
-
EOF
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
# mk_adr <nnn> <status> <title>
|
|
93
|
-
mk_adr() {
|
|
94
|
-
local nnn="$1" status="$2" title="$3"
|
|
95
|
-
cat > "$PROJ/docs/decisions/${nnn}-slug.${status}.md" <<EOF
|
|
96
|
-
---
|
|
97
|
-
status: "$status"
|
|
98
|
-
date: 2026-06-09
|
|
99
|
-
human-oversight: confirmed
|
|
100
|
-
---
|
|
101
|
-
|
|
102
|
-
# $title
|
|
103
|
-
|
|
104
|
-
## Decision Outcome
|
|
105
|
-
|
|
106
|
-
Chosen option: **"$title impl"**, because reasons.
|
|
107
|
-
|
|
108
|
-
## Confirmation
|
|
109
|
-
|
|
110
|
-
- (a) first
|
|
111
|
-
- (b) second
|
|
112
|
-
|
|
113
|
-
## Related
|
|
114
|
-
|
|
115
|
-
- Relates to [ADR-001](001-foo.proposed.md)
|
|
116
|
-
EOF
|
|
117
|
-
echo "$PROJ/docs/decisions/${nnn}-slug.${status}.md"
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
run_hook() {
|
|
121
|
-
local fp="$1"
|
|
122
|
-
echo "{\"tool_name\":\"Edit\",\"tool_input\":{\"file_path\":\"$fp\"},\"session_id\":\"s1\"}" \
|
|
123
|
-
| CLAUDE_PROJECT_DIR="$PROJ" bash "$HOOK"
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
@test "fires on Edit to a numbered ADR body and refreshes its entry in-place (criterion 1+4)" {
|
|
127
|
-
mk_readme
|
|
128
|
-
fp=$(mk_adr "049" "accepted" "FortyNine")
|
|
129
|
-
run run_hook "$fp"
|
|
130
|
-
[ "$status" -eq 0 ]
|
|
131
|
-
# In-place replacement: the entry now carries the stubbed Decides marker...
|
|
132
|
-
grep -q 'STUBBED-049 decision body' "$PROJ/docs/decisions/README.md"
|
|
133
|
-
# ...and there is exactly ONE ADR-049 header (no duplicate).
|
|
134
|
-
[ "$(grep -c '^### ADR-049 ' "$PROJ/docs/decisions/README.md")" -eq 1 ]
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
@test "produces an observable stderr signal on every invocation (criterion 2)" {
|
|
138
|
-
mk_readme
|
|
139
|
-
fp=$(mk_adr "049" "accepted" "FortyNine")
|
|
140
|
-
run run_hook "$fp"
|
|
141
|
-
[[ "$output" == *"architect-compendium-update-entry"* ]]
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
@test "emits the expected entry shape with a Decides line (criterion 3)" {
|
|
145
|
-
mk_readme
|
|
146
|
-
fp=$(mk_adr "049" "accepted" "FortyNine")
|
|
147
|
-
run_hook "$fp"
|
|
148
|
-
grep -qE '^\*\*Decides:\*\* STUBBED-049' "$PROJ/docs/decisions/README.md"
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
@test "inserts a new ADR entry in numeric-sort order within in-force (criterion 5)" {
|
|
152
|
-
mk_readme
|
|
153
|
-
fp=$(mk_adr "050" "proposed" "Fifty")
|
|
154
|
-
run run_hook "$fp"
|
|
155
|
-
[ "$status" -eq 0 ]
|
|
156
|
-
# ADR-050 must appear between ADR-049 and ADR-051 in the in-force section.
|
|
157
|
-
line049=$(grep -n '^### ADR-049 ' "$PROJ/docs/decisions/README.md" | cut -d: -f1)
|
|
158
|
-
line050=$(grep -n '^### ADR-050 ' "$PROJ/docs/decisions/README.md" | cut -d: -f1)
|
|
159
|
-
line051=$(grep -n '^### ADR-051 ' "$PROJ/docs/decisions/README.md" | cut -d: -f1)
|
|
160
|
-
[ -n "$line050" ]
|
|
161
|
-
[ "$line049" -lt "$line050" ]
|
|
162
|
-
[ "$line050" -lt "$line051" ]
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
@test "routes a superseded ADR's entry into the historical section (criterion 6)" {
|
|
166
|
-
mk_readme
|
|
167
|
-
fp=$(mk_adr "012" "superseded" "Twelve")
|
|
168
|
-
run run_hook "$fp"
|
|
169
|
-
[ "$status" -eq 0 ]
|
|
170
|
-
# ADR-012 must land AFTER the Historical-decisions header, not in In-force.
|
|
171
|
-
hist=$(grep -n '^## Historical decisions' "$PROJ/docs/decisions/README.md" | cut -d: -f1)
|
|
172
|
-
line012=$(grep -n '^### ADR-012 ' "$PROJ/docs/decisions/README.md" | cut -d: -f1)
|
|
173
|
-
[ -n "$line012" ]
|
|
174
|
-
[ "$line012" -gt "$hist" ]
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
@test "migrates an entry from in-force to historical when status flips (criterion 6)" {
|
|
178
|
-
mk_readme
|
|
179
|
-
# ADR-049 currently renders in the in-force fixture section; re-author it as
|
|
180
|
-
# superseded — the hook must remove the in-force block and place it historical.
|
|
181
|
-
fp=$(mk_adr "049" "superseded" "FortyNine")
|
|
182
|
-
run run_hook "$fp"
|
|
183
|
-
[ "$status" -eq 0 ]
|
|
184
|
-
# Exactly one ADR-049 entry, and it is now below the Historical header.
|
|
185
|
-
[ "$(grep -c '^### ADR-049 ' "$PROJ/docs/decisions/README.md")" -eq 1 ]
|
|
186
|
-
hist=$(grep -n '^## Historical decisions' "$PROJ/docs/decisions/README.md" | cut -d: -f1)
|
|
187
|
-
line049=$(grep -n '^### ADR-049 ' "$PROJ/docs/decisions/README.md" | cut -d: -f1)
|
|
188
|
-
[ "$line049" -gt "$hist" ]
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
@test "stages docs/decisions/README.md after refresh (criterion: same-commit pairing)" {
|
|
192
|
-
mk_readme
|
|
193
|
-
( cd "$PROJ" && git add -A && git commit -q -m init )
|
|
194
|
-
fp=$(mk_adr "049" "accepted" "FortyNine")
|
|
195
|
-
run_hook "$fp"
|
|
196
|
-
# README must be in the staged set.
|
|
197
|
-
( cd "$PROJ" && git diff --cached --name-only ) | grep -q 'docs/decisions/README.md'
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
@test "subprocess failure leaves README unchanged + does NOT block the edit (criterion 7)" {
|
|
201
|
-
mk_readme
|
|
202
|
-
before=$(cat "$PROJ/docs/decisions/README.md")
|
|
203
|
-
fp=$(mk_adr "049" "accepted" "FortyNine")
|
|
204
|
-
export PATH="$FAILSHIMDIR:$ORIG_PATH"
|
|
205
|
-
run run_hook "$fp"
|
|
206
|
-
[ "$status" -eq 0 ] # does not block the body edit
|
|
207
|
-
after=$(cat "$PROJ/docs/decisions/README.md")
|
|
208
|
-
[ "$before" = "$after" ] # README unchanged (degraded mode)
|
|
209
|
-
[[ "$output" == *"degraded mode"* ]]
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
@test "opt-out ARCHITECT_AUTO_UPDATE_COMPENDIUM=0 self-suppresses (criterion 8)" {
|
|
213
|
-
mk_readme
|
|
214
|
-
before=$(cat "$PROJ/docs/decisions/README.md")
|
|
215
|
-
fp=$(mk_adr "049" "accepted" "FortyNine")
|
|
216
|
-
run bash -c "echo '{\"tool_name\":\"Edit\",\"tool_input\":{\"file_path\":\"$fp\"}}' | ARCHITECT_AUTO_UPDATE_COMPENDIUM=0 CLAUDE_PROJECT_DIR='$PROJ' bash '$HOOK'"
|
|
217
|
-
[ "$status" -eq 0 ]
|
|
218
|
-
[ "$before" = "$(cat "$PROJ/docs/decisions/README.md")" ]
|
|
219
|
-
[[ "$output" == *"ARCHITECT_AUTO_UPDATE_COMPENDIUM=0"* ]]
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
@test "ignores README.md edits (no self-recursion)" {
|
|
223
|
-
mk_readme
|
|
224
|
-
before=$(cat "$PROJ/docs/decisions/README.md")
|
|
225
|
-
run run_hook "$PROJ/docs/decisions/README.md"
|
|
226
|
-
[ "$status" -eq 0 ]
|
|
227
|
-
[ "$before" = "$(cat "$PROJ/docs/decisions/README.md")" ]
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
@test "ignores non-decisions file edits" {
|
|
231
|
-
mk_readme
|
|
232
|
-
echo "x" > "$PROJ/other.ts"
|
|
233
|
-
run run_hook "$PROJ/other.ts"
|
|
234
|
-
[ "$status" -eq 0 ]
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
@test "fail-closed guard: rejects a subprocess entry that injects spurious ADR ids/sections — restores README, degraded, unstaged (P367)" {
|
|
238
|
-
mk_readme
|
|
239
|
-
( cd "$PROJ" && git add -A && git commit -q -m init )
|
|
240
|
-
before=$(cat "$PROJ/docs/decisions/README.md")
|
|
241
|
-
fp=$(mk_adr "049" "accepted" "FortyNine")
|
|
242
|
-
# Malformed shim: valid header for the edited id, but ALSO injects an
|
|
243
|
-
# unrelated ADR-999 header and a spurious '## ' section — the additive
|
|
244
|
-
# corruption shape empirically reproduced for P367.
|
|
245
|
-
BADDIR="$(mktemp -d)"
|
|
246
|
-
cat > "$BADDIR/claude" <<'SHIM'
|
|
247
|
-
#!/usr/bin/env bash
|
|
248
|
-
cat >/dev/null
|
|
249
|
-
entry="### ADR-049 — Hijacked
|
|
250
|
-
**Status:** accepted | **Oversight:** confirmed
|
|
251
|
-
**Decides:** body.
|
|
252
|
-
|
|
253
|
-
## Injected section
|
|
254
|
-
|
|
255
|
-
### ADR-999 — Sneaky"
|
|
256
|
-
jq -cn --arg r "$entry" '{result:$r}'
|
|
257
|
-
SHIM
|
|
258
|
-
chmod +x "$BADDIR/claude"
|
|
259
|
-
export PATH="$BADDIR:$ORIG_PATH"
|
|
260
|
-
run run_hook "$fp"
|
|
261
|
-
[ "$status" -eq 0 ] # never blocks the body edit
|
|
262
|
-
[ "$before" = "$(cat "$PROJ/docs/decisions/README.md")" ] # restored unchanged
|
|
263
|
-
! grep -q 'ADR-999' "$PROJ/docs/decisions/README.md" # no injected id survives
|
|
264
|
-
[[ "$output" == *"guard"* ]] # observable degraded signal
|
|
265
|
-
# README left in its committed state — no corrupted blob staged.
|
|
266
|
-
( cd "$PROJ" && git diff --cached --quiet -- docs/decisions/README.md )
|
|
267
|
-
rm -rf "$BADDIR"
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
@test "fail-closed guard: rejects an emit for the wrong ADR id (edited id absent) — restores, degraded (P367)" {
|
|
271
|
-
mk_readme
|
|
272
|
-
before=$(cat "$PROJ/docs/decisions/README.md")
|
|
273
|
-
fp=$(mk_adr "050" "proposed" "Fifty") # NEW adr — not yet in the compendium
|
|
274
|
-
# Shim emits an entry for the WRONG id (049, which already exists) instead of
|
|
275
|
-
# the edited 050: the edited id never lands and 049 is duplicated.
|
|
276
|
-
WRONGDIR="$(mktemp -d)"
|
|
277
|
-
cat > "$WRONGDIR/claude" <<'SHIM'
|
|
278
|
-
#!/usr/bin/env bash
|
|
279
|
-
cat >/dev/null
|
|
280
|
-
entry="### ADR-049 — WrongId
|
|
281
|
-
**Status:** proposed | **Oversight:** confirmed
|
|
282
|
-
**Decides:** body."
|
|
283
|
-
jq -cn --arg r "$entry" '{result:$r}'
|
|
284
|
-
SHIM
|
|
285
|
-
chmod +x "$WRONGDIR/claude"
|
|
286
|
-
export PATH="$WRONGDIR:$ORIG_PATH"
|
|
287
|
-
run run_hook "$fp"
|
|
288
|
-
[ "$status" -eq 0 ]
|
|
289
|
-
[ "$before" = "$(cat "$PROJ/docs/decisions/README.md")" ] # restored unchanged
|
|
290
|
-
! grep -q '^### ADR-050' "$PROJ/docs/decisions/README.md" # edited id never landed
|
|
291
|
-
[[ "$output" == *"guard"* ]]
|
|
292
|
-
rm -rf "$WRONGDIR"
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
@test "registered in hooks.json on PostToolUse Edit|Write (criterion 9)" {
|
|
296
|
-
HOOKS_JSON="$(cd "$(dirname "$BATS_TEST_FILENAME")/.." && pwd)/hooks.json"
|
|
297
|
-
run jq -e '.hooks.PostToolUse[] | select(.matcher | test("Edit")) | .hooks[] | select(.command | test("architect-compendium-update-entry"))' "$HOOKS_JSON"
|
|
298
|
-
[ "$status" -eq 0 ]
|
|
299
|
-
}
|