@thebassclef/lite 1.9.8 → 1.9.9
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 +6 -2
- package/dist/index.cjs +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/lite/.claude/hooks/git-stash-u-guard.sh +4 -1
- package/dist/lite/.claude/hooks/post-skill-friction-check.sh +6 -1
- package/dist/lite/.claude/hooks/pre-commit-gate.sh +18 -6
- package/dist/lite/.claude/hooks/pre-commit-identifier-leak-scrub.sh +10 -6
- package/dist/lite/.claude/hooks/pre-commit-manifest-autoregen.sh +4 -2
- package/dist/lite/.claude/hooks/save-state.sh +24 -0
- package/dist/lite/.claude/hooks/trace-helper.sh +29 -0
- package/dist/lite/.claude/rules/lite-manifest-schema-change-discipline.md +30 -0
- package/dist/lite/.claude/skills/build/SKILL.md +19 -7
- package/dist/lite/.claude/skills/onboard-repo/SKILL.md +41 -0
- package/dist/lite/.claude/skills/release-notes/SKILL.md +24 -0
- package/dist/lite/bassclef-version.json +5 -5
- package/dist/lite/gitignore +8 -0
- package/dist/lite/lib/deferred-actions.sh +4 -4
- package/dist/lite/lib/fixture-builder.sh +9 -9
- package/dist/lite/lib/identifier-leak-check.sh +85 -0
- package/dist/lite/lib/preview-state.sh +20 -20
- package/dist/lite/lib/skip-env-inline-check.sh +61 -0
- package/dist/lite/lib/state.sh +31 -28
- package/dist/lite/lib/tier-check.sh +9 -9
- package/dist/lite/lib/tier-dependency-audit.sh +23 -23
- package/dist/lite/presence/install/schedule-auto-save.taskscheduler.md +1 -1
- package/dist/lite/standards/lite-manifest-schema-changes.md +38 -0
- package/dist/lite/standards/lite-manifest.json +38 -38
- package/dist/lite/templates/spec-template.md +10 -6
- package/package.json +1 -1
- package/dist/lite/.claude/hooks/longrun-prep-compounding-axis-check.sh +0 -492
- package/dist/lite/.claude/rules/compounding-axis-fresh-analysis.md +0 -173
package/README.md
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
# @thebassclef/lite
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
**Security posture.** @thebassclef/lite ships with zero install-time scripts, SLSA v1 build provenance, Apache-2.0 license, and zero runtime dependencies. The CLI writes to `~/.claude` and `$CLAUDE_PROJECT_DIR` at runtime, not at install. See [SECURITY.md](SECURITY.md) for disclosure.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
Two commands.
|
|
4
8
|
|
|
5
9
|
```bash
|
|
6
10
|
npm install -g @thebassclef/lite
|
|
@@ -63,7 +67,7 @@ your browser.
|
|
|
63
67
|
|
|
64
68
|
## Current release
|
|
65
69
|
|
|
66
|
-
<!-- version-start -->1.9.
|
|
70
|
+
<!-- version-start -->1.9.9<!-- version-end -->
|
|
67
71
|
|
|
68
72
|
See [CHANGELOG.md](CHANGELOG.md) for the full changelog. Adopter-facing
|
|
69
73
|
release notes live at [docs.bassclef.dev/docs/whats-new/releases](https://docs.bassclef.dev/docs/whats-new/releases).
|
package/dist/index.cjs
CHANGED
package/dist/index.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const version: "1.9.
|
|
1
|
+
export declare const version: "1.9.9";
|
|
2
2
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.js
CHANGED
|
@@ -50,7 +50,10 @@ if ! echo "$CMD" | grep -qE '(^|[[:space:]])git[[:space:]]+stash([[:space:]]+pus
|
|
|
50
50
|
fi
|
|
51
51
|
|
|
52
52
|
# --- Override — SKIP_GIT_STASH_U_GUARD=1 bypasses (logged) ---
|
|
53
|
-
|
|
53
|
+
# Honors inline SKIP per #2036 Finding #4 (recipe must reach the hook).
|
|
54
|
+
# shellcheck source=/dev/null
|
|
55
|
+
source "${CLAUDE_PROJECT_DIR:-$(cd "$(dirname "$0")/../.." && pwd)}/lib/skip-env-inline-check.sh" 2>/dev/null || true
|
|
56
|
+
if declare -F skip_var_set >/dev/null && skip_var_set "SKIP_GIT_STASH_U_GUARD" "$CMD"; then
|
|
54
57
|
TRACE_LOG="${BASSCLEF_TRACE_LOG:-${BASSCLEF_TRACE_FILE:-$HOME/.claude/state/trace.log}}"
|
|
55
58
|
mkdir -p "$(dirname "$TRACE_LOG")" 2>/dev/null || true
|
|
56
59
|
echo "$(date -u +%FT%TZ) SKIP_GIT_STASH_U_GUARD $CMD" >> "$TRACE_LOG" 2>/dev/null || true
|
|
@@ -45,7 +45,12 @@ SKILL_NAME=$(jq <<< "$TOOL_INPUT" -r '.skill // ""' 2>/dev/null)
|
|
|
45
45
|
|
|
46
46
|
# Major composer skills — these chain other skills or do heavy work.
|
|
47
47
|
# Friction surfaces best at their boundaries.
|
|
48
|
-
|
|
48
|
+
# Per ADR-040 D1 vocabulary rename (grace window through 2026-10-31 per
|
|
49
|
+
# .claude/rules/we-dont-break-adopters.md): new names stage/launch added;
|
|
50
|
+
# old names preview/preview-build preserved for grace-window compat.
|
|
51
|
+
# Fix for bassclef-upstream#1777 (friction log never created because current
|
|
52
|
+
# vocabulary skipped the gate).
|
|
53
|
+
MAJOR_SKILLS_PATTERN="^(preview|preview-build|stage|launch|build|shape|longrun|decompose|spec|architect-review|frontend-design|prototype-variants|prototype-gallery|interpret-input|objectory-decompose|ux-migration|ia-model|interaction-design|use-case|user-stories|jtbd-tasks|task-scenarios)$"
|
|
49
54
|
|
|
50
55
|
if ! echo "$SKILL_NAME" | grep -qE "$MAJOR_SKILLS_PATTERN"; then
|
|
51
56
|
exit 0
|
|
@@ -41,7 +41,16 @@ trace_log "pre-commit" "git commit"
|
|
|
41
41
|
# Exempt: rules/hooks/skills/standards/docs/strategy/chronicle/scripts/tests
|
|
42
42
|
# (places where the policy itself is documented or test fixtures live).
|
|
43
43
|
# Override: SKIP_OPERATOR_PATHS=1 git commit (logged via trace-helper)
|
|
44
|
-
|
|
44
|
+
#
|
|
45
|
+
# Env-reach fix per #2036 Finding #4: the inline recipe sets the var in
|
|
46
|
+
# the git subshell env, not the hook env. Source the helper lib so the
|
|
47
|
+
# recipe reaches the check reliably across harness setups.
|
|
48
|
+
# shellcheck source=/dev/null
|
|
49
|
+
source "${CLAUDE_PROJECT_DIR:-$(cd "$(dirname "$0")/../.." && pwd)}/lib/skip-env-inline-check.sh" 2>/dev/null || true
|
|
50
|
+
if declare -F skip_var_set >/dev/null && skip_var_set "SKIP_OPERATOR_PATHS" "$COMMAND"; then
|
|
51
|
+
# Override honored; skip the check.
|
|
52
|
+
:
|
|
53
|
+
else
|
|
45
54
|
STAGED_NONEXEMPT=$(git diff --cached --name-only --diff-filter=AM 2>/dev/null | \
|
|
46
55
|
grep -vE '^\.claude/(rules|hooks|skills|luminaries|agents)/' | \
|
|
47
56
|
grep -vE '^standards/' | \
|
|
@@ -50,7 +59,8 @@ if [ "${SKIP_OPERATOR_PATHS:-0}" != "1" ]; then
|
|
|
50
59
|
grep -vE '^docs/' | \
|
|
51
60
|
grep -vE '^chronicle/' | \
|
|
52
61
|
grep -vE '^strategy/' | \
|
|
53
|
-
grep -vE '^scripts/tests/'
|
|
62
|
+
grep -vE '^scripts/tests/' | \
|
|
63
|
+
grep -vE '^lib/tests/' || true)
|
|
54
64
|
if [ -n "$STAGED_NONEXEMPT" ]; then
|
|
55
65
|
LEAKED=""
|
|
56
66
|
while IFS= read -r f; do
|
|
@@ -110,7 +120,8 @@ fi
|
|
|
110
120
|
# fast-fail layer per Saltzer-Schroeder complete mediation.
|
|
111
121
|
#
|
|
112
122
|
# Override: SKIP_COLD_ADOPTER_HARNESS=1 git commit (logged via trace-helper)
|
|
113
|
-
|
|
123
|
+
# Env-reach via skip_var_set helper per #2036 Finding #4.
|
|
124
|
+
if ! { declare -F skip_var_set >/dev/null && skip_var_set "SKIP_COLD_ADOPTER_HARNESS" "$COMMAND"; }; then
|
|
114
125
|
# The 12-pattern matcher mirrors .claude/rules/cold-adopter-harness-discipline.md
|
|
115
126
|
# § "When this rule fires". Keep these patterns in sync; a future WU may
|
|
116
127
|
# centralize the list via a sourced library function.
|
|
@@ -173,7 +184,8 @@ fi
|
|
|
173
184
|
# state/markers/lead-lens-signoff/<branch-slug>.marker
|
|
174
185
|
# Discipline lives in .claude/rules/loop-discipline.md Step 0.5 + 2a + 5.5.
|
|
175
186
|
# Override: SKIP_LEAD_LENS_SIGNOFF=1 git commit (logged via trace-helper)
|
|
176
|
-
|
|
187
|
+
# Env-reach via skip_var_set helper per #2036 Finding #4.
|
|
188
|
+
if ! { declare -F skip_var_set >/dev/null && skip_var_set "SKIP_LEAD_LENS_SIGNOFF" "$COMMAND"; }; then
|
|
177
189
|
# Reuse the substrate-affecting matcher shape from cold-adopter fast-path.
|
|
178
190
|
STAGED_SUBSTRATE_LL=$(git diff --cached --name-only --diff-filter=AM 2>/dev/null | grep -E \
|
|
179
191
|
-e '^\.claude/hooks/[^/]+\.sh$' \
|
|
@@ -305,7 +317,7 @@ fi
|
|
|
305
317
|
|
|
306
318
|
# --- Reserved skill names check (collision with Claude Code built-ins) ---
|
|
307
319
|
# See .claude/rules/reserved-skill-names.md + standards/reserved-skill-names.md
|
|
308
|
-
if
|
|
320
|
+
if ! { declare -F skip_var_set >/dev/null && skip_var_set "SKIP_RESERVED_NAMES" "$COMMAND"; }; then
|
|
309
321
|
STAGED_NEW_SKILLS=$(git diff --cached --name-only --diff-filter=A -- '.claude/skills/*/SKILL.md' 2>/dev/null || true)
|
|
310
322
|
if [ -n "$STAGED_NEW_SKILLS" ]; then
|
|
311
323
|
# Extract reserved names from the standards table (Name column values inside backticks)
|
|
@@ -351,7 +363,7 @@ fi
|
|
|
351
363
|
# counter, silencing the compliance signal (the failure mode that motivated
|
|
352
364
|
# this gate). Block commits to chronicle/*.md or docs/chronicle/*.md that
|
|
353
365
|
# lack the section. Override: SKIP_CHRONICLE_GATE_EVIDENCE=1 git commit.
|
|
354
|
-
if
|
|
366
|
+
if ! { declare -F skip_var_set >/dev/null && skip_var_set "SKIP_CHRONICLE_GATE_EVIDENCE" "$COMMAND"; }; then
|
|
355
367
|
STAGED_CHRONICLES=$(git diff --cached --name-only --diff-filter=AM -- 'chronicle/*.md' 'docs/chronicle/*.md' 2>/dev/null || true)
|
|
356
368
|
# Source spine accessor library when available — preferred frontmatter
|
|
357
369
|
# check route per state-spine-contract.md. Graceful fallback for repos
|
|
@@ -33,12 +33,6 @@
|
|
|
33
33
|
|
|
34
34
|
set -u
|
|
35
35
|
|
|
36
|
-
# Override path — logged via trace-helper per bassclef discipline
|
|
37
|
-
if [ "${SKIP_IDENTIFIER_LEAK_SCRUB:-0}" = "1" ]; then
|
|
38
|
-
echo " identifier-leak-scrub: SKIP_IDENTIFIER_LEAK_SCRUB=1 — bypassing scrub" >&2
|
|
39
|
-
exit 0
|
|
40
|
-
fi
|
|
41
|
-
|
|
42
36
|
# R-N1 fail-soft: if grep is unavailable, warn to stderr and exit 0
|
|
43
37
|
if ! command -v grep >/dev/null 2>&1; then
|
|
44
38
|
echo " identifier-leak-scrub: warn — grep unavailable; skipping scrub" >&2
|
|
@@ -59,6 +53,16 @@ else
|
|
|
59
53
|
COMMAND=$(echo "$INPUT" | grep -oE '"command"[[:space:]]*:[[:space:]]*"[^"]*"' | head -1 | sed 's/^"command"[[:space:]]*:[[:space:]]*"//' | sed 's/"$//')
|
|
60
54
|
fi
|
|
61
55
|
|
|
56
|
+
# Override path — honors inline SKIP_FOO=1 cmd per #2036 Finding #4.
|
|
57
|
+
# PreToolUse hooks run in Claude Code's env, not the git subshell env.
|
|
58
|
+
# The helper lib parses $COMMAND for the inline prefix + falls back to env.
|
|
59
|
+
# shellcheck source=/dev/null
|
|
60
|
+
source "${CLAUDE_PROJECT_DIR:-$(cd "$(dirname "$0")/../.." && pwd)}/lib/skip-env-inline-check.sh" 2>/dev/null || true
|
|
61
|
+
if declare -F skip_var_set >/dev/null && skip_var_set "SKIP_IDENTIFIER_LEAK_SCRUB" "$COMMAND"; then
|
|
62
|
+
echo " identifier-leak-scrub: SKIP_IDENTIFIER_LEAK_SCRUB=1 — bypassing scrub" >&2
|
|
63
|
+
exit 0
|
|
64
|
+
fi
|
|
65
|
+
|
|
62
66
|
# Pass through non-git-commit commands
|
|
63
67
|
if ! echo "$COMMAND" | grep -qE '^[[:space:]]*git[[:space:]]+commit'; then
|
|
64
68
|
exit 0
|
|
@@ -38,8 +38,10 @@ if ! echo "$COMMAND" | grep -q 'git commit'; then
|
|
|
38
38
|
exit 0
|
|
39
39
|
fi
|
|
40
40
|
|
|
41
|
-
# Override path
|
|
42
|
-
|
|
41
|
+
# Override path — honors inline SKIP_FOO=1 cmd per #2036 Finding #4.
|
|
42
|
+
# shellcheck source=/dev/null
|
|
43
|
+
source "${CLAUDE_PROJECT_DIR:-$(cd "$(dirname "$0")/../.." && pwd)}/lib/skip-env-inline-check.sh" 2>/dev/null || true
|
|
44
|
+
if declare -F skip_var_set >/dev/null && skip_var_set "SKIP_MANIFEST_AUTOREGEN" "$COMMAND"; then
|
|
43
45
|
# Best-effort trace log; ignore failure so hook stays exit 0
|
|
44
46
|
if command -v bash >/dev/null 2>&1 && [ -f ".claude/hooks/trace-helper.sh" ]; then
|
|
45
47
|
bash .claude/hooks/trace-helper.sh "skip" "manifest-autoregen" "SKIP_MANIFEST_AUTOREGEN=1" 2>/dev/null || true
|
|
@@ -216,6 +216,30 @@ if [ -n "$DIRTY" ]; then
|
|
|
216
216
|
git add 'state/' ':!state/markers' ':!state/markers/**' 2>/dev/null || true
|
|
217
217
|
git add .claude/projects/*/memory/ 2>/dev/null || true
|
|
218
218
|
|
|
219
|
+
STAGED=$(git diff --cached --name-only 2>/dev/null)
|
|
220
|
+
if [ -n "$STAGED" ]; then
|
|
221
|
+
# Operator-identifier leak check per #2036 Finding #1.
|
|
222
|
+
# save-state runs `git commit` directly, bypassing the PreToolUse
|
|
223
|
+
# pre-commit-gate.sh CCF-3 check. Even if CCF-3 fired, its grep
|
|
224
|
+
# exempts docs/ paths — so docs/sdlc-traces/*.log carrying absolute
|
|
225
|
+
# /Users/<name>/ paths would still get committed.
|
|
226
|
+
# Shared lib so this check matches pre-commit-gate CCF-3 shape.
|
|
227
|
+
REPO_ROOT_FOR_LIB="$(git rev-parse --show-toplevel 2>/dev/null)"
|
|
228
|
+
LEAK_LIB="${REPO_ROOT_FOR_LIB}/lib/identifier-leak-check.sh"
|
|
229
|
+
if [ -f "$LEAK_LIB" ]; then
|
|
230
|
+
# shellcheck source=/dev/null
|
|
231
|
+
source "$LEAK_LIB"
|
|
232
|
+
if ! check_staged_for_leaks; then
|
|
233
|
+
log "BLOCKED: auto-save detected operator paths in staged content; resetting stage"
|
|
234
|
+
git reset HEAD -- . 2>/dev/null || true
|
|
235
|
+
# Fall through — SESSION_LOCK cleanup below still fires, push is skipped
|
|
236
|
+
# because STAGED check below sees empty stage.
|
|
237
|
+
STAGED=""
|
|
238
|
+
fi
|
|
239
|
+
fi
|
|
240
|
+
fi
|
|
241
|
+
|
|
242
|
+
# Re-check STAGED after possible reset
|
|
219
243
|
STAGED=$(git diff --cached --name-only 2>/dev/null)
|
|
220
244
|
if [ -n "$STAGED" ]; then
|
|
221
245
|
TIMESTAMP=$(date +%Y-%m-%dT%H:%M:%S)
|
|
@@ -5,9 +5,28 @@
|
|
|
5
5
|
#
|
|
6
6
|
# Usage: source this file, then call:
|
|
7
7
|
# trace_log "hook-name" "trigger-description"
|
|
8
|
+
#
|
|
9
|
+
# Privacy — relative paths (#2036 Finding #1 — adopter report 2026-10-02):
|
|
10
|
+
# The ${trigger} field can carry filesystem paths from caller args
|
|
11
|
+
# (e.g., $FILE_PATH from PreToolUse hooks). We strip repo-root prefix
|
|
12
|
+
# so adopter traces never leak /Users/<name>/ or /home/<name>/ paths
|
|
13
|
+
# to adopter git. Fallback chain: $CLAUDE_PROJECT_DIR → git rev-parse
|
|
14
|
+
# --show-toplevel → no-strip (silent — trigger lands as-is).
|
|
8
15
|
|
|
9
16
|
TRACE_DIR="docs/sdlc-traces"
|
|
10
17
|
|
|
18
|
+
# _trace_repo_root — resolve repo root for prefix-strip.
|
|
19
|
+
# Precondition fallback per @luminary tony-hoare — Hoare R1 risk ledger.
|
|
20
|
+
_trace_repo_root() {
|
|
21
|
+
if [ -n "${CLAUDE_PROJECT_DIR:-}" ]; then
|
|
22
|
+
echo "$CLAUDE_PROJECT_DIR"
|
|
23
|
+
return 0
|
|
24
|
+
fi
|
|
25
|
+
local root
|
|
26
|
+
root=$(git rev-parse --show-toplevel 2>/dev/null) || return 1
|
|
27
|
+
echo "$root"
|
|
28
|
+
}
|
|
29
|
+
|
|
11
30
|
trace_log() {
|
|
12
31
|
local hook="$1"
|
|
13
32
|
local trigger="$2"
|
|
@@ -17,6 +36,16 @@ trace_log() {
|
|
|
17
36
|
today=$(date +%Y-%m-%d)
|
|
18
37
|
local log_file="${TRACE_DIR}/${today}.log"
|
|
19
38
|
|
|
39
|
+
# Strip repo-root prefix from trigger before writing.
|
|
40
|
+
# Only strips when root is a non-empty prefix of trigger — leaves
|
|
41
|
+
# unrelated absolute paths (/opt/anthropic/..., /usr/bin/...) intact.
|
|
42
|
+
local root
|
|
43
|
+
root=$(_trace_repo_root 2>/dev/null) || root=""
|
|
44
|
+
if [ -n "$root" ] && [ "$root" != "/" ]; then
|
|
45
|
+
trigger="${trigger//$root\//}"
|
|
46
|
+
trigger="${trigger//$root/}"
|
|
47
|
+
fi
|
|
48
|
+
|
|
20
49
|
mkdir -p "$TRACE_DIR" 2>/dev/null || true
|
|
21
50
|
echo "${timestamp} | ${hook} | ${trigger} | — | —" >> "$log_file" 2>/dev/null || true
|
|
22
51
|
}
|
|
@@ -36,11 +36,41 @@ 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
|
+
## Retirements
|
|
40
|
+
|
|
41
|
+
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:
|
|
42
|
+
|
|
43
|
+
1. **Ledger entry** under this ledger's `## Entries` section naming the removed slugs, the deletion SHA, the version bump (MINOR per content-remove policy), and the downstream cure (what adopters at an exact pin do).
|
|
44
|
+
2. **Allowlist row** in `.claude/hooks/tests/fixtures/2026-09-16-lite-catalog-closure/retired-entries.txt` naming each removed `<type>/<slug>` + `retired-in=<SHA>` + `reason=<one-line>`. The Feathers v1.6.1 snapshot guard (Test 16 in `.claude/hooks/tests/generate-lite-manifest.test.sh`) subtracts allowlist rows before asserting zero drops. Without the allowlist row the test fails and main CI goes red.
|
|
45
|
+
3. **Discipline amendment** when a new retirement class surfaces that this section does not cover (e.g., tier retirement, field retirement). Extend this `## Retirements` section rather than author a new rule, per Ousterhout's complexity-is-incremental principle.
|
|
46
|
+
|
|
47
|
+
When this rule fires on retirement:
|
|
48
|
+
|
|
49
|
+
- Any PR that deletes a file listed in the current `lite-manifest.json` under `.entries[].path`
|
|
50
|
+
- Any PR that retires a schema field that previously shipped as REQUIRED
|
|
51
|
+
|
|
52
|
+
Skips (same as additions):
|
|
53
|
+
|
|
54
|
+
- Content-only changes where the file stays but its content_hash shifts
|
|
55
|
+
- Comment-only edits
|
|
56
|
+
|
|
57
|
+
**Anchor luminaries for retirement:**
|
|
58
|
+
|
|
59
|
+
- `@luminary linus-torvalds` — retirement of a tier-tagged adopter-observable file is a userspace break; the ledger + allowlist + release-step6 check ship in one PR
|
|
60
|
+
- `@luminary hyrum-wright` — observable behavior becomes dependency with enough adopters; the 2 removed slugs were cited in rule bodies, sister docs, and the fixture snapshot; every retirement needs observable-surface enumeration first
|
|
61
|
+
- `@luminary michael-nygard` — current ledger discipline for additions is methodology at author time; retirement needs the same mechanical floor plus a stability-pattern gate (release-step6 pre-flight)
|
|
62
|
+
- `@luminary michael-feathers` — the fixture snapshot is the characterization test; allowlist is the author-time hook that keeps it green
|
|
63
|
+
- `@luminary vaughn-vernon` — `retired-entries.txt` is the anticorruption layer between the frozen snapshot and the current manifest
|
|
64
|
+
- `@luminary john-ousterhout` — contrarian: extend the existing rule with this section, do not author a new rule; the mechanical pre-commit hook (`pre-retire-discipline-check.sh`) is deferred to a follow-on PR so this one stays scope-tight
|
|
65
|
+
|
|
39
66
|
## Anti-patterns
|
|
40
67
|
|
|
41
68
|
**Schema field added but no ledger entry.** Release script pre-flight BLOCKs.
|
|
42
69
|
INSTEAD: add the entry in the same PR.
|
|
43
70
|
|
|
71
|
+
**File removed from the lite manifest without an allowlist row + ledger entry.** Test 16 (Feathers v1.6.1 snapshot guard) sees the drop and main CI goes red at the next run.
|
|
72
|
+
INSTEAD: append a row to `retired-entries.txt` naming `<type>/<slug> retired-in=<SHA> reason=<one-line>` AND add a `## Retirements`-aware entry to `standards/lite-manifest-schema-changes.md` in the same PR.
|
|
73
|
+
|
|
44
74
|
**Ledger entry written but wrong version bump.** Adopters plan their pin update against the wrong version.
|
|
45
75
|
INSTEAD: correct the bump before merge. AJV validation of the entry format catches inconsistency.
|
|
46
76
|
|
|
@@ -330,14 +330,25 @@ Parse the validated goal's structure:
|
|
|
330
330
|
```bash
|
|
331
331
|
# Already have BET_JSON, SPEC_PATH, DECOMP_PATH, UX_MIG_PATH from Phase 0
|
|
332
332
|
# Read spec body for step enumeration. Per bassclef#367, spec MUST have
|
|
333
|
-
# the
|
|
334
|
-
#
|
|
335
|
-
#
|
|
333
|
+
# the heading `## Steps enumerated` (pinned in /spec SKILL.md rules +
|
|
334
|
+
# templates/spec-template.md). /launch's Phase 9 emits this heading by
|
|
335
|
+
# contract. Specs that lack the section → exit 4.
|
|
336
|
+
#
|
|
337
|
+
# Finding #2 + #3 cure (#2036): the previous awk range
|
|
338
|
+
# `/^## Steps enumerated/,/^## /` closed on its own start line because
|
|
339
|
+
# `^## ` matches `^## Steps enumerated`. Flag-based extraction fixes it.
|
|
340
|
+
# Grace window per ADR-040 D1 (through 2026-10-31): accept both the new
|
|
341
|
+
# "Steps enumerated" / "### Step-N" and the historical "Workunits
|
|
342
|
+
# enumerated" / "### WU-N" headings so legacy specs still build.
|
|
336
343
|
SPEC_FILE="docs/specs/${SPEC_PATH}.md"
|
|
337
|
-
WU_LIST=$(awk '
|
|
344
|
+
WU_LIST=$(awk '
|
|
345
|
+
/^## (Steps|Workunits) enumerated/ { in_section=1; next }
|
|
346
|
+
/^## / { in_section=0 }
|
|
347
|
+
in_section
|
|
348
|
+
' "$SPEC_FILE" | grep -E '^### (Step|WU)-')
|
|
338
349
|
|
|
339
|
-
# Each
|
|
340
|
-
WU_COUNT=$(echo "$WU_LIST" | grep -
|
|
350
|
+
# Each step should reference at least one story (US-NNN)
|
|
351
|
+
WU_COUNT=$(echo "$WU_LIST" | grep -cE '^### (Step|WU)-' || echo 0)
|
|
341
352
|
```
|
|
342
353
|
|
|
343
354
|
Build structured object: `{ bet, spec_path, decomp_path, ux_migration_path, wus[] }`.
|
|
@@ -348,7 +359,8 @@ Verify each step traces to at least one story. Refuse if any step lacks
|
|
|
348
359
|
story traceability.
|
|
349
360
|
|
|
350
361
|
```bash
|
|
351
|
-
|
|
362
|
+
# Keep only step headings; drop body lines before the US-NNN check.
|
|
363
|
+
ORPHAN_WUS=$(echo "$WU_LIST" | grep -E '^### (Step|WU)-' | grep -v 'US-' || true)
|
|
352
364
|
if [ -n "$ORPHAN_WUS" ]; then
|
|
353
365
|
echo "/build: step enumeration lacks story traceability (bassclef#358 violation)" >&2
|
|
354
366
|
echo "Orphan steps (no US-NNN ref):" >&2
|
|
@@ -462,9 +462,28 @@ immediately without waiting for a sync cycle.
|
|
|
462
462
|
|
|
463
463
|
##### 1.2 Protect main from force-push and deletion
|
|
464
464
|
|
|
465
|
+
**Heads up — GitHub free-tier adopters on PRIVATE repos:** the API call
|
|
466
|
+
below returns HTTP 403 ("Upgrade to GitHub Pro or make this repository
|
|
467
|
+
public") on free-tier accounts, because branch protection is a paid
|
|
468
|
+
feature for private repos. Public repos get branch protection on every
|
|
469
|
+
plan, free included. Pick a path below BEFORE running the call.
|
|
470
|
+
|
|
471
|
+
Three ways to clear this step:
|
|
472
|
+
|
|
473
|
+
1. **Make the repo public** — simplest; free plan includes branch
|
|
474
|
+
protection on public repos. Good fit when the code is open source
|
|
475
|
+
or ships to adopters anyway.
|
|
476
|
+
2. **Upgrade the GitHub plan** — Pro / Team / Enterprise includes
|
|
477
|
+
branch protection on private repos.
|
|
478
|
+
3. **Skip the API call** — rely on local safety (no `git push --force`
|
|
479
|
+
habits, PR review, pre-commit hooks). Mark the repo config so later
|
|
480
|
+
phases know branch protection is operator-managed, not substrate-
|
|
481
|
+
managed. See the fallback block below.
|
|
482
|
+
|
|
465
483
|
```bash
|
|
466
484
|
DEFAULT_BRANCH=$(gh repo view "$REPO" --json defaultBranchRef -q '.defaultBranchRef.name')
|
|
467
485
|
|
|
486
|
+
# Pro / public-repo path — run the API call.
|
|
468
487
|
gh api "repos/$REPO/branches/$DEFAULT_BRANCH/protection" \
|
|
469
488
|
--method PUT \
|
|
470
489
|
--input - <<EOF
|
|
@@ -484,6 +503,28 @@ required here — agent-created PRs (bassclef-sync) need to merge
|
|
|
484
503
|
without human intervention for the mobile-sync story. Add stricter
|
|
485
504
|
rules per-repo in `.github/branch-protection.yml` if the repo warrants.
|
|
486
505
|
|
|
506
|
+
**Free-tier private-repo fallback (path 3 above).** If the API
|
|
507
|
+
returned 403 and the operator picked "skip and manage locally":
|
|
508
|
+
|
|
509
|
+
```bash
|
|
510
|
+
# Record the choice so later audits know main is not API-protected.
|
|
511
|
+
mkdir -p .claude
|
|
512
|
+
cat >> .claude/onboard-notes.md <<'NOTE'
|
|
513
|
+
## Branch protection — operator-managed
|
|
514
|
+
|
|
515
|
+
GitHub free-tier does not include branch protection on private repos.
|
|
516
|
+
This repo relies on:
|
|
517
|
+
- Local discipline: no `git push --force` to main.
|
|
518
|
+
- PR review before merge.
|
|
519
|
+
- Pre-commit hooks shipped by bassclef.
|
|
520
|
+
|
|
521
|
+
Revisit if the repo moves to public, or the plan upgrades to Pro+.
|
|
522
|
+
NOTE
|
|
523
|
+
```
|
|
524
|
+
|
|
525
|
+
The adopter can revisit this step after switching to public OR
|
|
526
|
+
upgrading the plan.
|
|
527
|
+
|
|
487
528
|
##### 1.3 Add the production approval gate (if this repo deploys to prod)
|
|
488
529
|
|
|
489
530
|
If the repo will deploy to production, per `manual-prod-approval.md`:
|
|
@@ -108,6 +108,30 @@ Rules for external notes:
|
|
|
108
108
|
- No feature names that aren't visible to users
|
|
109
109
|
- Test: could someone who's never seen the codebase understand this?
|
|
110
110
|
|
|
111
|
+
### 3.5. Run `/ogilvy-writing-audit` on each body
|
|
112
|
+
|
|
113
|
+
Before saving the drafted bodies to disk, run `/ogilvy-writing-audit`
|
|
114
|
+
on each one. The skill scans against Ogilvy + Roman-Raphaelson's
|
|
115
|
+
rules (see `standards/ogilvy-writing-rules.md`) and returns
|
|
116
|
+
severity-tagged findings.
|
|
117
|
+
|
|
118
|
+
```bash
|
|
119
|
+
/ogilvy-writing-audit docs/release-notes/YYYY-MM-DD-internal.md
|
|
120
|
+
/ogilvy-writing-audit docs/release-notes/YYYY-MM-DD-external.md
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
Fix every 🔴 and 🟡 finding before step 4. When a 🟡 ships without
|
|
124
|
+
a fix, cite the audit in the commit body and name why.
|
|
125
|
+
|
|
126
|
+
Why this step. Release notes land in reader surfaces that must read
|
|
127
|
+
clean — the GitHub Release artifact plus the Google Doc the team
|
|
128
|
+
reviews. Jargon and grade-13 prose creep in without a mechanical
|
|
129
|
+
check. PR #408 ran this discipline by hand across 8 entries, and
|
|
130
|
+
PR #412 formalizes it so every future entry inherits the floor.
|
|
131
|
+
|
|
132
|
+
Cross-ref: `.claude/rules/session-artifacts.md` §Release notes names
|
|
133
|
+
this step as the discipline anchor.
|
|
134
|
+
|
|
111
135
|
### 4. Save locally
|
|
112
136
|
|
|
113
137
|
```
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "v1.6.
|
|
3
|
-
"marker_tag": "release-2026-
|
|
4
|
-
"release_date": "2026-
|
|
5
|
-
"release_sha": "
|
|
6
|
-
"release_tag": "release-2026-
|
|
2
|
+
"version": "v1.6.6",
|
|
3
|
+
"marker_tag": "release-2026-10-03-89ceaa15",
|
|
4
|
+
"release_date": "2026-10-03T10:51:10Z",
|
|
5
|
+
"release_sha": "89ceaa15",
|
|
6
|
+
"release_tag": "release-2026-10-03-89ceaa15"
|
|
7
7
|
}
|
package/dist/lite/gitignore
CHANGED
|
@@ -31,6 +31,14 @@ state/luminary-implementations/
|
|
|
31
31
|
.claude/hooks/logs/
|
|
32
32
|
.claude/hooks/*.trace
|
|
33
33
|
|
|
34
|
+
# SDLC traces (operator-private; runtime artifacts carry operator paths)
|
|
35
|
+
# Per #2036 Finding #1 (adopter report 2026-10-02) — auto-save was
|
|
36
|
+
# committing /Users/<name>/ paths to adopter git every fire. Shipping this
|
|
37
|
+
# line stops future adopter installs from leaking. Existing installs run
|
|
38
|
+
# `git rm --cached -r docs/sdlc-traces/` one time to untrack files
|
|
39
|
+
# already in history.
|
|
40
|
+
docs/sdlc-traces/
|
|
41
|
+
|
|
34
42
|
# Build output
|
|
35
43
|
dist/
|
|
36
44
|
|
|
@@ -72,9 +72,9 @@ _da_resolve_path() {
|
|
|
72
72
|
da_get_field() {
|
|
73
73
|
local id="$1"
|
|
74
74
|
local field="$2"
|
|
75
|
-
local
|
|
76
|
-
|
|
77
|
-
[[ -f "$
|
|
75
|
+
local p
|
|
76
|
+
p="$(_da_resolve_path "$id")"
|
|
77
|
+
[[ -f "$p" ]] || { echo "not-found: $p" >&2; return 1; }
|
|
78
78
|
# Read frontmatter only (between two `---` lines)
|
|
79
79
|
awk -v f="$field" '
|
|
80
80
|
BEGIN { in_fm = 0; seen_open = 0 }
|
|
@@ -89,7 +89,7 @@ da_get_field() {
|
|
|
89
89
|
print
|
|
90
90
|
exit
|
|
91
91
|
}
|
|
92
|
-
' "$
|
|
92
|
+
' "$p"
|
|
93
93
|
}
|
|
94
94
|
|
|
95
95
|
da_resolve() {
|
|
@@ -82,16 +82,16 @@ fixture_cleanup() {
|
|
|
82
82
|
if [ "${SKIP_FIXTURE_BUILDER:-0}" = "1" ]; then
|
|
83
83
|
return 0
|
|
84
84
|
fi
|
|
85
|
-
local
|
|
86
|
-
if [ ! -e "$
|
|
85
|
+
local p="${1:?fixture_cleanup requires path}"
|
|
86
|
+
if [ ! -e "$p" ]; then
|
|
87
87
|
# Idempotent — nothing to do
|
|
88
88
|
return 0
|
|
89
89
|
fi
|
|
90
|
-
if [ ! -f "$
|
|
91
|
-
echo "fixture_cleanup: refusing to remove path without sentinel: $
|
|
90
|
+
if [ ! -f "$p/$_FIXTURE_SENTINEL" ]; then
|
|
91
|
+
echo "fixture_cleanup: refusing to remove path without sentinel: $p" >&2
|
|
92
92
|
return 1
|
|
93
93
|
fi
|
|
94
|
-
rm -rf "$
|
|
94
|
+
rm -rf "$p"
|
|
95
95
|
return 0
|
|
96
96
|
}
|
|
97
97
|
|
|
@@ -105,10 +105,10 @@ fixture_cleanup_all() {
|
|
|
105
105
|
return 0
|
|
106
106
|
fi
|
|
107
107
|
local IFS=$'\n'
|
|
108
|
-
local
|
|
109
|
-
for
|
|
110
|
-
[ -n "$
|
|
111
|
-
fixture_cleanup "$
|
|
108
|
+
local p
|
|
109
|
+
for p in $_FIXTURE_REGISTRY; do
|
|
110
|
+
[ -n "$p" ] || continue
|
|
111
|
+
fixture_cleanup "$p" 2>/dev/null || true
|
|
112
112
|
done
|
|
113
113
|
_FIXTURE_REGISTRY=""
|
|
114
114
|
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# tier: lite
|
|
3
|
+
# Operator-identifier leak check for staged git content.
|
|
4
|
+
#
|
|
5
|
+
# Shared across:
|
|
6
|
+
# - .claude/hooks/save-state.sh (inline before `git commit`;
|
|
7
|
+
# bypasses pre-commit-gate.sh because save-state runs git
|
|
8
|
+
# directly, not through the agent's PreToolUse path)
|
|
9
|
+
# - future: .claude/hooks/pre-commit-gate.sh CCF-3 (unification
|
|
10
|
+
# deferred to a follow-on; shape intentionally matches CCF-3
|
|
11
|
+
# today so the two can merge without semantic drift)
|
|
12
|
+
#
|
|
13
|
+
# Rejects staged content carrying absolute operator paths matching
|
|
14
|
+
# /Users/<name>/ or /home/<name>/. Mirrors pre-commit-gate.sh:50-53
|
|
15
|
+
# exemptions so policy docs that quote the pattern don't trip.
|
|
16
|
+
#
|
|
17
|
+
# Per #2036 Finding #1 (adopter report 2026-10-02).
|
|
18
|
+
# Per @luminary nygard — stability pattern (circuit breaker on commit).
|
|
19
|
+
# Per @luminary hoare — precondition contract before `git commit`.
|
|
20
|
+
|
|
21
|
+
# check_staged_for_leaks — scan git-staged content for operator paths.
|
|
22
|
+
#
|
|
23
|
+
# Returns 0 when staged content is clean OR when every leaking file
|
|
24
|
+
# sits in an exempt path (policy docs, rules, luminaries).
|
|
25
|
+
# Returns 1 when staged content outside exempt paths carries a leak.
|
|
26
|
+
# On exit 1, every leaking file is named on stderr with line number
|
|
27
|
+
# and the matched pattern.
|
|
28
|
+
check_staged_for_leaks() {
|
|
29
|
+
# Staged non-exempt files. Match pre-commit-gate.sh:46-53 exemption shape
|
|
30
|
+
# so the two gates agree on which paths carry policy docs legitimately
|
|
31
|
+
# quoting operator paths.
|
|
32
|
+
local staged
|
|
33
|
+
staged=$(git diff --cached --name-only --diff-filter=AM 2>/dev/null | \
|
|
34
|
+
grep -vE '^\.claude/(rules|hooks|skills|luminaries|agents)/' | \
|
|
35
|
+
grep -vE '^standards/' | \
|
|
36
|
+
grep -vE '^architecture/' | \
|
|
37
|
+
grep -vE '^design/' | \
|
|
38
|
+
grep -vE '^docs/standards/' | \
|
|
39
|
+
grep -vE '^docs/rules/' | \
|
|
40
|
+
grep -vE '^chronicle/' | \
|
|
41
|
+
grep -vE '^strategy/' | \
|
|
42
|
+
grep -vE '^scripts/tests/' || true)
|
|
43
|
+
|
|
44
|
+
[ -z "$staged" ] && return 0
|
|
45
|
+
|
|
46
|
+
local leaked=""
|
|
47
|
+
while IFS= read -r f; do
|
|
48
|
+
[ -z "$f" ] && continue
|
|
49
|
+
[ ! -f "$f" ] && continue
|
|
50
|
+
local matches
|
|
51
|
+
matches=$(grep -nE '/Users/[A-Za-z0-9_.-]+|/home/[A-Za-z0-9_.-]+' "$f" 2>/dev/null || true)
|
|
52
|
+
if [ -n "$matches" ]; then
|
|
53
|
+
leaked="${leaked}
|
|
54
|
+
${f}:
|
|
55
|
+
${matches}
|
|
56
|
+
"
|
|
57
|
+
fi
|
|
58
|
+
done <<< "$staged"
|
|
59
|
+
|
|
60
|
+
if [ -n "$leaked" ]; then
|
|
61
|
+
{
|
|
62
|
+
echo ""
|
|
63
|
+
echo "============================================"
|
|
64
|
+
echo "BLOCKED: operator path in staged content (#2036 Finding #1)"
|
|
65
|
+
echo "============================================"
|
|
66
|
+
echo "$leaked"
|
|
67
|
+
echo ""
|
|
68
|
+
echo "Operator-specific paths (/Users/<name>, /home/<name>) must NOT"
|
|
69
|
+
echo "ship in adopter git. Common cause: docs/sdlc-traces/*.log"
|
|
70
|
+
echo "carrying absolute paths from PreToolUse hook callers."
|
|
71
|
+
echo ""
|
|
72
|
+
echo "Fixes:"
|
|
73
|
+
echo " 1. Delete the staged file if it's a trace log auto-generated"
|
|
74
|
+
echo " (gitignored going forward per #2036 Finding #1 cure)"
|
|
75
|
+
echo " 2. Rewrite the path as repo-relative"
|
|
76
|
+
echo " 3. Move policy docs under docs/standards/ or docs/rules/ (exempt)"
|
|
77
|
+
echo ""
|
|
78
|
+
echo "Policy: standards/two-layer-config.md (bassclef#740)"
|
|
79
|
+
echo "============================================"
|
|
80
|
+
} >&2
|
|
81
|
+
return 1
|
|
82
|
+
fi
|
|
83
|
+
|
|
84
|
+
return 0
|
|
85
|
+
}
|