@windyroad/architect 0.15.1 → 0.15.2-preview.581

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.
@@ -123,5 +123,5 @@
123
123
  }
124
124
  },
125
125
  "name": "wr-architect",
126
- "version": "0.15.1"
126
+ "version": "0.15.2"
127
127
  }
@@ -41,29 +41,32 @@ case "$SUBAGENT" in
41
41
  VERDICT="FAIL"
42
42
  fi
43
43
 
44
+ # Substance-aware drift hash + atomic verdict-write (ADR-009 amendment
45
+ # 2026-06-06). The marker + hash file are written as an atomic pair via
46
+ # `_atomic_mark_with_hash` so a PASS never silently fails to persist
47
+ # (closes the "marker doesn't land after PASS" failure mode P353
48
+ # measured as ~12 subagent invocations + 3 BYPASS_RISK_GATE=1 uses
49
+ # per 3-filing session).
50
+ MARKER="/tmp/architect-reviewed-${SESSION_ID}"
44
51
  case "$VERDICT" in
45
- PASS)
46
- touch "/tmp/architect-reviewed-${SESSION_ID}"
52
+ PASS|"")
53
+ # PASS or unparseable verdict — allow with marker (the empty case
54
+ # preserves the pre-amendment "could not parse verdict" backward-
55
+ # compat allow-with-marker behaviour to avoid lockout).
56
+ if [ -d "$PROJECT_DIR/docs/decisions" ]; then
57
+ HASH=$(_substance_hash_path "$PROJECT_DIR/docs/decisions")
58
+ else
59
+ HASH="none"
60
+ fi
61
+ if ! _atomic_mark_with_hash "$MARKER" "$HASH"; then
62
+ echo "WARN: architect-mark-reviewed atomic marker-write failed for ${MARKER}" >&2
63
+ fi
47
64
  ;;
48
65
  FAIL)
49
66
  # Do NOT create marker — review found issues
50
67
  ;;
51
- *)
52
- # Could not parse verdict — allow with marker to avoid lockout
53
- touch "/tmp/architect-reviewed-${SESSION_ID}"
54
- ;;
55
68
  esac
56
69
 
57
- # Store decision hash for drift detection
58
- if [ -f "/tmp/architect-reviewed-${SESSION_ID}" ]; then
59
- if [ -d "$PROJECT_DIR/docs/decisions" ]; then
60
- HASH=$(find "$PROJECT_DIR/docs/decisions" -name '*.md' -not -name 'README.md' -print0 | sort -z | xargs -0 cat 2>/dev/null | _hashcmd | cut -d' ' -f1)
61
- else
62
- HASH="none"
63
- fi
64
- echo "$HASH" > "/tmp/architect-reviewed-${SESSION_ID}.hash"
65
- fi
66
-
67
70
  # Plan review marker
68
71
  touch "/tmp/architect-plan-reviewed-${SESSION_ID}"
69
72
  ;;
@@ -4,8 +4,8 @@
4
4
  # This prevents drift detection from invalidating the marker when creating
5
5
  # new decision files that the architect just approved.
6
6
 
7
- # Portable hash: tries md5sum, falls back to md5 -r, then shasum
8
- _hashcmd() { md5sum 2>/dev/null || md5 -r 2>/dev/null || shasum 2>/dev/null; }
7
+ SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
8
+ source "$SCRIPT_DIR/lib/gate-helpers.sh"
9
9
 
10
10
  # P191 Phase 2: anchor docs/decisions on the project root, not the hook's
11
11
  # runtime CWD (see architect-enforce-edit.sh). The refreshed hash must match
@@ -33,14 +33,24 @@ esac
33
33
  MARKER="/tmp/architect-reviewed-${SESSION_ID}"
34
34
  HASH_FILE="/tmp/architect-reviewed-${SESSION_ID}.hash"
35
35
 
36
- # Only refresh if a valid marker exists
36
+ # Only refresh if a valid marker exists. Uses substance-aware hash
37
+ # (ADR-009 amendment 2026-06-06) and atomic-rename write (mktemp + mv).
37
38
  if [ -f "$MARKER" ] && [ -f "$HASH_FILE" ]; then
38
39
  if [ -d "$PROJECT_DIR/docs/decisions" ]; then
39
- HASH=$(find "$PROJECT_DIR/docs/decisions" -name '*.md' -not -name 'README.md' -print0 | sort -z | xargs -0 cat 2>/dev/null | _hashcmd | cut -d' ' -f1)
40
+ HASH=$(_substance_hash_path "$PROJECT_DIR/docs/decisions")
40
41
  else
41
42
  HASH="none"
42
43
  fi
43
- echo "$HASH" > "$HASH_FILE"
44
+ htmp="${HASH_FILE}.tmp.$$.${RANDOM:-0}"
45
+ if printf '%s\n' "$HASH" > "$htmp" 2>/dev/null; then
46
+ if ! mv -f "$htmp" "$HASH_FILE" 2>/dev/null; then
47
+ rm -f "$htmp"
48
+ echo "WARN: architect-refresh-hash atomic rename failed for ${HASH_FILE}" >&2
49
+ fi
50
+ else
51
+ rm -f "$htmp"
52
+ echo "WARN: architect-refresh-hash tempfile write failed for ${HASH_FILE}" >&2
53
+ fi
44
54
  fi
45
55
 
46
56
  exit 0
@@ -22,13 +22,17 @@ check_architect_gate() {
22
22
  local MARKER_TIME=$(_mtime "$MARKER")
23
23
  local AGE=$(( NOW - MARKER_TIME ))
24
24
  if [ "$AGE" -lt "$TTL_SECONDS" ]; then
25
- # TTL still valid -- check for decision drift
25
+ # TTL still valid -- check for decision drift via substance-aware hash
26
+ # (ADR-009 amendment 2026-06-06: trivial whitespace / line-ending /
27
+ # trailing-newline edits do NOT trigger drift; substantive policy
28
+ # changes DO. Conservative boundary — ambiguous edits stay
29
+ # substantive. See gate-helpers.sh::_substance_hash_path).
26
30
  local HASH_FILE="/tmp/architect-reviewed-${SESSION_ID}.hash"
27
31
  if [ -f "$HASH_FILE" ]; then
28
32
  local STORED=$(cat "$HASH_FILE")
29
33
  local CURRENT
30
34
  if [ -d "$PROJECT_DIR/docs/decisions" ]; then
31
- CURRENT=$(find "$PROJECT_DIR/docs/decisions" -name '*.md' -not -name 'README.md' -print0 | sort -z | xargs -0 cat 2>/dev/null | _hashcmd | cut -d' ' -f1)
35
+ CURRENT=$(_substance_hash_path "$PROJECT_DIR/docs/decisions")
32
36
  else
33
37
  CURRENT="none"
34
38
  fi
@@ -13,6 +13,111 @@ _mtime() { stat -c%Y "$1" 2>/dev/null || /usr/bin/stat -f%m "$1" 2>/dev/null ||
13
13
  # Portable hash: tries md5sum, falls back to md5 -r, then shasum
14
14
  _hashcmd() { md5sum 2>/dev/null || md5 -r 2>/dev/null || shasum 2>/dev/null; }
15
15
 
16
+ # ---------------------------------------------------------------------------
17
+ # Substance-aware drift hash + atomic verdict-write (ADR-009 amendment
18
+ # 2026-06-06, P353 + P303 close).
19
+ #
20
+ # `_substance_hash_path` normalises trivial/no-op edits BEFORE hashing so a
21
+ # PASS marker survives whitespace / CRLF / trailing-newline edits while still
22
+ # detecting substantive policy changes. Conservative boundary: when in doubt
23
+ # whether an edit is trivial vs substantive, this helper treats it as
24
+ # substantive (re-review fires). Only whitespace + line-ending + trailing-
25
+ # newline are normalised in this iteration — single-numeral edits and
26
+ # frontmatter-key changes are intentionally NOT normalised. See ADR-009
27
+ # 2026-06-06 amendment for the ratified contract.
28
+ #
29
+ # `_atomic_mark_with_hash` writes the marker + hash file as an atomic pair
30
+ # (mktemp + mv) so a PASS NEVER silently fails to persist (the empirically-
31
+ # measured P353 failure mode that forced BYPASS_RISK_GATE=1 on every
32
+ # external-comms gate clearance). Either both files land, or neither does.
33
+ # Non-zero exit on failure so callers can emit a diagnostic.
34
+ # ---------------------------------------------------------------------------
35
+
36
+ # Substance-aware hash of a file or directory path.
37
+ # For directories: hashes the concatenated content of all *.md files
38
+ # (excluding README.md) in sorted order.
39
+ # For files: hashes the file content.
40
+ # Normalisation BEFORE hashing: CRLF → LF, strip trailing whitespace per
41
+ # line, normalise trailing whitespace to a single \n.
42
+ # Echoes "missing" for paths that do not exist (drop-in equivalence with the
43
+ # pre-amendment `cat | _hashcmd | cut -d' ' -f1` site behaviour).
44
+ # Echoes a hex sha256 of the normalised content on success.
45
+ _substance_hash_path() {
46
+ local path="$1"
47
+ if [ -z "$path" ]; then
48
+ echo "missing"
49
+ return 0
50
+ fi
51
+ if [ -f "$path" ]; then
52
+ cat "$path" 2>/dev/null | _substance_normalize_then_hash
53
+ elif [ -d "$path" ]; then
54
+ find "$path" -name '*.md' -not -name 'README.md' -print0 \
55
+ | sort -z \
56
+ | xargs -0 cat 2>/dev/null \
57
+ | _substance_normalize_then_hash
58
+ else
59
+ echo "missing"
60
+ fi
61
+ }
62
+
63
+ # Internal: reads from stdin, normalises whitespace + line endings, emits a
64
+ # hex sha256 of the normalised content. Conservative boundary documented in
65
+ # ADR-009 2026-06-06 amendment: ambiguous edits stay substantive.
66
+ _substance_normalize_then_hash() {
67
+ python3 -c "
68
+ import sys, hashlib
69
+ data = sys.stdin.buffer.read().decode('utf-8', errors='replace')
70
+ # CRLF / CR -> LF
71
+ data = data.replace('\r\n', '\n').replace('\r', '\n')
72
+ # Strip trailing whitespace per line.
73
+ lines = [line.rstrip() for line in data.split('\n')]
74
+ # Re-join and normalise trailing whitespace to a single \n.
75
+ normalised = '\n'.join(lines).rstrip() + '\n'
76
+ print(hashlib.sha256(normalised.encode('utf-8')).hexdigest())
77
+ " 2>/dev/null || echo "missing"
78
+ }
79
+
80
+ # Atomically write a presence marker + its paired hash file. Either both
81
+ # files land or neither does. Returns 0 on success, 1 on failure. On failure
82
+ # any partial state is rolled back.
83
+ # Usage: _atomic_mark_with_hash "/tmp/architect-reviewed-${SID}" "$HASH"
84
+ _atomic_mark_with_hash() {
85
+ local marker="$1"
86
+ local hash="$2"
87
+ local hash_file="${marker}.hash"
88
+
89
+ if [ -z "$marker" ]; then
90
+ return 1
91
+ fi
92
+
93
+ local htmp="${hash_file}.tmp.$$.${RANDOM:-0}"
94
+ local mtmp="${marker}.tmp.$$.${RANDOM:-0}"
95
+
96
+ # Write hash to tempfile.
97
+ if ! printf '%s\n' "$hash" > "$htmp" 2>/dev/null; then
98
+ rm -f "$htmp"
99
+ return 1
100
+ fi
101
+ # Write empty marker to tempfile.
102
+ if ! : > "$mtmp" 2>/dev/null; then
103
+ rm -f "$htmp" "$mtmp"
104
+ return 1
105
+ fi
106
+ # Atomic rename: hash file first.
107
+ if ! mv -f "$htmp" "$hash_file" 2>/dev/null; then
108
+ rm -f "$htmp" "$mtmp"
109
+ return 1
110
+ fi
111
+ # Atomic rename: marker second. If this fails, roll back the hash file
112
+ # so we never observe a hash-without-marker half-state.
113
+ if ! mv -f "$mtmp" "$marker" 2>/dev/null; then
114
+ rm -f "$mtmp"
115
+ rm -f "$hash_file"
116
+ return 1
117
+ fi
118
+ return 0
119
+ }
120
+
16
121
  # Paths excluded from pipeline state hashing and docs-only detection.
17
122
  _doc_exclusions() {
18
123
  echo ':!docs/' ':!.risk-reports/' ':!.changeset/' ':!governance/' ':!.claude/plans/' ':!CLAUDE.md' ':!AGENTS.md' ':!PRINCIPLES.md' ':!DECISION-MANAGEMENT.md' ':!AGENTIC_RISK_REGISTER.md' ':!PROBLEM-MANAGEMENT.md'
@@ -0,0 +1,243 @@
1
+ #!/usr/bin/env bats
2
+
3
+ # Behavioural tests for ADR-009 amendment 2026-06-06: substance-aware drift +
4
+ # atomic verdict-write. Closes P353 (hash-marker brittleness umbrella) +
5
+ # P303 (architect-gate multi-ADR deadlock drift-relock facet).
6
+ #
7
+ # Cases ratified by the user 2026-06-06:
8
+ # (a) Trivial edit (whitespace, CRLF, trailing newline) does NOT re-trigger.
9
+ # (b) Substantive edit (new content, changed line) DOES re-trigger.
10
+ # (c) Atomic write — marker + hash file land together on PASS, with the
11
+ # marker's mtime current and the hash file's content equal to the
12
+ # substance hash of the policy content.
13
+ # (d) Conservative fallback — semantic edits beyond the documented
14
+ # whitespace normalisation (e.g. single-numeral change) ARE treated as
15
+ # substantive (re-review fires).
16
+
17
+ setup() {
18
+ SCRIPT_DIR="$(cd "$(dirname "$BATS_TEST_FILENAME")/.." && pwd)"
19
+ source "$SCRIPT_DIR/lib/gate-helpers.sh"
20
+ source "$SCRIPT_DIR/lib/architect-gate.sh"
21
+
22
+ TEST_SESSION="bats-arch-substance-$$-${BATS_TEST_NUMBER}"
23
+ TEST_DIR=$(mktemp -d -t substance-aware-drift.XXXXXX)
24
+ mkdir -p "$TEST_DIR/docs/decisions"
25
+ CLAUDE_PROJECT_DIR="$TEST_DIR"
26
+ export CLAUDE_PROJECT_DIR
27
+
28
+ MARKER="/tmp/architect-reviewed-${TEST_SESSION}"
29
+ HASH_FILE="${MARKER}.hash"
30
+ rm -f "$MARKER" "$HASH_FILE"
31
+ }
32
+
33
+ teardown() {
34
+ rm -f "$MARKER" "$HASH_FILE"
35
+ rm -rf "$TEST_DIR"
36
+ }
37
+
38
+ # Helper: install ADR content, then store the substance hash + marker as the
39
+ # architect-mark-reviewed.sh PASS path would.
40
+ _install_and_mark() {
41
+ local body="$1"
42
+ printf '%s' "$body" > "$TEST_DIR/docs/decisions/123-test.proposed.md"
43
+ local hash
44
+ hash=$(_substance_hash_path "$TEST_DIR/docs/decisions")
45
+ _atomic_mark_with_hash "$MARKER" "$hash"
46
+ }
47
+
48
+ # ---------------------------------------------------------------------------
49
+ # Case (a) — trivial whitespace / line-ending edits do NOT re-trigger
50
+ # ---------------------------------------------------------------------------
51
+
52
+ @test "substance-aware: trailing-whitespace edit does NOT re-trigger drift" {
53
+ _install_and_mark "# ADR-123
54
+
55
+ A line of policy content.
56
+ "
57
+ # Add trailing spaces to the same line.
58
+ printf '%s' "# ADR-123
59
+
60
+ A line of policy content.
61
+ " > "$TEST_DIR/docs/decisions/123-test.proposed.md"
62
+
63
+ run check_architect_gate "$TEST_SESSION"
64
+ [ "$status" -eq 0 ]
65
+ [ -f "$MARKER" ]
66
+ [ -f "$HASH_FILE" ]
67
+ }
68
+
69
+ @test "substance-aware: CRLF→LF edit does NOT re-trigger drift" {
70
+ _install_and_mark "# ADR-123
71
+
72
+ A line of policy content.
73
+ "
74
+ # Convert the same content to CRLF line endings.
75
+ printf '# ADR-123\r\n\r\nA line of policy content.\r\n' \
76
+ > "$TEST_DIR/docs/decisions/123-test.proposed.md"
77
+
78
+ run check_architect_gate "$TEST_SESSION"
79
+ [ "$status" -eq 0 ]
80
+ [ -f "$MARKER" ]
81
+ }
82
+
83
+ @test "substance-aware: extra trailing newlines do NOT re-trigger drift" {
84
+ _install_and_mark "# ADR-123
85
+
86
+ A line of policy content.
87
+ "
88
+ # Add multiple trailing newlines.
89
+ printf '%s' "# ADR-123
90
+
91
+ A line of policy content.
92
+
93
+
94
+
95
+ " > "$TEST_DIR/docs/decisions/123-test.proposed.md"
96
+
97
+ run check_architect_gate "$TEST_SESSION"
98
+ [ "$status" -eq 0 ]
99
+ [ -f "$MARKER" ]
100
+ }
101
+
102
+ # ---------------------------------------------------------------------------
103
+ # Case (b) — substantive content edits DO re-trigger
104
+ # ---------------------------------------------------------------------------
105
+
106
+ @test "substance-aware: new-word edit DOES re-trigger drift" {
107
+ _install_and_mark "# ADR-123
108
+
109
+ A line of policy content.
110
+ "
111
+ # Real content change.
112
+ printf '%s' "# ADR-123
113
+
114
+ A different line of policy content.
115
+ " > "$TEST_DIR/docs/decisions/123-test.proposed.md"
116
+
117
+ run check_architect_gate "$TEST_SESSION"
118
+ [ "$status" -ne 0 ]
119
+ [ ! -f "$MARKER" ]
120
+ [ ! -f "$HASH_FILE" ]
121
+ }
122
+
123
+ @test "substance-aware: added-paragraph edit DOES re-trigger drift" {
124
+ _install_and_mark "# ADR-123
125
+
126
+ A line of policy content.
127
+ "
128
+ printf '%s' "# ADR-123
129
+
130
+ A line of policy content.
131
+
132
+ A whole new paragraph of substantive change.
133
+ " > "$TEST_DIR/docs/decisions/123-test.proposed.md"
134
+
135
+ run check_architect_gate "$TEST_SESSION"
136
+ [ "$status" -ne 0 ]
137
+ [ ! -f "$MARKER" ]
138
+ }
139
+
140
+ @test "substance-aware: new file in docs/decisions DOES re-trigger drift" {
141
+ _install_and_mark "# ADR-123
142
+
143
+ Content.
144
+ "
145
+ # Adding a NEW ADR file is a substantive change.
146
+ printf '%s' "# ADR-124
147
+
148
+ New ADR content.
149
+ " > "$TEST_DIR/docs/decisions/124-new.proposed.md"
150
+
151
+ run check_architect_gate "$TEST_SESSION"
152
+ [ "$status" -ne 0 ]
153
+ [ ! -f "$MARKER" ]
154
+ }
155
+
156
+ # ---------------------------------------------------------------------------
157
+ # Case (c) — atomic write persists the PASS marker reliably
158
+ # ---------------------------------------------------------------------------
159
+
160
+ @test "atomic-write: PASS write lands marker + hash together" {
161
+ printf '%s' "# ADR-123
162
+
163
+ Policy content.
164
+ " > "$TEST_DIR/docs/decisions/123-test.proposed.md"
165
+ local hash
166
+ hash=$(_substance_hash_path "$TEST_DIR/docs/decisions")
167
+
168
+ run _atomic_mark_with_hash "$MARKER" "$hash"
169
+ [ "$status" -eq 0 ]
170
+ [ -f "$MARKER" ]
171
+ [ -f "$HASH_FILE" ]
172
+ # The stored hash equals the substance hash of the policy content.
173
+ [ "$(cat "$HASH_FILE")" = "$hash" ]
174
+ }
175
+
176
+ @test "atomic-write: failed hash-write leaves neither file (no half-state)" {
177
+ # Point the marker into a non-existent directory to force mv failure.
178
+ local bad_marker="/tmp/does-not-exist-bats-$$/architect-reviewed-${TEST_SESSION}"
179
+ run _atomic_mark_with_hash "$bad_marker" "deadbeef"
180
+ [ "$status" -ne 0 ]
181
+ [ ! -f "$bad_marker" ]
182
+ [ ! -f "${bad_marker}.hash" ]
183
+ }
184
+
185
+ @test "atomic-write: PASS write enables a subsequent gate check to allow" {
186
+ printf '%s' "# ADR-123
187
+
188
+ Policy content.
189
+ " > "$TEST_DIR/docs/decisions/123-test.proposed.md"
190
+ local hash
191
+ hash=$(_substance_hash_path "$TEST_DIR/docs/decisions")
192
+ _atomic_mark_with_hash "$MARKER" "$hash"
193
+
194
+ run check_architect_gate "$TEST_SESSION"
195
+ [ "$status" -eq 0 ]
196
+ }
197
+
198
+ # ---------------------------------------------------------------------------
199
+ # Case (d) — conservative boundary: semantic edits beyond whitespace
200
+ # normalisation ARE treated as substantive
201
+ # ---------------------------------------------------------------------------
202
+
203
+ @test "conservative: single-numeral change DOES re-trigger drift" {
204
+ _install_and_mark "# ADR-123
205
+
206
+ Threshold is 5 minutes.
207
+ "
208
+ # A single-numeral change is semantic — conservative boundary re-reviews.
209
+ printf '%s' "# ADR-123
210
+
211
+ Threshold is 6 minutes.
212
+ " > "$TEST_DIR/docs/decisions/123-test.proposed.md"
213
+
214
+ run check_architect_gate "$TEST_SESSION"
215
+ [ "$status" -ne 0 ]
216
+ [ ! -f "$MARKER" ]
217
+ }
218
+
219
+ @test "conservative: frontmatter-key change DOES re-trigger drift" {
220
+ _install_and_mark "---
221
+ status: proposed
222
+ date: 2026-06-06
223
+ ---
224
+
225
+ # ADR-123
226
+
227
+ Body.
228
+ "
229
+ # Frontmatter date bump — conservative boundary re-reviews.
230
+ printf '%s' "---
231
+ status: proposed
232
+ date: 2026-06-07
233
+ ---
234
+
235
+ # ADR-123
236
+
237
+ Body.
238
+ " > "$TEST_DIR/docs/decisions/123-test.proposed.md"
239
+
240
+ run check_architect_gate "$TEST_SESSION"
241
+ [ "$status" -ne 0 ]
242
+ [ ! -f "$MARKER" ]
243
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@windyroad/architect",
3
- "version": "0.15.1",
3
+ "version": "0.15.2-preview.581",
4
4
  "description": "Architecture decision enforcement for AI coding agents",
5
5
  "bin": {
6
6
  "windyroad-architect": "./bin/install.mjs"
@@ -85,6 +85,8 @@ Before writing the ADR file, perform a decision-boundary analysis on the gathere
85
85
 
86
86
  **Non-interactive fallback**: When `AskUserQuestion` is unavailable (e.g., non-interactive/AFK mode), automatically split into separate ADRs with consecutive IDs and note the auto-split in output. Do not block creation.
87
87
 
88
+ **ADR-013 Rule 6 carve-out audit (P352, 2026-06-06 amendment)**: the universal AFK default is **queue-and-continue**; this site is a documented **AUTO-DEFAULT** carve-out. Authorising principle: policy-authorised safe default per ADR-044 category 4 (silent framework). Splitting is fully reversible (manual combine via supersession), the framework's WSJF / lifecycle model rewards explicit per-decision ranking, and "split when in doubt" is the persona-correct safe heuristic for JTBD-006 (the loop progresses; over-splits are cheap to combine; halt would cost more loop throughput than the over-split risk). Note: the Step 5 substance-confirm HALT below is a separate carve-out authorised by ADR-074 — substance-confirm cannot AUTO-DEFAULT because the dependent work (Decision Outcome / Consequences / Confirmation / Pros and Cons drafting) is built ON the chosen option.
89
+
88
90
  **Split implementation**: When splitting, assign consecutive IDs. Cross-reference each ADR in the other's Related section or as a linked decision in the consequences.
89
91
 
90
92
  **Scope**: Scoped to new ADR creation only (steps 2–5). Does not apply to supersession handling (step 6), where the scope of the new decision is already known and bounded.
@@ -238,6 +240,8 @@ oversight-date: YYYY-MM-DD # today
238
240
 
239
241
  The `wr-architect-mark-oversight-confirmed` call writes the session-scoped evidence marker (`/tmp/oversight-confirmed-<sha>-<sid>`) that the `architect-oversight-marker-discipline.sh` PreToolUse hook reads to authorise the subsequent Edit/Write — without the helper call, the hook will DENY the marker write. AFK iter subprocesses spawned via `claude -p` have no `AskUserQuestion` access; they MUST write `human-oversight: unconfirmed` instead (the AFK fallback enum value codified in ADR-066 amendment 2026-06-02), which the drain (`/wr-architect:review-decisions`) later promotes interactively. Calling the helper without a real user substance-confirm event is the P348 hollow-marker bug — every legitimate marker write traces back to an `AskUserQuestion` answer in the same turn.
240
242
 
243
+ **ADR-013 Rule 6 carve-out audit (P352, 2026-06-06 amendment)**: the universal AFK default is queue-and-continue. This Step 5 substance-confirm HALT-and-write-`human-oversight: unconfirmed` shape is a documented carve-out, authorised by **ADR-074** (Confirm decision substance before building dependent work). Rationale: an ADR with `human-oversight: confirmed` enters the world born-confirmed (it does not appear in `/wr-architect:review-decisions`' unoversighted set), so dependent work — every implementation that cites this ADR as authority — would be built on substance that was never user-affirmed. AFK writing `human-oversight: unconfirmed` IS the queue-and-continue shape: the loop continues; the substance-confirm decision is queued to the next interactive drain. Persona-correct for JTBD-006 ("queued for my return, not guessed at"); the carve-out is from the auto-confirm shape, not from queue-and-continue itself.
244
+
241
245
  **Mismatch handling.** If the substance-confirm answer selects a DIFFERENT option than the draft was authored against:
242
246
 
243
247
  - DO NOT write the marker.
@@ -59,6 +59,8 @@ For each ADR in the ordered queue, surface the decision as an `AskUserQuestion`
59
59
 
60
60
  The trailing clause exists for cross-reference value — it is optional, NOT a re-entry point for meta-leading. When the meta does not add load-bearing context for the confirm decision, omit it entirely.
61
61
 
62
+ **Brief-before-ID discipline (P350).** The `question` and `options` text MUST inline what each referenced artefact is and what is at stake before naming it by ID. `ADR-NNN` / `P-NNN` / `JTBD-NNN` / `RFC-NNN` references are audit-trail annotations, NEVER carriers of meaning — the user reads this prompt without project filesystem access (mobile clients, accessibility tooling, notification surfaces) and cannot follow links. Acceptable: *"This ADR decides: five per-tool-call hook patterns + their budget bands (sibling of the UserPromptSubmit-pattern decision and the SessionStart-pattern decision)."* Unacceptable: *"This ADR decides: 5 patterns; sibling to ADR-038 and ADR-040."* The trailing parenthetical IDs are permitted ONLY after a self-contained explanation, never as the explanation itself. Mirrors the canonical `/wr-architect:create-adr` Step 5 § 5a Rule 3 ("No IDs as explainers"). See also session memory `feedback_brief_before_id.md`.
63
+
62
64
  This is a genuine human-decision surface (the whole point of P283) — `AskUserQuestion` is correct here and is NOT over-asking. Do not auto-confirm; do not prose-ask.
63
65
 
64
66
  ### Step 4: Apply the outcome