@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
|
@@ -49,14 +49,14 @@ ps_exists() {
|
|
|
49
49
|
|
|
50
50
|
ps_create() {
|
|
51
51
|
local slug="$1"
|
|
52
|
-
local
|
|
53
|
-
|
|
54
|
-
mkdir -p "$(dirname "$
|
|
55
|
-
if [[ -f "$
|
|
56
|
-
echo "ps_create: already exists: $
|
|
52
|
+
local p
|
|
53
|
+
p="$(ps_path "$slug")"
|
|
54
|
+
mkdir -p "$(dirname "$p")"
|
|
55
|
+
if [[ -f "$p" ]]; then
|
|
56
|
+
echo "ps_create: already exists: $p" >&2
|
|
57
57
|
return 1
|
|
58
58
|
fi
|
|
59
|
-
cat > "$
|
|
59
|
+
cat > "$p" <<EOF
|
|
60
60
|
slug: ${slug}
|
|
61
61
|
created: $(date -u +'%Y-%m-%dT%H:%M:%S+0000')
|
|
62
62
|
current_tier: pending
|
|
@@ -73,16 +73,16 @@ EOF
|
|
|
73
73
|
ps_get() {
|
|
74
74
|
local slug="$1"
|
|
75
75
|
local field="$2"
|
|
76
|
-
local
|
|
77
|
-
|
|
78
|
-
[[ -f "$
|
|
76
|
+
local p
|
|
77
|
+
p="$(ps_path "$slug")"
|
|
78
|
+
[[ -f "$p" ]] || { echo "ps_get: not-found: $p" >&2; return 1; }
|
|
79
79
|
awk -v f="$field" '
|
|
80
80
|
$0 ~ "^" f ":" {
|
|
81
81
|
sub("^" f ":[[:space:]]*", "")
|
|
82
82
|
print
|
|
83
83
|
exit
|
|
84
84
|
}
|
|
85
|
-
' "$
|
|
85
|
+
' "$p"
|
|
86
86
|
}
|
|
87
87
|
|
|
88
88
|
ps_set() {
|
|
@@ -90,9 +90,9 @@ ps_set() {
|
|
|
90
90
|
local field="$2"
|
|
91
91
|
shift 2
|
|
92
92
|
local value="$*"
|
|
93
|
-
local
|
|
94
|
-
|
|
95
|
-
[[ -f "$
|
|
93
|
+
local p
|
|
94
|
+
p="$(ps_path "$slug")"
|
|
95
|
+
[[ -f "$p" ]] || { echo "ps_set: not-found: $p" >&2; return 1; }
|
|
96
96
|
local tmp
|
|
97
97
|
tmp="$(mktemp)"
|
|
98
98
|
awk -v f="$field" -v v="$value" '
|
|
@@ -108,8 +108,8 @@ ps_set() {
|
|
|
108
108
|
print f ": " v
|
|
109
109
|
}
|
|
110
110
|
}
|
|
111
|
-
' "$
|
|
112
|
-
mv "$tmp" "$
|
|
111
|
+
' "$p" > "$tmp"
|
|
112
|
+
mv "$tmp" "$p"
|
|
113
113
|
}
|
|
114
114
|
|
|
115
115
|
ps_status() {
|
|
@@ -146,9 +146,9 @@ ps_archive() {
|
|
|
146
146
|
ps_reset() {
|
|
147
147
|
local slug="$1"
|
|
148
148
|
local reason="${2:-no reason given}"
|
|
149
|
-
local
|
|
150
|
-
|
|
151
|
-
[[ -f "$
|
|
149
|
+
local p
|
|
150
|
+
p="$(ps_path "$slug")"
|
|
151
|
+
[[ -f "$p" ]] || { echo "ps_reset: not-found: $p" >&2; return 1; }
|
|
152
152
|
|
|
153
153
|
local now
|
|
154
154
|
now="$(date -u +'%Y-%m-%dT%H:%M:%S+0000')"
|
|
@@ -188,8 +188,8 @@ EOF
|
|
|
188
188
|
next
|
|
189
189
|
}
|
|
190
190
|
{ print }
|
|
191
|
-
' "$
|
|
192
|
-
mv "$tmp" "$
|
|
191
|
+
' "$p" > "$tmp"
|
|
192
|
+
mv "$tmp" "$p"
|
|
193
193
|
rm -f "$entry_file"
|
|
194
194
|
|
|
195
195
|
# Reset cycle-state fields
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# tier: lite
|
|
3
|
+
# SKIP_* env-reach check for PreToolUse Bash hooks.
|
|
4
|
+
#
|
|
5
|
+
# Per #2036 Finding #4 — adopter report 2026-10-02.
|
|
6
|
+
#
|
|
7
|
+
# PreToolUse Bash hooks run in Claude Code's process env. The agent's
|
|
8
|
+
# `SKIP_FOO=1 git commit ...` recipe sets FOO in the git subshell's env,
|
|
9
|
+
# not the hook's env. Hooks that only read `${SKIP_FOO:-0}` miss the
|
|
10
|
+
# inline override and the recipe lies to the adopter.
|
|
11
|
+
#
|
|
12
|
+
# This helper parses tool_input.command for inline `SKIP_FOO=1` and
|
|
13
|
+
# falls back to process env. The adopter's natural recipe
|
|
14
|
+
# `SKIP_FOO=1 cmd` now works regardless of harness env propagation.
|
|
15
|
+
#
|
|
16
|
+
# Retirement path: once Claude Code harness guarantees inline-env
|
|
17
|
+
# propagation to hook subprocesses, this helper becomes redundant.
|
|
18
|
+
# Comment stays as the audit trail.
|
|
19
|
+
#
|
|
20
|
+
# Usage:
|
|
21
|
+
# source "$CLAUDE_PROJECT_DIR/lib/skip-env-inline-check.sh"
|
|
22
|
+
# COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // ""')
|
|
23
|
+
# if skip_var_set "SKIP_FOO" "$COMMAND"; then
|
|
24
|
+
# # bypass
|
|
25
|
+
# exit 0
|
|
26
|
+
# fi
|
|
27
|
+
#
|
|
28
|
+
# Anchors:
|
|
29
|
+
# @luminary john-ousterhout — deep module; narrow interface
|
|
30
|
+
# @luminary david-parnas — information hiding; consumer-free of mechanism
|
|
31
|
+
# @luminary alan-cooper — adopter-goal reliable override recipe
|
|
32
|
+
|
|
33
|
+
# skip_var_set — return 0 if the named SKIP_* var is set to "1".
|
|
34
|
+
# Checks two sources:
|
|
35
|
+
# 1. Inline prefix in command string: `SKIP_FOO=1 cmd...`
|
|
36
|
+
# 2. Fallback: process env `$SKIP_FOO`
|
|
37
|
+
# Returns 1 otherwise.
|
|
38
|
+
#
|
|
39
|
+
# Args:
|
|
40
|
+
# $1 — var name (e.g., "SKIP_OPERATOR_PATHS")
|
|
41
|
+
# $2 — command string (from tool_input.command)
|
|
42
|
+
skip_var_set() {
|
|
43
|
+
local var_name="$1"
|
|
44
|
+
local cmd="$2"
|
|
45
|
+
|
|
46
|
+
# Source 1 — inline prefix. The pattern anchors on word boundary (start
|
|
47
|
+
# of string or whitespace) and requires =1 followed by whitespace or
|
|
48
|
+
# end-of-string. This matches bash's own parsing of inline env prefix.
|
|
49
|
+
if [ -n "$cmd" ]; then
|
|
50
|
+
if echo "$cmd" | grep -qE "(^|[[:space:]])${var_name}=1([[:space:]]|$)"; then
|
|
51
|
+
return 0
|
|
52
|
+
fi
|
|
53
|
+
fi
|
|
54
|
+
|
|
55
|
+
# Source 2 — process env fallback (works where harness propagates env).
|
|
56
|
+
if [ "$(eval "echo \"\${${var_name}:-0}\"")" = "1" ]; then
|
|
57
|
+
return 0
|
|
58
|
+
fi
|
|
59
|
+
|
|
60
|
+
return 1
|
|
61
|
+
}
|
package/dist/lite/lib/state.sh
CHANGED
|
@@ -186,17 +186,19 @@ __state_atomic_write() {
|
|
|
186
186
|
# crash caller).
|
|
187
187
|
__state_yaml_write() {
|
|
188
188
|
__state_check_binary_deps || return 4
|
|
189
|
-
local path
|
|
189
|
+
# Finding #6 (#2036): `local path` is a zsh-magic variable name — ties to
|
|
190
|
+
# $PATH when sourced from zsh. Renamed to `local p` throughout this file.
|
|
191
|
+
local p="$1"
|
|
190
192
|
local field="$2"
|
|
191
193
|
local value="$3"
|
|
192
|
-
if [ -z "$
|
|
194
|
+
if [ -z "$p" ] || [ -z "$field" ]; then
|
|
193
195
|
echo "__state_yaml_write: requires <path> <field> <value>" >&2
|
|
194
196
|
return 2
|
|
195
197
|
fi
|
|
196
198
|
local dir
|
|
197
|
-
dir="$(dirname "$
|
|
199
|
+
dir="$(dirname "$p")"
|
|
198
200
|
[ -d "$dir" ] || mkdir -p "$dir" || return 4
|
|
199
|
-
local tmp="${
|
|
201
|
+
local tmp="${p}.tmp.$$"
|
|
200
202
|
BASSCLEF_YAML_FIELD="$field" \
|
|
201
203
|
BASSCLEF_YAML_VALUE="$value" \
|
|
202
204
|
BASSCLEF_YAML_TMP="$tmp" \
|
|
@@ -213,7 +215,7 @@ except Exception as e:
|
|
|
213
215
|
print(f"__state_yaml_write: safe_dump failed: {e}", file=sys.stderr)
|
|
214
216
|
sys.exit(3)
|
|
215
217
|
' 2>&1 || { rm -f "$tmp"; return 3; }
|
|
216
|
-
mv "$tmp" "$
|
|
218
|
+
mv "$tmp" "$p" || { rm -f "$tmp"; return 4; }
|
|
217
219
|
}
|
|
218
220
|
|
|
219
221
|
# === Whereami (Shape A — JSON authoritative + markdown rendered) ===
|
|
@@ -239,21 +241,22 @@ state_whereami_get() {
|
|
|
239
241
|
state_whereami_set() {
|
|
240
242
|
local field="$1"
|
|
241
243
|
local value="$2"
|
|
242
|
-
local
|
|
244
|
+
local p="docs/whereami.json"
|
|
243
245
|
if [ -z "$field" ] || [ -z "$value" ]; then
|
|
244
246
|
echo "state_whereami_set: requires <field> <value>" >&2
|
|
245
247
|
return 2
|
|
246
248
|
fi
|
|
247
|
-
if [ ! -f "$
|
|
249
|
+
if [ ! -f "$p" ]; then
|
|
248
250
|
echo "state_whereami_set: docs/whereami.json does not exist" >&2
|
|
249
251
|
return 4
|
|
250
252
|
fi
|
|
251
253
|
local updated
|
|
252
|
-
# Use jq's setpath for nested field paths (e.g., 'phase.value')
|
|
254
|
+
# Use jq's setpath for nested field paths (e.g., 'phase.value').
|
|
255
|
+
# The jq `--arg path` names a jq variable (not shell) — safe to keep.
|
|
253
256
|
updated="$(jq --arg path "$field" --arg val "$value" '
|
|
254
257
|
setpath($path | split("."); $val)
|
|
255
|
-
' "$
|
|
256
|
-
__state_atomic_write "$
|
|
258
|
+
' "$p")" || return 3
|
|
259
|
+
__state_atomic_write "$p" "$updated"
|
|
257
260
|
}
|
|
258
261
|
|
|
259
262
|
# === IterationBet (Shape B — markdown + YAML frontmatter + body) ===
|
|
@@ -264,11 +267,11 @@ state_iteration_bet_get() {
|
|
|
264
267
|
echo "state_iteration_bet_get: requires <slug>" >&2
|
|
265
268
|
return 2
|
|
266
269
|
fi
|
|
267
|
-
local
|
|
268
|
-
if [ ! -f "$
|
|
270
|
+
local p="docs/iteration-bets/${slug}.md"
|
|
271
|
+
if [ ! -f "$p" ]; then
|
|
269
272
|
return 1
|
|
270
273
|
fi
|
|
271
|
-
__state_yq2json "$
|
|
274
|
+
__state_yq2json "$p"
|
|
272
275
|
}
|
|
273
276
|
|
|
274
277
|
# Special-purpose: returns the single active iteration bet (status=active),
|
|
@@ -333,25 +336,25 @@ state_roadmap_get() {
|
|
|
333
336
|
echo "state_roadmap_get: requires <slug>" >&2
|
|
334
337
|
return 2
|
|
335
338
|
fi
|
|
336
|
-
local
|
|
337
|
-
if [ ! -f "$
|
|
339
|
+
local p="docs/roadmaps/${slug}.md"
|
|
340
|
+
if [ ! -f "$p" ]; then
|
|
338
341
|
return 1
|
|
339
342
|
fi
|
|
340
|
-
__state_yq2json "$
|
|
343
|
+
__state_yq2json "$p"
|
|
341
344
|
}
|
|
342
345
|
|
|
343
346
|
# Internal: rewrite a roadmap's frontmatter from JSON, preserving the body.
|
|
344
347
|
__state_roadmap_write_frontmatter() {
|
|
345
348
|
local slug="$1"
|
|
346
349
|
local frontmatter_json="$2"
|
|
347
|
-
local
|
|
348
|
-
if [ ! -f "$
|
|
349
|
-
echo "__state_roadmap_write_frontmatter: roadmap $
|
|
350
|
+
local p="docs/roadmaps/${slug}.md"
|
|
351
|
+
if [ ! -f "$p" ]; then
|
|
352
|
+
echo "__state_roadmap_write_frontmatter: roadmap $p not found" >&2
|
|
350
353
|
return 1
|
|
351
354
|
fi
|
|
352
355
|
python3 -c "
|
|
353
356
|
import sys, yaml, json
|
|
354
|
-
path = '$
|
|
357
|
+
path = '$p'
|
|
355
358
|
fm = json.loads('''$frontmatter_json''')
|
|
356
359
|
with open(path, 'r') as f:
|
|
357
360
|
content = f.read()
|
|
@@ -726,15 +729,15 @@ state_chronicle_gate_evidence() {
|
|
|
726
729
|
return 2
|
|
727
730
|
fi
|
|
728
731
|
# Try bassclef (chronicle/) and consumer-repo (docs/chronicle/) paths
|
|
729
|
-
local
|
|
732
|
+
local p=""
|
|
730
733
|
if [ -f "chronicle/${id}.md" ]; then
|
|
731
|
-
|
|
734
|
+
p="chronicle/${id}.md"
|
|
732
735
|
elif [ -f "docs/chronicle/${id}.md" ]; then
|
|
733
|
-
|
|
736
|
+
p="docs/chronicle/${id}.md"
|
|
734
737
|
else
|
|
735
738
|
return 1
|
|
736
739
|
fi
|
|
737
|
-
__state_yq2json "$
|
|
740
|
+
__state_yq2json "$p" | jq -c '.gates_fired // []'
|
|
738
741
|
}
|
|
739
742
|
|
|
740
743
|
# List chronicle IDs (filename without .md extension), optionally limited
|
|
@@ -1081,16 +1084,16 @@ __state_platform_resolve_path() {
|
|
|
1081
1084
|
|
|
1082
1085
|
state_platform_get() {
|
|
1083
1086
|
local key="${1:-}"
|
|
1084
|
-
local
|
|
1085
|
-
|
|
1086
|
-
if [ ! -f "$
|
|
1087
|
+
local p
|
|
1088
|
+
p="$(__state_platform_resolve_path)"
|
|
1089
|
+
if [ ! -f "$p" ]; then
|
|
1087
1090
|
return 1
|
|
1088
1091
|
fi
|
|
1089
1092
|
local json
|
|
1090
1093
|
json="$(python3 -c "
|
|
1091
1094
|
import sys, yaml, json
|
|
1092
1095
|
try:
|
|
1093
|
-
with open('$
|
|
1096
|
+
with open('$p') as f:
|
|
1094
1097
|
data = yaml.safe_load(f) or {}
|
|
1095
1098
|
print(json.dumps(data, default=str))
|
|
1096
1099
|
except Exception as e:
|
|
@@ -131,35 +131,35 @@ PYEOF
|
|
|
131
131
|
# Mirrors detection shape from .claude/hooks/tier-tag-required-at-write.sh so
|
|
132
132
|
# the reader half and the writer half stay consistent.
|
|
133
133
|
resolve_file_tier() {
|
|
134
|
-
local
|
|
135
|
-
if [ ! -f "$
|
|
134
|
+
local p="$1"
|
|
135
|
+
if [ ! -f "$p" ]; then
|
|
136
136
|
return 0
|
|
137
137
|
fi
|
|
138
138
|
|
|
139
139
|
local tier=""
|
|
140
|
-
case "$
|
|
140
|
+
case "$p" in
|
|
141
141
|
*.md)
|
|
142
142
|
# Frontmatter between --- markers, first 30 lines
|
|
143
143
|
tier=$(awk '
|
|
144
144
|
/^---/ { in_fm = !in_fm; if (!in_fm) exit; next }
|
|
145
145
|
in_fm && /^tier:[[:space:]]/ { sub(/^tier:[[:space:]]*/, ""); sub(/[[:space:]]*$/, ""); print; exit }
|
|
146
146
|
NR > 30 { exit }
|
|
147
|
-
' "$
|
|
147
|
+
' "$p" 2>/dev/null)
|
|
148
148
|
;;
|
|
149
149
|
*.sh|*.py)
|
|
150
|
-
tier=$(head -10 "$
|
|
150
|
+
tier=$(head -10 "$p" 2>/dev/null | grep -E '^#[[:space:]]*tier:[[:space:]]' | head -1 | sed -E 's/^#[[:space:]]*tier:[[:space:]]*//; s/[[:space:]]*$//')
|
|
151
151
|
;;
|
|
152
152
|
*.yml|*.yaml)
|
|
153
|
-
tier=$(head -30 "$
|
|
153
|
+
tier=$(head -30 "$p" 2>/dev/null | grep -E '^#[[:space:]]*tier:[[:space:]]|^tier:[[:space:]]' | head -1 | sed -E 's/^#?[[:space:]]*tier:[[:space:]]*//; s/[[:space:]]*$//')
|
|
154
154
|
;;
|
|
155
155
|
*.json|*.jsonc)
|
|
156
|
-
tier=$(head -30 "$
|
|
156
|
+
tier=$(head -30 "$p" 2>/dev/null | grep -oE '"(_?tier)"[[:space:]]*:[[:space:]]*"[a-z]+"' | head -1 | sed -E 's/.*:[[:space:]]*"([a-z]+)"/\1/')
|
|
157
157
|
if [ -z "$tier" ]; then
|
|
158
|
-
tier=$(head -30 "$
|
|
158
|
+
tier=$(head -30 "$p" 2>/dev/null | grep -oE '//[[:space:]]*tier:[[:space:]]*[a-z]+' | head -1 | sed -E 's/.*tier:[[:space:]]*//')
|
|
159
159
|
fi
|
|
160
160
|
;;
|
|
161
161
|
*.txt)
|
|
162
|
-
tier=$(head -1 "$
|
|
162
|
+
tier=$(head -1 "$p" 2>/dev/null | grep -E '^#[[:space:]]*tier:[[:space:]]' | sed -E 's/^#[[:space:]]*tier:[[:space:]]*//; s/[[:space:]]*$//')
|
|
163
163
|
;;
|
|
164
164
|
esac
|
|
165
165
|
|
|
@@ -364,15 +364,15 @@ walk_graph_bfs() {
|
|
|
364
364
|
while [ -s "$queue_file" ] && [ "$iter" -lt "$max_iter" ]; do
|
|
365
365
|
iter=$((iter + 1))
|
|
366
366
|
local next_file; next_file="$(mktemp)"
|
|
367
|
-
while IFS= read -r
|
|
368
|
-
[ -z "$
|
|
367
|
+
while IFS= read -r p; do
|
|
368
|
+
[ -z "$p" ] && continue
|
|
369
369
|
# Skip if visited
|
|
370
|
-
if grep -Fxq "$
|
|
370
|
+
if grep -Fxq "$p" "$visited_file" 2>/dev/null; then
|
|
371
371
|
continue
|
|
372
372
|
fi
|
|
373
|
-
echo "$
|
|
373
|
+
echo "$p" >> "$visited_file"
|
|
374
374
|
# Extract references from this file
|
|
375
|
-
"$extractor" "$
|
|
375
|
+
"$extractor" "$p" 2>/dev/null | while IFS= read -r ref; do
|
|
376
376
|
[ -n "$ref" ] && echo "$ref" >> "$next_file"
|
|
377
377
|
done
|
|
378
378
|
done < "$queue_file"
|
|
@@ -504,17 +504,17 @@ classify_files() {
|
|
|
504
504
|
# tag == target tier → bucket 1 (ships correctly in this tier)
|
|
505
505
|
# no tag at all → bucket 2a (safe mechanical tag)
|
|
506
506
|
# tag == other tier → bucket 2b (chain-snap risk; per-file cure)
|
|
507
|
-
comm -12 "$intended_sorted" "$actual_sorted" | while IFS= read -r
|
|
507
|
+
comm -12 "$intended_sorted" "$actual_sorted" | while IFS= read -r p; do
|
|
508
508
|
# Look up tag for path in tags file. Returns "" for untagged (no tab)
|
|
509
509
|
# or missing entry; returns tier value when tagged.
|
|
510
510
|
local file_tag
|
|
511
|
-
file_tag="$(awk -F'\t' -v p="$
|
|
511
|
+
file_tag="$(awk -F'\t' -v p="$p" '$1 == p { print ($2 ? $2 : ""); exit }' "$tags")"
|
|
512
512
|
if [ "$file_tag" = "$tier" ]; then
|
|
513
|
-
echo "$
|
|
513
|
+
echo "$p" >> "$bucket_1"
|
|
514
514
|
elif [ -z "$file_tag" ]; then
|
|
515
|
-
echo "$
|
|
515
|
+
echo "$p" >> "$bucket_2a"
|
|
516
516
|
else
|
|
517
|
-
echo "$
|
|
517
|
+
echo "$p" >> "$bucket_2b"
|
|
518
518
|
fi
|
|
519
519
|
done
|
|
520
520
|
|
|
@@ -557,13 +557,13 @@ classify_files_combined() {
|
|
|
557
557
|
|
|
558
558
|
# Bucket 1 + Bucket 2 — split intersection by tag presence.
|
|
559
559
|
# In combined mode, ANY non-empty tag places the file in bucket 1.
|
|
560
|
-
comm -12 "$intended_sorted" "$actual_sorted" | while IFS= read -r
|
|
560
|
+
comm -12 "$intended_sorted" "$actual_sorted" | while IFS= read -r p; do
|
|
561
561
|
local file_tag
|
|
562
|
-
file_tag="$(awk -F'\t' -v p="$
|
|
562
|
+
file_tag="$(awk -F'\t' -v p="$p" '$1 == p { print ($2 ? $2 : ""); exit }' "$tags")"
|
|
563
563
|
if [ -n "$file_tag" ]; then
|
|
564
|
-
echo "$
|
|
564
|
+
echo "$p" >> "$bucket_1"
|
|
565
565
|
else
|
|
566
|
-
echo "$
|
|
566
|
+
echo "$p" >> "$bucket_2"
|
|
567
567
|
fi
|
|
568
568
|
done
|
|
569
569
|
|
|
@@ -738,12 +738,12 @@ write_manifest_combined() {
|
|
|
738
738
|
# - SDLC trace logs
|
|
739
739
|
# ---------------------------------------------------------------------------
|
|
740
740
|
_is_substrate_relevant_path() {
|
|
741
|
-
local
|
|
741
|
+
local p="$1"
|
|
742
742
|
# Normalize leading ./ prefix so patterns match consistently.
|
|
743
743
|
# Closure files mix "./foo" (from find) with "foo" (from git grep).
|
|
744
744
|
# Per #1385 walker cure — extends G3 filter beyond initial pattern set.
|
|
745
|
-
|
|
746
|
-
case "$
|
|
745
|
+
p="${p#./}"
|
|
746
|
+
case "$p" in
|
|
747
747
|
/*|../*|.git/*|*/.git/*) return 1 ;;
|
|
748
748
|
# Runtime state — walker outputs + gate markers + session state + analysis.
|
|
749
749
|
# None ship to adopters regardless of tier tag. Per #1385.
|
|
@@ -831,12 +831,12 @@ walk_graph_bfs_inbound() {
|
|
|
831
831
|
while [ -s "$queue_file" ] && [ "$iter" -lt "$max_iter" ]; do
|
|
832
832
|
iter=$((iter + 1))
|
|
833
833
|
local next_file; next_file="$(mktemp)"
|
|
834
|
-
while IFS= read -r
|
|
835
|
-
[ -z "$
|
|
836
|
-
if grep -Fxq "$
|
|
837
|
-
echo "$
|
|
838
|
-
# Look up callers of $
|
|
839
|
-
awk -F'\t' -v p="$
|
|
834
|
+
while IFS= read -r p; do
|
|
835
|
+
[ -z "$p" ] && continue
|
|
836
|
+
if grep -Fxq "$p" "$visited_file" 2>/dev/null; then continue; fi
|
|
837
|
+
echo "$p" >> "$visited_file"
|
|
838
|
+
# Look up callers of $p in the reverse index (path is column 1; caller is column 2)
|
|
839
|
+
awk -F'\t' -v p="$p" '$1 == p { print $2 }' "$rev_index" | while IFS= read -r caller; do
|
|
840
840
|
[ -n "$caller" ] && echo "$caller" >> "$next_file"
|
|
841
841
|
done
|
|
842
842
|
done < "$queue_file"
|
|
@@ -110,7 +110,7 @@ Unregister-ScheduledTask -TaskName "BassclefAutoSave" -Confirm:$false
|
|
|
110
110
|
|
|
111
111
|
## Alternatives
|
|
112
112
|
|
|
113
|
-
- **WSL2 available?** Skip this doc; use `presence/install/schedule-auto-save.cron.sh` inside WSL. Cron there watches your Windows filesystem via `/mnt/c
|
|
113
|
+
- **WSL2 available?** Skip this doc; use `presence/install/schedule-auto-save.cron.sh` inside WSL. Cron there watches your Windows filesystem via the mounted `/mnt/c/` tree under your user profile.
|
|
114
114
|
- **Prefer a .ps1 installer?** File a ticket. The current ship stays doc-only to avoid installer complexity for a population not yet observed. Path B (per-OS installers) upgrade is a follow-on.
|
|
115
115
|
|
|
116
116
|
## References
|
|
@@ -34,6 +34,44 @@ Every entry stays in this file for the life of the manifest. Never pruned. When
|
|
|
34
34
|
|
|
35
35
|
## Entries (newest first)
|
|
36
36
|
|
|
37
|
+
### #1777 — friction hook pattern adds stage|launch new skill vocabulary (v1.14.2 → v1.14.3, 2026-10-01)
|
|
38
|
+
|
|
39
|
+
- **Change type:** content-change (1 file: `post-skill-friction-check` hook)
|
|
40
|
+
- **Fields:** 439 entries → 439 entries (no add/remove). Content hash shifts on `post-skill-friction-check` slug (MAJOR_SKILLS_PATTERN regex extended with stage|launch per ADR-040 D1 rename grace window).
|
|
41
|
+
- **Version bump:** v1.14.2 → v1.14.3 (patch — content-only change; no field add/remove; no shape shift). Rebased after #523's retirement-discipline cure landed as v1.14.2.
|
|
42
|
+
- **Rationale:** PR #2028 closes #1777 (friction log never created because hook pattern matched old skill names only). Additive regex extension; grace-window compat preserved (preview|preview-build still match).
|
|
43
|
+
- **Downstream cure:** none — adopters re-sync and the hook starts firing on current-vocabulary chain dispatches. Lazy-create of `docs/session-friction-log.md` happens on first real friction case.
|
|
44
|
+
- **Old shape retired:** old pattern retired `2026-10-31` (grace window end for ADR-040 D1 vocabulary); current pattern keeps both until then.
|
|
45
|
+
|
|
46
|
+
### #523 — retirement discipline + fixture allowlist pattern (v1.14.1 → v1.14.2, 2026-10-01)
|
|
47
|
+
|
|
48
|
+
- **Change type:** content-change (2 files: `rule/lite-manifest-schema-change-discipline` + `standard/lite-manifest-schema-changes`)
|
|
49
|
+
- **Fields:** 439 entries (unchanged). Content hashes shift on `.claude/rules/lite-manifest-schema-change-discipline.md` (gains a `## Retirements` section covering retirement-class discipline alongside additions) and `standards/lite-manifest-schema-changes.md` (gains the v1.14.0 entry for #1992 that this PR backfilled plus this v1.14.2 entry).
|
|
50
|
+
- **Version bump:** v1.14.1 → v1.14.2 (patch; content-only change on 2 slugs; no schema shape shift; no entry count change).
|
|
51
|
+
- **Rationale:** sunj-labs/bassclef-upstream#523 closed. 4 consecutive main-CI failures 2026-09-29 to 09-30 (SHAs `3c997584`, `02da6b8d`, `924d489f` on `substrate-check-phase`) traced to Test 16 — the Feathers v1.6.1 snapshot guard — seeing 2 entries drop after PR #1992's compat shim retirement. Prior addition-class discipline fired a mechanical ledger-pre-flight gate; retirement-class did not. This PR extends the existing rule with a `## Retirements` section (per Ousterhout contrarian: one rule, two sections) + ships a fixture allowlist pattern (`.claude/hooks/tests/fixtures/2026-09-16-lite-catalog-closure/retired-entries.txt`) + updates Test 16 to subtract allowlist rows before asserting zero drops + backfills the v1.14.0 ledger entry #1992 missed.
|
|
52
|
+
- **Downstream cure:** none — adopters at `~1.14` get the rule content update at next sync. Any adopter authoring a retirement PR going forward follows the `## Retirements` section. Mechanical pre-commit hook (`pre-retire-discipline-check.sh`) deferred to a follow-on PR per Ousterhout (keep this PR scope-tight).
|
|
53
|
+
- **Old shape retired:** none — additive discipline extension.
|
|
54
|
+
- **Protects:** 4 upcoming retirement tickets (#266 S3 → npm move, #108 ADR-020 prototype deprecation, #1142 structural rename sweep, #1999 launch cadence rules) ship under the extended discipline.
|
|
55
|
+
|
|
56
|
+
### #1992 — compounding-axis compat shim retirement (v1.13.2 → v1.14.0, 2026-09-28)
|
|
57
|
+
|
|
58
|
+
- **Change type:** content-remove (2 entries dropped)
|
|
59
|
+
- **Fields:** 441 entries → 439 entries. Removed entries: `hook/longrun-prep-compounding-axis-check` (`.claude/hooks/longrun-prep-compounding-axis-check.sh`) + `rule/compounding-axis-fresh-analysis` (`.claude/rules/compounding-axis-fresh-analysis.md`). Both were compat shim symlinks from the 2026-07-03 compounding-axis → compounding-sequence rename per PR #611 (bet 2026-07-03b WU-2).
|
|
60
|
+
- **Version bump:** v1.13.2 → v1.14.0 (MINOR — content-remove per bump policy; adopters pinning major stay green, adopters pinning exact version must update).
|
|
61
|
+
- **Rationale:** sunj-labs/bassclef-upstream#1989 — compat shim grace window (ADR-031 default) elapsed on 2026-10-03; harness scanned each symlink twice inflating the rule set at session start. Grep across `standards/bassclef-source-consumers.json` returned zero adopter pins on the old slug, so the shim was defensive dogma rather than adopter-driven. Both symlinks + the duplicate embedding JSON deleted at SHA `9884278f`. Rename targets (`compounding-sequence-fresh-analysis.md` + `longrun-prep-compounding-sequence-check.sh`) stay live under their post-rename names.
|
|
62
|
+
- **Downstream cure:** none for adopters at `~1.13` or `~1.14`. Adopters pinning exact `manifest_version` (bassclef-web#256 + bassclef-cli#51) update pins at the next sync. Any adopter code reading `.entries[]` by legacy `compounding-axis` slug finds nothing — switch reads to `compounding-sequence` sister slugs.
|
|
63
|
+
- **Old shape retired:** immediately. ADR-031 grace window on compat shim ran its full term (2026-07-03 → 2026-10-03); no further compat preservation owed.
|
|
64
|
+
- **Retirement allowlist:** `.claude/hooks/tests/fixtures/2026-09-16-lite-catalog-closure/retired-entries.txt` carries both removed slugs so Test 16 (Feathers v1.6.1 snapshot guard) subtracts them before asserting zero drops. Backfilled by follow-on PR #<this PR> (sunj-labs/bassclef-upstream#523).
|
|
65
|
+
|
|
66
|
+
### #1777 — friction hook pattern adds stage|launch new skill vocabulary (v1.14.0 → v1.14.1, 2026-10-01)
|
|
67
|
+
|
|
68
|
+
- **Change type:** content-change (1 file: `post-skill-friction-check` hook)
|
|
69
|
+
- **Fields:** 439 entries → 439 entries (no add/remove). Content hash shifts on `post-skill-friction-check` slug (MAJOR_SKILLS_PATTERN regex extended with stage|launch per ADR-040 D1 rename grace window).
|
|
70
|
+
- **Version bump:** v1.14.0 → v1.14.1 (patch — content-only change; no field add/remove; no shape shift).
|
|
71
|
+
- **Rationale:** PR #2028 closes #1777 (friction log never created because hook pattern matched old skill names only). Additive regex extension; grace-window compat preserved (preview|preview-build still match).
|
|
72
|
+
- **Downstream cure:** none — adopters re-sync and the hook starts firing on current-vocabulary chain dispatches. Lazy-create of `docs/session-friction-log.md` happens on first real friction case.
|
|
73
|
+
- **Old shape retired:** old pattern retired `2026-10-31` (grace window end for ADR-040 D1 vocabulary); current pattern keeps both until then.
|
|
74
|
+
|
|
37
75
|
### #1961 — persistent statusline for bassclef version + sync status (v1.12.2 → v1.13.0, 2026-09-27)
|
|
38
76
|
|
|
39
77
|
- **Change type:** content-add (1 new entry: `statusline-version` lib) + content-change (2 files: `bassclef-sync` hook + `bassclef-orientation`)
|