@thebassclef/lite 1.9.16 → 1.9.18
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/README.md +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/lite/.claude/agents/builder.md +7 -4
- package/dist/lite/.claude/bassclef-orientation.md +22 -0
- package/dist/lite/.claude/hooks/artifact-ingestion-gate.sh +11 -3
- package/dist/lite/.claude/hooks/assert-verify-steering.sh +1 -19
- package/dist/lite/.claude/hooks/plain-english-steering.sh +86 -41
- package/dist/lite/.claude/hooks/pre-commit-gate.sh +10 -2
- package/dist/lite/.claude/hooks/pre-push-shellcheck.sh +144 -0
- package/dist/lite/.claude/hooks/reviewer-dispatch-check.sh +153 -0
- package/dist/lite/.claude/hooks/session-reflection.d/83-install-written-paths-rebuild.sh +15 -32
- package/dist/lite/.claude/hooks/turn-prose-density-measure.sh +109 -0
- package/dist/lite/.claude/hooks/turn-prose-kiss-check.sh +28 -0
- package/dist/lite/.claude/rules/branching.md +2 -0
- package/dist/lite/.claude/rules/identifier-leak-prevention.md +2 -1
- package/dist/lite/.claude/rules/lite-manifest-schema-change-discipline.md +23 -0
- package/dist/lite/.claude/rules/reviewer-dispatch.md +95 -0
- package/dist/lite/.claude/settings.json +10 -0
- package/dist/lite/.claude/skills/build/SKILL.md +81 -6
- package/dist/lite/.claude/skills/eisenhower/SKILL.md +26 -0
- package/dist/lite/.claude/skills/launch/SKILL.md +45 -2
- package/dist/lite/.claude/skills/onboard-repo/SKILL.md +204 -40
- package/dist/lite/.claude/skills/onboard-repo/concise-prompt.sh +73 -0
- package/dist/lite/.claude/skills/onboard-repo/write-state-marker.sh +81 -0
- package/dist/lite/.claude/skills/pre-mortem/SKILL.md +56 -0
- package/dist/lite/.claude/skills/sprint/SKILL.md +24 -3
- package/dist/lite/.claude/skills/temperance/SKILL.md +15 -4
- package/dist/lite/README.md +2 -0
- package/dist/lite/bassclef-version.json +5 -5
- package/dist/lite/lib/install-written-paths.sh +250 -3
- package/dist/lite/lib/prose-marker.sh +203 -0
- package/dist/lite/lib/tier-dependency-audit.sh +52 -25
- package/dist/lite/presence/install/bassclef-sync.template.sh +86 -8
- package/dist/lite/scripts/generate-lite-manifest.sh +15 -0
- package/dist/lite/scripts/launch/local-serve.sh +149 -8
- package/dist/lite/standards/bassclef-wiring-manifest.json +12 -0
- package/dist/lite/standards/lite-manifest-schema-changes.md +49 -0
- package/dist/lite/standards/lite-manifest.json +90 -27
- package/dist/lite/standards/prose-marker-format.md +105 -0
- package/package.json +1 -1
|
@@ -66,43 +66,26 @@ _iwp_fragment_main() {
|
|
|
66
66
|
return 0
|
|
67
67
|
fi
|
|
68
68
|
|
|
69
|
-
# Source lib — subshell so caller scope stays clean
|
|
69
|
+
# Source lib — subshell so caller scope stays clean.
|
|
70
|
+
# Per bassclef-upstream#2200 — bulk-rebuild verb replaces the prior
|
|
71
|
+
# 451×700ms serial register loop that timed out before completion.
|
|
72
|
+
# New verb builds one jq write + one ajv validate at the end.
|
|
70
73
|
(
|
|
71
74
|
# shellcheck source=/dev/null
|
|
72
75
|
. "$lib" 2>/dev/null || exit 0
|
|
73
76
|
|
|
74
|
-
if
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
install_written_paths_rebuild 2>/dev/null || true
|
|
82
|
-
|
|
83
|
-
local registered=0
|
|
84
|
-
local skipped=0
|
|
85
|
-
|
|
86
|
-
# Each lite-manifest entry has .path field — register when file exists on disk
|
|
87
|
-
while IFS= read -r path; do
|
|
88
|
-
[ -z "$path" ] && continue
|
|
89
|
-
local full
|
|
90
|
-
case "$path" in
|
|
91
|
-
/*) full="$path" ;;
|
|
92
|
-
*) full="$project_root/$path" ;;
|
|
93
|
-
esac
|
|
94
|
-
if [ -f "$full" ]; then
|
|
95
|
-
if install_written_paths_register "$path" "bassclef-sync-template" 2>/dev/null; then
|
|
96
|
-
registered=$((registered + 1))
|
|
97
|
-
else
|
|
98
|
-
skipped=$((skipped + 1))
|
|
99
|
-
fi
|
|
100
|
-
else
|
|
101
|
-
skipped=$((skipped + 1))
|
|
77
|
+
if declare -F install_written_paths_rebuild_from_lite_manifest >/dev/null 2>&1; then
|
|
78
|
+
# Preserve verb's stderr (ajv-fail warnings etc.) by piping to trace
|
|
79
|
+
# instead of swallowing with 2>/dev/null (architect-review F3).
|
|
80
|
+
local verb_stderr
|
|
81
|
+
verb_stderr=$(install_written_paths_rebuild_from_lite_manifest "$lite_manifest" 2>&1 >/dev/null) || true
|
|
82
|
+
if [ -n "$verb_stderr" ]; then
|
|
83
|
+
_iwp_fragment_log "bulk-rebuild stderr=$verb_stderr"
|
|
102
84
|
fi
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
85
|
+
_iwp_fragment_log "bulk-rebuild dispatched lite=$lite_manifest"
|
|
86
|
+
else
|
|
87
|
+
_iwp_fragment_log "skip reason=lib-missing-bulk-rebuild-verb"
|
|
88
|
+
fi
|
|
106
89
|
) || true
|
|
107
90
|
|
|
108
91
|
return 0
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# tier: lite
|
|
3
|
+
# install-class: dual
|
|
4
|
+
# Turn-prose density measure — Stop hook that reads the last assistant message
|
|
5
|
+
# from the transcript, counts non-standard hyphenated compounds, and writes
|
|
6
|
+
# the number to a marker file at state/markers/turn-prose-density/last-turn.json
|
|
7
|
+
# via lib/prose-marker.sh per standards/prose-marker-format.md.
|
|
8
|
+
#
|
|
9
|
+
# Density = count of [a-z]{2,}-[a-z]{2,}(?:-[a-z]{2,})? matches MINUS the
|
|
10
|
+
# 25-entry standard-tech allowlist. Proxy for jargon load distinct from
|
|
11
|
+
# grade-measure's existing hyphen_words count (which includes standard tech).
|
|
12
|
+
#
|
|
13
|
+
# The pre-turn plain-english-steering.sh reads the marker on next turn
|
|
14
|
+
# (extended at Step 2d) and injects the density number into the steering
|
|
15
|
+
# block alongside grade + appositive misses.
|
|
16
|
+
#
|
|
17
|
+
# Sibling scorers:
|
|
18
|
+
# - turn-prose-grade-measure.sh — grade metric
|
|
19
|
+
# - turn-prose-kiss-check.sh — appositive miss count (Step 2c extension)
|
|
20
|
+
#
|
|
21
|
+
# Input (stdin): JSON with transcript_path + session_id
|
|
22
|
+
# Output: writes state/markers/turn-prose-density/last-turn.json
|
|
23
|
+
# Exit: 0 always (measurement only, no gate)
|
|
24
|
+
#
|
|
25
|
+
# Override: SKIP_TURN_PROSE_DENSITY=1
|
|
26
|
+
|
|
27
|
+
set +e
|
|
28
|
+
|
|
29
|
+
# === Override path ===
|
|
30
|
+
if [ "${SKIP_TURN_PROSE_DENSITY:-0}" = "1" ]; then
|
|
31
|
+
exit 0
|
|
32
|
+
fi
|
|
33
|
+
|
|
34
|
+
# === Parse stdin JSON ===
|
|
35
|
+
STDIN_JSON=$(cat 2>/dev/null || echo '{}')
|
|
36
|
+
if ! command -v jq >/dev/null 2>&1; then
|
|
37
|
+
exit 0
|
|
38
|
+
fi
|
|
39
|
+
TRANSCRIPT_PATH=$(echo "$STDIN_JSON" | jq -r '.transcript_path // ""' 2>/dev/null)
|
|
40
|
+
|
|
41
|
+
[ -z "$TRANSCRIPT_PATH" ] && exit 0
|
|
42
|
+
[ ! -f "$TRANSCRIPT_PATH" ] && exit 0
|
|
43
|
+
|
|
44
|
+
# === Resolve lib path (install-class: dual) ===
|
|
45
|
+
# Project install — lib relative to this hook. Operator install — lib under $HOME.
|
|
46
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
47
|
+
LIB_PATH=""
|
|
48
|
+
for candidate in \
|
|
49
|
+
"${SCRIPT_DIR}/../../lib/prose-marker.sh" \
|
|
50
|
+
"${HOME:-/}/lib/prose-marker.sh" \
|
|
51
|
+
"${CLAUDE_PROJECT_DIR:-}/lib/prose-marker.sh"; do
|
|
52
|
+
if [ -f "$candidate" ]; then
|
|
53
|
+
LIB_PATH="$candidate"
|
|
54
|
+
break
|
|
55
|
+
fi
|
|
56
|
+
done
|
|
57
|
+
|
|
58
|
+
[ -z "$LIB_PATH" ] && exit 0
|
|
59
|
+
# shellcheck disable=SC1090
|
|
60
|
+
source "$LIB_PATH" 2>/dev/null || exit 0
|
|
61
|
+
|
|
62
|
+
# === Extract last assistant text ===
|
|
63
|
+
LAST_ASSISTANT_LINE=$(grep '"type":"assistant"' "$TRANSCRIPT_PATH" 2>/dev/null | tail -1)
|
|
64
|
+
[ -z "$LAST_ASSISTANT_LINE" ] && exit 0
|
|
65
|
+
|
|
66
|
+
MESSAGE_TEXT=$(echo "$LAST_ASSISTANT_LINE" \
|
|
67
|
+
| jq -r '.message.content[]? | select(.type == "text") | .text' 2>/dev/null)
|
|
68
|
+
[ -z "$MESSAGE_TEXT" ] && exit 0
|
|
69
|
+
|
|
70
|
+
# === Strip code blocks + inline backticks + URLs (prose only) ===
|
|
71
|
+
SCRUBBED=$(echo "$MESSAGE_TEXT" | awk '
|
|
72
|
+
BEGIN { in_code = 0 }
|
|
73
|
+
/^```/ { in_code = !in_code; next }
|
|
74
|
+
in_code { next }
|
|
75
|
+
{ print }
|
|
76
|
+
' | sed -E 's/`[^`]*`//g; s|https?://[^ )]+||g')
|
|
77
|
+
|
|
78
|
+
# Short message after strip → density=0
|
|
79
|
+
STRIPPED_LEN=${#SCRUBBED}
|
|
80
|
+
if [ "$STRIPPED_LEN" -lt 50 ]; then
|
|
81
|
+
write_prose_marker "turn-prose-density" "0" "short message under 50 chars"
|
|
82
|
+
exit 0
|
|
83
|
+
fi
|
|
84
|
+
|
|
85
|
+
# === Compute density via python ===
|
|
86
|
+
# Standard-tech allowlist excluded. Pattern per standards/prose-marker-format.md.
|
|
87
|
+
DENSITY=$(printf '%s' "$SCRUBBED" | python3 -c '
|
|
88
|
+
import sys, re
|
|
89
|
+
|
|
90
|
+
STANDARD_TECH = {
|
|
91
|
+
"pre-commit", "real-time", "data-driven", "end-to-end", "fire-and-forget",
|
|
92
|
+
"run-time", "compile-time", "read-only", "write-only", "fail-soft",
|
|
93
|
+
"fail-fast", "post-commit", "pre-push", "post-merge", "op-ed",
|
|
94
|
+
"self-test", "self-check", "re-read", "re-run", "re-fire",
|
|
95
|
+
"op-cost", "copy-paste", "cut-off", "set-up", "built-in",
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
text = sys.stdin.read().lower()
|
|
99
|
+
pattern = re.compile(r"\b[a-z]{2,}-[a-z]{2,}(?:-[a-z]{2,})?\b")
|
|
100
|
+
matches = pattern.findall(text)
|
|
101
|
+
density = sum(1 for m in matches if m not in STANDARD_TECH)
|
|
102
|
+
print(density)
|
|
103
|
+
' 2>/dev/null)
|
|
104
|
+
|
|
105
|
+
[ -z "$DENSITY" ] && DENSITY=0
|
|
106
|
+
|
|
107
|
+
# === Write density marker via lib ===
|
|
108
|
+
write_prose_marker "turn-prose-density" "$DENSITY" 2>/dev/null
|
|
109
|
+
exit 0
|
|
@@ -83,6 +83,20 @@ for _candidate in \
|
|
|
83
83
|
done
|
|
84
84
|
unset _candidate
|
|
85
85
|
|
|
86
|
+
# === Source lib/prose-marker.sh (deep module per Longrun 2 Step 2b #2197) ===
|
|
87
|
+
# Provides write_prose_marker for appositive-miss count persistence.
|
|
88
|
+
# Silent-fail — no lib = no marker write; existing stderr display path unchanged.
|
|
89
|
+
for _candidate in \
|
|
90
|
+
"${SCRIPT_DIR}/../../lib/prose-marker.sh" \
|
|
91
|
+
"${HOME_OVERRIDE:-${HOME:-/}}/lib/prose-marker.sh"; do
|
|
92
|
+
if [ -f "$_candidate" ]; then
|
|
93
|
+
# shellcheck disable=SC1090
|
|
94
|
+
source "$_candidate"
|
|
95
|
+
break
|
|
96
|
+
fi
|
|
97
|
+
done
|
|
98
|
+
unset _candidate
|
|
99
|
+
|
|
86
100
|
# === Parse stdin JSON ===
|
|
87
101
|
STDIN_JSON=$(cat 2>/dev/null || echo '{}')
|
|
88
102
|
TRANSCRIPT_PATH=$(echo "$STDIN_JSON" | jq -r '.transcript_path // ""' 2>/dev/null)
|
|
@@ -417,8 +431,15 @@ for pattern, label in patterns:
|
|
|
417
431
|
|
|
418
432
|
for f in flagged[:5]: # cap at 5 findings per turn to avoid noise
|
|
419
433
|
print(f)
|
|
434
|
+
# Emit total count on final line as sentinel — bash strips before display.
|
|
435
|
+
# Per Longrun 2 Step 2c (#2197) — scorer-stack appositive marker.
|
|
436
|
+
print(f"__APPOSITIVE_COUNT__={len(flagged)}")
|
|
420
437
|
' 2>/dev/null)
|
|
421
438
|
|
|
439
|
+
# Extract the count sentinel; strip it from the display output.
|
|
440
|
+
APPOSITIVE_COUNT=$(echo "$APPOSITIVE_FINDINGS" | grep '^__APPOSITIVE_COUNT__=' | tail -1 | sed 's/^__APPOSITIVE_COUNT__=//')
|
|
441
|
+
APPOSITIVE_FINDINGS=$(echo "$APPOSITIVE_FINDINGS" | grep -v '^__APPOSITIVE_COUNT__=')
|
|
442
|
+
|
|
422
443
|
if [ -n "$APPOSITIVE_FINDINGS" ]; then
|
|
423
444
|
cat >&2 <<EOF
|
|
424
445
|
[turn-prose-kiss-check: ADVISORY — appositive discipline (bassclef-upstream#1313)]
|
|
@@ -434,6 +455,13 @@ Override one-time: SKIP_APPOSITIVE_CHECK=1.
|
|
|
434
455
|
V1 advisory; V2 (deferred) BLOCKs after calibration.
|
|
435
456
|
EOF
|
|
436
457
|
fi
|
|
458
|
+
|
|
459
|
+
# === Write appositive-miss marker via lib/prose-marker.sh (Longrun 2 Step 2c) ===
|
|
460
|
+
# Count is total matches (not capped at 5 — that cap is display-only).
|
|
461
|
+
# Silent-fail — missing lib or write error does not affect existing path.
|
|
462
|
+
if declare -f write_prose_marker >/dev/null 2>&1 && [ -n "$APPOSITIVE_COUNT" ]; then
|
|
463
|
+
write_prose_marker "turn-prose-appositive" "$APPOSITIVE_COUNT" 2>/dev/null
|
|
464
|
+
fi
|
|
437
465
|
fi
|
|
438
466
|
|
|
439
467
|
# === Emit findings + exit per mode ===
|
|
@@ -13,6 +13,8 @@ Branch naming:
|
|
|
13
13
|
|
|
14
14
|
Every branch ties to a GitHub Issue. No branch without an issue.
|
|
15
15
|
|
|
16
|
+
**Exception — `/launch --local`.** Adopter prototype branches created by `/launch --local` are exempt from the issue-per-branch rule. Local prototype mode exists for adopters who do not yet have a GitHub repo or who iterate ahead of filing issues. Per bassclef-upstream#2175 Batch A (F2) + bassclef-cli#328 Finding 2.
|
|
17
|
+
|
|
16
18
|
Flow: issue → branch from main → work → PR → CI passes → squash merge → delete branch
|
|
17
19
|
|
|
18
20
|
Rules:
|
|
@@ -30,8 +30,9 @@ No commit lands with these patterns in message OR staged content:
|
|
|
30
30
|
|
|
31
31
|
- **Hostname shapes**: `[A-Za-z][A-Za-z0-9]*-MacBook-(Air|Pro)`, `DESKTOP-[A-Z0-9]{4,}`, `*-server-*`, `*-desktop-*`
|
|
32
32
|
- **Absolute paths**: `/Users/<name>`, `/home/<name>`
|
|
33
|
+
- **GitHub no-reply derived slug**: `operator-[0-9]+\+[a-zA-Z0-9][a-zA-Z0-9-]*` — the shape `/personas` historically derived from a GitHub no-reply email local part (`<id>+<handle>`), leaking account ID plus handle into committed persona + use-case + jobs docs. Per bassclef-upstream#2146. Scope-safe — the pattern targets the derived slug only; the raw email format `<id>+<handle>@users.noreply.github.com` stays allowed as a git author (Check 3 pins that in Test 22) because GitHub recommends no-reply as the privacy format for commit identity.
|
|
33
34
|
|
|
34
|
-
Extension mechanism — future pattern classes added to
|
|
35
|
+
Extension mechanism — future pattern classes added to `standards/identifier-leak-patterns.txt` + a corresponding Tier 0 test. The hook reads the pattern file at run time; adding a line there extends the scrub without a hook edit.
|
|
35
36
|
|
|
36
37
|
## Anti-patterns
|
|
37
38
|
|
|
@@ -36,6 +36,29 @@ When a PR changes the manifest schema OR output shape:
|
|
|
36
36
|
3. **Downstream cure named** — the entry names what adopters need to do. If nothing (backward-compatible add), say so explicitly. If code change (like the v0.36.0 `upstream_commit` removal), name the pattern.
|
|
37
37
|
4. **Release script pre-flight** — before every `/release`, the script reads the ledger and confirms an entry exists for the outgoing `manifest_version`. Missing entry BLOCKs the cascade.
|
|
38
38
|
|
|
39
|
+
## Ledger-edit-then-regen order (bassclef-upstream#2110)
|
|
40
|
+
|
|
41
|
+
The ledger file at `standards/lite-manifest-schema-changes.md` is ITSELF a `tier: lite` artifact. Its content_hash lives inside the manifest. So the regen must capture the FINAL ledger state. Order matters:
|
|
42
|
+
|
|
43
|
+
1. **Edit ledger first.** Write the entry explaining the schema change.
|
|
44
|
+
2. **Regen second.** Run `scripts/generate-lite-manifest.sh`.
|
|
45
|
+
|
|
46
|
+
Natural authoring instinct is "regen first (see the hashes), then explain." That order ships a stale ledger hash in the manifest. CI drift check fails. A second regen commit is then needed.
|
|
47
|
+
|
|
48
|
+
**Preferred cure — use the wrapper.** `scripts/update-lite-manifest.sh` enforces the order:
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
# Append a pre-written entry
|
|
52
|
+
scripts/update-lite-manifest.sh --entry-file path/to/entry.md
|
|
53
|
+
|
|
54
|
+
# Open $EDITOR on the ledger
|
|
55
|
+
scripts/update-lite-manifest.sh --edit
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Both modes hash the ledger before and after. The wrapper refuses to regen when the ledger is unchanged. Then it dispatches `scripts/generate-lite-manifest.sh` + the renderer when present.
|
|
59
|
+
|
|
60
|
+
Per `.claude/rules/bootstrap-pair-discipline.md` — the wrapper ships with Tier 0 tests at `scripts/tests/update-lite-manifest.test.sh`.
|
|
61
|
+
|
|
39
62
|
## Retirements
|
|
40
63
|
|
|
41
64
|
Retirement-class changes (content-remove: a file previously shipped in the lite manifest gets deleted intentionally) ship with the same discipline shape as additions. Three paired artifacts, same PR:
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
---
|
|
2
|
+
tier: lite
|
|
3
|
+
description: Any session that ships source code (files under src/, apps/, packages/, lib/, app/) MUST invoke Reviewer responsibilities before the session ends.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Reviewer Dispatch
|
|
7
|
+
|
|
8
|
+
Any session that ships source code (files under `src/`, `apps/`, `packages/`, `lib/`, `app/`) MUST invoke Reviewer responsibilities before the session ends. Mode-agnostic — orchestrator-gated mode dispatches the Reviewer agent; sequential mode self-invokes Reviewer responsibilities (read against spec, check tests, validate ADR compliance, flag TDD test-after patterns per Kent Beck).
|
|
9
|
+
|
|
10
|
+
The discipline lives at the rule layer (this file) plus the mechanical layer (`.claude/hooks/reviewer-dispatch-check.sh` — Stop hook). Both required for adopter code quality.
|
|
11
|
+
|
|
12
|
+
## When this rule fires
|
|
13
|
+
|
|
14
|
+
At session end (Stop event), the hook:
|
|
15
|
+
|
|
16
|
+
1. Counts source files changed since session-start (heuristic: `git diff` against `origin/main`; uncommitted + committed changes both count)
|
|
17
|
+
2. Path matchers for "source": `src/`, `apps/`, `packages/`, `lib/`, `app/`
|
|
18
|
+
3. When source count > 0, the hook checks for any marker at `state/markers/reviewer/*.md`
|
|
19
|
+
4. When no marker exists, the hook BLOCKs with reviewer-checklist remediation
|
|
20
|
+
|
|
21
|
+
## Reviewer marker contract
|
|
22
|
+
|
|
23
|
+
The marker captures the Reviewer pass per the agent definition (`~/.claude/agents/reviewer.md`):
|
|
24
|
+
|
|
25
|
+
```markdown
|
|
26
|
+
# Reviewer pass — <session-id>
|
|
27
|
+
|
|
28
|
+
mode: sequential | orchestrator-gated
|
|
29
|
+
reviewed_at: <ISO-8601>
|
|
30
|
+
source_files_changed: <count>
|
|
31
|
+
wu_id: <e.g., bet-2026-06-25k WU-3> # bet 25k WU-5 extension
|
|
32
|
+
iteration_count: <N> # bet 25k WU-5 extension
|
|
33
|
+
lens_used: [<luminary-slug>, ...] # bet 25k WU-5 extension
|
|
34
|
+
|
|
35
|
+
## Checks
|
|
36
|
+
- spec acceptance criteria: PASS | FAIL | N/A
|
|
37
|
+
- tests present + green: PASS | FAIL
|
|
38
|
+
- test-after pattern check: NONE FOUND | FOUND (list)
|
|
39
|
+
- ADR compliance: PASS | N/A
|
|
40
|
+
- Designer sign-off: PRESENT | N/A
|
|
41
|
+
- architect-review: INVOKED | N/A
|
|
42
|
+
|
|
43
|
+
## Findings
|
|
44
|
+
- <severity>: <description> # BLOCK / WARN / NOTE
|
|
45
|
+
|
|
46
|
+
acknowledged: true
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### Field extensions per bassclef-upstream#366 (bet 25k WU-5)
|
|
50
|
+
|
|
51
|
+
Three fields extend the prior contract to make the /loop discipline auditable per step:
|
|
52
|
+
|
|
53
|
+
- **`wu_id`** — goal identifier + WU number (e.g., `bet-2026-06-25k WU-3`). Lets future audits grep markers by WU. Required when the marker is written under /longrun dispatch; optional for ad-hoc reviews.
|
|
54
|
+
- **`iteration_count`** — number of /loop iterations the step ran before landing GREEN. Iteration 1 GREEN is the cleanest path; counts above 1 record rework cycles (RED → fix → GREEN). Required under /longrun dispatch.
|
|
55
|
+
- **`lens_used`** — YAML list of luminary slugs that drove the review. Mirrors the goal doc's `authoring_luminaries` for the relevant WU. Required under /longrun dispatch.
|
|
56
|
+
|
|
57
|
+
The three fields together let future operators answer questions like "which WUs in bet 25k ran more than one /loop iteration" or "which luminary lens caught the most rework" by greppable markers, not by chronicle archaeology. The mechanical layer (greppable fields) compounds across every goal; the bar (`## /temperance + /luminary + /loop discipline` section in the PR body) gives the immediate operator-visible read.
|
|
58
|
+
|
|
59
|
+
Forward-only enforcement. Markers written before WU-5 stay as-is; markers written from WU-5 onwards carry the three new fields under /longrun dispatch.
|
|
60
|
+
|
|
61
|
+
## Mode behavior
|
|
62
|
+
|
|
63
|
+
**Orchestrator-gated mode** — orchestrator dispatches Reviewer agent at code-shipping handoffs. Reviewer agent reads against spec, runs checks, writes the marker per the contract above, returns PASS/FAIL/WARN findings to orchestrator.
|
|
64
|
+
|
|
65
|
+
**Sequential mode** — agent self-invokes Reviewer responsibilities. Read the Reviewer agent definition. Apply checks against the session's source diff. Write the marker. The self-invocation is honor-system at the methodology layer; the marker is the mechanical evidence the hook reads.
|
|
66
|
+
|
|
67
|
+
## Override path
|
|
68
|
+
|
|
69
|
+
`SKIP_REVIEWER_CHECK=1 <command>` — logged via trace-helper. Use rarely:
|
|
70
|
+
|
|
71
|
+
- Documentation-only session that accidentally touched a comment in src/ (operator confirms the source touch is non-behavioral)
|
|
72
|
+
- Emergency rescue when the hook itself misclassifies (file substrate-defect)
|
|
73
|
+
- One-shot migration script that ships hundreds of source files (operator has reviewed externally)
|
|
74
|
+
- Bulk dependency-bump where the package-manager touched lockfile entries only
|
|
75
|
+
|
|
76
|
+
INSTEAD of overriding for routine work: write the marker. The Reviewer-self-invocation cost is small (~5 minutes); the audit trail compounds across every adopter session.
|
|
77
|
+
|
|
78
|
+
## What the rule does NOT cover
|
|
79
|
+
|
|
80
|
+
- Code review process content (covered in `~/.claude/agents/reviewer.md`)
|
|
81
|
+
- TDD test-list discipline (covered in `.claude/rules/test-list-discipline.md`)
|
|
82
|
+
- Sufficiency rubric for tests (covered in `.claude/rules/test-sufficiency.md`)
|
|
83
|
+
- ADR discipline (covered in `.claude/rules/adr-discipline.md`)
|
|
84
|
+
- Architect review timing (covered in `.claude/skills/architect-review/SKILL.md`)
|
|
85
|
+
|
|
86
|
+
This rule is the gate. Sister rules + skills carry the content.
|
|
87
|
+
|
|
88
|
+
## Composition with other rules
|
|
89
|
+
|
|
90
|
+
- `.claude/rules/testing-tier-config.md` — Tier 0 strict-TDD on hook + tests (this hook + its test file land Tier 0)
|
|
91
|
+
- `.claude/rules/blocked-items.md` — BLOCK protocol the hook fires
|
|
92
|
+
- `.claude/rules/test-sufficiency.md` — what Reviewer checks for under "tests present + green"
|
|
93
|
+
- `.claude/rules/test-list-discipline.md` — what Reviewer checks for under "test-after pattern check" (Beck's test-list-as-planning-artifact)
|
|
94
|
+
- `.claude/rules/adr-discipline.md` — what Reviewer checks for under "ADR compliance"
|
|
95
|
+
- `~/.claude/agents/reviewer.md` — the full Reviewer responsibilities definition
|
|
@@ -52,6 +52,11 @@
|
|
|
52
52
|
"type": "command",
|
|
53
53
|
"command": "$CLAUDE_PROJECT_DIR/.claude/hooks/git-stash-u-guard.sh",
|
|
54
54
|
"timeout": 5
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
"type": "command",
|
|
58
|
+
"command": "$CLAUDE_PROJECT_DIR/.claude/hooks/pre-push-shellcheck.sh",
|
|
59
|
+
"timeout": 60
|
|
55
60
|
}
|
|
56
61
|
]
|
|
57
62
|
},
|
|
@@ -167,6 +172,11 @@
|
|
|
167
172
|
"command": "$CLAUDE_PROJECT_DIR/.claude/hooks/turn-prose-grade-measure.sh",
|
|
168
173
|
"timeout": 10
|
|
169
174
|
},
|
|
175
|
+
{
|
|
176
|
+
"type": "command",
|
|
177
|
+
"command": "$CLAUDE_PROJECT_DIR/.claude/hooks/turn-prose-density-measure.sh",
|
|
178
|
+
"timeout": 10
|
|
179
|
+
},
|
|
170
180
|
{
|
|
171
181
|
"type": "command",
|
|
172
182
|
"command": "$CLAUDE_PROJECT_DIR/.claude/hooks/longrun-prep-compounding-sequence-check.sh",
|
|
@@ -214,13 +214,28 @@ if echo "$@" | grep -q -- '--explain'; then
|
|
|
214
214
|
EXPLAIN_MODE=true
|
|
215
215
|
fi
|
|
216
216
|
|
|
217
|
-
# Resolve goal via accessor
|
|
217
|
+
# Resolve goal via accessor. #2132 — capture stderr so parse errors surface
|
|
218
|
+
# instead of masquerading as "not found". Map accessor return codes:
|
|
219
|
+
# 1 → goal file not found; 3 → goal file could not be parsed.
|
|
218
220
|
source lib/state.sh
|
|
219
221
|
BET_SLUG="$1"
|
|
220
|
-
BET_JSON=$(state_iteration_bet_get "$BET_SLUG" 2
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
222
|
+
BET_JSON=$(state_iteration_bet_get "$BET_SLUG" 2>&1)
|
|
223
|
+
BET_RC=$?
|
|
224
|
+
if [ "$BET_RC" -ne 0 ]; then
|
|
225
|
+
case "$BET_RC" in
|
|
226
|
+
1)
|
|
227
|
+
echo "/build: goal file not found: $BET_SLUG" >&2
|
|
228
|
+
;;
|
|
229
|
+
3)
|
|
230
|
+
echo "/build: goal file could not be parsed: $BET_SLUG" >&2
|
|
231
|
+
;;
|
|
232
|
+
*)
|
|
233
|
+
echo "/build: goal '$BET_SLUG' lookup failed (rc=$BET_RC)" >&2
|
|
234
|
+
;;
|
|
235
|
+
esac
|
|
236
|
+
echo "$BET_JSON" >&2
|
|
237
|
+
exit "$BET_RC"
|
|
238
|
+
fi
|
|
224
239
|
|
|
225
240
|
# Check status active
|
|
226
241
|
STATUS=$(echo "$BET_JSON" | jq -r '.status // empty')
|
|
@@ -234,6 +249,15 @@ SPEC_PATH=$(echo "$BET_JSON" | jq -r '.references[]? | select(.type == "spec") |
|
|
|
234
249
|
DECOMP_PATH=$(echo "$BET_JSON" | jq -r '.references[]? | select(.type == "decomposition") | .id // empty')
|
|
235
250
|
UX_MIG_PATH=$(echo "$BET_JSON" | jq -r '.references[]? | select(.type == "ux-migration") | .id // empty')
|
|
236
251
|
|
|
252
|
+
# #2132 — goal MUST declare a spec reference. Empty SPEC_PATH means Phase 1
|
|
253
|
+
# would awk against docs/specs/.md and silently find nothing; cure refuses
|
|
254
|
+
# at Phase 0 instead with a clear error.
|
|
255
|
+
if [ -z "$SPEC_PATH" ]; then
|
|
256
|
+
echo "/build: goal '$BET_SLUG' has no spec reference — refusing" >&2
|
|
257
|
+
echo "Add a {type: spec, id: <spec-slug>} entry to goal frontmatter references[]." >&2
|
|
258
|
+
exit 4
|
|
259
|
+
fi
|
|
260
|
+
|
|
237
261
|
# Validate references exist
|
|
238
262
|
for ref_type in spec decomposition; do
|
|
239
263
|
ref_id=$(echo "$BET_JSON" | jq -r --arg t "$ref_type" '.references[]? | select(.type == $t) | .id // empty')
|
|
@@ -349,6 +373,16 @@ WU_LIST=$(awk '
|
|
|
349
373
|
|
|
350
374
|
# Each step should reference at least one story (US-NNN)
|
|
351
375
|
WU_COUNT=$(echo "$WU_LIST" | grep -cE '^### (Step|WU)-' || echo 0)
|
|
376
|
+
|
|
377
|
+
# #2132 — refuse zero-step specs. The Phase 2 orphan check does not fire
|
|
378
|
+
# on empty WU_LIST (no steps → no orphans → silent pass). Phase 1 refuses
|
|
379
|
+
# so adopters see the real problem.
|
|
380
|
+
if [ "$WU_COUNT" = "0" ]; then
|
|
381
|
+
echo "/build: spec has zero steps — refusing" >&2
|
|
382
|
+
echo "Spec file: $SPEC_FILE" >&2
|
|
383
|
+
echo "Need a Steps enumerated section with Step-N or WU-N entries." >&2
|
|
384
|
+
exit 4
|
|
385
|
+
fi
|
|
352
386
|
```
|
|
353
387
|
|
|
354
388
|
Build structured object: `{ bet, spec_path, decomp_path, ux_migration_path, wus[] }`.
|
|
@@ -483,6 +517,23 @@ Operator can force adopter-mode via `--adopter-mode` flag (V2). Operator can for
|
|
|
483
517
|
|
|
484
518
|
### Phase 3 — DryRunPlanner
|
|
485
519
|
|
|
520
|
+
**Read the pre-mortem marker first (per bassclef-upstream#2175 Batch B — F5).** Before enumerating steps, read `state/markers/pre-mortem/<branch-slug>.marker` if present. The marker names the risk ledger file plus top-5 risks. Surface the ledger path + top-5 risks in the dry-run output. Builder dispatches in future LIVE mode read the ledger before writing code.
|
|
521
|
+
|
|
522
|
+
<!-- FKGL-EXEMPT: bash command block; shell commands are not graded prose. -->
|
|
523
|
+
|
|
524
|
+
```bash
|
|
525
|
+
BRANCH_SLUG=$(git branch --show-current | tr '/' '-')
|
|
526
|
+
MARKER="state/markers/pre-mortem/${BRANCH_SLUG}.marker"
|
|
527
|
+
if [ -f "$MARKER" ]; then
|
|
528
|
+
echo "Pre-mortem ledger — reading top-5 risks:"
|
|
529
|
+
cat "$MARKER"
|
|
530
|
+
fi
|
|
531
|
+
```
|
|
532
|
+
|
|
533
|
+
<!-- /FKGL-EXEMPT -->
|
|
534
|
+
|
|
535
|
+
When the marker is absent, `/build` emits a one-line note that no pre-mortem fired for this branch and proceeds. `/pre-mortem` is mandatory on always-fire domains per its SKILL body; Phase 3 does not re-enforce that gate.
|
|
536
|
+
|
|
486
537
|
Walk step enumeration; for each, plan what would happen in live mode:
|
|
487
538
|
|
|
488
539
|
```
|
|
@@ -534,6 +585,8 @@ TODO: live execution — operator-supervised first run only.
|
|
|
534
585
|
- Operator-tap moments: 1 (prod deploy approval via GitHub Environment)
|
|
535
586
|
```
|
|
536
587
|
|
|
588
|
+
<!-- FKGL-EXEMPT: Phase 4 stub — technical dispatch TODO; OOAD artifact names + branch slug template resist short SVO rewrites. Per bassclef-upstream#2175 Batch A — slug canon matches Phase 3. -->
|
|
589
|
+
|
|
537
590
|
### Phase 4 — BuilderDispatcher (LIVE — STUBBED)
|
|
538
591
|
|
|
539
592
|
**Amendment per #1171 — Builder inputs name OOAD artifacts.**
|
|
@@ -545,7 +598,7 @@ TODO: live execution — operator-supervised first run only. Would:
|
|
|
545
598
|
dispatch Builder against step <slug> with context from spec section
|
|
546
599
|
<X> + decomp matrix entry <Y> + paired OOAD artifacts (decomposition
|
|
547
600
|
+ interaction-design + use-case per goal doc references). Branch:
|
|
548
|
-
feature/<goal-slug>-stack-<N>.
|
|
601
|
+
feature/<goal-slug>-stack-<N>-<wu-slug>.
|
|
549
602
|
```
|
|
550
603
|
|
|
551
604
|
The string `TODO: live execution` is grep-able for audit. Live mode
|
|
@@ -556,6 +609,10 @@ in the goal doc `references` array — decomposition (grep for
|
|
|
556
609
|
`type: interaction-design`), use-case (grep for `type: use-case`).
|
|
557
610
|
Per #1171 — every OOAD artifact consulted for any engineering change.
|
|
558
611
|
|
|
612
|
+
<!-- /FKGL-EXEMPT -->
|
|
613
|
+
|
|
614
|
+
<!-- FKGL-EXEMPT: Phase 5 stub + Phase 5a Reviewer dispatch — technical TODO blocks for live-mode scaffolding. F10 cure per bassclef-upstream#2175 Batch D. -->
|
|
615
|
+
|
|
559
616
|
### Phase 5 — VerifyOrchestrator (LIVE — STUBBED)
|
|
560
617
|
|
|
561
618
|
Tonight emits literal:
|
|
@@ -566,6 +623,24 @@ Match change type (UI smoke / unit / integration) and BLOCK on
|
|
|
566
623
|
failure with line-numbered output.
|
|
567
624
|
```
|
|
568
625
|
|
|
626
|
+
### Phase 5a — Reviewer dispatch (per bassclef-upstream#2175 Batch D — F10)
|
|
627
|
+
|
|
628
|
+
After `/verify` passes, Phase 5 adds a Reviewer dispatch step. Running `/verify` alone catches test failures. The Reviewer agent (`.claude/agents/reviewer.md`) catches spec-acceptance drift, missing-test patterns, ADR deviations, and friction the Builder did not surface. One review step per PR before merge.
|
|
629
|
+
|
|
630
|
+
Tonight emits literal:
|
|
631
|
+
|
|
632
|
+
```
|
|
633
|
+
TODO: live execution — would dispatch Reviewer agent against branch <name>.
|
|
634
|
+
Reviewer reads: spec acceptance criteria, decomposition matrix entry, PR
|
|
635
|
+
body friction report. Reviewer writes: state/markers/reviewer/<branch>.md
|
|
636
|
+
with PASS / FAIL / NOTE findings. BLOCK merge on FAIL; NOTE findings file
|
|
637
|
+
as follow-on tickets.
|
|
638
|
+
```
|
|
639
|
+
|
|
640
|
+
The Reviewer dispatch is MANDATORY per `.claude/rules/reviewer-dispatch.md` for any session shipping source code. Phase 5a is the Phase-level wiring. Live mode consumes the Reviewer marker before Phase 6 opens the PR.
|
|
641
|
+
|
|
642
|
+
<!-- /FKGL-EXEMPT -->
|
|
643
|
+
|
|
569
644
|
### Phase 5.5 — Amendment B2: /pattern-review trigger reads decomposition (per #1150 + #1167)
|
|
570
645
|
|
|
571
646
|
When the goal references a decomposition doc AND that doc contains `@pattern` references, dispatch `/pattern-review` with the decomposition path as input. Skip when the decomposition names no `@pattern`. Contract per `.claude/rules/pattern-annotation.md` L57-62.
|
|
@@ -151,6 +151,32 @@ Individual items: ...
|
|
|
151
151
|
- #127 — title — no path to advance the goal — CLOSE
|
|
152
152
|
```
|
|
153
153
|
|
|
154
|
+
### Step 4b — Emit the hard stack rank
|
|
155
|
+
|
|
156
|
+
After the per-quadrant tables, emit one final table. Columns fixed:
|
|
157
|
+
|
|
158
|
+
1. **Rank** — 1 to N, cross-repo, top-down
|
|
159
|
+
2. **Ticket** — `<repo>#<number>` form
|
|
160
|
+
3. **Repo** — the owning repo. Bold the repo name when work ships somewhere other than the filed repo.
|
|
161
|
+
4. **Why this rank** — one plain-English sentence. Grade-8 target. Cite the Depen. tie or class fit.
|
|
162
|
+
|
|
163
|
+
Row order:
|
|
164
|
+
|
|
165
|
+
- Q1 first, then Q2, then Q3, then Q4
|
|
166
|
+
- Within quadrant, sort by Depen. first (independents at top), then class, then ticket number
|
|
167
|
+
- Flag cross-repo work in the Repo column
|
|
168
|
+
|
|
169
|
+
Example row shape:
|
|
170
|
+
|
|
171
|
+
```
|
|
172
|
+
| Rank | Ticket | Repo | Why this rank |
|
|
173
|
+
|------|--------|-------------------|--------------------------------------|
|
|
174
|
+
| 1 | #2008 | **bassclef-web** | Q1 lead. Live Sam bounce. |
|
|
175
|
+
| 2 | #2011 | **bassclef-web** | Q1 rider. Same PR as #2008. |
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
Per bassclef-upstream#2096 amendment 3.
|
|
179
|
+
|
|
154
180
|
### Step 5 — Optional follow-on
|
|
155
181
|
|
|
156
182
|
If Q3 surfaces 5+ items in the same shape, the skill offers to draft one `/promote` ticket. That ticket proposes automating the class. You accept, edit, or skip.
|
|
@@ -264,6 +264,8 @@ fi
|
|
|
264
264
|
|
|
265
265
|
**Local mode (`--local`) — bassclef#923 A2:**
|
|
266
266
|
|
|
267
|
+
**Issue-per-branch exemption (per bassclef-upstream#2175 Batch A — F2).** `/launch --local` creates adopter prototype branches that are exempt from the `.claude/rules/branching.md` "every branch ties to a GitHub Issue" rule. Local prototype mode exists for adopters who do not yet have a GitHub repo. No issue is required for `--local` branches. All other `/launch` modes (full, scratch, operator-mode) still require an issue.
|
|
268
|
+
|
|
267
269
|
`/launch --local` prints a banner first. The banner shows which picker is active:
|
|
268
270
|
|
|
269
271
|
```
|
|
@@ -546,6 +548,43 @@ The gallery is THE cameo-review surface. Luminary perspectives
|
|
|
546
548
|
agent runtime per roadmap Stage 3) consume the gallery + spec + decomp
|
|
547
549
|
to produce reviews per perspective. Reviews surface in PR body.
|
|
548
550
|
|
|
551
|
+
### Phase 10a — Stack-choice ask (per bassclef-upstream#2175 Batch D — F13)
|
|
552
|
+
|
|
553
|
+
<!-- FKGL-EXEMPT: Phase 10a — technical ask step with config-key contract. Short SVO loses the stack-key list. Fires before Phase 11 /ux-migration so the migration plan picks the right adapter. -->
|
|
554
|
+
|
|
555
|
+
Before Phase 11 runs, ask the operator which frontend stack the migration targets. Today `/launch` silently assumes Next.js — nine SKILL bodies lean on Next.js examples. Non-React adopters hit the silent pick at Phase 11 and must rework. The ask fires once per `/launch` dispatch; the answer writes to `substrate.config.md` key `frontend_stack` for Phase 11 to read.
|
|
556
|
+
|
|
557
|
+
Read `substrate.config.md` for `frontend_stack`. If set, skip the ask. If unset, ask:
|
|
558
|
+
|
|
559
|
+
```
|
|
560
|
+
Which stack is this app?
|
|
561
|
+
|
|
562
|
+
a. Next.js — React + App Router (bassclef default; nine SKILL bodies lean on Next.js examples)
|
|
563
|
+
b. Vite + React — React + Vite dev server
|
|
564
|
+
c. Remix — React + Remix
|
|
565
|
+
d. SvelteKit — Svelte + Kit
|
|
566
|
+
e. SolidStart — Solid + Start
|
|
567
|
+
f. none — static HTML + CSS, no framework
|
|
568
|
+
g. other — operator names it
|
|
569
|
+
|
|
570
|
+
[a-g]
|
|
571
|
+
```
|
|
572
|
+
|
|
573
|
+
Write the answer to `substrate.config.md` `frontend_stack`:
|
|
574
|
+
- `a` → `frontend_stack: nextjs`
|
|
575
|
+
- `b` → `frontend_stack: vite-react`
|
|
576
|
+
- `c` → `frontend_stack: remix`
|
|
577
|
+
- `d` → `frontend_stack: sveltekit`
|
|
578
|
+
- `e` → `frontend_stack: solidstart`
|
|
579
|
+
- `f` → `frontend_stack: none`
|
|
580
|
+
- `g` → `frontend_stack: <operator-named>`
|
|
581
|
+
|
|
582
|
+
Default on Enter → `a` (Next.js) to preserve the historical silent pick. Operator can edit `substrate.config.md` directly at any time.
|
|
583
|
+
|
|
584
|
+
Phase 11 reads `frontend_stack` and picks the matching migration adapter. When unset at Phase 11 fire, Phase 11 BLOCKs with a one-line note citing this ask.
|
|
585
|
+
|
|
586
|
+
<!-- /FKGL-EXEMPT -->
|
|
587
|
+
|
|
549
588
|
### Phase 11 — `/ux-migration` (chosen variant → production migration plan)
|
|
550
589
|
|
|
551
590
|
```bash
|
|
@@ -699,8 +738,8 @@ Two templates — adopter-mode and operator-mode. Default is adopter when `--loc
|
|
|
699
738
|
After Phase 12-alt (`local-serve.sh` running) + Phase 13 (preview-state.yml on disk), emit this template directly to the adopter — no YAML edits, no frontmatter flip, no command-soup:
|
|
700
739
|
|
|
701
740
|
```
|
|
702
|
-
I made you N directions for your family recipe wiki. Walk through them
|
|
703
|
-
|
|
741
|
+
I made you N directions for your family recipe wiki. Walk through them
|
|
742
|
+
on this computer at <LOCAL_URL> and tell me which one feels right.
|
|
704
743
|
|
|
705
744
|
When you've picked, just tell me — "I like variant 2" or "#3 looks
|
|
706
745
|
great".
|
|
@@ -740,8 +779,12 @@ None of them feel right? Tell me what's off and I'll generate new
|
|
|
740
779
|
directions for you. (See /launch --refine, bassclef#942.)
|
|
741
780
|
```
|
|
742
781
|
|
|
782
|
+
<!-- FKGL-EXEMPT: substitution instructions name three distinct placeholders; short SVO loses the mapping precision -->
|
|
743
783
|
Substitute `<LOCAL_URL>` with the URL returned by Phase 12-alt. Substitute the actual variant count (`I made you N directions...`). Substitute the actual app description (`your family recipe wiki` etc.) from the InputArtifact's `intent` field.
|
|
744
784
|
|
|
785
|
+
**Phone or iPad reach — opt-in with `--lan`.** The default URL binds `127.0.0.1`. Only this computer can open it. The adopter may want the gallery on a phone or iPad on the same Wi-Fi. Re-run Phase 12-alt with `--lan`: `bash scripts/launch/local-serve.sh --lan <gallery-dir>`. The script binds `0.0.0.0` and prints a one-line warning. Any device on the Wi-Fi can then reach the URL. Swap the handoff line to "on your phone, laptop, or iPad at <LOCAL_URL>" when `--lan` fires.
|
|
786
|
+
<!-- /FKGL-EXEMPT -->
|
|
787
|
+
|
|
745
788
|
The adopter-mode template **surfaces** (per bassclef#969):
|
|
746
789
|
- The formal artifacts the chain will produce post-pick, in plain English (use case, user stories, IA, interaction design, responsibility map, build spec, migration plan)
|
|
747
790
|
- The chain-ordering rationale (variants are cheap shared data; formal artifacts are expensive per-direction; cheap first → commit → expensive once)
|