@windyroad/jtbd 0.6.0-preview.161 → 0.7.0-preview.168
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/hooks/jtbd-eval.sh
CHANGED
|
@@ -1,14 +1,26 @@
|
|
|
1
1
|
#!/bin/bash
|
|
2
|
-
# JTBD - UserPromptSubmit hook
|
|
2
|
+
# JTBD - UserPromptSubmit hook (P095 / ADR-038)
|
|
3
3
|
# Detects the docs/jtbd/ directory in the project and injects the
|
|
4
4
|
# delegation instruction. Canonical layout is docs/jtbd/ only
|
|
5
|
-
# (ADR-008, Option 3 chosen 2026-04-20).
|
|
6
|
-
#
|
|
7
|
-
#
|
|
8
|
-
#
|
|
5
|
+
# (ADR-008, Option 3 chosen 2026-04-20).
|
|
6
|
+
#
|
|
7
|
+
# Progressive disclosure (ADR-038): full MANDATORY block on first
|
|
8
|
+
# prompt; terse reminder on subsequent prompts in the same session.
|
|
9
|
+
|
|
10
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
11
|
+
# shellcheck source=lib/session-marker.sh
|
|
12
|
+
source "$SCRIPT_DIR/lib/session-marker.sh"
|
|
13
|
+
|
|
14
|
+
INPUT=$(cat)
|
|
15
|
+
SESSION_ID=$(echo "$INPUT" | jq -r '.session_id // empty' 2>/dev/null || echo "")
|
|
9
16
|
|
|
10
17
|
if [ -f "docs/jtbd/README.md" ]; then
|
|
11
|
-
|
|
18
|
+
if has_announced "jtbd" "$SESSION_ID"; then
|
|
19
|
+
cat <<'HOOK_OUTPUT'
|
|
20
|
+
MANDATORY JTBD gate active (docs/jtbd/ present). Delegate to wr-jtbd:agent before editing project files. See turn-1 instructions for full scope and exclusions.
|
|
21
|
+
HOOK_OUTPUT
|
|
22
|
+
else
|
|
23
|
+
cat <<'HOOK_OUTPUT'
|
|
12
24
|
INSTRUCTION: MANDATORY JTBD CHECK. YOU MUST FOLLOW THIS.
|
|
13
25
|
DETECTED: docs/jtbd/ exists in this project.
|
|
14
26
|
|
|
@@ -30,6 +42,8 @@ plan files, docs/problems/ (problem tickets), docs/BRIEFING.md,
|
|
|
30
42
|
RISK-POLICY.md, .risk-reports/, docs/jtbd/,
|
|
31
43
|
docs/PRODUCT_DISCOVERY.md, docs/VOICE-AND-TONE.md, docs/STYLE-GUIDE.md.
|
|
32
44
|
HOOK_OUTPUT
|
|
45
|
+
mark_announced "jtbd" "$SESSION_ID"
|
|
46
|
+
fi
|
|
33
47
|
else
|
|
34
48
|
cat <<'HOOK_OUTPUT'
|
|
35
49
|
NOTE: This project has no docs/jtbd/ directory. Edits to project files
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Shared session-announcement marker helpers (P095 / ADR-038).
|
|
3
|
+
#
|
|
4
|
+
# Used by UserPromptSubmit hooks to gate verbose MANDATORY instruction
|
|
5
|
+
# prose behind a once-per-session check. First prompt of a session emits
|
|
6
|
+
# the full block AND calls mark_announced; subsequent prompts see the
|
|
7
|
+
# marker via has_announced and emit only a terse reminder.
|
|
8
|
+
#
|
|
9
|
+
# Why no TTL or drift check (unlike review-gate.sh): announcement is
|
|
10
|
+
# bookkeeping for prose verbosity, not enforcement. PreToolUse gates
|
|
11
|
+
# still block unauthorised edits regardless of announcement state; the
|
|
12
|
+
# delegated agent re-reads policy when it runs. Extending the marker's
|
|
13
|
+
# lifetime across policy changes mid-session is safe — the gate, not
|
|
14
|
+
# the announcement, is load-bearing.
|
|
15
|
+
#
|
|
16
|
+
# Marker path convention: /tmp/${SYSTEM}-announced-${SESSION_ID}
|
|
17
|
+
# (mirrors the /tmp/${SYSTEM}-reviewed-${SESSION_ID} convention from
|
|
18
|
+
# style-guide/voice-tone/risk-scorer review-gate.sh; the -announced-
|
|
19
|
+
# suffix distinguishes announcement markers from clearance markers).
|
|
20
|
+
#
|
|
21
|
+
# Empty SESSION_ID fallback: has_announced returns 1 (not announced,
|
|
22
|
+
# full block emits) and mark_announced is a no-op (no file written).
|
|
23
|
+
# This covers manual hook invocation, test harnesses, and any rare
|
|
24
|
+
# case where Claude Code does not pass a session_id on stdin.
|
|
25
|
+
|
|
26
|
+
# Returns 0 if the hook for SYSTEM has already announced in SESSION_ID,
|
|
27
|
+
# 1 otherwise. Empty SESSION_ID => returns 1 (never announced).
|
|
28
|
+
#
|
|
29
|
+
# Usage: has_announced "architect" "$SESSION_ID"
|
|
30
|
+
has_announced() {
|
|
31
|
+
local SYSTEM="$1"
|
|
32
|
+
local SESSION_ID="$2"
|
|
33
|
+
[ -n "$SESSION_ID" ] || return 1
|
|
34
|
+
[ -f "/tmp/${SYSTEM}-announced-${SESSION_ID}" ]
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
# Writes the announcement marker for SYSTEM in SESSION_ID. Empty
|
|
38
|
+
# SESSION_ID => no-op. Safe to call more than once per session.
|
|
39
|
+
#
|
|
40
|
+
# Usage: mark_announced "architect" "$SESSION_ID"
|
|
41
|
+
mark_announced() {
|
|
42
|
+
local SYSTEM="$1"
|
|
43
|
+
local SESSION_ID="$2"
|
|
44
|
+
[ -n "$SESSION_ID" ] || return 0
|
|
45
|
+
: > "/tmp/${SYSTEM}-announced-${SESSION_ID}"
|
|
46
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
#!/usr/bin/env bats
|
|
2
|
+
|
|
3
|
+
# P095 / ADR-038: jtbd-eval.sh UserPromptSubmit hook must emit the full
|
|
4
|
+
# MANDATORY block only on the first prompt of a session; subsequent
|
|
5
|
+
# prompts emit a terse reminder.
|
|
6
|
+
|
|
7
|
+
setup() {
|
|
8
|
+
REPO_ROOT="$(cd "$(dirname "$BATS_TEST_FILENAME")/../../../.." && pwd)"
|
|
9
|
+
HOOK="$REPO_ROOT/packages/jtbd/hooks/jtbd-eval.sh"
|
|
10
|
+
|
|
11
|
+
WORKDIR="$(mktemp -d)"
|
|
12
|
+
mkdir -p "$WORKDIR/docs/jtbd"
|
|
13
|
+
: > "$WORKDIR/docs/jtbd/README.md"
|
|
14
|
+
|
|
15
|
+
SID="jtbd-eval-test-$$-$RANDOM"
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
teardown() {
|
|
19
|
+
rm -f "/tmp/jtbd-announced-${SID}"
|
|
20
|
+
rm -f "/tmp/jtbd-announced-${SID}-alt"
|
|
21
|
+
rm -rf "$WORKDIR"
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
run_hook() {
|
|
25
|
+
local sid="$1"
|
|
26
|
+
(cd "$WORKDIR" && echo "{\"session_id\":\"$sid\"}" | bash "$HOOK")
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
@test "jtbd-eval: first invocation emits the full MANDATORY block" {
|
|
30
|
+
run run_hook "$SID"
|
|
31
|
+
[ "$status" -eq 0 ]
|
|
32
|
+
[ "${#output}" -gt 400 ]
|
|
33
|
+
[[ "$output" == *"MANDATORY JTBD CHECK"* ]]
|
|
34
|
+
[[ "$output" == *"wr-jtbd:agent"* ]]
|
|
35
|
+
[[ "$output" == *"SCOPE:"* ]]
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
@test "jtbd-eval: first invocation writes the announcement marker" {
|
|
39
|
+
run run_hook "$SID"
|
|
40
|
+
[ -f "/tmp/jtbd-announced-${SID}" ]
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
@test "jtbd-eval: second invocation emits only a terse reminder" {
|
|
44
|
+
run_hook "$SID" >/dev/null
|
|
45
|
+
run run_hook "$SID"
|
|
46
|
+
[ "$status" -eq 0 ]
|
|
47
|
+
[ "${#output}" -lt 250 ]
|
|
48
|
+
[[ "$output" == *"jtbd"* ]] || [[ "$output" == *"JTBD"* ]]
|
|
49
|
+
[[ "$output" == *"wr-jtbd:agent"* ]]
|
|
50
|
+
[[ "$output" != *"Does NOT apply to"* ]]
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
@test "jtbd-eval: terse reminder preserves the MANDATORY signal word" {
|
|
54
|
+
run_hook "$SID" >/dev/null
|
|
55
|
+
run run_hook "$SID"
|
|
56
|
+
[[ "$output" == *"MANDATORY"* ]] || [[ "$output" == *"REQUIRED"* ]] || [[ "$output" == *"NON-OPTIONAL"* ]]
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
@test "jtbd-eval: terse reminder names the trigger artifact" {
|
|
60
|
+
run_hook "$SID" >/dev/null
|
|
61
|
+
run run_hook "$SID"
|
|
62
|
+
[[ "$output" == *"docs/jtbd"* ]]
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
@test "jtbd-eval: different session_id re-emits the full block" {
|
|
66
|
+
run_hook "$SID" >/dev/null
|
|
67
|
+
local SID2="${SID}-alt"
|
|
68
|
+
run run_hook "$SID2"
|
|
69
|
+
[ "${#output}" -gt 400 ]
|
|
70
|
+
[[ "$output" == *"MANDATORY JTBD CHECK"* ]]
|
|
71
|
+
rm -f "/tmp/jtbd-announced-${SID2}"
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
@test "jtbd-eval: empty session_id emits the full block and writes no marker" {
|
|
75
|
+
run run_hook ""
|
|
76
|
+
[ "${#output}" -gt 400 ]
|
|
77
|
+
[[ "$output" == *"MANDATORY JTBD CHECK"* ]]
|
|
78
|
+
[ ! -f "/tmp/jtbd-announced-" ]
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
@test "jtbd-eval: absent docs/jtbd/ emits the no-docs NOTE (unchanged by this ADR)" {
|
|
82
|
+
local NO_JTBD="$(mktemp -d)"
|
|
83
|
+
run bash -c "cd '$NO_JTBD' && echo '{\"session_id\":\"$SID\"}' | bash '$HOOK'"
|
|
84
|
+
[[ "$output" == *"no docs/jtbd/ directory"* ]]
|
|
85
|
+
[[ "$output" != *"MANDATORY JTBD CHECK"* ]]
|
|
86
|
+
rm -rf "$NO_JTBD"
|
|
87
|
+
}
|