@windyroad/jtbd 0.8.3 → 0.8.4-preview.430
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/README.md +9 -16
- package/agents/agent.md +19 -3
- package/agents/test/jtbd-unratified-dependency-verdict.bats +62 -0
- package/bin/wr-jtbd-detect-unoversighted +3 -0
- package/bin/wr-jtbd-is-job-or-persona-unconfirmed +3 -0
- package/hooks/hooks.json +3 -0
- package/hooks/jtbd-oversight-nudge.sh +42 -0
- package/hooks/test/jtbd-oversight-nudge.bats +69 -0
- package/package.json +2 -1
- package/scripts/detect-unoversighted.sh +47 -0
- package/scripts/is-job-or-persona-unconfirmed.sh +89 -0
- package/scripts/test/detect-unoversighted.bats +85 -0
- package/scripts/test/is-job-or-persona-unconfirmed.bats +126 -0
- package/skills/confirm-jobs-and-personas/SKILL.md +74 -0
- package/skills/update-guide/SKILL.md +19 -1
package/README.md
CHANGED
|
@@ -33,6 +33,14 @@ The plugin works automatically. On first use in a project without a JTBD directo
|
|
|
33
33
|
|
|
34
34
|
This examines your existing features and asks about your user jobs, personas, and desired outcomes to generate `docs/jtbd/<persona>/persona.md` plus per-job files at `docs/jtbd/<persona>/JTBD-NNN-<title>.<status>.md`. If a legacy `docs/JOBS_TO_BE_DONE.md` exists, it is migrated into the directory structure on first run (per [ADR-008](../../docs/decisions/008-jtbd-directory-structure.proposed.md)).
|
|
35
35
|
|
|
36
|
+
**Confirm jobs and personas that lack human oversight:**
|
|
37
|
+
|
|
38
|
+
```
|
|
39
|
+
/wr-jtbd:confirm-jobs-and-personas
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
The `confirm-jobs-and-personas` skill drains the set of jobs/personas that were recorded without a human confirming they reflect real user/business need (per [ADR-068](../../docs/decisions/068-jtbd-persona-human-oversight-marker-and-confirm-drain.proposed.md)). It surfaces each via AskUserQuestion so you confirm, amend, or reject it, then writes a `human-oversight: confirmed` marker. Detection is a token-cheap grep over `docs/jtbd/` frontmatter; a session-start nudge reports the unoversighted count. Jobs/personas created through `update-guide` are born oversighted, so the unconfirmed set only shrinks. This is the read-write oversight drain — distinct from the read-only `/wr-jtbd:review-jobs` alignment reviewer.
|
|
43
|
+
|
|
36
44
|
## How It Works
|
|
37
45
|
|
|
38
46
|
| Hook | Trigger | What it does |
|
|
@@ -41,6 +49,7 @@ This examines your existing features and asks about your user jobs, personas, an
|
|
|
41
49
|
| `jtbd-enforce-edit.sh` | Edit or Write | Blocks edits until the JTBD agent has reviewed |
|
|
42
50
|
| `jtbd-mark-reviewed.sh` | Agent completes | Marks the review as done (TTL: 3600s) |
|
|
43
51
|
| `jtbd-slide-marker.sh` | Agent or Bash | Slides the review marker forward across non-edit operations so an active review session is not invalidated by intervening Bash or sub-agent calls |
|
|
52
|
+
| `jtbd-oversight-nudge.sh` | Session start | Reports how many jobs/personas lack human oversight and points to `/wr-jtbd:confirm-jobs-and-personas`; silent when none, and self-suppressed inside AFK iterations |
|
|
44
53
|
|
|
45
54
|
## Agent
|
|
46
55
|
|
|
@@ -50,22 +59,6 @@ The `wr-jtbd:agent` reads your `docs/jtbd/` directory and reviews proposed UI ch
|
|
|
50
59
|
- Persona definitions and constraints
|
|
51
60
|
- Screen-to-job mappings
|
|
52
61
|
|
|
53
|
-
## Jobs to be Done
|
|
54
|
-
|
|
55
|
-
This plugin serves the [Jobs to be Done](../../docs/jtbd/) below. Per [ADR-051](../../docs/decisions/051-jtbd-anchored-readme-with-drift-advisory.proposed.md), the persona-grouped JTBD anchor is the canonical source of truth for the README's value framing.
|
|
56
|
-
|
|
57
|
-
### Solo developer
|
|
58
|
-
|
|
59
|
-
- **[JTBD-001 Enforce Governance Without Slowing Down](../../docs/jtbd/solo-developer/JTBD-001-enforce-governance.proposed.md)** — JTBD review fires automatically on every UI edit; manual review is replaced by an agent reading the persona files the project already maintains.
|
|
60
|
-
|
|
61
|
-
### Tech lead / consultant
|
|
62
|
-
|
|
63
|
-
- **[JTBD-202 Run Pre-Flight Governance Checks Before Release or Handover](../../docs/jtbd/tech-lead/JTBD-202-pre-flight-governance-check.proposed.md)** — `/wr-jtbd:review-jobs` produces an on-demand alignment report against documented jobs, attachable to a release note or handover doc.
|
|
64
|
-
|
|
65
|
-
### Plugin user
|
|
66
|
-
|
|
67
|
-
- **[JTBD-302 Trust That the README Describes the Plugin I Just Installed](../../docs/jtbd/plugin-user/JTBD-302-trust-readme-describes-installed-behaviour.proposed.md)** — this README is anchored on current JTBD job IDs; drift between prose and shipped behaviour is detectable at retro time per ADR-051.
|
|
68
|
-
|
|
69
62
|
## Updating and Uninstalling
|
|
70
63
|
|
|
71
64
|
```bash
|
package/agents/agent.md
CHANGED
|
@@ -44,6 +44,22 @@ All review criteria come from the JTBD documentation. Read the docs first and ap
|
|
|
44
44
|
- If the change involves API interactions, do the actions align with the job's expected flow?
|
|
45
45
|
- Are new actions documented in the relevant job's action list?
|
|
46
46
|
|
|
47
|
+
### Unratified Dependency (build-upon guard — ADR-068 enforcement surface 3)
|
|
48
|
+
|
|
49
|
+
When the change or plan under review **explicitly cites, implements, or serves** a specific persona or job — an `@jtbd JTBD-NNN` annotation, a `persona: <name>` reference, or it is authoring that artifact's own flow — check whether that persona/job has been **ratified** (carries `human-oversight: confirmed` in its frontmatter) before letting the change stand. You have `Bash`, so run the predicate by **exit code** (you do NOT need to grep frontmatter yourself):
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
wr-jtbd-is-job-or-persona-unconfirmed <persona-name | JTBD-NNN>
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
- **Exit 0** (frontmatter lacks the marker AND the artifact is not superseded) → the artifact is **unratified**. Emit **ISSUES FOUND / [Unratified Dependency]** with action: "ratify `<persona | JTBD-NNN>` via `/wr-jtbd:confirm-jobs-and-personas` before this lands." (The predicate prints the resolved path on stdout.)
|
|
56
|
+
- **Exit 1** (ratified, or superseded) → do NOT flag.
|
|
57
|
+
- **Exit 2** (ref not found) → the change cites a persona/job that does not exist; that is a separate Job Gap / Persona Mismatch, not an Unratified Dependency.
|
|
58
|
+
|
|
59
|
+
**Key the flag on the oversight marker, NEVER on `status:`.** `status: proposed`/`accepted` and `human-oversight:` are orthogonal axes (ADR-066). Building on a **ratified** job whose `status` is still `proposed` is fine — do NOT flag it; only the *unratified* (marker-absent, non-superseded) case flags.
|
|
60
|
+
|
|
61
|
+
**Bound to explicit cite/implement — NOT ambient alignment.** You already match every change to a job ID for the PASS verdict (see Job Alignment above); the `[Unratified Dependency]` flag must NOT fire on that mere match — only on an **explicit** dependency the change names. This is the inverse-P078 / P132 over-fire guard. Note: the JTBD unratified set is currently large (the P288 drain is in progress), so unlike the architect surface this will fire more often until that drain completes — that is the intended forcing function, not noise. The `developer`-persona jobs still pending the P288 drain (e.g. `JTBD-001`) are the canonical first-fire cases — the `developer` persona itself was ratified via P289.
|
|
62
|
+
|
|
47
63
|
## Output Formatting
|
|
48
64
|
|
|
49
65
|
When referencing JTBD IDs, problem IDs (P<NNN>), or ADR IDs in prose output, always include the human-readable title on first mention. Use the format `JTBD-001 (Enforce Governance Without Slowing Down)`, not bare `JTBD-001`.
|
|
@@ -59,11 +75,11 @@ If there are misalignments or gaps:
|
|
|
59
75
|
|
|
60
76
|
> **JTBD Review: ISSUES FOUND**
|
|
61
77
|
>
|
|
62
|
-
> 1. **[Job Gap / Persona Mismatch / Missing Annotation]**
|
|
78
|
+
> 1. **[Job Gap / Persona Mismatch / Missing Annotation / Unratified Dependency]**
|
|
63
79
|
> - **File**: `path`, Line ~N
|
|
64
|
-
> - **Issue**: What is misaligned
|
|
80
|
+
> - **Issue**: What is misaligned (for **Unratified Dependency**: the change builds on `<persona | JTBD-NNN>` which lacks `human-oversight: confirmed`)
|
|
65
81
|
> - **Job**: Which job is affected (or "no matching job")
|
|
66
|
-
> - **Fix**: Suggested resolution (update JTBD doc, adjust UI, add annotation)
|
|
82
|
+
> - **Fix**: Suggested resolution (update JTBD doc, adjust UI, add annotation; for **Unratified Dependency**: ratify via `/wr-jtbd:confirm-jobs-and-personas` before this lands)
|
|
67
83
|
>
|
|
68
84
|
> 2. ...
|
|
69
85
|
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
#!/usr/bin/env bats
|
|
2
|
+
# Doc-lint guard: jtbd agent.md must carry the [Unratified Dependency] verdict
|
|
3
|
+
# (ADR-068 enforcement surface 3 / RFC-011 / P323) — flag a change or plan that
|
|
4
|
+
# explicitly cites/implements/serves a persona or job lacking `human-oversight:
|
|
5
|
+
# confirmed` (unratified, non-superseded), keyed on the oversight marker NOT
|
|
6
|
+
# `status:`. The JTBD twin of the architect side's surface 3 (RFC-010 / P318).
|
|
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). When P176 lands, upgrade to a behavioural test that
|
|
12
|
+
# feeds the agent a change citing an unratified persona/job and asserts the
|
|
13
|
+
# verdict. The single-artifact predicate IS behaviourally tested today — see
|
|
14
|
+
# packages/jtbd/scripts/test/is-job-or-persona-unconfirmed.bats.
|
|
15
|
+
#
|
|
16
|
+
# Cross-reference:
|
|
17
|
+
# ADR-068 (JTBD oversight marker + drain — surface 3 amendment 2026-05-27)
|
|
18
|
+
# ADR-074 (Confirm a decision's substance before building dependent work)
|
|
19
|
+
# ADR-066 (oversight marker; orthogonal status/oversight axes)
|
|
20
|
+
# RFC-011 / P323 (this enforcement surface); RFC-010 / P318 (the ADR-side twin)
|
|
21
|
+
# ADR-052 Surface 2 (structural-justified verdict) + P176 (harness gap)
|
|
22
|
+
# @jtbd JTBD-202 (pre-flight governance checks) / JTBD-101 (extend the suite)
|
|
23
|
+
|
|
24
|
+
setup() {
|
|
25
|
+
AGENT_DIR="$(cd "$(dirname "$BATS_TEST_FILENAME")/.." && pwd)"
|
|
26
|
+
AGENT_FILE="${AGENT_DIR}/agent.md"
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
@test "agent.md lists [Unratified Dependency] as a verdict/issue type (ADR-068 surface 3)" {
|
|
30
|
+
run grep -n '\[Unratified Dependency\]' "$AGENT_FILE"
|
|
31
|
+
[ "$status" -eq 0 ]
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
@test "agent.md has an 'Unratified Dependency (build-upon guard' section citing ADR-068" {
|
|
35
|
+
run grep -niE "Unratified Dependency \(build-upon guard" "$AGENT_FILE"
|
|
36
|
+
[ "$status" -eq 0 ]
|
|
37
|
+
run grep -n "ADR-068" "$AGENT_FILE"
|
|
38
|
+
[ "$status" -eq 0 ]
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
@test "agent.md keys the flag on the oversight marker, NOT on status (orthogonal axes)" {
|
|
42
|
+
# ADR-066 / user correction 2026-05-27: building on a ratified-but-proposed job is fine.
|
|
43
|
+
run grep -niE "NEVER on .?status|not .?\`?status|orthogonal" "$AGENT_FILE"
|
|
44
|
+
[ "$status" -eq 0 ]
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
@test "agent.md invokes the predicate by exit code (the jtbd agent has Bash; not a prose-grep mirror)" {
|
|
48
|
+
run grep -n "wr-jtbd-is-job-or-persona-unconfirmed" "$AGENT_FILE"
|
|
49
|
+
[ "$status" -eq 0 ]
|
|
50
|
+
run grep -niE "exit code|exit 0|exit 1" "$AGENT_FILE"
|
|
51
|
+
[ "$status" -eq 0 ]
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
@test "agent.md guards against over-firing on ambient alignment (inverse-P078 / explicit cite)" {
|
|
55
|
+
run grep -niE "explicit(ly)? cite|ambient alignment|over-?fire|NOT fire on that mere match" "$AGENT_FILE"
|
|
56
|
+
[ "$status" -eq 0 ]
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
@test "agent.md routes the fix to /wr-jtbd:confirm-jobs-and-personas (the surface-2 drain)" {
|
|
60
|
+
run grep -n "/wr-jtbd:confirm-jobs-and-personas" "$AGENT_FILE"
|
|
61
|
+
[ "$status" -eq 0 ]
|
|
62
|
+
}
|
package/hooks/hooks.json
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"hooks": {
|
|
3
|
+
"SessionStart": [
|
|
4
|
+
{ "matcher": "startup", "hooks": [{ "type": "command", "command": "${CLAUDE_PLUGIN_ROOT}/hooks/jtbd-oversight-nudge.sh" }] }
|
|
5
|
+
],
|
|
3
6
|
"UserPromptSubmit": [
|
|
4
7
|
{ "hooks": [{ "type": "command", "command": "${CLAUDE_PLUGIN_ROOT}/hooks/jtbd-eval.sh" }] }
|
|
5
8
|
],
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# wr-jtbd — SessionStart hook (ADR-068)
|
|
3
|
+
#
|
|
4
|
+
# Surfaces a one-line nudge when jobs/personas lack the human-oversight marker,
|
|
5
|
+
# so the user can drain them via /wr-jtbd:confirm-jobs-and-personas. Sibling of
|
|
6
|
+
# packages/architect/hooks/architect-oversight-nudge.sh (ADR-066); same shape as
|
|
7
|
+
# the ADR-040 session-start surface.
|
|
8
|
+
#
|
|
9
|
+
# Detection is token-cheap: delegates to detect-unoversighted.sh (a grep over
|
|
10
|
+
# docs/jtbd/ frontmatter — no body reads, no per-file LLM call). Silent when the
|
|
11
|
+
# unoversighted count is zero (steady state once the set is drained).
|
|
12
|
+
#
|
|
13
|
+
# AFK self-suppress: shares the suite-wide WR_SUPPRESS_OVERSIGHT_NUDGE guard with
|
|
14
|
+
# the architect oversight nudge (ADR-068 § shared cross-plugin contracts). AFK
|
|
15
|
+
# orchestrators export it once (work-problems Step 5) and every oversight nudge
|
|
16
|
+
# self-suppresses — so this interactive batch-confirm nudge never fires into an
|
|
17
|
+
# absent-user subprocess (JTBD-006 friction guard). Only the literal "1" suppresses.
|
|
18
|
+
|
|
19
|
+
set -euo pipefail
|
|
20
|
+
|
|
21
|
+
if [ "${WR_SUPPRESS_OVERSIGHT_NUDGE:-}" = "1" ]; then
|
|
22
|
+
exit 0
|
|
23
|
+
fi
|
|
24
|
+
|
|
25
|
+
PROJECT_DIR="${CLAUDE_PROJECT_DIR:-.}"
|
|
26
|
+
JTBD_DIR="$PROJECT_DIR/docs/jtbd"
|
|
27
|
+
|
|
28
|
+
[ -d "$JTBD_DIR" ] || exit 0
|
|
29
|
+
|
|
30
|
+
DETECT="${CLAUDE_PLUGIN_ROOT:-$(dirname "$0")/..}/scripts/detect-unoversighted.sh"
|
|
31
|
+
[ -x "$DETECT" ] || DETECT="$(dirname "$0")/../scripts/detect-unoversighted.sh"
|
|
32
|
+
|
|
33
|
+
COUNT="$(bash "$DETECT" "$JTBD_DIR" 2>/dev/null | grep -c . || true)"
|
|
34
|
+
COUNT="${COUNT:-0}"
|
|
35
|
+
|
|
36
|
+
[ "$COUNT" -gt 0 ] 2>/dev/null || exit 0
|
|
37
|
+
|
|
38
|
+
if [ "$COUNT" -eq 1 ]; then
|
|
39
|
+
echo "[wr-jtbd] 1 job/persona lacks human oversight — run /wr-jtbd:confirm-jobs-and-personas to confirm it."
|
|
40
|
+
else
|
|
41
|
+
echo "[wr-jtbd] $COUNT jobs/personas lack human oversight — run /wr-jtbd:confirm-jobs-and-personas to confirm them."
|
|
42
|
+
fi
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
#!/usr/bin/env bats
|
|
2
|
+
|
|
3
|
+
# ADR-068: jtbd-oversight-nudge.sh (SessionStart) emits a one-line nudge when
|
|
4
|
+
# jobs/personas lack the human-oversight marker, is silent when none do, and
|
|
5
|
+
# self-suppresses under the shared AFK guard (WR_SUPPRESS_OVERSIGHT_NUDGE=1).
|
|
6
|
+
# Behavioural — exercises the hook against fixture docs/jtbd/ trees.
|
|
7
|
+
|
|
8
|
+
setup() {
|
|
9
|
+
REPO_ROOT="$(cd "$(dirname "$BATS_TEST_FILENAME")/../../../.." && pwd)"
|
|
10
|
+
HOOK="$REPO_ROOT/packages/jtbd/hooks/jtbd-oversight-nudge.sh"
|
|
11
|
+
PLUGIN_ROOT="$REPO_ROOT/packages/jtbd"
|
|
12
|
+
DIR="$(mktemp -d)"
|
|
13
|
+
mkdir -p "$DIR/docs/jtbd/solo-developer"
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
teardown() { rm -rf "$DIR"; }
|
|
17
|
+
|
|
18
|
+
mk_unmarked() {
|
|
19
|
+
mkdir -p "$(dirname "$DIR/docs/jtbd/$1")"
|
|
20
|
+
{ echo "---"; echo "status: proposed"; echo "date-created: 2026-05-25"; echo "---"; echo "# $1"; } \
|
|
21
|
+
> "$DIR/docs/jtbd/$1"
|
|
22
|
+
}
|
|
23
|
+
mk_marked() {
|
|
24
|
+
mkdir -p "$(dirname "$DIR/docs/jtbd/$1")"
|
|
25
|
+
{ echo "---"; echo "status: proposed"; echo "human-oversight: confirmed"; echo "---"; echo "# $1"; } \
|
|
26
|
+
> "$DIR/docs/jtbd/$1"
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
@test "emits a count line when there are unoversighted jobs/personas" {
|
|
30
|
+
mk_unmarked "solo-developer/persona.md"
|
|
31
|
+
mk_unmarked "solo-developer/JTBD-001-foo.proposed.md"
|
|
32
|
+
run env CLAUDE_PROJECT_DIR="$DIR" CLAUDE_PLUGIN_ROOT="$PLUGIN_ROOT" bash "$HOOK"
|
|
33
|
+
[ "$status" -eq 0 ]
|
|
34
|
+
[[ "$output" == *"2 jobs/personas lack human oversight"* ]]
|
|
35
|
+
[[ "$output" == *"/wr-jtbd:confirm-jobs-and-personas"* ]]
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
@test "uses singular wording for exactly one unoversighted artifact" {
|
|
39
|
+
mk_unmarked "solo-developer/persona.md"
|
|
40
|
+
run env CLAUDE_PROJECT_DIR="$DIR" CLAUDE_PLUGIN_ROOT="$PLUGIN_ROOT" bash "$HOOK"
|
|
41
|
+
[[ "$output" == *"1 job/persona lacks human oversight"* ]]
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
@test "silent when every job/persona is confirmed" {
|
|
45
|
+
mk_marked "solo-developer/persona.md"
|
|
46
|
+
run env CLAUDE_PROJECT_DIR="$DIR" CLAUDE_PLUGIN_ROOT="$PLUGIN_ROOT" bash "$HOOK"
|
|
47
|
+
[ "$status" -eq 0 ]
|
|
48
|
+
[ -z "$output" ]
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
@test "shared AFK guard suppresses the nudge entirely" {
|
|
52
|
+
mk_unmarked "solo-developer/persona.md"
|
|
53
|
+
run env WR_SUPPRESS_OVERSIGHT_NUDGE=1 CLAUDE_PROJECT_DIR="$DIR" CLAUDE_PLUGIN_ROOT="$PLUGIN_ROOT" bash "$HOOK"
|
|
54
|
+
[ "$status" -eq 0 ]
|
|
55
|
+
[ -z "$output" ]
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
@test "guard value other than 1 does not suppress" {
|
|
59
|
+
mk_unmarked "solo-developer/persona.md"
|
|
60
|
+
mk_unmarked "solo-developer/JTBD-001-foo.proposed.md"
|
|
61
|
+
run env WR_SUPPRESS_OVERSIGHT_NUDGE=0 CLAUDE_PROJECT_DIR="$DIR" CLAUDE_PLUGIN_ROOT="$PLUGIN_ROOT" bash "$HOOK"
|
|
62
|
+
[[ "$output" == *"lack human oversight"* ]]
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
@test "silent when project has no docs/jtbd dir" {
|
|
66
|
+
run env CLAUDE_PROJECT_DIR="$DIR/empty" CLAUDE_PLUGIN_ROOT="$PLUGIN_ROOT" bash "$HOOK"
|
|
67
|
+
[ "$status" -eq 0 ]
|
|
68
|
+
[ -z "$output" ]
|
|
69
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@windyroad/jtbd",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.4-preview.430",
|
|
4
4
|
"description": "Jobs-to-be-done enforcement for UI changes",
|
|
5
5
|
"bin": {
|
|
6
6
|
"windyroad-jtbd": "./bin/install.mjs"
|
|
@@ -23,6 +23,7 @@
|
|
|
23
23
|
"agents/",
|
|
24
24
|
"hooks/",
|
|
25
25
|
"skills/",
|
|
26
|
+
"scripts/",
|
|
26
27
|
".claude-plugin/",
|
|
27
28
|
"lib/"
|
|
28
29
|
]
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# wr-jtbd — detect jobs/personas lacking the human-oversight marker (ADR-068)
|
|
3
|
+
#
|
|
4
|
+
# @jtbd JTBD-202 (Run Pre-Flight Governance Checks — oversight state of the JTBD corpus)
|
|
5
|
+
# @jtbd JTBD-101 (Extend the Suite with New Plugins — reusable adopter-portable primitive)
|
|
6
|
+
#
|
|
7
|
+
# Sibling of packages/architect/scripts/detect-unoversighted.sh (ADR-066). Token-cheap:
|
|
8
|
+
# greps each job/persona's YAML frontmatter for `human-oversight: confirmed`. No body
|
|
9
|
+
# reads, no per-file LLM call. A file is "unoversighted" when its frontmatter does not
|
|
10
|
+
# carry that marker (or has no frontmatter).
|
|
11
|
+
#
|
|
12
|
+
# Usage:
|
|
13
|
+
# detect-unoversighted.sh [JTBD_DIR]
|
|
14
|
+
# JTBD_DIR defaults to docs/jtbd
|
|
15
|
+
#
|
|
16
|
+
# Output: one unoversighted job/persona file path per line, sorted. Empty output = the
|
|
17
|
+
# whole set is confirmed. Always exits 0 (it is a detector, not a gate).
|
|
18
|
+
#
|
|
19
|
+
# Consumed by: jtbd-oversight-nudge.sh (SessionStart count) and
|
|
20
|
+
# /wr-jtbd:confirm-jobs-and-personas (the drain list). Marker contract: ADR-068 (= ADR-066 field).
|
|
21
|
+
|
|
22
|
+
set -euo pipefail
|
|
23
|
+
|
|
24
|
+
JTBD_DIR="${1:-docs/jtbd}"
|
|
25
|
+
[ -d "$JTBD_DIR" ] || exit 0
|
|
26
|
+
|
|
27
|
+
# JTBD layout (ADR-008): docs/jtbd/<persona>/persona.md + docs/jtbd/<persona>/JTBD-NNN-*.md,
|
|
28
|
+
# with docs/jtbd/README.md as the top-level index. Match the per-persona files; README is
|
|
29
|
+
# never a job/persona record.
|
|
30
|
+
shopt -s nullglob
|
|
31
|
+
for f in "$JTBD_DIR"/*/*.md "$JTBD_DIR"/*.md; do
|
|
32
|
+
base="$(basename "$f")"
|
|
33
|
+
[ "$base" = "README.md" ] && continue
|
|
34
|
+
# Superseded artifacts (if an adopter uses a .superseded.md suffix) are retired.
|
|
35
|
+
case "$base" in *.superseded.md) continue ;; esac
|
|
36
|
+
|
|
37
|
+
fm="$(awk '
|
|
38
|
+
NR==1 && $0 != "---" { exit }
|
|
39
|
+
NR==1 { next }
|
|
40
|
+
/^---[[:space:]]*$/ { exit }
|
|
41
|
+
{ print }
|
|
42
|
+
' "$f")"
|
|
43
|
+
|
|
44
|
+
if ! printf '%s\n' "$fm" | grep -qiE '^human-oversight:[[:space:]]*confirmed[[:space:]]*$'; then
|
|
45
|
+
echo "$f"
|
|
46
|
+
fi
|
|
47
|
+
done | sort
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# wr-jtbd — predicate: is a referenced persona or job unconfirmed? (ADR-068 surface 3)
|
|
3
|
+
#
|
|
4
|
+
# Single-artifact sibling of detect-unoversighted.sh (ADR-068). Where the
|
|
5
|
+
# detector LISTS the whole unoversighted set and always exits 0, this answers
|
|
6
|
+
# ONE question for ONE persona/job via its EXIT CODE — for the build-upon guard
|
|
7
|
+
# the wr-jtbd:agent runs (the [Unratified Dependency] verdict, RFC-011 / P323).
|
|
8
|
+
# The JTBD twin of packages/architect/scripts/is-decision-unconfirmed.sh
|
|
9
|
+
# (ADR-074). A separate script (not a mode flag on the detector) keeps the
|
|
10
|
+
# detector's "always exit 0, path-list on stdout" contract intact.
|
|
11
|
+
#
|
|
12
|
+
# "Unconfirmed" mirrors detect-unoversighted.sh EXACTLY:
|
|
13
|
+
# - the artifact's frontmatter lacks `human-oversight: confirmed`, AND
|
|
14
|
+
# - the artifact is not superseded.
|
|
15
|
+
# CANONICAL SHAPE: detect-unoversighted.sh. Keep the frontmatter-extraction
|
|
16
|
+
# awk block + the `human-oversight: confirmed` grep + the superseded skip in
|
|
17
|
+
# sync with that script. The `@test "agrees with detect-unoversighted ..."`
|
|
18
|
+
# case in test/is-job-or-persona-unconfirmed.bats fails if these two drift.
|
|
19
|
+
#
|
|
20
|
+
# Usage:
|
|
21
|
+
# is-job-or-persona-unconfirmed.sh <ref> [JTBD_DIR]
|
|
22
|
+
# <ref> = JTBD-NNN | NNN | <persona-name> | path/to/<file>.md
|
|
23
|
+
# JTBD_DIR defaults to docs/jtbd
|
|
24
|
+
#
|
|
25
|
+
# Ref resolution (ADR-008 layout: docs/jtbd/<persona>/persona.md +
|
|
26
|
+
# docs/jtbd/<persona>/JTBD-NNN-*.md):
|
|
27
|
+
# - a path to an existing file → that file
|
|
28
|
+
# - JTBD-NNN or a bare NNN → docs/jtbd/*/JTBD-NNN-*.md (first match)
|
|
29
|
+
# - anything else (a persona name) → docs/jtbd/<name>/persona.md
|
|
30
|
+
#
|
|
31
|
+
# Exit codes:
|
|
32
|
+
# 0 = unconfirmed — the build-upon guard SHOULD fire. Prints the resolved path.
|
|
33
|
+
# 1 = confirmed OR superseded — guard should NOT fire. No stdout.
|
|
34
|
+
# 2 = not found / unparseable ref. No stdout; reason on stderr.
|
|
35
|
+
|
|
36
|
+
set -uo pipefail
|
|
37
|
+
|
|
38
|
+
REF="${1:-}"
|
|
39
|
+
JTBD_DIR="${2:-docs/jtbd}"
|
|
40
|
+
|
|
41
|
+
[ -n "$REF" ] || { echo "is-job-or-persona-unconfirmed: missing <ref>" >&2; exit 2; }
|
|
42
|
+
|
|
43
|
+
# ── Resolve the persona/job file ──────────────────────────────────────────
|
|
44
|
+
file=""
|
|
45
|
+
if [ -f "$REF" ]; then
|
|
46
|
+
file="$REF"
|
|
47
|
+
elif printf '%s' "$REF" | grep -qiE 'JTBD-?[0-9]+|^[0-9]+$'; then
|
|
48
|
+
# Job ref: JTBD-NNN or a bare numeric. Match the per-persona job file.
|
|
49
|
+
num="$(printf '%s' "$REF" | grep -oE '[0-9]+' | head -1)"
|
|
50
|
+
[ -n "$num" ] || { echo "is-job-or-persona-unconfirmed: cannot parse job id from '$REF'" >&2; exit 2; }
|
|
51
|
+
shopt -s nullglob
|
|
52
|
+
for cand in "$JTBD_DIR"/*/JTBD-"$num"-*.md "$JTBD_DIR"/*/"$num"-*.md; do
|
|
53
|
+
file="$cand"; break
|
|
54
|
+
done
|
|
55
|
+
shopt -u nullglob
|
|
56
|
+
else
|
|
57
|
+
# Persona-name ref → the persona's persona.md.
|
|
58
|
+
cand="$JTBD_DIR/$REF/persona.md"
|
|
59
|
+
[ -f "$cand" ] && file="$cand"
|
|
60
|
+
fi
|
|
61
|
+
|
|
62
|
+
[ -n "$file" ] && [ -f "$file" ] || {
|
|
63
|
+
echo "is-job-or-persona-unconfirmed: no persona/job file for '$REF' under $JTBD_DIR" >&2
|
|
64
|
+
exit 2
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
base="$(basename "$file")"
|
|
68
|
+
|
|
69
|
+
# Superseded artifacts are retired — a newer job/persona replaced them. The
|
|
70
|
+
# build-upon guard does not fire (mirror of detect-unoversighted.sh's skip).
|
|
71
|
+
case "$base" in *.superseded.md) exit 1 ;; esac
|
|
72
|
+
|
|
73
|
+
# Extract the frontmatter block (mirror of detect-unoversighted.sh): lines
|
|
74
|
+
# between the leading `---` and the next `---`. No leading `---` ⇒ no
|
|
75
|
+
# frontmatter ⇒ treated as unconfirmed.
|
|
76
|
+
fm="$(awk '
|
|
77
|
+
NR==1 && $0 != "---" { exit }
|
|
78
|
+
NR==1 { next }
|
|
79
|
+
/^---[[:space:]]*$/ { exit }
|
|
80
|
+
{ print }
|
|
81
|
+
' "$file")"
|
|
82
|
+
|
|
83
|
+
if printf '%s\n' "$fm" | grep -qiE '^human-oversight:[[:space:]]*confirmed[[:space:]]*$'; then
|
|
84
|
+
exit 1 # confirmed — OK to build on
|
|
85
|
+
fi
|
|
86
|
+
|
|
87
|
+
# Unconfirmed — the build-upon guard SHOULD fire. Name the file for the guard.
|
|
88
|
+
echo "$file"
|
|
89
|
+
exit 0
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
#!/usr/bin/env bats
|
|
2
|
+
|
|
3
|
+
# ADR-068: detect-unoversighted.sh prints jobs/personas whose frontmatter lacks the
|
|
4
|
+
# `human-oversight: confirmed` marker. Behavioural — exercises the script against
|
|
5
|
+
# fixture docs/jtbd/<persona>/ trees and asserts on stdout.
|
|
6
|
+
|
|
7
|
+
setup() {
|
|
8
|
+
REPO_ROOT="$(cd "$(dirname "$BATS_TEST_FILENAME")/../../../.." && pwd)"
|
|
9
|
+
SCRIPT="$REPO_ROOT/packages/jtbd/scripts/detect-unoversighted.sh"
|
|
10
|
+
DIR="$(mktemp -d)"
|
|
11
|
+
mkdir -p "$DIR/docs/jtbd/solo-developer"
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
teardown() { rm -rf "$DIR"; }
|
|
15
|
+
|
|
16
|
+
mk() { # mk <relpath under docs/jtbd> <extra frontmatter lines...>
|
|
17
|
+
local rel="$1"; shift
|
|
18
|
+
mkdir -p "$(dirname "$DIR/docs/jtbd/$rel")"
|
|
19
|
+
{
|
|
20
|
+
echo "---"
|
|
21
|
+
echo "status: proposed"
|
|
22
|
+
echo "date-created: 2026-05-25"
|
|
23
|
+
for line in "$@"; do echo "$line"; done
|
|
24
|
+
echo "---"
|
|
25
|
+
echo "# $rel"
|
|
26
|
+
} > "$DIR/docs/jtbd/$rel"
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
@test "a job without the marker is reported" {
|
|
30
|
+
mk "solo-developer/JTBD-001-foo.proposed.md"
|
|
31
|
+
run bash "$SCRIPT" "$DIR/docs/jtbd"
|
|
32
|
+
[ "$status" -eq 0 ]
|
|
33
|
+
[[ "$output" == *"JTBD-001-foo.proposed.md"* ]]
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
@test "a persona file without the marker is reported" {
|
|
37
|
+
mk "solo-developer/persona.md"
|
|
38
|
+
run bash "$SCRIPT" "$DIR/docs/jtbd"
|
|
39
|
+
[[ "$output" == *"solo-developer/persona.md"* ]]
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
@test "a job carrying human-oversight: confirmed is NOT reported" {
|
|
43
|
+
mk "solo-developer/JTBD-002-bar.proposed.md" "human-oversight: confirmed" "oversight-date: 2026-05-25"
|
|
44
|
+
run bash "$SCRIPT" "$DIR/docs/jtbd"
|
|
45
|
+
[[ "$output" != *"JTBD-002-bar.proposed.md"* ]]
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
@test "the top-level docs/jtbd/README.md is never reported" {
|
|
49
|
+
echo "# JTBD index" > "$DIR/docs/jtbd/README.md"
|
|
50
|
+
run bash "$SCRIPT" "$DIR/docs/jtbd"
|
|
51
|
+
[[ "$output" != *"README.md"* ]]
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
@test "a per-persona README is also excluded" {
|
|
55
|
+
echo "# persona index" > "$DIR/docs/jtbd/solo-developer/README.md"
|
|
56
|
+
run bash "$SCRIPT" "$DIR/docs/jtbd"
|
|
57
|
+
[[ "$output" != *"README.md"* ]]
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
@test "a file with no frontmatter counts as unoversighted" {
|
|
61
|
+
mkdir -p "$DIR/docs/jtbd/tech-lead"
|
|
62
|
+
echo "# bare persona, no frontmatter" > "$DIR/docs/jtbd/tech-lead/persona.md"
|
|
63
|
+
run bash "$SCRIPT" "$DIR/docs/jtbd"
|
|
64
|
+
[[ "$output" == *"tech-lead/persona.md"* ]]
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
@test "marker match is case-insensitive and tolerant of trailing space" {
|
|
68
|
+
mk "solo-developer/JTBD-003-baz.proposed.md" "human-oversight: confirmed "
|
|
69
|
+
run bash "$SCRIPT" "$DIR/docs/jtbd"
|
|
70
|
+
[[ "$output" != *"JTBD-003-baz.proposed.md"* ]]
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
@test "missing jtbd dir exits 0 with no output" {
|
|
74
|
+
run bash "$SCRIPT" "$DIR/docs/nonexistent"
|
|
75
|
+
[ "$status" -eq 0 ]
|
|
76
|
+
[ -z "$output" ]
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
@test "fully-confirmed corpus produces empty output" {
|
|
80
|
+
mk "solo-developer/persona.md" "human-oversight: confirmed"
|
|
81
|
+
mk "solo-developer/JTBD-001-foo.proposed.md" "human-oversight: confirmed"
|
|
82
|
+
run bash "$SCRIPT" "$DIR/docs/jtbd"
|
|
83
|
+
[ "$status" -eq 0 ]
|
|
84
|
+
[ -z "$output" ]
|
|
85
|
+
}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
#!/usr/bin/env bats
|
|
2
|
+
|
|
3
|
+
# ADR-068 surface 3 / RFC-011 / P323: is-job-or-persona-unconfirmed.sh is the
|
|
4
|
+
# single-artifact predicate for the JTBD build-upon guard (the wr-jtbd:agent
|
|
5
|
+
# [Unratified Dependency] verdict). It answers "is this referenced persona or
|
|
6
|
+
# job unconfirmed?" via its EXIT CODE, where "unconfirmed" mirrors
|
|
7
|
+
# detect-unoversighted.sh EXACTLY (frontmatter lacks `human-oversight:
|
|
8
|
+
# confirmed`, and the artifact is not superseded). The JTBD twin of the
|
|
9
|
+
# architect side's is-decision-unconfirmed.sh (ADR-074).
|
|
10
|
+
#
|
|
11
|
+
# Behavioural — exercises the script against fixture trees and asserts on its
|
|
12
|
+
# exit code + stdout, not its source text (ADR-052).
|
|
13
|
+
|
|
14
|
+
setup() {
|
|
15
|
+
REPO_ROOT="$(cd "$(dirname "$BATS_TEST_FILENAME")/../../../.." && pwd)"
|
|
16
|
+
SCRIPT="$REPO_ROOT/packages/jtbd/scripts/is-job-or-persona-unconfirmed.sh"
|
|
17
|
+
DETECT="$REPO_ROOT/packages/jtbd/scripts/detect-unoversighted.sh"
|
|
18
|
+
DIR="$(mktemp -d)"
|
|
19
|
+
mkdir -p "$DIR/docs/jtbd"
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
teardown() {
|
|
23
|
+
rm -rf "$DIR"
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
# mk_persona <persona-name> <confirmed?yes|no>
|
|
27
|
+
mk_persona() {
|
|
28
|
+
local name="$1"; local confirmed="$2"
|
|
29
|
+
mkdir -p "$DIR/docs/jtbd/$name"
|
|
30
|
+
{
|
|
31
|
+
echo "---"
|
|
32
|
+
echo "name: $name"
|
|
33
|
+
echo "description: test persona $name"
|
|
34
|
+
[ "$confirmed" = "yes" ] && { echo "human-oversight: confirmed"; echo "oversight-date: 2026-05-27"; }
|
|
35
|
+
echo "---"
|
|
36
|
+
echo "# $name"
|
|
37
|
+
} > "$DIR/docs/jtbd/$name/persona.md"
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
# mk_job <persona-name> <NNN> <slug> <confirmed?yes|no> [state]
|
|
41
|
+
mk_job() {
|
|
42
|
+
local persona="$1"; local num="$2"; local slug="$3"; local confirmed="$4"; local state="${5:-proposed}"
|
|
43
|
+
mkdir -p "$DIR/docs/jtbd/$persona"
|
|
44
|
+
{
|
|
45
|
+
echo "---"
|
|
46
|
+
echo "status: $state"
|
|
47
|
+
echo "job-id: $slug"
|
|
48
|
+
echo "persona: $persona"
|
|
49
|
+
[ "$confirmed" = "yes" ] && { echo "human-oversight: confirmed"; echo "oversight-date: 2026-05-27"; }
|
|
50
|
+
echo "---"
|
|
51
|
+
echo "# $slug"
|
|
52
|
+
} > "$DIR/docs/jtbd/$persona/JTBD-$num-$slug.md"
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
@test "persona without the marker is unconfirmed (exit 0, prints path)" {
|
|
56
|
+
mk_persona "solo-developer" "no"
|
|
57
|
+
run bash "$SCRIPT" "solo-developer" "$DIR/docs/jtbd"
|
|
58
|
+
[ "$status" -eq 0 ]
|
|
59
|
+
[[ "$output" == *"solo-developer/persona.md"* ]]
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
@test "persona carrying human-oversight: confirmed is confirmed (exit 1, no stdout)" {
|
|
63
|
+
mk_persona "tech-lead" "yes"
|
|
64
|
+
run bash "$SCRIPT" "tech-lead" "$DIR/docs/jtbd"
|
|
65
|
+
[ "$status" -eq 1 ]
|
|
66
|
+
[ -z "$output" ]
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
@test "job (JTBD-NNN) without the marker is unconfirmed (exit 0, prints path)" {
|
|
70
|
+
mk_job "solo-developer" "001" "enforce-governance" "no"
|
|
71
|
+
run bash "$SCRIPT" "JTBD-001" "$DIR/docs/jtbd"
|
|
72
|
+
[ "$status" -eq 0 ]
|
|
73
|
+
[[ "$output" == *"JTBD-001-enforce-governance.md"* ]]
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
@test "job carrying the marker is confirmed (exit 1)" {
|
|
77
|
+
mk_job "tech-lead" "201" "restore-service-fast" "yes"
|
|
78
|
+
run bash "$SCRIPT" "JTBD-201" "$DIR/docs/jtbd"
|
|
79
|
+
[ "$status" -eq 1 ]
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
@test "superseded job (even without marker) does NOT fire the guard (exit 1)" {
|
|
83
|
+
mkdir -p "$DIR/docs/jtbd/solo-developer"
|
|
84
|
+
{
|
|
85
|
+
echo "---"; echo "status: superseded"; echo "persona: solo-developer"; echo "---"; echo "# retired"
|
|
86
|
+
} > "$DIR/docs/jtbd/solo-developer/JTBD-009-retired.superseded.md"
|
|
87
|
+
run bash "$SCRIPT" "JTBD-009" "$DIR/docs/jtbd"
|
|
88
|
+
[ "$status" -eq 1 ]
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
@test "a bare numeric job ref resolves" {
|
|
92
|
+
mk_job "solo-developer" "002" "ship-with-confidence" "no"
|
|
93
|
+
run bash "$SCRIPT" "002" "$DIR/docs/jtbd"
|
|
94
|
+
[ "$status" -eq 0 ]
|
|
95
|
+
[[ "$output" == *"JTBD-002-ship-with-confidence.md"* ]]
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
@test "a direct path ref resolves" {
|
|
99
|
+
mk_persona "plugin-user" "no"
|
|
100
|
+
run bash "$SCRIPT" "$DIR/docs/jtbd/plugin-user/persona.md"
|
|
101
|
+
[ "$status" -eq 0 ]
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
@test "an unknown persona name exits 2 (not found)" {
|
|
105
|
+
run bash "$SCRIPT" "ghost-persona" "$DIR/docs/jtbd"
|
|
106
|
+
[ "$status" -eq 2 ]
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
@test "an unknown job ref exits 2 (not found)" {
|
|
110
|
+
run bash "$SCRIPT" "JTBD-999" "$DIR/docs/jtbd"
|
|
111
|
+
[ "$status" -eq 2 ]
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
@test "agrees with detect-unoversighted on the same fixture (sync guard)" {
|
|
115
|
+
# The two scripts share the frontmatter/marker/superseded shape. This guard
|
|
116
|
+
# fails if a future edit drifts one from the other.
|
|
117
|
+
mk_persona "solo-developer" "no"
|
|
118
|
+
mk_persona "tech-lead" "yes"
|
|
119
|
+
detect_out="$(bash "$DETECT" "$DIR/docs/jtbd")"
|
|
120
|
+
# solo-developer is in the detector's unoversighted list AND the predicate exits 0.
|
|
121
|
+
[[ "$detect_out" == *"solo-developer/persona.md"* ]]
|
|
122
|
+
run bash "$SCRIPT" "solo-developer" "$DIR/docs/jtbd"; [ "$status" -eq 0 ]
|
|
123
|
+
# tech-lead is NOT in the detector's list AND the predicate exits 1.
|
|
124
|
+
[[ "$detect_out" != *"tech-lead/persona.md"* ]]
|
|
125
|
+
run bash "$SCRIPT" "tech-lead" "$DIR/docs/jtbd"; [ "$status" -eq 1 ]
|
|
126
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: wr-jtbd:confirm-jobs-and-personas
|
|
3
|
+
description: Drain the set of Jobs To Be Done and personas that lack human oversight. Surfaces each auto-derived job/persona via AskUserQuestion so a human confirms, amends, or rejects it, then writes the human-oversight marker. Use when the session-start nudge reports jobs/personas lack oversight, or any time you want to review the documented JTBD corpus. This is the read-write oversight drain — distinct from /wr-jtbd:review-jobs (the read-only alignment reviewer).
|
|
4
|
+
allowed-tools: Read, Glob, Grep, Bash, Edit, AskUserQuestion
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Confirm Jobs and Personas — human-oversight drain
|
|
8
|
+
|
|
9
|
+
Lift auto-derived Jobs To Be Done and personas to human decisions. Documented jobs/personas are load-bearing: the JTBD edit gate reviews every project change against `docs/jtbd/`, so a job or persona that was agent-derived without a human confirming it reflects real need propagates wrong alignment verdicts. This skill drains the **unoversighted set** (jobs/personas lacking `human-oversight: confirmed`, per ADR-068): it surfaces each via `AskUserQuestion`, and writes the oversight marker only when a human confirms.
|
|
10
|
+
|
|
11
|
+
This is the P288 / ADR-068 drain surface — the JTBD sibling of `/wr-architect:review-decisions`. It is **read-write** (writes the marker on confirm). It is NOT the same as `/wr-jtbd:review-jobs`, which is a read-only alignment review ("do my changes trace to documented jobs?"); this skill confirms the jobs/personas themselves.
|
|
12
|
+
|
|
13
|
+
## When to use
|
|
14
|
+
|
|
15
|
+
- The session-start nudge reported `N jobs/personas lack human oversight`.
|
|
16
|
+
- Pre-handover / pre-release: confirm the JTBD corpus reflects real user/business need.
|
|
17
|
+
- After an `update-guide` run or agent-derived job/persona authoring landed files without confirmation.
|
|
18
|
+
- Any focused sitting — designed for **batches over multiple sittings**, not one blocking pass.
|
|
19
|
+
|
|
20
|
+
## How it works
|
|
21
|
+
|
|
22
|
+
The marker persists (ADR-009 never-re-ask principle), so a partially-drained set resumes cleanly on the next run.
|
|
23
|
+
|
|
24
|
+
### Step 1: Enumerate the unoversighted set
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
wr-jtbd-detect-unoversighted docs/jtbd
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
The `wr-jtbd-detect-unoversighted` command is a `$PATH`-resolved shim (ADR-049) dispatching `packages/jtbd/scripts/detect-unoversighted.sh`. It prints one unoversighted job/persona path per line (README excluded; token-cheap grep over frontmatter — no body reads). Empty output → the corpus is fully confirmed; report and stop.
|
|
31
|
+
|
|
32
|
+
### Step 2: Cluster + order
|
|
33
|
+
|
|
34
|
+
Read **only the frontmatter + title + Job Statement / persona "Who" section** of each unoversighted file (not full bodies — keep it cheap). Group by persona directory and order **load-bearing first**: personas before their jobs (a persona frames its jobs), and jobs that other artifacts (problems, RFCs, READMEs) cite most heavily before narrow ones.
|
|
35
|
+
|
|
36
|
+
### Step 3: Present each via AskUserQuestion (batched)
|
|
37
|
+
|
|
38
|
+
For each job/persona in the ordered queue, surface it as an `AskUserQuestion` (cap **4 per call** per ADR-013 Rule 1; issue further calls sequentially):
|
|
39
|
+
|
|
40
|
+
- **Question**: the job statement (for a JTBD) or the persona definition (who they are + key constraints), in one line.
|
|
41
|
+
- **Context**: grounded in what the file actually says (per ADR-026) — the persona served, the desired outcomes, any cited problems/RFCs.
|
|
42
|
+
- **Options** per artifact:
|
|
43
|
+
- **Confirm** — the job/persona accurately reflects real need; write the marker.
|
|
44
|
+
- **Amend** — mostly right but needs a change; capture it, apply it to the file, then write the marker.
|
|
45
|
+
- **Reject** — the auto-derived job/persona does not reflect real need; do NOT write the marker. Note the rework (a `wr-jtbd:update-guide` rewrite, or retirement).
|
|
46
|
+
- **Defer** — skip this sitting; leave unoversighted for later.
|
|
47
|
+
|
|
48
|
+
This is a genuine human-decision surface (the point of P288/ADR-068) — `AskUserQuestion` is correct here, not over-asking. Do not auto-confirm; do not prose-ask.
|
|
49
|
+
|
|
50
|
+
### Step 4: Apply the outcome
|
|
51
|
+
|
|
52
|
+
- **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.
|
|
53
|
+
- **Reject**: leave the marker absent; record the rework.
|
|
54
|
+
- **Defer**: no write.
|
|
55
|
+
|
|
56
|
+
**Unoversighted ≠ unusable** (ADR-068): an unconfirmed job/persona stays fully readable and review-anchorable. The marker records provenance; it never quarantines the doc or blocks reviews from reading it.
|
|
57
|
+
|
|
58
|
+
### Step 5: Commit + report
|
|
59
|
+
|
|
60
|
+
Commit the confirmed/amended files per ADR-014 (one commit per drain sitting is acceptable). Report: confirmed / amended / rejected / deferred counts, and the remaining unoversighted count (re-run the detector). The session-start nudge count drops by the number confirmed.
|
|
61
|
+
|
|
62
|
+
## Notes
|
|
63
|
+
|
|
64
|
+
- **Never re-ask** — a confirmed job/persona carries the marker permanently and is excluded from future runs (ADR-009). Write-once **except** when the job statement / persona definition is materially rewritten — a material amend clears the marker for re-confirmation (ADR-068 Reassessment).
|
|
65
|
+
- **AFK** — interactive by construction (the confirm IS the human decision); not dispatched in AFK iteration subprocesses. The session-start nudge self-suppresses there (`WR_SUPPRESS_OVERSIGHT_NUDGE=1`).
|
|
66
|
+
- **Born-confirmed going forward** — `/wr-jtbd:update-guide` writes the marker when the user confirms a new/edited job or persona, so new artifacts enter the set already oversighted and the unoversighted count only shrinks.
|
|
67
|
+
|
|
68
|
+
## Related
|
|
69
|
+
|
|
70
|
+
- **ADR-068** — this drain + the marker + detector + nudge. **ADR-066 / P283** — the architect precedent this mirrors.
|
|
71
|
+
- **ADR-008** — JTBD directory structure (the marker is additive to its frontmatter contract).
|
|
72
|
+
- **ADR-009** — never-re-ask persistent-marker principle. **ADR-013 / ADR-044** — structured user interaction + decision-delegation taxonomy.
|
|
73
|
+
- `packages/jtbd/skills/review-jobs/SKILL.md` — the read-only alignment reviewer (distinct from this drain).
|
|
74
|
+
- `packages/jtbd/skills/update-guide/SKILL.md` — born-confirmed write site.
|
|
@@ -80,7 +80,7 @@ description: <one-line description>
|
|
|
80
80
|
<bullet list of frustrations this product addresses>
|
|
81
81
|
```
|
|
82
82
|
|
|
83
|
-
Use kebab-case for the directory name (e.g., `
|
|
83
|
+
Use kebab-case for the directory name (e.g., `developer`, `tech-lead`).
|
|
84
84
|
|
|
85
85
|
### 4. Confirm personas with the user
|
|
86
86
|
|
|
@@ -89,6 +89,15 @@ Use AskUserQuestion to present the drafted personas and ask:
|
|
|
89
89
|
- Any missing user segments?
|
|
90
90
|
- Any constraints or pain points to add?
|
|
91
91
|
|
|
92
|
+
**Born-confirmed write (ADR-068).** Once the user confirms a persona via this AskUserQuestion pass, write the human-oversight marker into that persona's frontmatter — insert after the `description:` line:
|
|
93
|
+
|
|
94
|
+
```yaml
|
|
95
|
+
human-oversight: confirmed
|
|
96
|
+
oversight-date: YYYY-MM-DD # today
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
This is the born-confirmed gate: a persona authored through update-guide enters the world already human-oversighted (it does not appear in `/wr-jtbd:confirm-jobs-and-personas`' unoversighted set). Do NOT write the marker for a persona the user has not confirmed. The marker is orthogonal to status.
|
|
100
|
+
|
|
92
101
|
### 5. Draft jobs
|
|
93
102
|
|
|
94
103
|
For each job (3-8 per persona), create a file at
|
|
@@ -135,6 +144,15 @@ Use AskUserQuestion to present the drafted jobs and ask:
|
|
|
135
144
|
- Do the job statements ring true?
|
|
136
145
|
- Any missing jobs or user flows?
|
|
137
146
|
|
|
147
|
+
**Born-confirmed write (ADR-068).** Once the user confirms a job via this AskUserQuestion pass, write the human-oversight marker into that job's frontmatter — insert after the `date-created:` line:
|
|
148
|
+
|
|
149
|
+
```yaml
|
|
150
|
+
human-oversight: confirmed
|
|
151
|
+
oversight-date: YYYY-MM-DD # today
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
A job authored through update-guide is born human-oversighted, so the `/wr-jtbd:confirm-jobs-and-personas` unoversighted set only ever shrinks. Do NOT write the marker for a job the user has not confirmed (drafted-but-unconfirmed jobs stay unmarked). The marker is orthogonal to `status:` — a `proposed` job can be `human-oversight: confirmed`.
|
|
155
|
+
|
|
138
156
|
### 7. Generate README.md index
|
|
139
157
|
|
|
140
158
|
Write `docs/jtbd/README.md` with tables grouping jobs by persona and status:
|