@windyroad/jtbd 0.14.0 → 0.14.1

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.
@@ -90,5 +90,5 @@
90
90
  }
91
91
  },
92
92
  "name": "wr-jtbd",
93
- "version": "0.14.0"
93
+ "version": "0.14.1"
94
94
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wr-jtbd",
3
- "version": "0.14.0",
3
+ "version": "0.14.1",
4
4
  "description": "Jobs-to-be-done governance for product changes",
5
5
  "author": {
6
6
  "name": "Windy Road Technology",
@@ -2,9 +2,9 @@
2
2
  # JTBD - PostToolUse:Agent|Bash|Skill slide-marker hook (P111 + P213).
3
3
  # Slides the parent session's existing jtbd-reviewed marker forward on
4
4
  # subprocess return, treating subprocess wall-clock as continuous parent-
5
- # session work for TTL purposes. Only TOUCHES an existing marker — never
6
- # creates one (creation requires a real JTBD review parsed from the agent's
7
- # verdict file in jtbd-mark-reviewed.sh).
5
+ # session work for TTL purposes. It only TOUCHES existing JTBD-review markers.
6
+ # P368 also lets an exact successful oversight-helper event create its separate
7
+ # document-and-session evidence marker.
8
8
  #
9
9
  # See ADR-009 "Subprocess-boundary refresh" and P111 for context. Failed
10
10
  # subprocesses (tool_response.is_error=true) do NOT extend the trust window
@@ -16,6 +16,50 @@ source "$SCRIPT_DIR/lib/gate-helpers.sh"
16
16
  _parse_input
17
17
 
18
18
  SESSION_ID=$(_get_session_id)
19
+
20
+ # P368: bind oversight evidence to the exact successful Bash event rather
21
+ # than guessing from the set of recently announced sessions.
22
+ OVERSIGHT_PATH=$(printf '%s' "$_HOOK_INPUT" | python3 -c '
23
+ import json, shlex, sys
24
+ try:
25
+ data = json.load(sys.stdin)
26
+ response = data.get("tool_response", {})
27
+ argv = shlex.split(data.get("tool_input", {}).get("command", ""))
28
+ if (data.get("tool_name") == "Bash"
29
+ and isinstance(response, dict)
30
+ and response.get("is_error") is not True
31
+ and len(argv) == 2
32
+ and argv[0] == "wr-jtbd-mark-oversight-confirmed"):
33
+ print(argv[1])
34
+ except Exception:
35
+ pass
36
+ ' 2>/dev/null)
37
+
38
+ if [ -n "$OVERSIGHT_PATH" ]; then
39
+ if [ -z "$SESSION_ID" ]; then
40
+ echo "wr-jtbd-mark-oversight-confirmed: missing session id; no oversight marker written" >&2
41
+ else
42
+ ABS_DIR="$(cd "$(dirname "$OVERSIGHT_PATH")" 2>/dev/null && pwd)" || ABS_DIR=""
43
+ ABS_PATH="${ABS_DIR:+$ABS_DIR/}$(basename "$OVERSIGHT_PATH")"
44
+ case "$ABS_PATH" in
45
+ */docs/jtbd/*.md|*/docs/jtbd/*/*.md)
46
+ if command -v sha256sum >/dev/null 2>&1; then
47
+ PATH_HASH=$(printf '%s' "$ABS_PATH" | sha256sum | cut -d' ' -f1 | cut -c1-16)
48
+ elif command -v shasum >/dev/null 2>&1; then
49
+ PATH_HASH=$(printf '%s' "$ABS_PATH" | shasum -a 256 | cut -d' ' -f1 | cut -c1-16)
50
+ else
51
+ echo "wr-jtbd-mark-oversight-confirmed: no SHA-256 utility; no oversight marker written" >&2
52
+ PATH_HASH=""
53
+ fi
54
+ if [ -n "$PATH_HASH" ] && ! : > "${SESSION_MARKER_DIR:-/tmp}/oversight-confirmed-${PATH_HASH}-${SESSION_ID}"; then
55
+ echo "wr-jtbd-mark-oversight-confirmed: marker write failed" >&2
56
+ fi
57
+ ;;
58
+ *) echo "wr-jtbd-mark-oversight-confirmed: invalid JTBD path; no oversight marker written" >&2 ;;
59
+ esac
60
+ fi
61
+ fi
62
+
19
63
  [ -n "$SESSION_ID" ] || exit 0
20
64
 
21
65
  slide_marker_on_subprocess_return "/tmp/jtbd-reviewed-${SESSION_ID}"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@windyroad/jtbd",
3
- "version": "0.14.0",
3
+ "version": "0.14.1",
4
4
  "description": "Jobs-to-be-done enforcement for UI changes",
5
5
  "bin": {
6
6
  "windyroad-jtbd": "./bin/install.mjs"
@@ -34,6 +34,7 @@
34
34
  ".agents/",
35
35
  ".codex-plugin/",
36
36
  "lib/",
37
+ "!skills/*/eval/",
37
38
  "!agents/eval/",
38
39
  "!agents/test/",
39
40
  "!hooks/test/",
@@ -3,11 +3,10 @@
3
3
  # user-substance-confirmed (P348 / ADR-110).
4
4
  #
5
5
  # JTBD-side sibling of packages/architect/scripts/mark-oversight-confirmed.sh.
6
- # Companion to jtbd-oversight-marker-discipline.sh (also P348). SKILLs invoke
7
- # this script AFTER an AskUserQuestion lands the user's substance-confirm
8
- # answer for a specific job or persona; the script writes the evidence marker
9
- # that the hook reads to permit the subsequent Edit/Write that introduces
10
- # `human-oversight: confirmed` into that artefact's frontmatter.
6
+ # Companion to jtbd-oversight-marker-discipline.sh. SKILLs invoke this as a
7
+ # standalone Bash command AFTER an AskUserQuestion lands the user's substance-
8
+ # confirm answer. The existing PostToolUse:Bash hook binds that exact command
9
+ # event's path and session id into the marker.
11
10
  #
12
11
  # Why the marker is required:
13
12
  # ADR-068 mirrors ADR-066's `human-oversight: confirmed` marker contract on
@@ -23,100 +22,25 @@
23
22
  # ADR-110), which the drain
24
23
  # (/wr-jtbd:confirm-jobs-and-personas) later promotes.
25
24
  #
26
- # Marker convention:
27
- # /tmp/oversight-confirmed-<sha256-of-path>-<session-id>
28
- # Shared marker namespace with the architect hook — both hooks consume the
29
- # same marker file shape. Written under EVERY recent candidate session SID
30
- # per ADR-050 Option C.
31
- #
32
25
  # Usage:
33
26
  # wr-jtbd-mark-oversight-confirmed <artefact-path>
34
- # <artefact-path> — the JTBD or persona file path the user just
35
- # substance-confirmed.
27
+ # Must be the whole Bash command, with exactly one JTBD/persona path.
36
28
  #
37
29
  # Exit codes:
38
- # 0 — marker(s) written for at least one candidate SID, OR no candidate
39
- # SID was discoverable (cold path: no announce markers yet). The latter
40
- # writes no marker and prints a loud stderr diagnostic (P368) explaining
41
- # the downstream hook deny, then exits 0 so SKILL flows do not crash
42
- # before any hook has fired in the session.
43
- # 2 — bad argument (missing or empty artefact-path).
30
+ # 0 — command validated; PostToolUse writes the exact-session marker.
31
+ # 2 — bad argument count.
44
32
  #
45
33
  # @adr ADR-068 (JTBD/persona human-oversight marker)
46
34
  # @adr ADR-049 (PATH shim grammar)
47
- # @adr ADR-050 (multi-SID candidate enumeration)
48
35
  # @adr ADR-013 (Rule 6 fail-safe-defer in non-interactive contexts)
49
36
  # @problem P348 (iter subprocesses set human-oversight: confirmed without user event)
37
+ # @problem P368 (candidate enumeration grants unrelated sessions)
50
38
 
51
39
  set -uo pipefail
52
40
 
53
- ARTEFACT_PATH="${1:-}"
54
-
55
- if [ -z "$ARTEFACT_PATH" ]; then
56
- echo "wr-jtbd-mark-oversight-confirmed: missing <artefact-path>" >&2
57
- exit 2
58
- fi
59
-
60
- # Normalize to absolute path so the hash is stable regardless of CWD.
61
- abs_dir="$(cd "$(dirname "$ARTEFACT_PATH")" 2>/dev/null && pwd)" || abs_dir=""
62
- if [ -n "$abs_dir" ]; then
63
- ABS_PATH="$abs_dir/$(basename "$ARTEFACT_PATH")"
64
- else
65
- ABS_PATH="$ARTEFACT_PATH"
66
- fi
67
-
68
- # Path hash — sha256, first 16 hex chars. Portable across macOS (shasum) and
69
- # Linux (sha256sum / shasum).
70
- if command -v sha256sum >/dev/null 2>&1; then
71
- PATH_HASH=$(printf '%s' "$ABS_PATH" | sha256sum | cut -d' ' -f1 | cut -c1-16)
72
- elif command -v shasum >/dev/null 2>&1; then
73
- PATH_HASH=$(printf '%s' "$ABS_PATH" | shasum -a 256 | cut -d' ' -f1 | cut -c1-16)
74
- else
75
- echo "wr-jtbd-mark-oversight-confirmed: no sha256 tool available" >&2
41
+ if [ "$#" -ne 1 ] || [ -z "$1" ]; then
42
+ echo "wr-jtbd-mark-oversight-confirmed: expected exactly one <artefact-path>" >&2
76
43
  exit 2
77
44
  fi
78
45
 
79
- MARKER_DIR="${SESSION_MARKER_DIR:-/tmp}"
80
- WINDOW_MINS="${SESSION_CANDIDATE_WINDOW_MINS:-1440}"
81
-
82
- # Candidate SID enumeration — recent announce markers across all systems
83
- # within the mtime window. Inlined for plugin self-containment (no cross-
84
- # plugin lib source — jtbd must not depend on architect-internal helpers
85
- # per ADR-002 plugin packaging).
86
- candidates=$(
87
- {
88
- if [ -n "${CLAUDE_SESSION_ID:-}" ]; then
89
- echo "$CLAUDE_SESSION_ID"
90
- fi
91
- # `-L` follows the start-point symlink — on macOS MARKER_DIR defaults to /tmp,
92
- # a symlink to /private/tmp, which `find` would otherwise refuse to descend
93
- # (no-op on Linux where /tmp is a real dir). P380.
94
- find -L "$MARKER_DIR" -maxdepth 1 -name '*-announced-*' -mmin "-${WINDOW_MINS}" 2>/dev/null \
95
- | sed 's|.*/||; s/.*-announced-//'
96
- } | awk 'NF && !seen[$0]++'
97
- )
98
-
99
- # No candidate SID — cold path. Emit a loud stderr diagnostic (P368) and still
100
- # exit 0. Exit 0 preserves the documented contract (do not crash SKILL flows
101
- # before any announce marker has fired this session); the diagnostic replaces the
102
- # prior SILENT no-op, which masqueraded as success and left the caller to hit the
103
- # oversight-marker-discipline hook's deny with no idea why — the confusing loop
104
- # P368 documents (the deny points back at this shim, which the caller already ran).
105
- if [ -z "$candidates" ]; then
106
- {
107
- echo "wr-jtbd-mark-oversight-confirmed: no candidate session id discoverable"
108
- echo " (CLAUDE_SESSION_ID empty and no *-announced-* markers in ${MARKER_DIR} within ${WINDOW_MINS}min)."
109
- echo " NO oversight marker was written for: ${ABS_PATH}"
110
- echo " The oversight-marker-discipline hook will DENY the 'human-oversight: confirmed' Edit"
111
- echo " until a session announce marker exists. Start a fresh session, or point SESSION_MARKER_DIR"
112
- echo " at a dir containing a *-announced-<sid> file, then re-run this shim (P368)."
113
- } >&2
114
- exit 0
115
- fi
116
-
117
- while IFS= read -r sid; do
118
- [ -n "$sid" ] || continue
119
- : > "$MARKER_DIR/oversight-confirmed-${PATH_HASH}-${sid}"
120
- done <<< "$candidates"
121
-
122
46
  exit 0
@@ -63,7 +63,7 @@ This is a genuine human-decision surface (the point of P288/ADR-068) — `reques
63
63
 
64
64
  ### Step 4: Apply the outcome
65
65
 
66
- - **Confirm / Amend**: write `human-oversight: confirmed` + `oversight-date: <today, YYYY-MM-DD>` into the file's frontmatter (insert after the `status:`/`date-created:` line if absent; never duplicate). For Amend, apply the directed change first. Edits go through the standard JTBD / architect edit gate per ADR-014.
66
+ - **Confirm / Amend**: for Amend, apply the directed change first. Then run `wr-jtbd-mark-oversight-confirmed <job-or-persona-path>` as a standalone Bash command; do not combine it with another command, because its PostToolUse event binds the evidence to this exact session and file. Finally, write `human-oversight: confirmed` + `oversight-date: <today, YYYY-MM-DD>` into the file's frontmatter (insert after the `status:`/`date-created:` line if absent; never duplicate). Edits go through the standard JTBD / architect edit gate per ADR-014.
67
67
  - **Reject** (ADR-068 amendment per P316, mirroring ADR-066):
68
68
  1. Capture the supersede ticket via a follow-up `request_user_input`: "Which problem ticket tracks the rework?" — options: existing `P<NNN>` IDs surfaced from `docs/problems/`, **Capture a new ticket** (delegate to `/wr-itil:capture-problem`), or **Defer (leave un-tracked for now)**.
69
69
  2. If a ticket ID is captured, write `human-oversight: rejected-pending-supersede` + `supersede-ticket: P<NNN>` into the file's frontmatter. The detector excludes artifacts carrying both, so the drain stops re-asking until either the rework lands (file renamed to `*.superseded.md`) or the rejection is revisited.
@@ -103,12 +103,14 @@ Use request_user_input to present the drafted personas and ask:
103
103
  wr-jtbd-mark-oversight-confirmed docs/jtbd/<persona-name>/persona.md
104
104
  ```
105
105
 
106
+ Run that helper as the complete standalone Bash command. Do not combine it with another command: the PostToolUse hook binds this exact successful command event's session id and persona path into the evidence marker.
107
+
106
108
  ```yaml
107
109
  human-oversight: confirmed
108
110
  oversight-date: YYYY-MM-DD # today
109
111
  ```
110
112
 
111
- The `wr-jtbd-mark-oversight-confirmed` call writes the session-scoped evidence marker (`/tmp/oversight-confirmed-<sha>-<sid>`) that the `jtbd-oversight-marker-discipline.sh` PreToolUse hook reads to authorise the subsequent Edit/Write — without the helper call, the hook will DENY the marker write. This is the load-bearing born-confirmed gate: a persona authored through update-guide enters the world human-oversighted ONLY because the helper above paired the user's substance-confirm answer to the marker write. Do NOT write the marker for a persona the user has not confirmed. AFK iter subprocesses spawned via `native Codex subagent` have no `request_user_input` access; they MUST write `human-oversight: unconfirmed` (the AFK fallback enum value codified in ADR-110), which the drain (`/wr-jtbd:confirm-jobs-and-personas`) later promotes interactively. The marker is orthogonal to status.
113
+ The helper's PostToolUse event writes the session-scoped evidence marker (`/tmp/oversight-confirmed-<sha>-<sid>`) that the `jtbd-oversight-marker-discipline.sh` PreToolUse hook reads to authorise the subsequent Edit/Write — without the helper call, the hook will DENY the marker write. This is the load-bearing born-confirmed gate: a persona authored through update-guide enters the world human-oversighted ONLY because the helper above paired the user's substance-confirm answer to the marker write. Do NOT write the marker for a persona the user has not confirmed. AFK iter subprocesses spawned via `native Codex subagent` have no `request_user_input` access; they MUST write `human-oversight: unconfirmed` (the AFK fallback enum value codified in ADR-110), which the drain (`/wr-jtbd:confirm-jobs-and-personas`) later promotes interactively. The marker is orthogonal to status.
112
114
 
113
115
  ### 5. Draft jobs
114
116
 
@@ -162,12 +164,14 @@ Use request_user_input to present the drafted jobs and ask:
162
164
  wr-jtbd-mark-oversight-confirmed docs/jtbd/<persona-name>/JTBD-NNN-<kebab-title>.proposed.md
163
165
  ```
164
166
 
167
+ Run that helper as the complete standalone Bash command so its PostToolUse event binds the evidence to this exact session and job path.
168
+
165
169
  ```yaml
166
170
  human-oversight: confirmed
167
171
  oversight-date: YYYY-MM-DD # today
168
172
  ```
169
173
 
170
- Without the `wr-jtbd-mark-oversight-confirmed` call, the `jtbd-oversight-marker-discipline.sh` PreToolUse hook will DENY the marker write. A job authored through update-guide is born human-oversighted ONLY because the helper above paired the user's substance-confirm answer to the marker write. Do NOT write the marker for a job the user has not confirmed. AFK iter subprocesses MUST write `human-oversight: unconfirmed` instead (the AFK fallback enum value codified in ADR-110); the drain (`/wr-jtbd:confirm-jobs-and-personas`) later promotes it interactively. The marker is orthogonal to `status:`.
174
+ Without the standalone `wr-jtbd-mark-oversight-confirmed` call, the `jtbd-oversight-marker-discipline.sh` PreToolUse hook will DENY the marker write. A job authored through update-guide is born human-oversighted ONLY because the helper above paired the user's substance-confirm answer to the marker write. Do NOT write the marker for a job the user has not confirmed. AFK iter subprocesses MUST write `human-oversight: unconfirmed` instead (the AFK fallback enum value codified in ADR-110); the drain (`/wr-jtbd:confirm-jobs-and-personas`) later promotes it interactively. The marker is orthogonal to `status:`.
171
175
 
172
176
  ### 7. Generate README.md index
173
177