@thebassclef/lite 1.1.1 → 1.2.0
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/dist/cli.cjs +23 -6
- package/dist/cli.js +23 -6
- package/dist/index.cjs +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/lite/.claude/bassclef-configs.jsonc +56 -0
- package/dist/lite/.claude/bassclef-orientation.md +120 -0
- package/dist/lite/.claude/hooks/adr-discipline-check.sh +132 -0
- package/dist/lite/.claude/hooks/atomic-pr-check.sh +169 -0
- package/dist/lite/.claude/hooks/auto-save-idle.sh +51 -0
- package/dist/lite/.claude/hooks/bassclef-sync.sh +51 -13
- package/dist/lite/.claude/hooks/bet-doc-gate.sh +283 -0
- package/dist/lite/.claude/hooks/bug-diagnosis.sh +64 -0
- package/dist/lite/.claude/hooks/destructive-command-guard.sh +136 -0
- package/dist/lite/.claude/hooks/file-rename-discipline-check.sh +147 -0
- package/dist/lite/.claude/hooks/hook-classifier-canary.sh +158 -0
- package/dist/lite/.claude/hooks/kiss-default-check.sh +129 -0
- package/dist/lite/.claude/hooks/multi-tenancy-check.sh +83 -0
- package/dist/lite/.claude/hooks/oo-ad-entry-point-check.sh +216 -0
- package/dist/lite/.claude/hooks/operator-private-path-check.sh +203 -0
- package/dist/lite/.claude/hooks/pattern-annotation-validate.sh +142 -0
- package/dist/lite/.claude/hooks/plain-english-steering.sh +15 -1
- package/dist/lite/.claude/hooks/post-write-trace.sh +66 -0
- package/dist/lite/.claude/hooks/pr-body-scrub-check.sh +446 -0
- package/dist/lite/.claude/hooks/pre-commit-identifier-leak-scrub.sh +137 -0
- package/dist/lite/.claude/hooks/pre-gh-pr-body-scrub.sh +192 -0
- package/dist/lite/.claude/hooks/pre-gh-pr-title-scrub.sh +172 -0
- package/dist/lite/.claude/hooks/role-entry-gate.sh +115 -0
- package/dist/lite/.claude/hooks/save-state.sh +268 -0
- package/dist/lite/.claude/hooks/session-reflection.d/08-settings-drift.sh +8 -1
- package/dist/lite/.claude/hooks/session-reflection.d/10-abrupt-stop-recovery.sh +52 -0
- package/dist/lite/.claude/hooks/session-reflection.d/55-orientation-gate.sh +40 -4
- package/dist/lite/.claude/hooks/session-reflection.d/80-hook-heartbeat-check.sh +12 -5
- package/dist/lite/.claude/hooks/session-reflection.d/81-hook-manifest-staleness.sh +18 -2
- package/dist/lite/.claude/hooks/session-reflection.d/91-npm-version-drift.sh +54 -0
- package/dist/lite/.claude/hooks/session-reflection.d/tests/10-abrupt-stop-recovery.test.sh +95 -0
- package/dist/lite/.claude/hooks/session-reflection.sh +62 -15
- package/dist/lite/.claude/hooks/tool-failure-diagnosis.sh +50 -0
- package/dist/lite/.claude/luminaries/andrej-karpathy.md +165 -0
- package/dist/lite/.claude/luminaries/dave-gray.md +155 -0
- package/dist/lite/.claude/luminaries/gary-klein.md +166 -0
- package/dist/lite/.claude/luminaries/kaoru-ishikawa.md +162 -0
- package/dist/lite/.claude/luminaries/lean-product-canvas.md +33 -0
- package/dist/lite/.claude/luminaries/luke-wroblewski.md +224 -0
- package/dist/lite/.claude/luminaries/ryan-singer.md +113 -0
- package/dist/lite/.claude/luminaries/steve-krug.md +168 -0
- package/dist/lite/.claude/luminaries/tony-ulwick.md +134 -0
- package/dist/lite/.claude/rules/arc-level-orientation-before-leaf.md +60 -0
- package/dist/lite/.claude/rules/architect-review-discipline.md +71 -0
- package/dist/lite/.claude/rules/auto-fix-criteria.md +126 -0
- package/dist/lite/.claude/rules/bassclef-hook-drift-blocker.md +144 -0
- package/dist/lite/.claude/rules/bassclef-models-discovery.md +107 -0
- package/dist/lite/.claude/rules/bassclef-workflow-parity.md +104 -0
- package/dist/lite/.claude/rules/cost-governance.md +154 -0
- package/dist/lite/.claude/rules/file-rename-discipline.md +95 -0
- package/dist/lite/.claude/rules/mechanism-fidelity.md +88 -0
- package/dist/lite/.claude/rules/prototype-production-coupling.md +125 -0
- package/dist/lite/.claude/rules/quick-fix-criteria.md +90 -0
- package/dist/lite/.claude/rules/session-artifacts.md +30 -0
- package/dist/lite/.claude/rules/sibling-smoke-after-substrate-change.md +107 -0
- package/dist/lite/.claude/rules/substrate-as-system.md +67 -0
- package/dist/lite/.claude/rules/usability.md +22 -0
- package/dist/lite/.claude/settings.json +20 -0
- package/dist/lite/.claude/skills/agent-research-spawn/SKILL.md +100 -0
- package/dist/lite/.claude/skills/architect-review/SKILL.md +201 -0
- package/dist/lite/.claude/skills/auto-fix/SKILL.md +297 -0
- package/dist/lite/.claude/skills/autonomous/SKILL.md +300 -0
- package/dist/lite/.claude/skills/bind-subdomain/SKILL.md +232 -0
- package/dist/lite/.claude/skills/canvas/SKILL.md +41 -0
- package/dist/lite/.claude/skills/deploy-prod/SKILL.md +106 -0
- package/dist/lite/.claude/skills/docs-sync/SKILL.md +188 -0
- package/dist/lite/.claude/skills/eisenhower/SKILL.md +273 -0
- package/dist/lite/.claude/skills/empathy-map/SKILL.md +168 -0
- package/dist/lite/.claude/skills/frontend-design/SKILL.md +53 -0
- package/dist/lite/.claude/skills/interview-me/SKILL.md +171 -0
- package/dist/lite/.claude/skills/journal-export/SKILL.md +293 -0
- package/dist/lite/.claude/skills/jtbd-tasks/SKILL.md +203 -0
- package/dist/lite/.claude/skills/objectory-decompose/SKILL.md +256 -0
- package/dist/lite/.claude/skills/pick-luminaries/SKILL.md +100 -0
- package/dist/lite/.claude/skills/pre-mortem/SKILL.md +180 -0
- package/dist/lite/.claude/skills/prototype-gallery/SKILL.md +264 -0
- package/dist/lite/.claude/skills/provision-deploy-host/SKILL.md +257 -0
- package/dist/lite/.claude/skills/quick-fix/SKILL.md +273 -0
- package/dist/lite/.claude/skills/release/SKILL.md +311 -0
- package/dist/lite/.claude/skills/release-notes/SKILL.md +202 -0
- package/dist/lite/.claude/skills/rfc/SKILL.md +222 -0
- package/dist/lite/.claude/skills/riff-prototypes/SKILL.md +637 -0
- package/dist/lite/.claude/skills/secrets-bootstrap/SKILL.md +209 -0
- package/dist/lite/.claude/skills/skill/SKILL.md +304 -0
- package/dist/lite/.claude/skills/smoke-test/SKILL.md +100 -0
- package/dist/lite/.claude/skills/state/SKILL.md +204 -0
- package/dist/lite/.claude/skills/substrate-check/SKILL.md +218 -0
- package/dist/lite/.claude/skills/synthetic-user/SKILL.md +381 -0
- package/dist/lite/.claude/skills/task-scenarios/SKILL.md +105 -0
- package/dist/lite/.claude/skills/tier-dependency-audit/SKILL.md +139 -0
- package/dist/lite/.claude/skills/ux-migration/SKILL.md +424 -0
- package/dist/lite/.claude/skills/value-prop-canvas/SKILL.md +297 -0
- package/dist/lite/README.md +5 -1
- package/dist/lite/gitignore +6 -0
- package/dist/lite/lib/bassclef-dir-resolver.sh +85 -0
- package/dist/lite/lib/clone-failure-classifier.sh +59 -0
- package/dist/lite/lib/fixture-builder.sh +190 -0
- package/dist/lite/lib/fresh-install-check.sh +49 -0
- package/dist/lite/lib/hook-heartbeat.sh +0 -1
- package/dist/lite/lib/mechanism-fidelity.sh +569 -0
- package/dist/lite/lib/npm-version-check.sh +112 -0
- package/dist/lite/lib/preview-state.sh +229 -0
- package/dist/lite/lib/session-rescue.sh +0 -1
- package/dist/lite/scripts/generate-lite-manifest.sh +145 -4
- package/dist/lite/scripts/lite-manifest-drift-check.sh +5 -1
- package/dist/lite/standards/architect-review-discipline.md +222 -0
- package/dist/lite/standards/bassclef-platform.md +249 -0
- package/dist/lite/standards/bassclef-test-repos.md +97 -0
- package/dist/lite/standards/bassclef-wiring-manifest.json +25 -1
- package/dist/lite/standards/cold-adopter-harness.md +205 -0
- package/dist/lite/standards/data-management.md +135 -0
- package/dist/lite/standards/frontend-stack/nextjs-prisma.md +124 -0
- package/dist/lite/standards/lite-manifest-schema-changes.md +27 -0
- package/dist/lite/standards/lite-manifest.json +1456 -14
- package/dist/lite/standards/mechanism-fidelity.md +205 -0
- package/dist/lite/standards/operator-private-discipline.md +200 -0
- package/dist/lite/standards/operator-to-public-flow.md +195 -0
- package/dist/lite/standards/prototype-gallery.md +251 -0
- package/dist/lite/standards/pseudonym-key-schema.md +109 -0
- package/dist/lite/standards/sibling-validation-checklist.md +181 -0
- package/dist/lite/standards/slack-notifications.md +161 -0
- package/dist/lite/standards/synthetic-user-testing.md +173 -0
- package/dist/lite/standards/vendor-bindings.md +165 -0
- package/dist/lite/standards/vendor-plugins/frontend-design.md +66 -0
- package/dist/lite/standards/vpc-fit-validation.md +167 -0
- package/dist/lite/templates/gallery-yml.md +113 -0
- package/dist/lite/templates/product-canvas.md +169 -0
- package/dist/lite/templates/value-prop-canvas.md +180 -0
- package/package.json +3 -3
- /package/dist/lite/{whereami.md → docs/whereami.md} +0 -0
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# tier: lite
|
|
3
|
+
#
|
|
4
|
+
# fresh-install-check.sh — is this a fresh-install repo or an established one?
|
|
5
|
+
# Cures sunj-labs/bassclef-cli#102 + sunj-labs/bassclef-cli#108 where two
|
|
6
|
+
# session-start fragments (55-orientation-gate + 10-abrupt-stop-recovery)
|
|
7
|
+
# false-fire on the first session after `bassclef init` in an empty repo.
|
|
8
|
+
#
|
|
9
|
+
# A repo is fresh-install when BOTH conditions hold:
|
|
10
|
+
# 1. No git commits (git rev-parse HEAD fails), AND
|
|
11
|
+
# 2. No session lock at .claude/SESSION_LOCK
|
|
12
|
+
#
|
|
13
|
+
# The composite catches the cli#108 case (init leaves 379 untracked files
|
|
14
|
+
# but no commits, and no prior session) while preserving the genuine-stop
|
|
15
|
+
# case (a real session stops abruptly and leaves a lock — that lock alone
|
|
16
|
+
# fires the check even before any commit).
|
|
17
|
+
#
|
|
18
|
+
# Contract: exit 0 = fresh, exit 1 = established (or defensive fallback).
|
|
19
|
+
# Silent — no stderr output on any path. Never crashes the parent.
|
|
20
|
+
#
|
|
21
|
+
# Anchor luminaries:
|
|
22
|
+
# @luminary john-ousterhout — deep module (composite predicate hidden
|
|
23
|
+
# behind zero-arg function)
|
|
24
|
+
# @luminary tony-hoare — postcondition (exit code contract)
|
|
25
|
+
# @luminary michael-nygard — fail-soft (missing git = defensive-1;
|
|
26
|
+
# assume not fresh; skip false-silence)
|
|
27
|
+
|
|
28
|
+
# @pattern patterns/code/gof/strategy.md
|
|
29
|
+
# Composite strategy — two discriminators combined via short-circuit AND.
|
|
30
|
+
is_fresh_install() {
|
|
31
|
+
# Defensive: if git is not on PATH, assume not-fresh — better a false
|
|
32
|
+
# BLOCKED banner an operator can dismiss than a silent-skip that hides
|
|
33
|
+
# a real state.
|
|
34
|
+
command -v git >/dev/null 2>&1 || return 1
|
|
35
|
+
|
|
36
|
+
# Discriminator 1: git HEAD does not resolve → no commits
|
|
37
|
+
if git rev-parse HEAD >/dev/null 2>&1; then
|
|
38
|
+
return 1
|
|
39
|
+
fi
|
|
40
|
+
|
|
41
|
+
# Discriminator 2: session lock present → interrupted session
|
|
42
|
+
if [ -f ".claude/SESSION_LOCK" ]; then
|
|
43
|
+
return 1
|
|
44
|
+
fi
|
|
45
|
+
|
|
46
|
+
return 0
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export -f is_fresh_install 2>/dev/null || true
|
|
@@ -0,0 +1,569 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# tier: lite
|
|
3
|
+
# install-class: dual
|
|
4
|
+
# lib/mechanism-fidelity.sh — shared scanner for the mechanism-fidelity
|
|
5
|
+
# audit per standards/mechanism-fidelity.md.
|
|
6
|
+
#
|
|
7
|
+
# This library is SOURCED by the audit skill (.claude/skills/audit-rule-
|
|
8
|
+
# mechanism-fidelity/SKILL.md — lands in WU-4) AND by the cold-adopter
|
|
9
|
+
# harness check classes (scripts/cold-adopter-harness-sync.sh extension —
|
|
10
|
+
# lands in WU-5). Single source of truth per @luminary john-ousterhout
|
|
11
|
+
# (deep modules).
|
|
12
|
+
#
|
|
13
|
+
# Authority: ADR-035 (substrate-as-system tenet)
|
|
14
|
+
# Tier 0 strict TDD: lib/tests/mechanism-fidelity.test.sh
|
|
15
|
+
# Anchors: @luminary michael-feathers (legacy code = code without tests),
|
|
16
|
+
# @luminary kent-beck (TDD), @luminary tony-hoare (pre/post).
|
|
17
|
+
#
|
|
18
|
+
# Exposed functions:
|
|
19
|
+
# scan_rule_for_mechanism_claims <rule-path> # extracts mechanism refs
|
|
20
|
+
# verify_hook_exists <hook-path> # step 1 of verification chain
|
|
21
|
+
# verify_hook_wired <hook-name> # step 2 (settings.json wiring)
|
|
22
|
+
# verify_path_resolution <hook-path> # step 4 (Class A check)
|
|
23
|
+
# verify_hook_test_pins_contract <hook-or-lib> # step 3 (Tier 0 test present)
|
|
24
|
+
# classify_hook_install_pattern <hook-name> [root] # detects ci|launchd|lib|standard
|
|
25
|
+
# classify_finding [--flags] # returns 1 of 10 labels
|
|
26
|
+
#
|
|
27
|
+
# Env vars consumed:
|
|
28
|
+
# CLAUDE_PROJECT_DIR — project root
|
|
29
|
+
# HOME_OVERRIDE — test-only $HOME replacement
|
|
30
|
+
# USER_SETTINGS_OVERRIDE — test-only user settings.json replacement
|
|
31
|
+
# CANONICAL_MANIFEST_OVERRIDE — test-only canonical wiring manifest path override
|
|
32
|
+
# (per bet 2026-08-05c: verify_hook_wired reads
|
|
33
|
+
# standards/bassclef-wiring-manifest.json as an
|
|
34
|
+
# added source of hook wiring; shipped 2026-08-04h)
|
|
35
|
+
|
|
36
|
+
# === Resolve $HOME with test-override support ===
|
|
37
|
+
__mech_fidelity_home() {
|
|
38
|
+
echo "${HOME_OVERRIDE:-$HOME}"
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
# === Resolve user settings.json with test-override support ===
|
|
42
|
+
__mech_fidelity_user_settings() {
|
|
43
|
+
if [ -n "${USER_SETTINGS_OVERRIDE:-}" ]; then
|
|
44
|
+
echo "$USER_SETTINGS_OVERRIDE"
|
|
45
|
+
else
|
|
46
|
+
echo "$(__mech_fidelity_home)/.claude/settings.json"
|
|
47
|
+
fi
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
# === Resolve canonical wiring manifest path with test-override support ===
|
|
51
|
+
# Per bet 2026-08-05c: verify_hook_wired reads this file as an added source
|
|
52
|
+
# of hook wiring alongside project + user settings.json. The manifest shipped
|
|
53
|
+
# 2026-08-04h per design/discoveries/2026-08-04-managed-settings-d-as-bassclef-wiring-channel.md.
|
|
54
|
+
__mech_fidelity_canonical_manifest() {
|
|
55
|
+
if [ -n "${CANONICAL_MANIFEST_OVERRIDE:-}" ]; then
|
|
56
|
+
echo "$CANONICAL_MANIFEST_OVERRIDE"
|
|
57
|
+
else
|
|
58
|
+
echo "${CLAUDE_PROJECT_DIR:-}/standards/bassclef-wiring-manifest.json"
|
|
59
|
+
fi
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
# === _is_placeholder_hook_name ===
|
|
63
|
+
# Match candidate hook names against conventional example / placeholder
|
|
64
|
+
# names that rule bodies use as illustrations. Real hook names are
|
|
65
|
+
# descriptive (verb-object shape). Bare placeholders leak as UNWIRED
|
|
66
|
+
# audit noise per sunj-labs/bassclef-upstream#1164.
|
|
67
|
+
#
|
|
68
|
+
# Kept STRICT (exact bare names only) to avoid false negatives on real
|
|
69
|
+
# hooks that happen to prefix/suffix with a placeholder root (e.g.,
|
|
70
|
+
# foo-hook.sh, install-test.sh). Add new entries as new noise leaks
|
|
71
|
+
# into future audits.
|
|
72
|
+
#
|
|
73
|
+
# Returns 0 (true) when candidate is a bare placeholder name.
|
|
74
|
+
_is_placeholder_hook_name() {
|
|
75
|
+
local name="${1:-}"
|
|
76
|
+
case "$name" in
|
|
77
|
+
foo.sh|bar.sh|baz.sh|test.sh) return 0 ;;
|
|
78
|
+
example.sh|template.sh|sample.sh) return 0 ;;
|
|
79
|
+
whatever.sh|myhook.sh) return 0 ;;
|
|
80
|
+
esac
|
|
81
|
+
return 1
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
# === scan_rule_for_mechanism_claims ===
|
|
85
|
+
# Greps a rule body for mechanism claims (hook references, event names,
|
|
86
|
+
# matchers). Returns one claim per line (compact form: hook-name + optional
|
|
87
|
+
# event/matcher hint).
|
|
88
|
+
#
|
|
89
|
+
# Args: $1 — rule file path
|
|
90
|
+
# Stdout: claims, one per line (empty if no claims)
|
|
91
|
+
# Exit: 0 success (even if zero claims); 1 file missing; 2 missing arg
|
|
92
|
+
scan_rule_for_mechanism_claims() {
|
|
93
|
+
local rule="${1:-}"
|
|
94
|
+
local project_root="${2:-${CLAUDE_PROJECT_DIR:-$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)}}"
|
|
95
|
+
if [ -z "$rule" ]; then
|
|
96
|
+
echo "scan_rule_for_mechanism_claims: missing rule arg" >&2
|
|
97
|
+
return 2
|
|
98
|
+
fi
|
|
99
|
+
if [ ! -f "$rule" ]; then
|
|
100
|
+
echo "scan_rule_for_mechanism_claims: file not found: $rule" >&2
|
|
101
|
+
return 1
|
|
102
|
+
fi
|
|
103
|
+
|
|
104
|
+
# Pattern 1: "<name>-hook.sh" or "<name>.sh" anywhere in body (most direct)
|
|
105
|
+
# Pattern 2: "Mechanical enforcement" section header (formal claim)
|
|
106
|
+
# Pattern 3: "fires at PreToolUse|PostToolUse|Stop|SessionStart|UserPromptSubmit"
|
|
107
|
+
|
|
108
|
+
# Extract candidate .sh references
|
|
109
|
+
# Regex accepts capital letters at match start + inside the name (bassclef-upstream#1423).
|
|
110
|
+
# Prior lowercase-only pattern silently dropped leading capital letters — e.g.,
|
|
111
|
+
# `Bassclef-sync.sh` at sentence start extracted as phantom `assclef-sync.sh`.
|
|
112
|
+
# Normalize to lowercase before dedup so the file lookup below finds the real file.
|
|
113
|
+
local candidates
|
|
114
|
+
candidates=$(grep -oE '[A-Za-z0-9][a-zA-Z0-9-]*-?[a-zA-Z0-9]*\.sh' "$rule" 2>/dev/null \
|
|
115
|
+
| tr '[:upper:]' '[:lower:]' \
|
|
116
|
+
| sort -u)
|
|
117
|
+
[ -z "$candidates" ] && return 0
|
|
118
|
+
|
|
119
|
+
# Filter to reduce false positives (bet 28a WU-4):
|
|
120
|
+
# - Emit candidate IF .sh exists in .claude/hooks/ (true positive — real hook)
|
|
121
|
+
# - Skip candidate IF .sh exists elsewhere in repo (lib/script/test/fixture; not a hook claim)
|
|
122
|
+
# - Emit candidate IF .sh does not exist anywhere AND has hook-context proximity
|
|
123
|
+
# ("hook", "fires at", "wired", "enforced by", "mechanism", "matcher") within ±2 lines
|
|
124
|
+
while IFS= read -r candidate; do
|
|
125
|
+
[ -z "$candidate" ] && continue
|
|
126
|
+
# True positive: real hook file
|
|
127
|
+
if [ -f "$project_root/.claude/hooks/$candidate" ]; then
|
|
128
|
+
echo "$candidate"
|
|
129
|
+
continue
|
|
130
|
+
fi
|
|
131
|
+
# Check if file exists elsewhere in repo (excluding .git/)
|
|
132
|
+
local found_elsewhere=0
|
|
133
|
+
if [ -d "$project_root" ]; then
|
|
134
|
+
if find "$project_root" -name .git -prune -o -type f -name "$candidate" -print 2>/dev/null | grep -q .; then
|
|
135
|
+
found_elsewhere=1
|
|
136
|
+
fi
|
|
137
|
+
fi
|
|
138
|
+
if [ "$found_elsewhere" -eq 1 ]; then
|
|
139
|
+
# File exists but not in .claude/hooks/ — not a hook claim, skip
|
|
140
|
+
continue
|
|
141
|
+
fi
|
|
142
|
+
# (bassclef-upstream#1164) Placeholder-name filter — skip conventional
|
|
143
|
+
# example filenames (foo.sh, test.sh, template.sh, bar.sh, etc.) that
|
|
144
|
+
# rule bodies use as illustrations. Real hook names are descriptive;
|
|
145
|
+
# placeholders leak as UNWIRED audit noise.
|
|
146
|
+
if _is_placeholder_hook_name "$candidate"; then
|
|
147
|
+
continue
|
|
148
|
+
fi
|
|
149
|
+
# File doesn't exist anywhere. Check negative-context patterns first
|
|
150
|
+
# (sibling/example/renamed/library references; bet 28a WU-6 + bet 2026-08-05c WU-4).
|
|
151
|
+
# Skip when candidate appears within ±2 lines of:
|
|
152
|
+
# "sibling of" "alias of" "renamed from/to" "former name"
|
|
153
|
+
# "Example for/of" "e.g.," "such as"
|
|
154
|
+
# "sourced by" "library" "CI hook" "GitHub Actions" "workflow"
|
|
155
|
+
# "launchd" "cron" "scheduled job"
|
|
156
|
+
# Deferred-future patterns (bet 2026-08-05c WU-4 CI cure) — rules that
|
|
157
|
+
# cite hooks planned for later ship are not active claims:
|
|
158
|
+
# "deferred (follow-on|to|until)"
|
|
159
|
+
# "will (add|ship|introduce|land)"
|
|
160
|
+
# "planned (hook|for|to)"
|
|
161
|
+
# "not yet"
|
|
162
|
+
# Dropped "V[0-9]+ (mechanical|...)" — too broad; matches "V2 mechanical
|
|
163
|
+
# layer" heading even when adjacent to an unrelated active claim.
|
|
164
|
+
local nearby
|
|
165
|
+
nearby=$(grep -B2 -A2 -F "$candidate" "$rule" 2>/dev/null)
|
|
166
|
+
if echo "$nearby" | grep -qiE "(sibling of|alias of|renamed (from|to)|former name|Example (for|of)|e\.g\.,|such as|sourced by|library|CI hook|GitHub Actions|workflow|launchd|cron|scheduled job|deferred (follow-on|to|until)|will (add|ship|introduce|land)|planned (hook|for|to)|not yet)"; then
|
|
167
|
+
continue
|
|
168
|
+
fi
|
|
169
|
+
# Check hook-context proximity (within ±2 lines).
|
|
170
|
+
if echo "$nearby" | grep -qiE "(hook|fires at|wired|enforced by|mechanism|matcher)"; then
|
|
171
|
+
echo "$candidate"
|
|
172
|
+
fi
|
|
173
|
+
done <<< "$candidates"
|
|
174
|
+
return 0
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
# === verify_hook_exists ===
|
|
178
|
+
# Step 1 of the 5-step verification chain.
|
|
179
|
+
# Args: $1 — hook file path (absolute or relative to repo)
|
|
180
|
+
# Exit: 0 file exists; 1 file missing; 2 missing arg
|
|
181
|
+
verify_hook_exists() {
|
|
182
|
+
local hook="${1:-}"
|
|
183
|
+
if [ -z "$hook" ]; then
|
|
184
|
+
echo "verify_hook_exists: missing arg" >&2
|
|
185
|
+
return 2
|
|
186
|
+
fi
|
|
187
|
+
if [ -f "$hook" ]; then
|
|
188
|
+
return 0
|
|
189
|
+
fi
|
|
190
|
+
echo "verify_hook_exists: not found: $hook" >&2
|
|
191
|
+
return 1
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
# === verify_hook_wired ===
|
|
195
|
+
# Step 2 of the 5-step chain. Greps project settings, user settings, AND
|
|
196
|
+
# the canonical wiring manifest for the hook name appearing in any command
|
|
197
|
+
# path. Returns 0 on FIRST match (short-circuits per R1 pre-mortem).
|
|
198
|
+
# Args: $1 — hook base name (e.g., "wired-hook.sh")
|
|
199
|
+
# Exit: 0 wired in at least one source; 1 wired nowhere; 2 missing arg
|
|
200
|
+
# Sources checked (in order): project settings.json, user settings.json,
|
|
201
|
+
# bassclef-wiring-manifest.json
|
|
202
|
+
# Per bet 2026-08-05c: added the canonical manifest source. Prior versions
|
|
203
|
+
# missed hooks wired only in the manifest → classified them DEAD-LETTER.
|
|
204
|
+
verify_hook_wired() {
|
|
205
|
+
local hook_name="${1:-}"
|
|
206
|
+
if [ -z "$hook_name" ]; then
|
|
207
|
+
echo "verify_hook_wired: missing arg" >&2
|
|
208
|
+
return 2
|
|
209
|
+
fi
|
|
210
|
+
|
|
211
|
+
local project_settings="${CLAUDE_PROJECT_DIR:-}/.claude/settings.json"
|
|
212
|
+
local user_settings
|
|
213
|
+
user_settings="$(__mech_fidelity_user_settings)"
|
|
214
|
+
local canonical_manifest
|
|
215
|
+
canonical_manifest="$(__mech_fidelity_canonical_manifest)"
|
|
216
|
+
|
|
217
|
+
if [ -f "$project_settings" ] && grep -qF "$hook_name" "$project_settings" 2>/dev/null; then
|
|
218
|
+
return 0
|
|
219
|
+
fi
|
|
220
|
+
if [ -f "$user_settings" ] && grep -qF "$hook_name" "$user_settings" 2>/dev/null; then
|
|
221
|
+
return 0
|
|
222
|
+
fi
|
|
223
|
+
if [ -f "$canonical_manifest" ] && grep -qF "$hook_name" "$canonical_manifest" 2>/dev/null; then
|
|
224
|
+
return 0
|
|
225
|
+
fi
|
|
226
|
+
|
|
227
|
+
echo "verify_hook_wired: '$hook_name' not in project settings, user settings, or canonical manifest" >&2
|
|
228
|
+
return 1
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
# === verify_path_resolution ===
|
|
232
|
+
# Step 4 of the 5-step chain. Checks for Class A pattern: hook uses
|
|
233
|
+
# SCRIPT_DIR/../../ to reach project resources but is declared install-class
|
|
234
|
+
# operator (mismatch). Reads the install-class header.
|
|
235
|
+
# Args: $1 — hook file path
|
|
236
|
+
# Exit: 0 safe; 1 unsafe (Class A) or no install-class declared with risky pattern
|
|
237
|
+
verify_path_resolution() {
|
|
238
|
+
local hook="${1:-}"
|
|
239
|
+
if [ -z "$hook" ] || [ ! -f "$hook" ]; then
|
|
240
|
+
echo "verify_path_resolution: hook missing: $hook" >&2
|
|
241
|
+
return 2
|
|
242
|
+
fi
|
|
243
|
+
|
|
244
|
+
# Check if hook uses SCRIPT_DIR/../../ pattern (the Class A risk pattern)
|
|
245
|
+
local uses_risky_pattern=0
|
|
246
|
+
if grep -qE 'SCRIPT_DIR.*\.\./\.\.' "$hook"; then
|
|
247
|
+
uses_risky_pattern=1
|
|
248
|
+
fi
|
|
249
|
+
|
|
250
|
+
# No risky pattern → safe regardless of install-class
|
|
251
|
+
if [ $uses_risky_pattern -eq 0 ]; then
|
|
252
|
+
return 0
|
|
253
|
+
fi
|
|
254
|
+
|
|
255
|
+
# Risky pattern present — install-class header decides
|
|
256
|
+
local install_class
|
|
257
|
+
install_class=$(grep -m1 '^# install-class:' "$hook" | sed 's/^# install-class:[[:space:]]*//' | tr -d '[:space:]')
|
|
258
|
+
|
|
259
|
+
case "$install_class" in
|
|
260
|
+
dual)
|
|
261
|
+
# Author declared lib/hook-inject.sh resolve-helpers handle both layouts
|
|
262
|
+
return 0
|
|
263
|
+
;;
|
|
264
|
+
project)
|
|
265
|
+
# Project-class wiring + project-relative reads = safe
|
|
266
|
+
return 0
|
|
267
|
+
;;
|
|
268
|
+
operator)
|
|
269
|
+
# Class A failure — operator-class wiring + project-relative reads
|
|
270
|
+
echo "verify_path_resolution: Class A failure — '$hook' uses SCRIPT_DIR/../../ but declares install-class: operator" >&2
|
|
271
|
+
echo " Under \$HOME wiring, SCRIPT_DIR/../../<resource> resolves to nonexistent path" >&2
|
|
272
|
+
echo " Fix: declare install-class: dual + use lib/hook-inject.sh resolve-helpers" >&2
|
|
273
|
+
return 1
|
|
274
|
+
;;
|
|
275
|
+
"")
|
|
276
|
+
# No header — defaults to project; but risky pattern with no declaration warns
|
|
277
|
+
echo "verify_path_resolution: '$hook' uses SCRIPT_DIR/../../ with no install-class header" >&2
|
|
278
|
+
echo " Default is 'project' but the declaration should be explicit per standards/hook-install-class.md" >&2
|
|
279
|
+
return 1
|
|
280
|
+
;;
|
|
281
|
+
*)
|
|
282
|
+
echo "verify_path_resolution: '$hook' declares unknown install-class: '$install_class'" >&2
|
|
283
|
+
return 1
|
|
284
|
+
;;
|
|
285
|
+
esac
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
# === verify_hook_test_pins_contract ===
|
|
289
|
+
# Step 3 of the 5-step chain. Checks for sibling Tier 0 test file.
|
|
290
|
+
# Convention: <dir>/<name>.sh has test at <dir>/tests/<name>.test.sh
|
|
291
|
+
# Args: $1 — hook or lib path
|
|
292
|
+
# Exit: 0 sibling test exists; 1 no sibling test; 2 missing arg
|
|
293
|
+
verify_hook_test_pins_contract() {
|
|
294
|
+
local source="${1:-}"
|
|
295
|
+
if [ -z "$source" ]; then
|
|
296
|
+
echo "verify_hook_test_pins_contract: missing arg" >&2
|
|
297
|
+
return 2
|
|
298
|
+
fi
|
|
299
|
+
|
|
300
|
+
local dir base
|
|
301
|
+
dir="$(dirname "$source")"
|
|
302
|
+
base="$(basename "$source" .sh)"
|
|
303
|
+
|
|
304
|
+
if [ -f "$dir/tests/$base.test.sh" ]; then
|
|
305
|
+
return 0
|
|
306
|
+
fi
|
|
307
|
+
|
|
308
|
+
echo "verify_hook_test_pins_contract: no test at $dir/tests/$base.test.sh" >&2
|
|
309
|
+
return 1
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
# === classify_hook_install_pattern ===
|
|
313
|
+
# Determines how a hook is meant to be installed. Some hooks live in
|
|
314
|
+
# .github/workflows/*.yml (CI hooks), some are scheduled by launchd, some
|
|
315
|
+
# are sourced as libraries by other hooks — none of these belong in
|
|
316
|
+
# .claude/settings.json. The audit needs this signal so it does not flag
|
|
317
|
+
# them as DEAD-LETTER when they correctly do not appear in settings.json.
|
|
318
|
+
#
|
|
319
|
+
# Detection priority:
|
|
320
|
+
# 1. CI — hook name appears in any .github/workflows/*.yml
|
|
321
|
+
# 2. launchd — first 10 lines of the hook header mention launchd / cron / scheduled
|
|
322
|
+
# 3. lib — first 10 lines mention "sourced by" (library used by other hooks)
|
|
323
|
+
# 4. standard — default; hook is meant to live in settings.json
|
|
324
|
+
#
|
|
325
|
+
# CI takes precedence over launchd: a hook can be both scheduled locally
|
|
326
|
+
# AND fire in CI; the CI presence is the dominant signal because the
|
|
327
|
+
# workflow IS the wiring.
|
|
328
|
+
#
|
|
329
|
+
# Args: $1 — hook base name (e.g., "pr-scrub.sh")
|
|
330
|
+
# $2 — project root (default: $CLAUDE_PROJECT_DIR or lib parent dir)
|
|
331
|
+
# Stdout: one of "ci", "launchd", "lib", "standard"
|
|
332
|
+
# Exit: 0 always (default "standard" is safe); 2 missing arg
|
|
333
|
+
classify_hook_install_pattern() {
|
|
334
|
+
local hook_name="${1:-}"
|
|
335
|
+
local project_root="${2:-${CLAUDE_PROJECT_DIR:-$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)}}"
|
|
336
|
+
|
|
337
|
+
if [ -z "$hook_name" ]; then
|
|
338
|
+
echo "classify_hook_install_pattern: missing arg" >&2
|
|
339
|
+
return 2
|
|
340
|
+
fi
|
|
341
|
+
|
|
342
|
+
# Layer 2 — read pattern list from standards/hook-invocation-patterns.json
|
|
343
|
+
# when available (sunj-labs/bassclef-upstream#1163). Falls back to
|
|
344
|
+
# hardcoded case cascade if JSON missing OR jq unavailable — keeps
|
|
345
|
+
# existing tests passing per @luminary michael-feathers characterization.
|
|
346
|
+
local patterns_json="$project_root/standards/hook-invocation-patterns.json"
|
|
347
|
+
if [ -f "$patterns_json" ] && command -v jq >/dev/null 2>&1; then
|
|
348
|
+
local result
|
|
349
|
+
result=$(_classify_via_patterns_json "$hook_name" "$project_root" "$patterns_json") || result=""
|
|
350
|
+
if [ -n "$result" ]; then
|
|
351
|
+
echo "$result"
|
|
352
|
+
return 0
|
|
353
|
+
fi
|
|
354
|
+
fi
|
|
355
|
+
|
|
356
|
+
# Fallback — hardcoded cascade (identical to pre-Layer-2 behavior)
|
|
357
|
+
# 1. CI — workflow reference wins
|
|
358
|
+
if [ -d "$project_root/.github/workflows" ]; then
|
|
359
|
+
if grep -qrlF "$hook_name" "$project_root/.github/workflows/" 2>/dev/null; then
|
|
360
|
+
echo "ci"
|
|
361
|
+
return 0
|
|
362
|
+
fi
|
|
363
|
+
fi
|
|
364
|
+
|
|
365
|
+
# 1.5 Release
|
|
366
|
+
local release_script="$project_root/scripts/release-to-bassclef.sh"
|
|
367
|
+
if [ -f "$release_script" ]; then
|
|
368
|
+
if grep -qF "$hook_name" "$release_script" 2>/dev/null; then
|
|
369
|
+
echo "release"
|
|
370
|
+
return 0
|
|
371
|
+
fi
|
|
372
|
+
fi
|
|
373
|
+
|
|
374
|
+
# 2/3 — header inspection
|
|
375
|
+
local hook_path="$project_root/.claude/hooks/$hook_name"
|
|
376
|
+
if [ -f "$hook_path" ]; then
|
|
377
|
+
local header
|
|
378
|
+
header=$(head -10 "$hook_path" 2>/dev/null)
|
|
379
|
+
if echo "$header" | grep -qiE "(launchd|launch agent|scheduled job|via cron|cron job)"; then
|
|
380
|
+
echo "launchd"
|
|
381
|
+
return 0
|
|
382
|
+
fi
|
|
383
|
+
if echo "$header" | grep -qiE "(sourced by|sourced into|sourced from)"; then
|
|
384
|
+
echo "lib"
|
|
385
|
+
return 0
|
|
386
|
+
fi
|
|
387
|
+
fi
|
|
388
|
+
|
|
389
|
+
# 4 — default
|
|
390
|
+
echo "standard"
|
|
391
|
+
return 0
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
# === _classify_via_patterns_json (private helper for Layer 2) ===
|
|
395
|
+
# Reads standards/hook-invocation-patterns.json + iterates entries
|
|
396
|
+
# in precedence order. First match wins. Returns pattern name on
|
|
397
|
+
# stdout OR empty string when nothing matches (fallback to hardcoded).
|
|
398
|
+
#
|
|
399
|
+
# Args: $1 hook_name, $2 project_root, $3 patterns_json_path
|
|
400
|
+
_classify_via_patterns_json() {
|
|
401
|
+
local hook_name="$1"
|
|
402
|
+
local project_root="$2"
|
|
403
|
+
local patterns_json="$3"
|
|
404
|
+
|
|
405
|
+
# Extract patterns sorted by precedence ASC — each line is TSV:
|
|
406
|
+
# name<TAB>detect.kind<TAB>detect.arg
|
|
407
|
+
local pattern_rows
|
|
408
|
+
pattern_rows=$(jq -r '.patterns | sort_by(.precedence)[] | [.name, .detect.kind, (.detect.arg // "")] | @tsv' "$patterns_json" 2>/dev/null) || return 1
|
|
409
|
+
[ -z "$pattern_rows" ] && return 1
|
|
410
|
+
|
|
411
|
+
local IFS=$'\n'
|
|
412
|
+
local row
|
|
413
|
+
for row in $pattern_rows; do
|
|
414
|
+
local name kind arg
|
|
415
|
+
name=$(echo "$row" | cut -f1)
|
|
416
|
+
kind=$(echo "$row" | cut -f2)
|
|
417
|
+
arg=$(echo "$row" | cut -f3)
|
|
418
|
+
|
|
419
|
+
case "$kind" in
|
|
420
|
+
workflow_grep)
|
|
421
|
+
if [ -d "$project_root/$arg" ]; then
|
|
422
|
+
if grep -qrlF "$hook_name" "$project_root/$arg/" 2>/dev/null; then
|
|
423
|
+
echo "$name"
|
|
424
|
+
return 0
|
|
425
|
+
fi
|
|
426
|
+
fi
|
|
427
|
+
;;
|
|
428
|
+
script_grep)
|
|
429
|
+
if [ -f "$project_root/$arg" ]; then
|
|
430
|
+
if grep -qF "$hook_name" "$project_root/$arg" 2>/dev/null; then
|
|
431
|
+
echo "$name"
|
|
432
|
+
return 0
|
|
433
|
+
fi
|
|
434
|
+
fi
|
|
435
|
+
;;
|
|
436
|
+
header_regex)
|
|
437
|
+
local hook_path="$project_root/.claude/hooks/$hook_name"
|
|
438
|
+
if [ -f "$hook_path" ]; then
|
|
439
|
+
local header
|
|
440
|
+
header=$(head -10 "$hook_path" 2>/dev/null)
|
|
441
|
+
if echo "$header" | grep -qiE "$arg"; then
|
|
442
|
+
echo "$name"
|
|
443
|
+
return 0
|
|
444
|
+
fi
|
|
445
|
+
fi
|
|
446
|
+
;;
|
|
447
|
+
default)
|
|
448
|
+
echo "$name"
|
|
449
|
+
return 0
|
|
450
|
+
;;
|
|
451
|
+
esac
|
|
452
|
+
done
|
|
453
|
+
|
|
454
|
+
# No match — signal fallback
|
|
455
|
+
return 1
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
# === _install_pattern_is_valid (private helper for Layer 2) ===
|
|
459
|
+
# Returns 0 if pattern is in the JSON pattern list OR in the hardcoded
|
|
460
|
+
# fallback list (ci|launchd|lib|release|standard). Returns 1 otherwise.
|
|
461
|
+
_install_pattern_is_valid() {
|
|
462
|
+
local candidate="${1:-}"
|
|
463
|
+
[ -z "$candidate" ] && return 1
|
|
464
|
+
|
|
465
|
+
# Try JSON first
|
|
466
|
+
local project_root="${CLAUDE_PROJECT_DIR:-$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)}"
|
|
467
|
+
local patterns_json="$project_root/standards/hook-invocation-patterns.json"
|
|
468
|
+
if [ -f "$patterns_json" ] && command -v jq >/dev/null 2>&1; then
|
|
469
|
+
local names
|
|
470
|
+
names=$(jq -r '.patterns[].name' "$patterns_json" 2>/dev/null) || names=""
|
|
471
|
+
if [ -n "$names" ]; then
|
|
472
|
+
local n
|
|
473
|
+
while IFS= read -r n; do
|
|
474
|
+
[ "$n" = "$candidate" ] && return 0
|
|
475
|
+
done <<< "$names"
|
|
476
|
+
return 1
|
|
477
|
+
fi
|
|
478
|
+
fi
|
|
479
|
+
|
|
480
|
+
# Fallback — hardcoded list
|
|
481
|
+
case "$candidate" in
|
|
482
|
+
ci|launchd|lib|release|standard) return 0 ;;
|
|
483
|
+
*) return 1 ;;
|
|
484
|
+
esac
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
# === classify_finding ===
|
|
488
|
+
# Returns 1 of 10 classification labels per standards/mechanism-fidelity.md.
|
|
489
|
+
# Flags (exit codes from each verification step):
|
|
490
|
+
# --exists=N step 1: verify_hook_exists exit code (0 OK, 1 missing)
|
|
491
|
+
# --wired=N step 2: verify_hook_wired exit code (0 OK, 1 unwired)
|
|
492
|
+
# --path-ok=N step 4: verify_path_resolution exit code (0 OK, 1 broken)
|
|
493
|
+
# --test=N step 3: verify_hook_test_pins_contract exit code (0 OK, 1 missing)
|
|
494
|
+
# --install-pattern=V install pattern (ci|launchd|lib|standard) — bet 29a WU-1
|
|
495
|
+
# Overrides DEAD-LETTER → NOT-WIRED-BY-DESIGN-{CI|LAUNCHD|LIB}
|
|
496
|
+
# when the hook correctly lives outside settings.json
|
|
497
|
+
# --methodology-only short-circuit when the rule names no mechanism
|
|
498
|
+
#
|
|
499
|
+
# Stdout: one of CLEAN / DEAD-LETTER / UNWIRED / PATH-BROKEN / TEST-MISSING /
|
|
500
|
+
# METHODOLOGY-ONLY / SILENT-PASS / NOT-WIRED-BY-DESIGN-CI /
|
|
501
|
+
# NOT-WIRED-BY-DESIGN-LAUNCHD / NOT-WIRED-BY-DESIGN-LIB
|
|
502
|
+
# Exit: 0 success; 2 no args OR unknown --install-pattern value
|
|
503
|
+
classify_finding() {
|
|
504
|
+
if [ $# -eq 0 ]; then
|
|
505
|
+
echo "classify_finding: no args" >&2
|
|
506
|
+
return 2
|
|
507
|
+
fi
|
|
508
|
+
|
|
509
|
+
local exists wired path_ok test_present methodology_only=0 install_pattern="standard"
|
|
510
|
+
for arg in "$@"; do
|
|
511
|
+
case "$arg" in
|
|
512
|
+
--exists=*) exists="${arg#*=}" ;;
|
|
513
|
+
--wired=*) wired="${arg#*=}" ;;
|
|
514
|
+
--path-ok=*) path_ok="${arg#*=}" ;;
|
|
515
|
+
--test=*) test_present="${arg#*=}" ;;
|
|
516
|
+
--install-pattern=*)
|
|
517
|
+
install_pattern="${arg#*=}"
|
|
518
|
+
# Layer 2 (bassclef-upstream#1163) — validate against JSON
|
|
519
|
+
# pattern list when available; fall back to hardcoded enum.
|
|
520
|
+
if ! _install_pattern_is_valid "$install_pattern"; then
|
|
521
|
+
echo "classify_finding: unknown install-pattern value: $install_pattern" >&2
|
|
522
|
+
return 2
|
|
523
|
+
fi
|
|
524
|
+
;;
|
|
525
|
+
--methodology-only) methodology_only=1 ;;
|
|
526
|
+
*) echo "classify_finding: unknown arg: $arg" >&2; return 2 ;;
|
|
527
|
+
esac
|
|
528
|
+
done
|
|
529
|
+
|
|
530
|
+
# Short-circuit on methodology-only
|
|
531
|
+
if [ "$methodology_only" -eq 1 ]; then
|
|
532
|
+
echo "METHODOLOGY-ONLY"
|
|
533
|
+
return 0
|
|
534
|
+
fi
|
|
535
|
+
|
|
536
|
+
# Classification cascade
|
|
537
|
+
if [ "$exists" = "1" ]; then
|
|
538
|
+
echo "UNWIRED"
|
|
539
|
+
return 0
|
|
540
|
+
fi
|
|
541
|
+
if [ "$wired" = "1" ]; then
|
|
542
|
+
# Hook lives on disk but settings.json wiring absent.
|
|
543
|
+
# If the hook is meant to live elsewhere (CI / launchd / sourced lib),
|
|
544
|
+
# this is by design, not a failure.
|
|
545
|
+
# Layer 2 (bassclef-upstream#1163) — algorithmic label derivation:
|
|
546
|
+
# "standard" → DEAD-LETTER; anything else → NOT-WIRED-BY-DESIGN-<UPPER>.
|
|
547
|
+
# New patterns land in standards/hook-invocation-patterns.json; their
|
|
548
|
+
# classification label flows through this branch without cascade edits.
|
|
549
|
+
if [ "$install_pattern" = "standard" ]; then
|
|
550
|
+
echo "DEAD-LETTER"
|
|
551
|
+
return 0
|
|
552
|
+
fi
|
|
553
|
+
local upper
|
|
554
|
+
upper=$(echo "$install_pattern" | tr '[:lower:]' '[:upper:]')
|
|
555
|
+
echo "NOT-WIRED-BY-DESIGN-${upper}"
|
|
556
|
+
return 0
|
|
557
|
+
fi
|
|
558
|
+
if [ "$path_ok" = "1" ]; then
|
|
559
|
+
echo "PATH-BROKEN"
|
|
560
|
+
return 0
|
|
561
|
+
fi
|
|
562
|
+
if [ "$test_present" = "1" ]; then
|
|
563
|
+
echo "TEST-MISSING"
|
|
564
|
+
return 0
|
|
565
|
+
fi
|
|
566
|
+
|
|
567
|
+
echo "CLEAN"
|
|
568
|
+
return 0
|
|
569
|
+
}
|