@chrono-meta/fh-gate 1.4.48 → 1.4.50

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.
@@ -100,11 +100,20 @@ Local 4090 = **canary tier** (evidence-of, never terminal verdict).
100
100
 
101
101
  ## Step 6 — Degrade ladder (the intelligent scale-down)
102
102
 
103
+ **Consent branch first — declined ≠ degraded** (`[[capability_escalation_consent]]`): if the UAP has
104
+ `sidecar_consent: declined`, do **not** probe/recruit — route straight to **Tier-3 CC-only sub-agent
105
+ verification** (multiple isolated Claude sub-agents, isolation-decorrelation) as a **first-class chosen
106
+ mode**, with an honest *same-family* note but **no "reduced value / degraded" framing** — the user chose
107
+ this floor. `unset` → ask-once at first load-bearing need (accept → proceed; decline → record + this
108
+ branch). Only proceed to the discovery ladder below when consent is `accepted`.
109
+
103
110
  1. frontier cross-family CLI present → recruit it (decorrelated, at-floor) — best.
104
111
  2. only local 4090 present → canary pre-screen + in-session opus governor (canary, not full decorrelation).
105
112
  3. nothing present → in-session same-family + **honest below-floor/same-family note** (residual named).
106
113
 
107
- Env non-determinism (CLI presence varies) → **silent degrade, never hard-fail**.
114
+ Env non-determinism (CLI presence varies) → **silent degrade, never hard-fail**. Distinguish this
115
+ **unavailable-but-wanted** case (consent given, panel down → degrade-with-note; for a *load-bearing corp*
116
+ surface, fail-closed per `local_pmh_context.md`) from the **declined** case above (chosen floor, first-class).
108
117
 
109
118
  ## Step 7 — Output
110
119
 
@@ -117,7 +117,7 @@ Run the audit bash (§Step-Bash) and apply thresholds:
117
117
  | CLAUDE.md | Exceeds 300 lines | Section-by-section compression / move completed sections to archive |
118
118
  | MEMORY.md | Exceeds 180 lines | Check entry count + move `✅ CLOSED` items to archive section |
119
119
  | memory/*.md single file | Exceeds 30K (300 lines) | Suggest splitting accumulated history into separate files |
120
- | SKILL.md (any) | > 300 lines AND no SKILL_detail.md | Propose `/skill-splitter` — governance-semantic split (not compression); compression removes content, splitting routes it on-demand |
120
+ | SKILL.md (any) | > 300 lines AND no SKILL_detail.md | Propose `/salience-splitter` — governance-semantic split (not compression); compression removes content, splitting routes it on-demand |
121
121
 
122
122
  **Frequency**: When explicitly called with `/context-doctor` or auto-invoked at session start when MEMORY.md is detected at 180+ lines.
123
123
 
@@ -259,7 +259,7 @@ context-doctor (token/context) · harness-doctor (structure) · sim-conductor (s
259
259
  |---|---|
260
260
  | Want to also check structure after resolving token waste | `/harness-doctor` |
261
261
  | Want to validate prescription results from external user perspective | `/sim-conductor Area A` |
262
- | SKILL.md diagnosed as over-loaded (> 300 lines, no SKILL_detail.md) | `/skill-splitter` — governance-semantic split |
262
+ | SKILL.md diagnosed as over-loaded (> 300 lines, no SKILL_detail.md) | `/salience-splitter` — governance-semantic split |
263
263
  | All three skills mentioned simultaneously | Three-Doctor Loop circuit activated — diagnosis→prescription→re-diagnosis cycle
264
264
 
265
265
  ## Done When
@@ -278,4 +278,4 @@ context-doctor (token/context) · harness-doctor (structure) · sim-conductor (s
278
278
  **→ Three-Doctor Loop chain (auto-propose after diagnosis):**
279
279
  - Prescription modifies SKILL.md / rules / CLAUDE.md → **propose `/harness-doctor`** re-check after fix (structural integrity)
280
280
  - Prescription addresses user-facing context (onboarding, README, install guides) → **propose `/sim-conductor Area A`** (external user impact validation)
281
- - SKILL.md detected as over-loaded → **auto-propose `/skill-splitter`**: `"I see [skill-name] SKILL.md is [N] lines with no SKILL_detail.md. Want me to run /skill-splitter to do a governance-semantic split?"`
281
+ - SKILL.md detected as over-loaded → **auto-propose `/salience-splitter`**: `"I see [skill-name] SKILL.md is [N] lines with no SKILL_detail.md. Want me to run /salience-splitter to do a governance-semantic split?"`
@@ -73,11 +73,11 @@ wc -l memory/MEMORY.md 2>/dev/null
73
73
  # memory/*.md files exceeding 30K
74
74
  find memory -name "*.md" -size +30k 2>/dev/null | xargs wc -l | sort -rn | head -10
75
75
 
76
- # SKILL.md files > 300 lines with no SKILL_detail.md (skill-splitter candidates)
76
+ # SKILL.md files > 300 lines with no SKILL_detail.md (salience-splitter candidates)
77
77
  find plugins -name "SKILL.md" 2>/dev/null | while read f; do
78
78
  lines=$(wc -l < "$f")
79
79
  detail=$(dirname "$f")/SKILL_detail.md
80
- [ "$lines" -gt 300 ] && [ ! -f "$detail" ] && echo "[skill-splitter candidate] $f ($lines lines)"
80
+ [ "$lines" -gt 300 ] && [ ! -f "$detail" ] && echo "[salience-splitter candidate] $f ($lines lines)"
81
81
  done
82
82
  ```
83
83
 
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: goal-quench
3
3
  description: >-
4
- Wraps /goal with a tiered safety + orchestration ladder. core (default): a token budget gate (pre-run estimate), mid-run budget thresholds, and an automatic post-run quality verification via pipeline-conductor — closing /goal's two gaps (Haiku evaluates completion, pipeline-conductor evaluates correctness). pro: adds context-doctor token reduction and agent-composer goal decomposition. max: adds plugin-recommender capability-gap fill and cross-ecosystem-synergy-detection pre-validation. The Phase-1 budget verdict auto-recommends the mode. Triggered by "goal with quality gate", "safe goal", "goal-quench", "orchestrate this goal", or before running /goal on high-stakes tasks.
4
+ Wraps /goal with a tiered safety + orchestration ladder. core (default): a token budget gate (pre-run estimate), mid-run budget thresholds, and an automatic post-run quality verification via pipeline-conductor — closing /goal's two gaps (Haiku evaluates completion, pipeline-conductor evaluates correctness). pro: adds context-doctor token reduction and agent-composer goal decomposition. max: adds internal-first capability-gap fill (LOCAL_SKILL_REGISTRY skill-bus scan → plugin-recommender external search → auto_project_mapping map/cluster) and cross-ecosystem-synergy-detection pre-validation. The Phase-1 budget verdict auto-recommends the mode. Triggered by "goal with quality gate", "safe goal", "goal-quench", "orchestrate this goal", or before running /goal on high-stakes tasks.
5
5
  user-invocable: true
6
6
  allowed-tools: ["Read", "Write", "Bash", "Grep"]
7
7
  model: sonnet
@@ -39,7 +39,7 @@ goal-quench is a ladder, not a fixed shape. The default (**core**) is the narrow
39
39
  |---|---|---|---|
40
40
  | **core** (default) | budget gate + mid-run thresholds + post-run quality gate | token-budget-gate, pipeline-conductor --quick | budget GREEN / YELLOW |
41
41
  | **pro** | token-reduction pre-pass + goal decomposition into Waves | + context-doctor, agent-composer | budget ORANGE |
42
- | **max** | capability-gap fill + synergy pre-validation before the run | + plugin-recommender, cross-ecosystem-synergy-detection | budget RED |
42
+ | **max** | **internal-first** capability-gap fill (skill-bus scan → external → map/cluster) + synergy pre-validation | + LOCAL_SKILL_REGISTRY scan, plugin-recommender, cross-ecosystem-synergy-detection, auto_project_mapping | budget RED |
43
43
 
44
44
  Each mode is a **superset** of the one before it — pro does everything core does, plus more. Nothing in core is removed by escalating.
45
45
 
@@ -105,6 +105,7 @@ The clean-streak graduation above governs **unattended-no-review** operation (ru
105
105
  - "goal-quench", before any long /goal session
106
106
  - `/goal-quench --pro`, `/goal-quench --max`, "orchestrate this goal", "decompose this goal", "optimize then run this goal"
107
107
  - "this goal is too big for one run", "find a tool for this goal if FH lacks one" (→ max mode)
108
+ - "assemble capabilities for this project", "develop project X with everything FH has", "what can FH + tools do for this project", "use all of FH's and others' sharp parts on this" (→ max mode, **project-dev capability-assembly** entry — internal skill-bus scan first, then external gap-fill, then map+cluster; token-efficient, non-expert-friendly)
108
109
  - Automatically proposed when user mentions `/goal` on tasks estimated > 15K tokens
109
110
  - Mode is auto-recommended by the Phase-1 budget verdict (GREEN/YELLOW → core, ORANGE → pro, RED → max)
110
111
 
@@ -190,14 +191,16 @@ goal-quench does **not** re-implement agent-composer's gates — its destructive
190
191
 
191
192
  > **Detail**: See `SKILL_detail.md §Queue-Format` — queue file format, plan-ready output text, sidecar `.active` fields — read when writing the queue (Step B) or the sidecar fields (Step D).
192
193
 
193
- ### Step C — plugin-recommender + synergy pre-validation · max only
194
+ ### Step C — capability fill: internal-first, then external · max only
194
195
 
195
- Triggered only when agent-composer Step 0.2 reports a capability **GAP** (`fit_score < 0.5` on a required-weight sub-task):
196
- 1. `plugin-recommender` searches FH + Codex + Claude Code marketplaces for a fitting skill/agent.
197
- 2. For each candidate, `cross-ecosystem-synergy-detection` pre-validates fit + overlap **before anything is installed**.
198
- 3. User decides: install / skip / general-purpose fallback (agent-composer's degraded-composition rule applies — `⚠️ degraded: [role]`).
196
+ Triggered only when agent-composer Step 0.2 reports a capability **GAP** (`fit_score < 0.5` on a required-weight sub-task). Fill order is **internal before external** — honors no-reinvention (use FH's + sibling projects' sharp parts before installing anything), and cheaper **on an internal hit** (a local registry read ≪ an external marketplace search; on an internal *miss* the scan is added overhead before the same external path):
199
197
 
200
- max mode never installs anything silently discovery and synergy-check are surfaced for approval first.
198
+ 1. **Internal scan first.** Run the `LOCAL_SKILL_REGISTRY` scan / Cross-Project Skill Bus (the runtime-root-derived scan under `.claude/registry/`) to check whether an existing **FH meta-skill or a sibling-project skill** already fills the gap. **Trust-gated dispatch** (the fields the registry carries are *consumed*, not just surfaced): an **FH meta-skill** hit → propose directly (Agent + Context Card, no install, token-cheap); a **non-FH sibling** hit → if `trust≠trusted` OR `writes≠read-only` → **ask-tier HITL before dispatch** (never propose-directly — sibling code is an injection surface), else propose. **Degrade direction**: if `.claude/registry/` is missing/malformed, treat as an internal-miss and **fall through to external (step 2), warn** — never silently skip capability fill (that drops the GAP sub-task unnoticed; capability-fill is a reversible surface → advisory-degrade per the Surface-Class Degrade Invariant).
199
+ 2. **External only if internal has nothing.** `plugin-recommender` searches FH + Codex + Claude Code marketplaces + external repos for a fitting skill/agent; `cross-ecosystem-synergy-detection` pre-validates fit + overlap **before anything is installed**.
200
+ 3. User decides: use-internal / install-external / skip / general-purpose fallback (agent-composer's degraded-composition rule applies — `⚠️ degraded: [role]`).
201
+ 4. **Approved external capability → map + cluster (deferred to session close).** An adopted external skill or a newly-relevant sibling project is registered via `auto_project_mapping` — but that write **edits CLAUDE.md's track-mapping table and creates `tracks/{project}/` (an FH-asset change that trips the 4-axis auto-gate and shifts the onboarding is-mapped signal — [[feedback_tracks_dir_is_mapped_signal]])**, so it is **deferred to session close**, not run mid-`/goal`-loop: it passes the 4-axis gate as its own change instead of interrupting the run. The **next** run then discovers the capability internally at step 1 — the loop compounds.
202
+
203
+ max mode never installs or maps anything silently — internal hit, external discovery, synergy-check, and mapping are all surfaced for approval first.
201
204
 
202
205
  ### Step D — scope-driven sidecar configuration · pro + max
203
206
 
@@ -308,8 +311,10 @@ After each goal-quench run, append a calibration entry to `tracks/_meta/goal_que
308
311
  - `token-budget-gate` — Phase 1 cost estimation (all modes)
309
312
  - `context-doctor` — Phase 1.5 Step A token-reduction pre-pass (pro + max)
310
313
  - `agent-composer` — Phase 1.5 Step B goal decomposition into Waves (pro + max)
311
- - `plugin-recommender` — Phase 1.5 Step C capability-gap fill (max only, GAP-triggered)
314
+ - `LOCAL_SKILL_REGISTRY` scan / Cross-Project Skill Bus — Phase 1.5 Step C internal-first scan (max only, GAP-triggered, before external)
315
+ - `plugin-recommender` — Phase 1.5 Step C external capability-gap fill (max only, GAP-triggered, only if no internal hit)
312
316
  - `cross-ecosystem-synergy-detection` — Phase 1.5 Step C pre-validation of discovered candidates (max only)
317
+ - `auto_project_mapping` — Phase 1.5 Step C map/cluster of an adopted external capability (max only, approval-gated, deferred to session close)
313
318
  - `pipeline-conductor` — Phase 3 quality gate (`--quick` for core/pro, `--full` for max; called by Stop hook)
314
319
  - `field-harvest` — capture calibration data as reusable pattern after 10 runs
315
320
 
@@ -321,7 +326,9 @@ After each goal-quench run, append a calibration entry to `tracks/_meta/goal_que
321
326
  Phase 1: token-budget-gate verdict output + mode resolved (core default, or pro/max via budget verdict / explicit flag)
322
327
  + .claude/goal-quench.active written (with mode: field) + thresholds injected
323
328
  + If pro/max: Phase 1.5 ran — context-doctor pre-pass + agent-composer plan;
324
- max additionally: GAP-triggered plugin-recommender + cross-ecosystem-synergy-detection pre-validation
329
+ max additionally: GAP-triggered internal LOCAL_SKILL_REGISTRY scan FIRST (trust-gated dispatch);
330
+ plugin-recommender + cross-ecosystem-synergy pre-validation ONLY if no internal hit;
331
+ auto_project_mapping map/cluster deferred to session close only if an external capability was adopted (each surfaced for approval)
325
332
  + Phase 3 (on next response after /goal): .pending file detected + pipeline-conductor run
326
333
  (--quick for core/pro, --full for max)
327
334
  + Verification verdict output (CLEAN/PENDING/BLOCKED/ESCALATE)
@@ -76,7 +76,7 @@ confirms the gap*, never a verdict the report emits on its own; surface each gap
76
76
  | CLAUDE.md 100~200 lines | S-tier warning |
77
77
  | CLAUDE.md 200+ lines | M-tier — separation or reduction needed |
78
78
  | 15+ `##` sections in CLAUDE.md | S-tier warning |
79
- | SKILL.md > 300 lines AND no `SKILL_detail.md` | S-tier — propose `/skill-splitter` (governance-semantic split, not compression) |
79
+ | SKILL.md > 300 lines AND no `SKILL_detail.md` | S-tier — propose `/salience-splitter` (governance-semantic split, not compression) |
80
80
  | Rules files unreferenced in CLAUDE.md | R-tier |
81
81
  | weekly_audit 14~30 days elapsed | S-tier |
82
82
  | weekly_audit 30+ days elapsed | M-tier |
@@ -318,7 +318,7 @@ Verdict: PASS (M-tier 0, "Structure healthy") | CONDITIONAL_PASS (S/R remain, no
318
318
  **Three-Doctor Loop chain** (auto-propose after prescription report):
319
319
  - M-tier + context/token waste → propose `/context-doctor`
320
320
  - M-tier + user-facing behavior changes → propose `/sim-conductor Area A`
321
- - SKILL.md S-tier (> 300 lines, no SKILL_detail.md) → auto-propose `/skill-splitter`: `"I see [skill] SKILL.md is [N] lines with no SKILL_detail.md. Want me to run /skill-splitter?"`
321
+ - SKILL.md S-tier (> 300 lines, no SKILL_detail.md) → auto-propose `/salience-splitter`: `"I see [skill] SKILL.md is [N] lines with no SKILL_detail.md. Want me to run /salience-splitter?"`
322
322
  - Both → propose full Three-Doctor Loop: context-doctor → sim-conductor Area A
323
323
 
324
324
  ---
@@ -137,7 +137,7 @@ FAIL routing: First FAIL → 1 re-synthesis allowed. FAIL after re-synthesis →
137
137
 
138
138
  ### Step 6 — Curator Lifecycle Review
139
139
 
140
- **6-1 SKILL.md Lifecycle**: 30+ day unused → [STALE] candidate. `pinned: true` → never touch. ≥70% overlap → merge candidate suggestion. **> 300 lines AND no `SKILL_detail.md`** → propose `/skill-splitter` (governance-semantic split — not compression; the grew-through-harvest pattern is a natural split trigger).
140
+ **6-1 SKILL.md Lifecycle**: 30+ day unused → [STALE] candidate. `pinned: true` → never touch. ≥70% overlap → merge candidate suggestion. **> 300 lines AND no `SKILL_detail.md`** → propose `/salience-splitter` (governance-semantic split — not compression; the grew-through-harvest pattern is a natural split trigger).
141
141
 
142
142
  **6-1-a Archive-candidate auto-tag**: When 0 invocations in 30 days detected (cross-check `tracks/_meta/skill_usage.md`), auto-append `#archive-candidate` tag to that skill's CATALOG.md entry. No file deletion — tag only. User reviews tagged entries at next session start.
143
143
 
@@ -139,7 +139,9 @@ Y (add integration plan items to Step 1) / N (add-only, keep existing rules) / S
139
139
 
140
140
  **[Prerequisite] install-doctor conflict diagnosis (only in environments without install history)**: if `~/.cc_sentinels/{project-name}_wizard_done` doesn't exist (first install), call `/install-doctor --plugin fh-meta` first. CONFLICT/WARNING items → add ❗ markers to Step 2 proposal list. Items the doctor already diagnosed (`FH plugin install` · `zshrc hook` · `.claudeignore`) → map results directly, skip re-diagnosis; all other items → check directly.
141
141
 
142
- Auto-check each item as PASS / MISS / FAIL. Check items: `.claudeignore` · `local_fh_context.md` · `zshrc hook` · `weekly_audit` freshness · `sentinel` setup · FH plugin install · `.git/info/exclude` · MCP plugin · `deep-insight` plugin (optional) · `fh_env_context.jsonc` · `phantom-gate` (Python + AI-output projects only) · domain pattern pack (optional, none ship by default) · local-LLM offload (optional — surface ONLY if the Step 0 bash emitted the literal line `Local LLM runtime: detected`; absent that line, this item does not exist).
142
+ Auto-check each item as PASS / MISS / FAIL. Check items: `.claudeignore` · `local_fh_context.md` · `zshrc hook` · `weekly_audit` freshness · `sentinel` setup · FH plugin install · `.git/info/exclude` · MCP plugin · `deep-insight` plugin (optional) · `fh_env_context.jsonc` · `phantom-gate` (Python + AI-output projects only) · domain pattern pack (optional, none ship by default) · local-LLM offload (optional — surface ONLY if the Step 0 bash emitted the literal line `Local LLM runtime: detected`; absent that line, this item does not exist) · **`env-delta SessionStart hook`** (Mode D — `scripts/fh_env_delta_scan.sh` registered in SessionStart; MISS if absent when the hub has sibling repos) · **`capability-escalation consent`** (per `[[capability_escalation_consent]]` — negotiate BOTH axes at onboarding so no later escalation is a surprise: *"Allow cross-family sidecars (external billing) for load-bearing verification?"* → `sidecar_consent`, and *"Allow Sonnet→Opus floor-up on depth-heavy turns (~3–5× cost)? Decline = stay at the Sonnet floor, asked per-occasion instead."* → `floorup_consent`; record both to the UAP. MISS if the UAP lacks either field — a skipped user is asked-once at first need, never sprung).
143
+
144
+ **env-delta detection (Mode D — the mechanical floor for claim ② auto-trigger)**: FH's "undeployed-asset discovery + auto-mapping" (CLAUDE.md claim ②) previously fired **only on explicit invocation** — a new sibling repo pulled, or a task-first session in an unmapped project, was **not** self-detected (the onboarding menu is suppressed on task-first entry by the metadata-is-not-intent / task-first guards, which are load-bearing and must stay). The `scripts/fh_env_delta_scan.sh` SessionStart hook (sibling of `fh_session_load.sh`) closes this **mechanically**: it scans the projects root for git repos that are neither mapped (`tracks/{name}/`) nor wizard-done nor skip-sentineled, and emits a **one-line PROPOSAL** into turn-0 context, firing regardless of task-first entry. It **proposes only** — mapping/install stays HITL; a skipped repo is recorded via a `{name}_mapping_skipped` sentinel so it never re-nags. This is the mechanical anchor over the prose/salience layer (three-family audit 2026-07-06 rated ② PARTIAL/THEATER precisely because the auto-trigger lived only in prose).
143
145
 
144
146
  **Local-LLM offload (conditional, recommend-only)**: when Step 0 detected a local LLM runtime (Ollama / LM Studio), surface one optional item — route to `/plugin-recommender` for local-model offload tooling. FH recommends, never rebuilds (no-reinvention). Two complementary offload shapes the user picks per workload: **input-side context routing** (a small local model returns line ranges, so the cloud model receives only the dense slices instead of whole files) and **output-side generation delegation** (the local model generates and self-reviews code while the frontier model decomposes and validates). The benefit is tier-dependent — largest in headless/scripted pipelines and on weaker cloud tiers; an interactive session already triages via targeted reads. Local models suit **bounded, well-specified** work (triage, codebase explanation, instructed maintenance), not long-horizon autonomous tasks where small models loop or hallucinate — so the frontier model keeps decomposition and validation. Skip silently when no local runtime is present.
145
147
 
@@ -9,8 +9,17 @@ model: sonnet
9
9
  # memory-hygiene — Stale Memory Detection and Re-Verification
10
10
 
11
11
  > Addresses the "stale-but-confident" failure mode: verified information that silently drifts
12
- > while remaining highly ranked in retrieval — identified as a critical harness failure mode
12
+ > while remaining highly ranked in retrieval — identified as a harness failure mode
13
13
  > in *Scaling the Harness in Agentic AI* (arXiv:2605.26112).
14
+ >
15
+ > **Sister asset**: arXiv:2607.01935 (*A-TMA: Decoupling State-Aware Memory Failures in Long-Term
16
+ > Agent Memory*, Shi/Tang/Tung 2026) names this exact class "ghost memory" — outdated / current /
17
+ > transitional facts intermixing during retrieval — and benchmarks it (LTP; conflict-accuracy +0.240,
18
+ > temporal-F1 0.03→0.17 on LoCoMo with a bank-maintenance layer). It is the tighter external frame for
19
+ > what memory-hygiene detects: this skill is the FH-native *detection + archival* pass over the same
20
+ > failure A-TMA formalizes at the retrieval layer. (Source-verified 2026-07-03; a broader bounded-memory
21
+ > testbed, arXiv:2607.02255 AgenticSTS, was considered and set aside — it targets context-assembly, not
22
+ > staleness, so it is not the sister here.)
14
23
 
15
24
  FH is an online-first harness. Its memory entries point to live external resources (GitHub
16
25
  repos, arXiv records, Zenodo DOIs, monitoring routines). These drift faster than in
@@ -103,38 +103,16 @@ literal private token.
103
103
 
104
104
  ## Step 3. Scan
105
105
 
106
- For each pattern in Step 1, grep the tracked set, then drop allowlisted hits.
107
-
108
- ```bash
109
- cd "$REPO_PATH" || exit 1
110
- # Build the tracked-file list once.
111
- git ls-files > /tmp/_psa_tracked.txt
112
-
113
- # Load your real patterns from the gitignored source (one "severity<TAB>regex" per line).
114
- PATTERN_SRC="${PSA_PATTERNS:-.claude/rules/.public-surface-patterns}"
115
- # Absent file ≠ CLEAN. An absent file is unconfigured (silent-failure risk); an EMPTY file is an
116
- # explicit "no tokens to protect" → CLEAN. Distinguish the two.
117
- [ -e "$PATTERN_SRC" ] || { echo "⚪ NOT CONFIGURED: no pattern source at $PATTERN_SRC. Create it (empty = explicit CLEAN) before trusting any verdict. Not scanning."; exit 2; }
118
-
119
- # One grep pass per pattern row; the regex comes from the file, never hardcoded here.
120
- while IFS=$'\t' read -r severity regex; do
121
- [ -z "$regex" ] && continue
122
- grep -nIE "$regex" $(cat /tmp/_psa_tracked.txt) 2>/dev/null | sed "s/^/[$severity] /"
123
- done < "$PATTERN_SRC"
124
- ```
125
-
126
- For each pattern, run `grep -nIE "<regex>" $(git ls-files)`:
127
- - `-n` → line numbers (required for `file:line` output)
128
- - `-I` → skip binary files
129
- - `-E` → extended regex (alternation in the pattern table)
130
-
131
- Then remove any hit whose `file` + matched `token` is on the Step 2 allowlist. Do this for **every**
106
+ For each pattern in Step 1, grep the tracked set, then drop allowlisted hits. Do this for **every**
132
107
  pattern row before producing the report — do not stop at the first HIT.
133
108
 
134
- **Binary / generated carve-out**: `-I` already skips binaries. Additionally note (do not auto-suppress)
135
- hits inside generated artifacts (e.g. `paper/*.html` exported from a private source) — these are real
136
- leaks on the public surface and must be reported, but the fix is "regenerate from a sanitized source",
137
- not "edit the HTML by hand". Flag them with a `(generated artifact)` note.
109
+ **Binary / generated carve-out**: skip binaries. Additionally note (do not auto-suppress) hits inside
110
+ generated artifacts (e.g. `paper/*.html` exported from a private source) — these are real leaks on the
111
+ public surface and must be reported, but the fix is "regenerate from a sanitized source," not "edit the
112
+ HTML by hand." Flag them with a `(generated artifact)` note.
113
+
114
+ > **Detail**: See `SKILL_detail.md §Step3-Scan-Script` — the grep-per-pattern bash implementation,
115
+ > flag reference (`-n`/`-I`/`-E`) — read when executing this step.
138
116
 
139
117
  ---
140
118
 
@@ -151,23 +129,13 @@ reports):
151
129
  `changeme`, `REDACTED`, `xxxx`, AWS-doc keys like `AKIAIOSFODNN7EXAMPLE`). A high-entropy *example* is
152
130
  not a secret.
153
131
 
154
- ```bash
155
- # FP-hygiene tests the MATCHED TOKEN only — never the whole line. A line-level `grep -v` would
156
- # suppress a real leak that merely *mentions* an example (e.g. `user=<realname> # see EXAMPLE.md`),
157
- # violating PSA's "allowlist tight" rule. So extract the matched span per hit and drop it only when
158
- # the span is *entirely* a placeholder/example (anchored ^…$).
159
- PLACEHOLDER='^(<[a-z0-9_-]+>|\{project\}|EXAMPLE|dummy|changeme|REDACTED|xxxx)$'
160
- grep -nIE "$regex" $(cat /tmp/_psa_tracked.txt) 2>/dev/null | while IFS= read -r hit; do
161
- tok=$(printf '%s' "$hit" | grep -oiE "$regex" | head -1)
162
- printf '%s' "$tok" | grep -qiE "$PLACEHOLDER" && continue # token IS a placeholder → drop
163
- printf '%s\n' "$hit"
164
- done
165
- ```
166
-
167
132
  This differs from the Step 2 allowlist: Step 2 suppresses by **file::token legitimacy**, Step 3b by
168
133
  **token value-shape**. Both run — Step 2 then Step 3b. Keep it tight (PSA's "allowlist tight" rule): if a
169
134
  token only *contains* an example substring but is otherwise a real private value, it still reports.
170
135
 
136
+ > **Detail**: See `SKILL_detail.md §Step3b-FP-Hygiene-Script` — the token-shape-only extraction bash
137
+ > implementation (never line-level) — read when executing this step.
138
+
171
139
  ---
172
140
 
173
141
  ## Step 3c. Ignore-Verification — the gitignore-mistake safeguard (root-cause, runs before Step 4)
@@ -178,39 +146,14 @@ tracked leak. For each expected-private path, `git check-ignore -v` reports whet
178
146
  by which layer** (committed `.gitignore` · local `.git/info/exclude` · global `core.excludesFile`). A
179
147
  path that resolves to no ignore rule is a MISS — surface it before it is ever `git add`ed.
180
148
 
181
- ```bash
182
- # Expected-private set = conventional FH local-only files, EXTENDED with any `# private-path: <path>`
183
- # lines the operator added to the gitignored pattern source (self-extends per repo — not a frozen
184
- # operator snapshot). Built one-path-per-line + while-read so it is portable across bash AND zsh
185
- # (zsh does not word-split an unquoted variable, so `for f in $VAR` would break). A non-existent file
186
- # is skipped; an all-absent set emits n/a, never a silent pass.
187
- present=$({ printf '%s\n' CLAUDE.local.md .claude/rules/.public-surface-patterns \
188
- .claude/rules/local_fh_context.md tracks/_meta/user_adaptation_profile.md
189
- grep -E '^# private-path:' .claude/rules/.public-surface-patterns 2>/dev/null \
190
- | sed -E 's/^# private-path:[[:space:]]*//'; } \
191
- | awk 'NF' | sort -u | while IFS= read -r f; do [ -e "$f" ] && printf '%s\n' "$f"; done)
192
- [ -z "$present" ] && echo "n/a (no expected-private files present in this repo — add '# private-path:' lines to the pattern source if any exist)"
193
- printf '%s\n' "$present" | while IFS= read -r f; do
194
- [ -z "$f" ] && continue
195
- # Tracked status is tested FIRST: a file can match an ignore rule yet still be force-added
196
- # (`git add -f`) — the exact ignored-but-committed mechanism behind the PR #109 leak. Tracked wins,
197
- # so an ignored-but-committed file reports TRACKED (not a false-clean OK).
198
- if git ls-files --error-unmatch "$f" >/dev/null 2>&1; then
199
- echo "TRACKED $f (already committed — Step 3 scans its contents; un-track if it must be private: git rm --cached)"
200
- elif rule=$(git check-ignore -v "$f" 2>/dev/null); then
201
- echo "OK $f → ignored by [$rule]"
202
- else
203
- echo "MISS $f (exists, NOT ignored, NOT tracked — one 'git add .' from a leak; add an ignore rule)"
204
- fi
205
- done
206
- ```
207
-
208
149
  - **OK** — ignored; the report names which layer covers it (operator-private files should resolve to
209
150
  `.git/info/exclude` or `.gitignore`, never end up tracked).
210
151
  - **MISS** — exists but no ignore rule covers it → counts as **🟠 MED** in the Step 4 verdict (a latent
211
152
  leak, not yet a breach).
212
153
  - **TRACKED** — already committed: Step 3 scans its *contents*; this also flags it for un-tracking if it
213
- was meant to be private.
154
+ was meant to be private. Tracked status is tested **before** ignore status — a file can match an
155
+ ignore rule yet still be force-added (`git add -f`), the exact ignored-but-committed mechanism behind
156
+ the PR #109 leak, so TRACKED must win over a false-clean OK.
214
157
 
215
158
  Why this is the safeguard for **gitignore mistakes** (a wrong assumption about what is ignored):
216
159
  `.gitignore` is committed/shared, `.git/info/exclude` is local/personal, and a global `core.excludesFile`
@@ -218,6 +161,10 @@ ignores across all repos — `git check-ignore -v` is the one command that says
218
161
  applies, so an "I thought it was ignored" error surfaces here instead of in a public PR (the PR #109
219
162
  class of leak). Diagnostic-only: this step never writes — it reports, the operator adds the ignore rule.
220
163
 
164
+ > **Detail**: See `SKILL_detail.md §Step3c-Ignore-Verification-Script` — the expected-private-path
165
+ > enumeration + `git check-ignore`/`git ls-files --error-unmatch` bash implementation, bash/zsh
166
+ > portability note — read when executing this step.
167
+
221
168
  ---
222
169
 
223
170
  ## Step 4. Report
@@ -0,0 +1,150 @@
1
+ ---
2
+ name: public-surface-audit-detail
3
+ description: On-demand execution detail for public-surface-audit — scan scripts, report/JSON templates, provenance.
4
+ load: on-demand
5
+ ---
6
+
7
+ ## §Step3-Scan-Script
8
+
9
+ ```bash
10
+ cd "$REPO_PATH" || exit 1
11
+ # Build the tracked-file list once.
12
+ git ls-files > /tmp/_psa_tracked.txt
13
+
14
+ # Load your real patterns from the gitignored source (one "severity<TAB>regex" per line).
15
+ PATTERN_SRC="${PSA_PATTERNS:-.claude/rules/.public-surface-patterns}"
16
+ # Absent file ≠ CLEAN. An absent file is unconfigured (silent-failure risk); an EMPTY file is an
17
+ # explicit "no tokens to protect" → CLEAN. Distinguish the two.
18
+ [ -e "$PATTERN_SRC" ] || { echo "⚪ NOT CONFIGURED: no pattern source at $PATTERN_SRC. Create it (empty = explicit CLEAN) before trusting any verdict. Not scanning."; exit 2; }
19
+
20
+ # One grep pass per pattern row; the regex comes from the file, never hardcoded here.
21
+ while IFS=$'\t' read -r severity regex; do
22
+ [ -z "$regex" ] && continue
23
+ grep -nIE "$regex" $(cat /tmp/_psa_tracked.txt) 2>/dev/null | sed "s/^/[$severity] /"
24
+ done < "$PATTERN_SRC"
25
+ ```
26
+
27
+ For each pattern, run `grep -nIE "<regex>" $(git ls-files)`:
28
+ - `-n` → line numbers (required for `file:line` output)
29
+ - `-I` → skip binary files
30
+ - `-E` → extended regex (alternation in the pattern table)
31
+
32
+ Then remove any hit whose `file` + matched `token` is on the Step 2 allowlist. Do this for **every**
33
+ pattern row before producing the report — do not stop at the first HIT.
34
+
35
+ **Binary / generated carve-out**: `-I` already skips binaries. Additionally note (do not auto-suppress)
36
+ hits inside generated artifacts (e.g. `paper/*.html` exported from a private source) — these are real
37
+ leaks on the public surface and must be reported, but the fix is "regenerate from a sanitized source",
38
+ not "edit the HTML by hand". Flag them with a `(generated artifact)` note.
39
+
40
+ ---
41
+
42
+ ## §Step3b-FP-Hygiene-Script
43
+
44
+ ```bash
45
+ # FP-hygiene tests the MATCHED TOKEN only — never the whole line. A line-level `grep -v` would
46
+ # suppress a real leak that merely *mentions* an example (e.g. `user=<realname> # see EXAMPLE.md`),
47
+ # violating PSA's "allowlist tight" rule. So extract the matched span per hit and drop it only when
48
+ # the span is *entirely* a placeholder/example (anchored ^…$).
49
+ PLACEHOLDER='^(<[a-z0-9_-]+>|\{project\}|EXAMPLE|dummy|changeme|REDACTED|xxxx)$'
50
+ grep -nIE "$regex" $(cat /tmp/_psa_tracked.txt) 2>/dev/null | while IFS= read -r hit; do
51
+ tok=$(printf '%s' "$hit" | grep -oiE "$regex" | head -1)
52
+ printf '%s' "$tok" | grep -qiE "$PLACEHOLDER" && continue # token IS a placeholder → drop
53
+ printf '%s\n' "$hit"
54
+ done
55
+ ```
56
+
57
+ This differs from the Step 2 allowlist: Step 2 suppresses by **file::token legitimacy**, Step 3b by
58
+ **token value-shape**. Both run — Step 2 then Step 3b. Keep it tight (PSA's "allowlist tight" rule): if a
59
+ token only *contains* an example substring but is otherwise a real private value, it still reports.
60
+
61
+ ---
62
+
63
+ ## §Step3c-Ignore-Verification-Script
64
+
65
+ ```bash
66
+ # Expected-private set = conventional FH local-only files, EXTENDED with any `# private-path: <path>`
67
+ # lines the operator added to the gitignored pattern source (self-extends per repo — not a frozen
68
+ # operator snapshot). Built one-path-per-line + while-read so it is portable across bash AND zsh
69
+ # (zsh does not word-split an unquoted variable, so `for f in $VAR` would break). A non-existent file
70
+ # is skipped; an all-absent set emits n/a, never a silent pass.
71
+ present=$({ printf '%s\n' CLAUDE.local.md .claude/rules/.public-surface-patterns \
72
+ .claude/rules/local_fh_context.md tracks/_meta/user_adaptation_profile.md
73
+ grep -E '^# private-path:' .claude/rules/.public-surface-patterns 2>/dev/null \
74
+ | sed -E 's/^# private-path:[[:space:]]*//'; } \
75
+ | awk 'NF' | sort -u | while IFS= read -r f; do [ -e "$f" ] && printf '%s\n' "$f"; done)
76
+ [ -z "$present" ] && echo "n/a (no expected-private files present in this repo — add '# private-path:' lines to the pattern source if any exist)"
77
+ printf '%s\n' "$present" | while IFS= read -r f; do
78
+ [ -z "$f" ] && continue
79
+ # Tracked status is tested FIRST: a file can match an ignore rule yet still be force-added
80
+ # (`git add -f`) — the exact ignored-but-committed mechanism behind the PR #109 leak. Tracked wins,
81
+ # so an ignored-but-committed file reports TRACKED (not a false-clean OK).
82
+ if git ls-files --error-unmatch "$f" >/dev/null 2>&1; then
83
+ echo "TRACKED $f (already committed — Step 3 scans its contents; un-track if it must be private: git rm --cached)"
84
+ elif rule=$(git check-ignore -v "$f" 2>/dev/null); then
85
+ echo "OK $f → ignored by [$rule]"
86
+ else
87
+ echo "MISS $f (exists, NOT ignored, NOT tracked — one 'git add .' from a leak; add an ignore rule)"
88
+ fi
89
+ done
90
+ ```
91
+
92
+ Why this is the safeguard for **gitignore mistakes** (a wrong assumption about what is ignored):
93
+ `.gitignore` is committed/shared, `.git/info/exclude` is local/personal, and a global `core.excludesFile`
94
+ ignores across all repos — `git check-ignore -v` is the one command that says *which* rule (if any)
95
+ applies, so an "I thought it was ignored" error surfaces here instead of in a public PR (the PR #109
96
+ class of leak). Diagnostic-only: this step never writes — it reports, the operator adds the ignore rule.
97
+
98
+ ---
99
+
100
+ ## §Report-Template
101
+
102
+ ```
103
+ public-surface-audit — Operator-Private Token Scan
104
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
105
+ Target: {REPO_PATH} | Tracked files scanned: {N}
106
+
107
+ 🔴 HIGH ({count})
108
+ {file}:{line} → {matched token} [class: username | company asset]
109
+ 🟠 MED ({count})
110
+ {file}:{line} → {matched token} [class: absolute home path | ignore-MISS (Step 3c)]
111
+ 🟡 LOW ({count})
112
+ {file}:{line} → {matched token} [class: companion-store | private wiring]
113
+
114
+ Allowlist-suppressed: {count} hit(s) (legitimate references — not leaks)
115
+
116
+ Verdict:
117
+ ⚪ NOT CONFIGURED — pattern source absent (nothing scanned — NOT a clean result; set up first)
118
+ 🟢 CLEAN — pattern source present (incl. empty), 0 HIGH + 0 MED + 0 LOW (after allowlist)
119
+ 🟡 REVIEW — 0 HIGH + 0 MED, LOW-only (drift, not a breach)
120
+ 🔴 LEAK — 1+ HIGH or 1+ MED (block publish / fix before commit)
121
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
122
+ ```
123
+
124
+ ---
125
+
126
+ ## §JSON-Schema
127
+
128
+ ```json
129
+ {
130
+ "target": "{REPO_PATH}",
131
+ "tracked_files": 0,
132
+ "findings": [
133
+ {"file": "path", "line": 42, "token": "<matched>", "severity": "HIGH", "class": "username"}
134
+ ],
135
+ "counts": {"HIGH": 0, "MED": 0, "LOW": 0, "suppressed": 0},
136
+ "verdict": "CLEAN"
137
+ }
138
+ ```
139
+
140
+ ---
141
+
142
+ ## §Sister-Asset-Provenance
143
+
144
+ Step 3b (FP hygiene) and Step 5 (`--json`) were imported from **garrytan/gstack** `gstack-redact`
145
+ (`lib/redact-engine.ts`) during a hands-on sister-asset cross-audit (2026-06-06; see
146
+ `tracks/_audit/session_2026_06_06_gstack_sister_handson.md`). They are adapted to PSA's operator-IP
147
+ ontology — `gstack-redact`'s generic secret/PII classes (AWS / PEM / JWT / hostname) stay out of PSA's
148
+ scope (orthogonal coverage: PSA = operator-IP leak, redact = generic secret). The reverse direction
149
+ (PSA's operator private-codename + bare-username classes, which `gstack-redact` structurally cannot
150
+ detect) is a candidate contribution back to gstack.
@@ -1,15 +1,25 @@
1
1
  ---
2
- name: skill-splitter
3
- description: Splits an over-loaded SKILL.md into always-loaded (SKILL.md) + on-demand (SKILL_detail.md) layers using a governance-semantic criterion not length, but when the content is needed. Connects the two files with imperative pointers. Based on paper §9.5 Protocol-Priority Split pattern. Diagnoses, classifies, splits, and verifies in one pass.
2
+ name: salience-splitter
3
+ description: Splits an over-loaded always-loaded context asset — a SKILL.md, CLAUDE.md, or memory index — into a lean always-loaded layer + an on-demand layer, using a governance-semantic criterion (not length, but when the content is needed), connected by imperative pointers. Based on paper §9.5 Protocol-Priority Split pattern. Diagnoses, classifies, splits, and verifies in one pass. Renamed from skill-splitter (old name still routes here).
4
4
  user-invocable: true
5
5
  allowed-tools: ["Read", "Write", "Edit", "Bash", "Grep", "Glob"]
6
6
  model: sonnet
7
7
  ---
8
8
 
9
- # skill-splitter — SKILL.md Governance-Semantic Split
9
+ # salience-splitter — Governance-Semantic Context Split
10
10
 
11
- > A SKILL.md that does everything in one file is not simple — it is unscoped.
12
- > The goal is two files each smaller than the original, not one file and its appendix.
11
+ > **Renamed from `skill-splitter` (2026-07-05).** Old-name references still route here. The rename reflects
12
+ > the generalized scope: the split criterion applies to **any always-loaded context asset** a SKILL.md,
13
+ > a CLAUDE.md, or a memory index — not only skills. The label now names the substance (salience-tiering of
14
+ > always-loaded context), not one instance of it. Dogfood: applied to a memory index 2026-07-05.
15
+
16
+ > An always-loaded asset that holds everything in one layer is not simple — it is unscoped.
17
+ > The goal is a lean always-loaded layer + an on-demand layer, not one file and its appendix.
18
+
19
+ **Scope (all three share one criterion — salience: what must be in attention every load):**
20
+ - **SKILL.md** → always-loaded SKILL.md + on-demand `SKILL_detail.md`
21
+ - **CLAUDE.md** → lean rules + on-demand detail docs (imperative `> **Detail**: See …` pointers)
22
+ - **memory index** → hot `MEMORY.md` + on-demand `MEMORY_archive.md` (cold/closed/superseded entries)
13
23
 
14
24
  ## Trigger Phrases
15
25
 
@@ -19,7 +29,7 @@ model: sonnet
19
29
  | "context-doctor flagged this skill", "SKILL.md is bloated" | Post-diagnosis split |
20
30
  | "I can't see the key parts", "too much detail in the skill file" | Readability problem |
21
31
  | "separate the bash from the logic", "move the templates out" | Structural refactor request |
22
- | `/skill-splitter` | Explicit invocation |
32
+ | `/salience-splitter` | Explicit invocation |
23
33
 
24
34
  ---
25
35
 
@@ -63,7 +73,7 @@ Step 4 — Verify
63
73
 
64
74
  > **Detail**: See `SKILL_detail.md §Verification-Checklist` — pre-commit checklist table (8 checks) — read when running Step 4 verification.
65
75
 
66
- > **Detail**: See `SKILL_detail.md §Split-Execution` — step-by-step trimming procedure, SKILL_detail.md front-matter format, orphan §section check — read when executing Steps 2–3.
76
+ > **Detail**: See `SKILL_detail.md §Split-Execution` — step-by-step trimming procedure, SKILL_detail.md front-matter format, orphan-section check — read when executing Steps 2–3.
67
77
 
68
78
  > **Detail**: See `SKILL_detail.md §Classification` — ambiguous content decision algorithm, behavioral-vs-implementation test, 12 annotated examples — read when unsure which layer a section belongs to.
69
79
 
@@ -76,7 +86,7 @@ Pointers must be **imperative** (not advisory). The difference:
76
86
  | Form | Risk |
77
87
  |---|---|
78
88
  | Advisory: `"see SKILL_detail.md for details"` | Consumer agent may skip |
79
- | **Imperative**: `"> **Detail**: See \`SKILL_detail.md §SectionName\` — [what's there] — read when [specific condition]."` | Consumer agent loads on trigger |
89
+ | **Imperative**: `"> **Detail**: See \`SKILL_detail.md §<SectionName>\` — [what's there] — read when [specific condition]."` | Consumer agent loads on trigger |
80
90
 
81
91
  Every removed section must have exactly one imperative pointer at the point of removal in SKILL.md.
82
92
 
@@ -113,7 +123,7 @@ Run on a SKILL.md when **any one** of:
113
123
  ```
114
124
  Step 1 classification table produced
115
125
  + SKILL.md trimmed: triggers · principles · step overview · decision tables · Done When retained
116
- + SKILL.md has imperative pointer for every removed section (> **Detail**: See SKILL_detail.md §X)
126
+ + SKILL.md has imperative pointer for every removed section (> **Detail**: See SKILL_detail.md §<X>)
117
127
  + SKILL_detail.md created: ## §SectionName header for every pointer in SKILL.md
118
128
  + phantom-quench: 0 phantoms (all §pointers resolve)
119
129
  → Fallback (skill unavailable): run §Verification-Checklist manually from SKILL_detail.md
@@ -1,10 +1,10 @@
1
1
  ---
2
- name: skill-splitter-detail
3
- description: Detail reference for skill-splitter — content classification algorithm, pointer format variants, verification checklist. Load when executing a specific step.
2
+ name: salience-splitter-detail
3
+ description: Detail reference for salience-splitter — content classification algorithm, pointer format variants, verification checklist. Load when executing a specific step.
4
4
  load: on-demand
5
5
  ---
6
6
 
7
- # skill-splitter — Detail Reference
7
+ # salience-splitter — Detail Reference
8
8
 
9
9
  > Load when executing a specific step. SKILL.md contains the core principle, step overview, pointer format requirement, and Done When.
10
10
 
@@ -70,13 +70,13 @@ Behavioral rules always pass this test (even if rarely triggered, the consumer n
70
70
  ### Standard pointer (single section reference)
71
71
 
72
72
  ```markdown
73
- > **Detail**: See `SKILL_detail.md §SectionName` — [one-line description of what's there] — read when [specific condition that triggers need].
73
+ > **Detail**: See `SKILL_detail.md §<SectionName>` — [one-line description of what's there] — read when [specific condition that triggers need].
74
74
  ```
75
75
 
76
76
  ### Multi-item pointer (several related sections)
77
77
 
78
78
  ```markdown
79
- > **Detail**: See `SKILL_detail.md §SectionName-A` (bash scripts) · SectionName-B` (format templates) — read when executing this step.
79
+ > **Detail**: See `SKILL_detail.md §<SectionName-A>` (bash scripts) · `§<SectionName-B>` (format templates) — read when executing this step.
80
80
  ```
81
81
 
82
82
  ### Pointer placement rules
@@ -96,7 +96,7 @@ Advisory (risky — consumer may skip):
96
96
 
97
97
  Imperative (required form):
98
98
  ```
99
- "> **Detail**: See `SKILL_detail.md §Step6-Detail` — bash for STALE detection, memory scan, skill usage leaderboard — read when executing Step 6."
99
+ "> **Detail**: See `SKILL_detail.md §<Step6-Detail>` — bash for STALE detection, memory scan, skill usage leaderboard — read when executing Step 6."
100
100
  ```
101
101
 
102
102
  The imperative form includes: