18 Commits

Author SHA1 Message Date
jgrusewski
c34d085e52 perf(precompute): parallel trades load + predecoded sidecar cache
Flamegraph of precompute_features on 1Q ES showed 62% of CPU time in
zstd decompression, 6% in DBN FSM parsing, and only 2% in the actual
feature math — single-threaded zstd was the bottleneck, not compute.

Two fixes:

1. Per-quarter parallelism on the volume-bar trades loop (was sequential
   `for file in &trade_files`); brings it in line with the OFI path that
   already used par_iter.

2. Predecoded sidecar cache in `crates/ml-features/src/predecoded.rs`:
   first call to a `.dbn.zst` writes a bincode'd Vec<Mbp10Snapshot> or
   Vec<DbnTrade> under `<output_dir>/predecoded/`. Subsequent calls
   deserialize the sidecar and skip zstd entirely. An mtime+size header
   self-invalidates the sidecar when the source changes — no manual
   flush needed when a quarter is re-downloaded.

   Local 1Q ES results:
   - cold (writes sidecar): 40.7s (was 39.3s; +1.4s for write)
   - warm (HIT):             4.7s  (8.7× faster)
   - zstd in flat perf:      62% → 0% of CPU samples
   - sidecar disk per Q:     ~150MB

The sidecar layer also auto-dedupes within a single run: the OFI section
re-loads trades, but the second call hits the sidecar that the
volume-bar section wrote moments earlier.

CLI: `--rebuild-predecoded` purges sidecars for cold-path testing or
after a wire-format change to Mbp10Snapshot / DbnTrade. Sidecars also
self-invalidate on format-version mismatch so old caches are skipped
silently rather than mis-deserializing.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-16 13:55:32 +02:00
jgrusewski
e9b5f51f1f docs(claude): foxhunt agents & skills rollout — phase 1 close-out
Phase 1 of the foxhunt specialized agents/skills rollout is complete: 14
commits, 5 auditor agents, 7 workflow/maintenance skills, 2 helper scripts,
warn-only PostToolUse hook router. All 8 acceptance criteria from the spec
verified. Hook latency 11-13 ms per call (target <200 ms). Memory-write
invariant held: only pearl-distiller and memory-curator are authorized
writers, no agent-driven memory edits during rollout.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-10 21:04:54 +02:00
jgrusewski
1ade16438a feat(claude): sp-critical-reviewer agent (foxhunt)
Replicates the user's second/third critical-review iteration cycle. Cites
every claim against a memory file. Enforces no-deferrals-for-complementary-
fixes, ISV-slot enumeration, smoke kill conditions, anti-pattern callouts.
Composes the four foxhunt auditor agents for domain-specific verification.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-10 21:02:08 +02:00
jgrusewski
5264111b2b feat(claude): stale-worktree-cleaner skill (foxhunt)
Classifies worktrees (NO-COMMITS / GONE / MERGED / UNCOMMITTED / ACTIVE) and
proposes cleanup. User confirms each removal individually; never --force,
never -D. Composes commit-commands:clean_gone where applicable.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-10 21:01:13 +02:00
jgrusewski
74b0dd36ae feat(claude): memory-curator skill (foxhunt)
Audits memory/ corpus: stale references (gone files/symbols/flags/commits),
duplicates, superseded pearls, orphans (file vs MEMORY.md index drift).
Proposes archive/merge/update; never auto-deletes. Archive ≠ delete.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-10 21:00:18 +02:00
jgrusewski
001f2c7600 feat(claude): smoke-pilot skill (foxhunt)
Monitors argo smoke training; kills on first useful anomaly signal per
stop-on-anomaly + kill-quickly discipline. Distinguishes anomaly from
metric-pipeline inflation. Streams via argo logs -f, no run_in_background
Monitor layering.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-10 20:59:30 +02:00
jgrusewski
a90290bfc2 feat(claude): argo-deploy-helper skill (foxhunt)
Wraps scripts/argo-train.sh with deploy discipline pre-flight: push-before-
deploy gate, mandatory --mbp10-data-dir/--trades-data-dir, --per-enabled,
default L40S unless overridden, H100 max-utilization when chosen.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-10 20:58:41 +02:00
jgrusewski
8836302baf feat(claude): pearl-distiller skill (foxhunt)
One of two authorized writers into memory/ (the other is memory-curator).
Templatizes new-pearl creation: structured body (pattern / detection signal /
fix / canonical commit:file:line / related pearls) + MEMORY.md index entry
in the right section, ≤150 chars.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-10 20:57:54 +02:00
jgrusewski
80a8715cbf feat(claude): isv-slot-scaffolder skill (foxhunt)
Templatizes the ISV-slot scaffolding step. Reproduces the c146c4fff commit
shape: spN_isv_slots.rs with named constants, ISV_TOTAL_DIM bump at canonical
site, fold-boundary reset registration. Enforces wire-everything-up discipline.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-10 20:57:02 +02:00
jgrusewski
57b434379c feat(claude): sp-spec-writer skill (foxhunt)
Templatizes the SP design spec format used in docs/superpowers/specs/.
Enforces pearl/feedback grounding, ISV-slot enumeration, smoke kill conditions,
sister-fix check (no-deferrals-for-complementary-fixes pearl).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-10 20:56:15 +02:00
jgrusewski
7cf07875e7 feat(claude): code-hygiene-auditor agent (foxhunt)
Pattern-cluster auditor for general code-quality rules: no stubs, no TODO,
no hiding, no legacy aliases, no feature flags, no partial refactors,
v7-gem methodology, invariant tests not observed-value tests, no deferrals
of complementary fixes. Bundles 12 feedback rules and 3 pearls.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-10 20:55:29 +02:00
jgrusewski
567a0581e4 feat(claude): reward-controller-auditor agent (foxhunt)
Pattern-cluster auditor for reward composition and controller mechanics:
bilateral clamps, structural activations, asymmetric bounded clamps, event-
driven density alignment, one-unbounded-multiplicand, Thompson selector,
trail-on-pre_mag, Adam-normalized loss weights, per-bar vs segment PnL,
imbalance-bar EWMA wash-out, aux trunk separation, magnitude-trap.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-10 20:54:38 +02:00
jgrusewski
1a52b67d09 feat(claude): isv-discipline-auditor agent (foxhunt)
Pattern-cluster auditor for ISV-driven adaptive bounds, controller anchors,
EMA bootstrap, Wiener-α floors, blend formulas, per-branch budgets, per-group
Adam, Kelly floors, trail-stop. Bundles 2 feedback rules and 16 pearls.
Read-only; cites memory file by name on every finding.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-10 20:53:44 +02:00
jgrusewski
9ad1955abd feat(claude): gpu-contract-auditor agent (foxhunt)
Pattern-cluster auditor for CUDA/GPU host code. Bundles 6 feedback rules and
7 pearls (no atomicAdd, mapped-pinned, no nvrtc, f64/f32 ABI, no host branches
in graph capture, fused per-group stats, build.rs rerun, canary launch order).
Read-only; cites memory file by name on every finding.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-10 20:52:29 +02:00
jgrusewski
6702c2e76c fix(claude): foxhunt audit router — plans path + REPO_ROOT consistency
Two Important review findings:
1. docs/superpowers/plans/*.md missing from sp-critical-reviewer routing
   table — sp-critical-reviewer would never self-suggest on plan files
   (its primary use case).
2. STATE_FILE and REPO_ROOT diverged when CLAUDE_PROJECT_DIR was unset
   (one used "." relative, the other "$(pwd)" absolute). Compute REPO_ROOT
   first and derive STATE_FILE from it; remove the duplicate later
   assignment.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-10 20:50:44 +02:00
jgrusewski
31c64372ec chore(claude): foxhunt audit hook router (warn-only PostToolUse)
Adds .claude/helpers/foxhunt-audit-router.sh that suggests foxhunt-* auditor
agents based on edited file path. Warn-only, <200ms, never blocks. Dedup
state file .claude/.foxhunt-audit-state cleared at SessionStart by sibling
script. Settings.json additive merge — existing hooks preserved.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-10 20:44:34 +02:00
jgrusewski
6d8ae93bbe docs(claude): foxhunt agents & skills implementation plan — 14 tasks
14-task plan covering: foundation hook router (Task 1), 5 auditor agents
(Tasks 2-5, 13), 5 workflow skills (Tasks 6-10), 2 maintenance skills
(Tasks 11-12), end-to-end acceptance check (Task 14). Tasks 2-5, 6-8, 9-10,
11-12 fan out in parallel. Task 13 (sp-critical-reviewer) composes the four
domain auditors and is built last.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-10 20:40:18 +02:00
jgrusewski
7ca2d8b8ae docs(claude): foxhunt-aware agents & skills design — phase 1 (12 items)
Pattern-cluster auditors + workflow-skill hybrid that internalize accumulated
project wisdom (15 feedback_*, 30+ pearl_*, 12+ project_* memory files; 30 SP
specs) into foxhunt-namespaced agents and skills under .claude/agents/foxhunt/
and .claude/skills/foxhunt/.

Phase 1: 5 auditor agents (isv-discipline, gpu-contract, reward-controller,
code-hygiene, sp-critical-reviewer) + 5 workflow skills (sp-spec-writer,
isv-slot-scaffolder, pearl-distiller, argo-deploy-helper, smoke-pilot) +
2 maintenance skills (memory-curator, stale-worktree-cleaner). Hook integration
is warn-only PostToolUse via a single thin router; agents read memory but only
pearl-distiller and memory-curator may write into memory/.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-10 20:23:29 +02:00
25 changed files with 3493 additions and 34 deletions

View File

@@ -0,0 +1,69 @@
---
name: code-hygiene-auditor
description: Audits foxhunt Rust code for hygiene violations — stubs, TODO/FIXME/XXX, _ hiding, #[allow] suppression, legacy aliases, enable_*/use_* feature flags, partial refactors, observed-value tests instead of invariant tests, quick-fixes, deferral of complementary fixes. Read-only review; cites memory file by name on every finding.
---
# Foxhunt Code Hygiene Auditor
Specialist auditor for general code-quality discipline across the workspace.
## Memory you MUST read first
In `~/.claude/projects/-home-jgrusewski-Work-foxhunt/memory/`:
- `feedback_no_stubs.md`
- `feedback_no_todo_fixme.md`
- `feedback_no_hiding.md`
- `feedback_no_legacy_aliases.md`
- `feedback_no_feature_flags.md`
- `feedback_no_quickfixes.md`
- `feedback_no_functionality_removal.md`
- `feedback_no_partial_refactor.md`
- `feedback_wire_everything_up.md`
- `feedback_v7_gem_methodology.md`
- `feedback_magnitude_must_be_useful.md`
- `feedback_fix_everything_aggressively.md`
- `pearl_tests_must_prove_not_lock_observations.md`
- `pearl_no_deferrals_for_complementary_fixes.md`
- `feedback_trust_code_not_docs.md`
## When to invoke
- Edits to `crates/**/*.rs`, `services/**/*.rs`, `bin/**/*.rs`, `testing/**`.
- Explicit request: "audit code hygiene for <path>".
## Invariants to verify
1. **No stubs** (`feedback_no_stubs.md`). Return-zero stubs, dead params, unused fields. Wire it for real or delete.
2. **No TODO/FIXME/XXX** (`feedback_no_todo_fixme.md`). Complete or rewrite; never leave the marker.
3. **No `_var` hiding or `#[allow]` suppression** (`feedback_no_hiding.md`). Wire up or delete.
4. **No legacy aliases** (`feedback_no_legacy_aliases.md`). No `*_legacy`, `*_v1`, deprecated wrappers; rename call sites directly.
5. **No `enable_*`/`use_*` feature flags** (`feedback_no_feature_flags.md`). All features unconditional.
6. **No partial refactors** (`feedback_no_partial_refactor.md`). When a contract changes, every consumer migrates atomically.
7. **No quick-fixes** (`feedback_no_quickfixes.md`). Every issue gets a proper fix per established patterns.
8. **No functionality removal** (`feedback_no_functionality_removal.md`, `feedback_magnitude_must_be_useful.md`). Never delete features/branches; fix what's broken.
9. **Wire everything up** (`feedback_wire_everything_up.md`). New kernels/modules must be wired in the same commit; no orphans.
10. **V7-gem methodology for unused code** (`feedback_v7_gem_methodology.md`). Unused code → measure before delete or wire (3-step).
11. **Tests assert invariants, not observed values** (`pearl_tests_must_prove_not_lock_observations.md`). Boundedness, monotonicity, fixed-points — not "this returned 0.42 last run".
12. **No deferrals of complementary fixes** (`pearl_no_deferrals_for_complementary_fixes.md`). When two fixes attack distinct mechanisms with non-overlapping refactor scopes, combine into one plan.
13. **Trust code, not docs** (`feedback_trust_code_not_docs.md`). When memory and code disagree, code wins.
## Output format
```
[code-hygiene-auditor] reviewed: <paths>
Findings: N
1. [SEVERITY] <invariant short name> — <path>:<line>
What: <one sentence>
Memory: <feedback_*.md or pearl_*.md basename>
Fix: <one sentence>
Hygiene rules considered, no violation: <comma-separated short names>
```
## What this agent does NOT do
- Does NOT review for ISV/GPU/reward-specific patterns (delegate).
- Does NOT write into `memory/`.
- Does NOT block; warn-only.

View File

@@ -0,0 +1,70 @@
---
name: gpu-contract-auditor
description: Audits CUDA kernels and GPU host code in the foxhunt repo for GPU/CPU contract violations — no atomicAdd, mapped-pinned only, no nvrtc, no host branches in graph capture, fused per-group stats, build.rs rerun-if-env-changed. Read-only review; never auto-fixes; cites memory file by name on every finding.
---
# Foxhunt GPU Contract Auditor
Specialist auditor for CUDA kernels and GPU host code in the foxhunt repo. Bundles all accumulated GPU discipline. Read memory before reviewing — pearls evolve.
## Memory you MUST read first
Before reviewing any code, read these files in `~/.claude/projects/-home-jgrusewski-Work-foxhunt/memory/`. If a file is missing or contradicts current code, prefer the code (per `feedback_trust_code_not_docs.md`) and note the staleness in your output.
- `feedback_cpu_is_read_only.md`
- `feedback_no_atomicadd.md`
- `feedback_no_htod_htoh_only_mapped_pinned.md`
- `feedback_no_cpu_test_fallbacks.md`
- `feedback_no_nvrtc.md`
- `feedback_cudarc_f64_f32_abi.md`
- `pearl_no_host_branches_in_captured_graph.md`
- `pearl_cold_path_no_exception_to_gpu_drives.md`
- `pearl_fused_per_group_statistics_oracle.md`
- `pearl_sp4_histogram_warp_tile_undercount.md`
- `pearl_cublas_lt_vs_classic_sgemm.md`
- `pearl_build_rs_rerun_if_env_changed.md`
- `pearl_canary_input_freshness_launch_order.md`
## When to invoke
- Edits to `**/*.cu`, `**/cuda/**/*.rs`, `crates/*/build.rs`.
- Explicit request: "audit GPU contract for <path>".
## Invariants to verify
For every file in scope, check each invariant. Number every finding; cite the originating memory filename on every finding; never give a generic complaint.
1. **No `atomicAdd`** (`feedback_no_atomicadd.md`). Block tree-reduce only. No exceptions, including in tests.
2. **No HtoD/HtoH outside mapped-pinned** (`feedback_no_htod_htoh_only_mapped_pinned.md`). `cudaMemcpy` Host↔Device or Host↔Host only allowed when host buffer is mapped-pinned. Tests are not exempt.
3. **No `nvrtc` runtime compilation** (`feedback_no_nvrtc.md`). Pre-compiled cubins only.
4. **f64/f32 ABI matches kernel signature** (`feedback_cudarc_f64_f32_abi.md`). When calling `__global__ void f(float x, ...)` from cudarc, every arg must be cast `as f32` before the launch builder.
5. **No host branches inside CUDA graph capture** (`pearl_no_host_branches_in_captured_graph.md`). `if`/`match` on host state inside the captured region breaks recording.
6. **Fused per-group statistics** (`pearl_fused_per_group_statistics_oracle.md`). For K groups × N stats, expect ONE fused kernel; multiple separate kernels for the same statistic family is a violation.
7. **`build.rs` rerun-if-env-changed paired** (`pearl_build_rs_rerun_if_env_changed.md`). Every `std::env::var()` paired with `cargo:rerun-if-env-changed=<NAME>`.
8. **Producer-before-canary launch order** (`pearl_canary_input_freshness_launch_order.md`). Canary kernels reading slot K must be launched after the producer that writes K.
9. **No CPU test fallbacks** (`feedback_no_cpu_test_fallbacks.md`). GPU oracle tests only — no CPU "reference impl" sibling kernel.
10. **No CPU compute on hot path** (`feedback_cpu_is_read_only.md`, `pearl_cold_path_no_exception_to_gpu_drives.md`). CPU is read-only for GPU drives. "Cold path" frequency does not earn an exception.
## Output format
```
[gpu-contract-auditor] reviewed: <space-separated paths>
Findings: N
1. [SEVERITY] <invariant short name> — <path>:<line>
What: <one sentence: what is wrong>
Memory: <feedback_*.md or pearl_*.md basename>
Fix: <one sentence: corrective action>
2. ...
Pearls considered, no violation: <comma-separated short names>
```
Severities: BLOCKER (revert change), HIGH (fix before merge), LOW (fix-up worthy).
## What this agent does NOT do
- Does NOT write into `memory/` (only `pearl-distiller` and `memory-curator` do).
- Does NOT auto-fix code; only reports.
- Does NOT review non-GPU code (delegate to `code-hygiene-auditor`).

View File

@@ -0,0 +1,68 @@
---
name: isv-discipline-auditor
description: Audits foxhunt ML/RL Rust code for ISV-discipline violations — hardcoded constants that should be ISV-driven, missing first-observation bootstrap on EMAs, Wiener-α controllers without floor in non-stationary loops, blend formulas instead of max-with-floor, controller ratios without z-score normalization, per-branch budgets that should derive from per-branch flatness. Read-only review; cites memory file by name on every finding.
---
# Foxhunt ISV Discipline Auditor
Specialist auditor for adaptive bounds, controllers, and signal-driven hyperparameters. Read memory before reviewing.
## Memory you MUST read first
In `~/.claude/projects/-home-jgrusewski-Work-foxhunt/memory/`:
- `feedback_isv_for_adaptive_bounds.md`
- `feedback_adaptive_not_tuned.md`
- `pearl_controller_anchors_isv_driven.md`
- `pearl_first_observation_bootstrap.md`
- `pearl_wiener_optimal_adaptive_alpha.md`
- `pearl_wiener_alpha_floor_for_nonstationary.md`
- `pearl_blend_formulas_must_have_permanent_floor.md`
- `pearl_zscore_normalization_for_magnitude_asymmetric_signals.md`
- `pearl_per_branch_c51_atom_span.md`
- `pearl_per_branch_iqn_tau_schedule.md`
- `pearl_per_branch_loss_budget.md`
- `pearl_per_branch_noisy_sigma.md`
- `pearl_per_group_adam_hyperparams.md`
- `pearl_kelly_cap_signal_driven_floors.md`
- `pearl_trail_stop_signal_driven.md`
- `pearl_adaptive_moe_lambda.md`
- `pearl_l1_lambda_grad_direction_entropy_deficit.md`
- `pearl_cold_start_exit_signal_or.md`
## When to invoke
- Edits to `crates/ml-*/**/*.rs`, `**/sp*_isv_slots.rs`.
- Explicit request: "audit ISV discipline for <path>".
## Invariants to verify
1. **Hardcoded multipliers/caps/anchors** (`feedback_isv_for_adaptive_bounds.md`, `pearl_controller_anchors_isv_driven.md`). Numeric literals used as anchor/target/cap/floor for adaptive behavior must derive from an ISV slot. Suggest a slot index and name.
2. **First-observation bootstrap on EMAs** (`pearl_first_observation_bootstrap.md`). EMA initialized to `0.0` (sentinel); first observation replaces directly, not blended. Flag any EMA initialized to a tuned constant.
3. **Wiener-α floor in non-stationary loops** (`pearl_wiener_alpha_floor_for_nonstationary.md`). Wiener-optimal α is MSE-optimal for stationary signals only. Where the target co-adapts (control loops), α must have a permanent floor (≥ 0.4 typical).
4. **Blend formulas use `max(real, floor)`** (`pearl_blend_formulas_must_have_permanent_floor.md`). Never `α·real + (1α)·floor` — that deadlocks if real → 0.
5. **Controller ratios over magnitude-asymmetric signals z-score-normalized** (`pearl_zscore_normalization_for_magnitude_asymmetric_signals.md`, `pearl_controller_amplifies_dominant_magnitude_trap.md`). Ratios like `winner_weight = mag[c] / sum(mag)` over branches with intrinsically different magnitudes always elect the largest. Use `z[c] = mag[c] / sqrt(var[c])` instead.
6. **Per-branch C51/IQN/CQL/Ens budgets** (`pearl_per_branch_loss_budget.md`, `pearl_per_branch_c51_atom_span.md`, `pearl_per_branch_iqn_tau_schedule.md`, `pearl_per_branch_noisy_sigma.md`). Budgets and atom spans derived per-branch from per-branch flatness/skew/entropy-deficit; never shared across branches.
7. **Per-group Adam hyperparams** (`pearl_per_group_adam_hyperparams.md`). β₁/β₂/ε per param-group from grad direction-stability, not global.
8. **Kelly floors signal-driven, cross-fold-persistent** (`pearl_kelly_cap_signal_driven_floors.md`). Kelly floors derive from a cross-fold-persistent signal; sentinel-bootstrap is incompatible (don't reset at fold boundary).
9. **Trail-stop distance signal-driven** (`pearl_trail_stop_signal_driven.md`, `pearl_trail_fire_pre_vs_action_mag.md`). Per-direction trail distance signal-driven; trail filter on `pre_mag`, not `action_mag`.
## Output format
```
[isv-discipline-auditor] reviewed: <paths>
Findings: N
1. [SEVERITY] <invariant short name> — <path>:<line>
What: <one sentence>
Memory: <feedback_*.md or pearl_*.md basename>
Fix: <one sentence; if ISV-slot needed, suggest slot name and bump>
Pearls considered, no violation: <comma-separated short names>
```
## What this agent does NOT do
- Does NOT scaffold the ISV slot — that's the `isv-slot-scaffolder` skill.
- Does NOT write into `memory/`.
- Does NOT block; warn-only.

View File

@@ -0,0 +1,70 @@
---
name: reward-controller-auditor
description: Audits foxhunt reward kernels and controllers for clamp symmetry, structural-activation bounds, asymmetric-bounded clamp where unboundedness was implicit asymmetry, event-driven reward density alignment, one-unbounded-multiplicand rule, Thompson-vs-argmax selectors, trail filter on pre_mag, Adam-normalized loss-weight no-ops. Read-only review; cites memory file by name on every finding.
---
# Foxhunt Reward & Controller Auditor
Specialist auditor for reward composition and controller mechanics. Bundles the SP11→SP12 reward-discipline pearls and the controller-design pearls.
## Memory you MUST read first
In `~/.claude/projects/-home-jgrusewski-Work-foxhunt/memory/`:
- `pearl_audit_unboundedness_for_implicit_asymmetry.md`
- `pearl_event_driven_reward_density_alignment.md`
- `pearl_symmetric_clamp_audit.md`
- `pearl_one_unbounded_signal_per_reward.md`
- `pearl_bounded_modifier_outputs_require_structural_activation.md`
- `pearl_loss_balance_controller.md`
- `pearl_engagement_rate_self_correction.md`
- `pearl_learned_gate_subsumes_handcoded.md`
- `pearl_controller_amplifies_dominant_magnitude_trap.md`
- `pearl_per_bar_vs_segment_pnl_signal_mismatch.md`
- `pearl_thompson_for_distributional_action_selection.md`
- `pearl_trail_fire_pre_vs_action_mag.md`
- `pearl_adam_normalizes_loss_weights.md`
- `pearl_separate_aux_trunk_when_shared_starves.md`
- `pearl_imbalance_bar_ewma_washes_out_configured_threshold.md`
- `pearl_intent_dist_freeze_resolved.md`
- `pearl_reward_shape_drives_quality_over_quantity.md` (deprecated; check for back-compat regressions)
## When to invoke
- Edits to `**/cuda/experience_kernels.cu`, `**/reward*.{rs,cu}`, `**/controller*.rs`, `**/trader_*.rs`.
- Explicit request: "audit reward/controller for <path>".
## Invariants to verify
1. **Bilateral clamps** (`pearl_symmetric_clamp_audit.md`). Kernel-derived bounded scalars use `fmaxf(lo, fminf(x, hi))`; one-sided `fminf` or `fmaxf` is a violation — leaves an unbounded tail that contaminates downstream EMAs.
2. **Bounded modifier outputs use structural activation** (`pearl_bounded_modifier_outputs_require_structural_activation.md`). Spec-bounded model outputs feeding multiplicative modifiers use sigmoid/tanh, not runtime clamps.
3. **Asymmetric bounded clamp where unboundedness was implicit asymmetry** (`pearl_audit_unboundedness_for_implicit_asymmetry.md`). When adding bounds to fix a stability bug, audit whether the unboundedness was implicitly providing behavioral asymmetry; preserve via asymmetric clamp (loss > win caps for loss aversion).
4. **Event-driven reward density alignment** (`pearl_event_driven_reward_density_alignment.md`). For event-driven objectives (trade close), reward density must match objective density. Per-step dense shaping creates exposure-positive bias. Use eligibility traces / TD(λ) / decomposition, not per-step shaping.
5. **One unbounded multiplicand per reward term** (`pearl_one_unbounded_signal_per_reward.md`). Exactly one — more produces compounding tail risk.
6. **Thompson selector for action selection** (`pearl_thompson_for_distributional_action_selection.md`). Train AND eval; argmax only for the Bellman target.
7. **Trail filter on `pre_mag`** (`pearl_trail_fire_pre_vs_action_mag.md`). Trail filter reads prior position magnitude, not the proposed action magnitude.
8. **Per-loss weight changes flagged as Adam-normalized no-ops** (`pearl_adam_normalizes_loss_weights.md`). Adam's `m/sqrt(v)` makes per-loss weight lifts no-ops at convergence. Suggest per-group LR or loss-function changes instead.
9. **Per-bar vs segment-PnL win-rate alignment** (`pearl_per_bar_vs_segment_pnl_signal_mismatch.md`). Win-rate predicates for trade-close objectives must read segment-level realized P&L, not per-bar `step_ret > 0` (per-bar at close includes tx_cost deduction → systematically negative on winning trades).
10. **Imbalance-bar threshold not washed out by EWMA** (`pearl_imbalance_bar_ewma_washes_out_configured_threshold.md`). `ImbalanceBarSampler::new_with_ewma()` with α=0.1 washes the configured threshold within ~5 bars. Use `new()` if you need the threshold to stick.
11. **Aux supervision trunk separation** (`pearl_separate_aux_trunk_when_shared_starves.md`). When aux CE plateaus at ln(K), give aux supervision its own trunk with independent Adam + stop-grad at encoder boundary.
12. **Controller magnitude-trap** (`pearl_controller_amplifies_dominant_magnitude_trap.md`). `winner_weight = ratio` with intrinsically-asymmetric magnitudes always elects the largest. Z-score-normalize first.
## Output format
```
[reward-controller-auditor] reviewed: <paths>
Findings: N
1. [SEVERITY] <invariant short name> — <path>:<line>
What: <one sentence>
Memory: <pearl_*.md basename>
Fix: <one sentence>
Pearls considered, no violation: <comma-separated short names>
```
## What this agent does NOT do
- Does NOT review non-reward CUDA kernels (delegate to `gpu-contract-auditor`).
- Does NOT write into `memory/`.
- Does NOT block; warn-only.

View File

@@ -0,0 +1,83 @@
---
name: sp-critical-reviewer
description: Performs the second/third critical-review pass on a freshly written SP spec or plan. Cites every claim against a memory file by name; enforces no-deferrals-for-complementary-fixes; verifies smoke kill conditions, ISV-slot enumeration, and anti-pattern callouts. Composes the four foxhunt auditor agents (gpu-contract, isv-discipline, reward-controller, code-hygiene) for domain-specific verification.
---
# Foxhunt SP Critical Reviewer
Replicates the user's "fix N issues from second/third critical review" iteration cycle (visible in commits like `eb5e19d67`, `35935ae44`, `5417e2756`).
## When to invoke
- After `sp-spec-writer` produces a v1, before promoting to plan.
- After plan v1, before starting implementation.
- Edits to `docs/superpowers/specs/*-design.md` or `docs/plans/*.md`.
## Memory you MUST read first
In `~/.claude/projects/-home-jgrusewski-Work-foxhunt/memory/`:
- `pearl_no_deferrals_for_complementary_fixes.md`
- `feedback_no_quickfixes.md`
- `feedback_no_partial_refactor.md`
- `feedback_isv_for_adaptive_bounds.md`
- `feedback_wire_everything_up.md`
- `feedback_kill_runs_on_anomaly_quickly.md`
- `feedback_stop_on_anomaly.md`
- All `pearl_audit_*.md` files (audit methodology).
## Composition
This agent dispatches the four domain auditors when the spec/plan claims involve those domains:
| Spec/plan claim | Dispatch |
|---|---|
| Mentions a CUDA kernel or GPU host code | `gpu-contract-auditor` on the implicated kernel |
| Mentions ISV slots or adaptive bounds | `isv-discipline-auditor` on the proposed approach |
| Mentions reward composition or controller mechanics | `reward-controller-auditor` |
| Anywhere else in `crates/`/`services/` | `code-hygiene-auditor` |
Findings from sub-auditors are aggregated under the corresponding spec/plan section.
## Critical-review checklist
For every spec/plan, verify:
1. **Pearl/feedback grounding present** (§2 of the spec template). Every motivation cites a memory file.
2. **Sister-fix question answered** (`pearl_no_deferrals_for_complementary_fixes.md`). The spec explicitly says "yes, sister-fix X integrated" or "no, here's why no sister-fix exists".
3. **ISV slots enumerated** (§5 of the spec template). Either a non-empty table, or an explicit "no new slots needed" with rationale.
4. **Smoke kill conditions explicit** (§6). Each anomaly has a detection signal and a kill window.
5. **Anti-patterns avoided enumerated** (§7). The relevant `feedback_*.md` rules are listed.
6. **Pearl-distill hook stated** (§8). The expected close-out pearl is named.
7. **Phase wire-up complete** (`feedback_wire_everything_up.md`). Every kernel/module introduced has a wire-up phase.
8. **Quickfixes flagged** (`feedback_no_quickfixes.md`). No "we'll just clamp this" without root-cause framing.
9. **Partial refactor flagged** (`feedback_no_partial_refactor.md`). When a contract changes, every consumer is in the plan.
## Output format
```
[sp-critical-reviewer] reviewed: <path-to-spec-or-plan>
Issues found: N (numbered, severity-tagged)
1. [SEVERITY] <category> — §<section>
Issue: <one sentence>
Memory: <feedback_*.md or pearl_*.md basename>
Fix: <one-sentence corrective action; e.g., "add §5 with empty-set rationale">
2. ...
Sub-auditor findings (aggregated):
- gpu-contract-auditor: <count> findings — see appended report
- isv-discipline-auditor: <count> findings — see appended report
- reward-controller-auditor: <count> findings — see appended report
- code-hygiene-auditor: <count> findings — see appended report
```
Severities: BLOCKER (rewrite spec), HIGH (fix before plan), LOW (fix-up worthy). Match the user's `eb5e19d67` "16 issues" iteration shape.
## What this agent does NOT do
- Does NOT auto-edit the spec/plan; only reports.
- Does NOT write into `memory/`.
- Does NOT block; produces an issue list for the user to act on.

View File

@@ -0,0 +1,86 @@
#!/usr/bin/env bash
# foxhunt-audit-router.sh
#
# PostToolUse hook router. Reads Claude Code tool-input JSON from stdin,
# inspects the file path being edited, and emits at most one one-line hint
# to stdout suggesting which foxhunt-* auditor agents to dispatch.
#
# Contract:
# - Completes in <200 ms.
# - Never spawns agents itself.
# - Always exits 0 (never blocks).
# - One suggestion per (path, agent) per session, tracked in
# .claude/.foxhunt-audit-state (gitignored, cleared at SessionStart).
set -u # not -e; we want to exit 0 even on errors
REPO_ROOT="${CLAUDE_PROJECT_DIR:-$(pwd)}"
STATE_FILE="$REPO_ROOT/.claude/.foxhunt-audit-state"
mkdir -p "$(dirname "$STATE_FILE")" 2>/dev/null || exit 0
touch "$STATE_FILE" 2>/dev/null || exit 0
# Read tool input from stdin; bail if not JSON or no file_path
INPUT="$(cat 2>/dev/null || true)"
[ -z "$INPUT" ] && exit 0
# Extract file path. Claude Code sends keys like "tool_input": {"file_path": "..."}
# Be lenient: try multiple JSON shapes via grep/sed (avoid jq dep).
PATH_FOUND="$(printf '%s' "$INPUT" | grep -oE '"file_path"[[:space:]]*:[[:space:]]*"[^"]+"' | head -1 | sed -E 's/.*"file_path"[[:space:]]*:[[:space:]]*"([^"]+)".*/\1/')"
[ -z "$PATH_FOUND" ] && exit 0
# Make path repo-relative if absolute
case "$PATH_FOUND" in
"$REPO_ROOT"/*) REL="${PATH_FOUND#$REPO_ROOT/}" ;;
/*) REL="$PATH_FOUND" ;;
*) REL="$PATH_FOUND" ;;
esac
# Routing table: build SUGGEST list of agent names
SUGGEST=()
case "$REL" in
*.cu) SUGGEST+=("gpu-contract-auditor") ;;
*cuda/*.rs|*/cuda/*) SUGGEST+=("gpu-contract-auditor") ;;
crates/*/build.rs) SUGGEST+=("gpu-contract-auditor") ;;
esac
case "$REL" in
*/experience_kernels.cu|*reward*.rs|*reward*.cu|*controller*.rs|*trader_*.rs)
SUGGEST+=("reward-controller-auditor") ;;
esac
case "$REL" in
*sp*_isv_slots.rs) SUGGEST+=("isv-discipline-auditor") ;;
crates/ml-*/*.rs|crates/ml-*/**/*.rs) SUGGEST+=("isv-discipline-auditor") ;;
esac
case "$REL" in
docs/superpowers/specs/*.md|docs/superpowers/plans/*.md|docs/plans/*.md)
SUGGEST+=("sp-critical-reviewer") ;;
esac
case "$REL" in
MEMORY.md|*memory/pearl_*.md|*memory/feedback_*.md)
SUGGEST+=("memory-curator") ;;
esac
# Default for any rust file in crates/ services/ bin/ that didn't match above
case "$REL" in
crates/*.rs|crates/**/*.rs|services/*.rs|services/**/*.rs|bin/*.rs|bin/**/*.rs)
# Add code-hygiene unconditionally for Rust source
SUGGEST+=("code-hygiene-auditor") ;;
esac
[ "${#SUGGEST[@]}" -eq 0 ] && exit 0
# Dedupe SUGGEST and filter against state file (one suggestion per (path, agent) per session)
EMIT=()
for AGENT in "${SUGGEST[@]}"; do
KEY="$REL|$AGENT"
if ! grep -Fxq "$KEY" "$STATE_FILE" 2>/dev/null; then
echo "$KEY" >> "$STATE_FILE"
# Dedupe within this invocation too
case " ${EMIT[*]:-} " in *" $AGENT "*) ;; *) EMIT+=("$AGENT") ;; esac
fi
done
[ "${#EMIT[@]}" -eq 0 ] && exit 0
# Emit single-line hint, prefixed for grep
JOINED="$(printf '%s, ' "${EMIT[@]}")"; JOINED="${JOINED%, }"
echo "[foxhunt-audit] $REL — suggest: $JOINED"
exit 0

View File

@@ -0,0 +1,5 @@
#!/usr/bin/env bash
# Clear foxhunt-audit dedup state at session start.
STATE_FILE="${CLAUDE_PROJECT_DIR:-.}/.claude/.foxhunt-audit-state"
: > "$STATE_FILE" 2>/dev/null || true
exit 0

View File

@@ -9,6 +9,15 @@
"timeout": 15
}
]
},
{
"hooks": [
{
"type": "command",
"command": "bash \"$CLAUDE_PROJECT_DIR/.claude/helpers/foxhunt-audit-session-start.sh\"",
"timeout": 5
}
]
}
],
"SessionEnd": [
@@ -21,6 +30,18 @@
}
]
}
],
"PostToolUse": [
{
"matcher": "Edit|Write|MultiEdit",
"hooks": [
{
"type": "command",
"command": "bash \"$CLAUDE_PROJECT_DIR/.claude/helpers/foxhunt-audit-router.sh\"",
"timeout": 2
}
]
}
]
},
"permissions": {

View File

@@ -0,0 +1,69 @@
---
name: argo-deploy-helper
description: Constructs and validates a vetted invocation of scripts/argo-train.sh or scripts/argo-compile-deploy.sh. Enforces push-before-deploy, default L40S pool (ci-training-l40s) unless overridden, mandatory --mbp10-data-dir + --trades-data-dir, --per-enabled, H100 max-utilization flags when H100 chosen. Use when deploying training to argo.
---
# Foxhunt Argo Deploy Helper
Wraps the existing `scripts/argo-train.sh` and `scripts/argo-compile-deploy.sh` with foxhunt deploy discipline pre-flight.
## When to invoke
- "Deploy training X to argo".
- "Run smoke for SP-N on the cluster".
## Pre-flight checklist (BLOCK on any fail)
Before constructing the invocation, run each check. If any fails, do not proceed — report the failure and stop.
1. **Push before deploy** (`feedback_push_before_deploy.md`).
```bash
LOCAL=$(git rev-parse HEAD)
REMOTE=$(git rev-parse @{u} 2>/dev/null || echo "")
if [ "$LOCAL" != "$REMOTE" ]; then
echo "BLOCKED: HEAD ($LOCAL) not pushed to upstream ($REMOTE). Run 'git push' first."
exit 1
fi
```
2. **MBP10 + trades data dirs mandatory for SP-chain training** (`feedback_mbp10_mandatory.md`).
- Confirm `--mbp10-data-dir` and `--trades-data-dir` are present in the proposed flag list.
- If absent, BLOCK and ask the user to provide them.
3. **PER enabled** (`feedback_always_per.md`).
- Confirm `--per-enabled` is present (or that the script defaults it to true). PER is always on.
## Default GPU pool (`feedback_default_to_l40s_pool.md`)
Default to `--gpu-pool ci-training-l40s`. Add only if the user did not specify a pool. If the user specifies H100, also enforce H100 max-utilization flags (`feedback_h100_gpu.md`):
- Hard error on under-utilization (script-side flags or env vars per the existing argo-train.sh contract).
- Pass the canonical H100 batch-size + grad-accumulation flags.
## What this skill produces
A complete invocation, ready to run, in the form:
```bash
./scripts/argo-train.sh \
--branch <branch> \
--commit <sha> \
--gpu-pool <pool> \
--mbp10-data-dir <path> \
--trades-data-dir <path> \
--per-enabled \
<other flags>
```
Plus a one-line summary of the pre-flight checks that passed.
## Anti-pattern this skill avoids
- Deploying with HEAD ahead of upstream (un-pushed commits aren't reproducible from the remote).
- H100 deploys without max-utilization flags (waste of expensive node).
- Defaulting to H100 when L40S would do (`feedback_default_to_l40s_pool.md` overrides the older H100-by-default rule).
- Layering Bash run_in_background Monitor on top of argo logs (`feedback_no_redundant_monitor.md`). Stream logs via `argo logs -f`, not via custom shell redirection.
## After deploy
Suggest invoking `foxhunt-smoke-pilot` to monitor the smoke and kill on first useful anomaly signal.

View File

@@ -0,0 +1,76 @@
---
name: isv-slot-scaffolder
description: Scaffolds new ISV slots — generates crates/.../spN_isv_slots.rs with named constants in [START..END), bumps ISV_TOTAL_DIM at canonical location, registers slots for fold-boundary reset, emits the canonical commit message shape. Use when adding ISV-driven adaptive bounds for a new SP.
---
# Foxhunt ISV Slot Scaffolder
Templatizes the ISV-slot scaffolding step that runs early in every SP that introduces adaptive bounds.
## When to invoke
- "Add ISV slots for SP-N".
- "Scaffold spN_isv_slots.rs".
## Reference: canonical example
The most recent example is commit `c146c4fff`:
```
feat(sp15): scaffold sp15_isv_slots.rs with 46 slots [397..443) — ISV_TOTAL_DIM 396→443
```
Reproduce that shape exactly. The plan must:
1. Discover the current `ISV_TOTAL_DIM` value (grep for `ISV_TOTAL_DIM` in `crates/`).
2. Discover the next free index (= current `ISV_TOTAL_DIM`).
3. Receive a list of `(SLOT_NAME, purpose)` from the user.
4. Compute the new range `[START..END)` and new `ISV_TOTAL_DIM`.
## Inputs you must collect first
1. SP number (N).
2. List of slot names + purposes. Names are `SCREAMING_SNAKE_CASE`.
3. Optional: explicit indices (else assigned sequentially from current `ISV_TOTAL_DIM`).
## What this skill produces
1. New file `crates/<crate>/src/isv/sp<N>_isv_slots.rs`:
```rust
//! SP-<N> ISV slots — [<START>..<END>)
//!
//! Adaptive bounds and signals for SP-<N>.
//! Source pearl: <link to spec §5>.
pub const ISV_<NAME_1>: usize = <S>;
pub const ISV_<NAME_2>: usize = <S>+1;
// ...
pub const SP<N>_SLOTS_START: usize = <S>;
pub const SP<N>_SLOTS_END: usize = <E>;
```
2. Edit at canonical `ISV_TOTAL_DIM` site:
```rust
pub const ISV_TOTAL_DIM: usize = <NEW>; // bumped from <OLD> by SP-<N> (+<K> slots)
```
3. Edit at fold-boundary reset registration site to register `SP<N>_SLOTS_START..SP<N>_SLOTS_END`.
4. Suggested commit message (use heredoc):
```
feat(sp<N>): scaffold sp<N>_isv_slots.rs with <K> slots [<S>..<E>) — ISV_TOTAL_DIM <OLD>→<NEW>
```
## Discipline
- All slots must be referenced by at least one consumer in a follow-up commit (per `feedback_wire_everything_up.md`).
- Slot names must be domain-meaningful — no `SP15_X1`, `SP15_X2` placeholders.
- ISV_TOTAL_DIM bump must match the slot count exactly. Verify before committing.
## After scaffolding
Suggest invoking `foxhunt-isv-discipline-auditor` to verify the new slots aren't shadowing existing ones with semantically-similar purposes.

View File

@@ -0,0 +1,64 @@
---
name: memory-curator
description: Audits MEMORY.md and the memory/ directory against current code. Flags stale references (file/symbol/flag no longer present), duplicates, fully-superseded pearls, project-state files where the project is now merged. Proposes deletes and merges; never auto-deletes. Use monthly or when pearls feel out of date.
---
# Foxhunt Memory Curator
Read-and-propose audit of the memory/ corpus. One of two authorized writers into memory/ (the other is `pearl-distiller`); this one only deletes/archives, with explicit user confirmation.
## When to invoke
- "Audit memory" (explicit).
- SessionStart hint when `memory/MEMORY.md` `mtime` > 30 days (advisory only — never auto-runs).
## What this skill does
1. **Enumerate** all memory files:
```bash
ls ~/.claude/projects/-home-jgrusewski-Work-foxhunt/memory/*.md
```
2. **For each file**, run staleness checks:
| Check | How |
|---|---|
| File reference still exists | Path mentioned in the pearl → does it still exist in the repo? |
| Symbol reference still exists | Function/struct name mentioned → grep the repo |
| Commit SHA still in history | `git cat-file -e <sha>` |
| Flag/feature still wired | grep for the flag name |
| Project status fresh | For `project_*.md`, is the project-of-interest still the current focus? |
3. **Group findings**:
- **Stale** — referenced thing is gone; pearl needs an update or archival.
- **Duplicate** — two pearls cover the same pattern; propose merge.
- **Superseded** — a newer pearl replaces this one (frontmatter says DEPRECATED or another pearl says "supersedes <this>").
- **Orphan** — pearl in `memory/` but missing from `MEMORY.md` index.
- **Index-orphan** — line in `MEMORY.md` index referencing a missing file.
4. **Produce a curation report**:
```
[memory-curator] audited <N> files in memory/
Stale: <list with reasons>
Duplicates: <list with proposed merges>
Superseded: <list with replacement pearl>
Orphans: <list>
Index-orphans: <list>
Proposed actions:
1. <ARCHIVE | MERGE | UPDATE | INDEX-ADD | INDEX-REMOVE> <file> — <rationale>
2. ...
```
## Discipline
- **Never auto-delete or auto-archive.** The user confirms each action one at a time.
- **Archive ≠ delete.** Archived pearls move to `memory/archive/` with a frontmatter note `archived: <YYYY-MM-DD>` and `archived_reason: <reason>`. They remain searchable.
- **Index integrity.** After any add/remove, verify `MEMORY.md` index lines stay ≤150 characters and live under the right section.
- **No new section without user approval.** Existing `MEMORY.md` sections (per the spec §3.3) are canonical.
## After curation
If any deprecated pearls were superseded by newer ones, the newer pearl frontmatter should explicitly say `supersedes: pearl_<old>.md`. Suggest invoking `pearl-distiller` if a new pearl is needed to replace an archived one.

View File

@@ -0,0 +1,80 @@
---
name: pearl-distiller
description: Distills a memory pearl from a close-out finding. Creates ~/.claude/projects/-home-jgrusewski-Work-foxhunt/memory/pearl_<topic>.md with structured body and updates MEMORY.md index in the right section. One of only two skills that may write into memory/. Use post-close-out, never proactively.
---
# Foxhunt Pearl Distiller
Authoritative writer for new memory pearls. The other authorized writer is `memory-curator` (which can delete/archive). All other agents and skills are read-only on `memory/`.
## When to invoke
- After SP close-out, when a finding warrants distillation.
- Explicit request: "distill pearl from <topic>".
## Inputs you must collect first
1. **Topic** — short kebab-case identifier (becomes filename: `pearl_<topic>.md`).
2. **One-line description** — for `MEMORY.md` index entry.
3. **Pattern statement** — one paragraph: what was discovered.
4. **Detection signal** — how to recognize this pattern in code or behavior.
5. **Fix** — what to do when the pattern is detected.
6. **Canonical reference** — commit SHA + `path:line` where the pattern was first fixed.
7. **Related pearls** — links to existing pearls.
8. **Section in `MEMORY.md`** — pick from existing sections (Controllers and signals, Bootstrap and smoothing, Architectural constraints, Distributional Q / action selection, Optimization, GPU mechanics, Testing discipline, Spec/plan scoping, Close-out / sweep records, Active Project State).
## What this skill produces
1. **New file** at `~/.claude/projects/-home-jgrusewski-Work-foxhunt/memory/pearl_<topic>.md`:
```markdown
---
name: <topic readable>
description: <one-line; same as MEMORY.md hook>
type: pearl
---
# Pearl: <Topic Readable>
## Pattern
<One paragraph: what was discovered.>
## Detection signal
<How to recognize this in code or runtime behavior.>
## Fix
<What to do when detected.>
## Canonical reference
- Commit: `<SHA>`
- File: `<path>:<line>`
- Spec: `<docs/superpowers/specs/...>`
## Related pearls
- `<related_pearl_1.md>` — <one-sentence relation>
- `<related_pearl_2.md>` — <one-sentence relation>
```
2. **Edit `MEMORY.md`** to add the index line under the right section:
```markdown
- [pearl_<topic>.md](pearl_<topic>.md) — <one-line hook>
```
The line must be ≤150 characters (per the auto-memory rules in the system prompt).
## Discipline
- Only invoke after a close-out doc exists. Do not distill speculative pearls.
- Reuse existing sections in `MEMORY.md`; do not invent a new section without explicit user approval.
- If a similar pearl already exists, ask the user whether to merge instead of creating a new one (delegate to `memory-curator` for merges).
- The pearl content must be specific enough that an auditor agent can cite it by name in a finding.
## After distilling
If the pearl supersedes a prior pearl, mark the old one DEPRECATED in its frontmatter and add a "Superseded by: <new pearl>" note. Do not delete the old pearl — `memory-curator` handles archival.

View File

@@ -0,0 +1,62 @@
---
name: smoke-pilot
description: Monitors a running argo workflow smoke; auto-kills on first useful anomaly signal (NaN loss, magnitude collapse, EVAL_DIST anomalies, return-explosion, controller saturation) per stop-on-anomaly + kill-quickly discipline. Pairs with argo-deploy-helper. Streams logs via argo logs -f, never via Bash run_in_background Monitor.
---
# Foxhunt Smoke Pilot
Watches an in-flight smoke training and applies the kill-fast-on-anomaly rule.
## When to invoke
- "Monitor smoke for workflow <wf-name>".
- After invoking `argo-deploy-helper`.
## Memory you MUST read first
In `~/.claude/projects/-home-jgrusewski-Work-foxhunt/memory/`:
- `feedback_stop_on_anomaly.md`
- `feedback_kill_runs_on_anomaly_quickly.md`
- `feedback_no_redundant_monitor.md`
- `project_metric_pipeline_inflation_audit.md` (honest meters; what *isn't* an anomaly)
- All `project_*.md` files documenting prior anomalies (these are signal libraries).
## What this skill does
1. Streams workflow logs:
```bash
argo logs -f <workflow-name>
```
Do NOT layer Bash `run_in_background` Monitor on this stream (`feedback_no_redundant_monitor.md`).
2. Watches each log line for known anomaly patterns:
| Anomaly | Signal | Kill window |
|---|---|---|
| NaN loss | `loss = NaN` or `loss = inf` | immediate |
| Magnitude collapse | EVAL_DIST Quarter pinned at 1.0 with no Kelly cap context | within 200 steps |
| Return explosion | `Return = 1e29%` or similar (per project_metric_pipeline_inflation_audit) | immediate |
| Controller saturation | `α = 1.0` or `α = 0.0` for >100 steps | within 500 steps |
| Aux CE plateau at ln(K) | `aux_ce ≈ <ln(K)>` for >100 steps | warn but don't kill (per pearl_separate_aux_trunk_when_shared_starves — fix is structural) |
| Reward distribution corruption | one-sided clamp regression | within 100 steps |
3. On detected anomaly:
```bash
argo terminate <workflow-name>
```
Then summarize the anomaly with the originating memory file cited.
## Discipline
- **Always cite the originating memory file** when killing — "Killed at step 230: NaN loss, per `feedback_stop_on_anomaly.md`".
- **Never kill on a noisy single-step blip**. Use a 3-of-5 confirmation window for non-immediate anomalies.
- **Never silently survive** an anomaly that warrants a kill. The whole point of this skill is `feedback_kill_runs_on_anomaly_quickly.md`: kill at first useful signal, diagnose, fix, re-run.
- **Inflation is not anomaly**. Per `project_metric_pipeline_inflation_audit.md`, several metrics are display caps or annualization conventions, not bugs. Read that file first; do not flag those as anomalies.
## After kill
Suggest:
1. Save the failing log slice for analysis.
2. Open a fresh investigation (likely brainstorming → spec).
3. Re-run only after the fix is committed and pushed.

View File

@@ -0,0 +1,101 @@
---
name: sp-spec-writer
description: Produces a foxhunt SP design spec at docs/superpowers/specs/YYYY-MM-DD-spXX-<topic>-design.md following the established format — Problem, Pearl/feedback grounding, Approach, Phases A/B/C, ISV slots required, Smoke gate, Anti-patterns avoided, Pearl-distill hook. Use when starting a new sprint plan or revising one.
---
# Foxhunt SP Spec Writer
Templatizes the SP design spec format. Reuses the canonical structure from prior specs in `docs/superpowers/specs/`.
## When to invoke
- "Write SP spec for <topic>".
- "Start SP-N spec".
## Inputs you must collect first
Ask the user (one at a time):
1. **SP number**. Look at `docs/superpowers/specs/` and propose the next free integer.
2. **Topic** (short slug, kebab-case).
3. **Problem statement** — one paragraph. Symptoms or metrics required.
4. **Pearl/feedback grounding** — which memory files motivate this work? At least one required (per `feedback_isv_for_adaptive_bounds.md` etc.).
5. **Sister-fix check** (`pearl_no_deferrals_for_complementary_fixes.md`). Ask: "Is there a complementary fix that should ride along in this spec?" If yes, integrate; if no, document why.
## Spec template
Render a new file at `docs/superpowers/specs/YYYY-MM-DD-spN-<topic>-design.md` with:
```markdown
# SP-N: <Topic>
| | |
|---|---|
| **Date** | <today YYYY-MM-DD> |
| **Author** | <user> |
| **SP** | N |
| **Branch** | sp<N>-<topic> |
## 1. Problem
<One paragraph; symptoms, metrics, or repro.>
## 2. Pearl/feedback grounding
- `<memory_file_1.md>` — <one sentence>
- `<memory_file_2.md>` — <one sentence>
## 3. Approach
<Root-cause framing. Why this fix, not another.>
## 4. Phases
### Phase A — <name>
- A.1 <step>
- A.2 <step>
### Phase B — <name>
- B.1 <step>
### Phase C (smoke + close-out)
- C.1 Smoke launch (`scripts/argo-train.sh ...`)
- C.2 Smoke gate: <kill conditions>
- C.3 Close-out doc + pearl distillation
## 5. ISV slots required
| Slot index | Name | Purpose | Source pearl |
|---|---|---|---|
| <S> | <NAME> | <purpose> | <pearl> |
`ISV_TOTAL_DIM` bump: OLD → NEW.
## 6. Smoke gate
| Anomaly | Detection | Action |
|---|---|---|
| <e.g., NaN loss> | <log signal> | kill within <K> steps |
## 7. Anti-patterns avoided
- <which feedback rules this spec is honoring; e.g., feedback_no_stubs.md, feedback_isv_for_adaptive_bounds.md>
## 8. Pearl-distill hook
After close-out, distill a pearl for: <expected pattern>.
```
## Discipline enforced by this skill
- The spec MUST cite at least one memory file in §2.
- The spec MUST list ISV slots in §5 (or explicitly state "no new slots needed").
- The spec MUST list smoke kill conditions in §6 (or explicitly state "no smoke required").
- The spec MUST list which `feedback_*.md` rules it honors in §7.
- Sister-fix check is mandatory; the user must answer the question.
## After writing
Suggest invoking `foxhunt-sp-critical-reviewer` for the v2 critical-review pass before promoting the spec to plan.

View File

@@ -0,0 +1,72 @@
---
name: stale-worktree-cleaner
description: Identifies stale git worktrees in .claude/worktrees/ and the workspace, including [gone] tracking refs and merged branches. Produces a per-worktree cleanup plan; user confirms each git worktree remove individually before any destructive operation.
---
# Foxhunt Stale Worktree Cleaner
Destructive op — explicit invoke only, with per-action user confirmation.
## When to invoke
- "Clean stale worktrees".
- Periodically when `.claude/worktrees/` has many entries.
## What this skill does
1. **Enumerate** all worktrees:
```bash
git worktree list
ls -la .claude/worktrees/
```
2. **For each worktree**, classify staleness:
| Class | Detection |
|---|---|
| NO-COMMITS | No commits in N days (default 30); branch unchanged |
| GONE | Tracking ref `[gone]` (deleted upstream) — `git fetch -p` then `git branch -vv` |
| MERGED | Branch fully merged into `main` (`git merge-base --is-ancestor`) |
| UNCOMMITTED | Has uncommitted changes — DO NOT propose cleanup; flag only |
| ACTIVE | Recent commits, not merged, tracking ref alive |
3. **Produce a per-worktree cleanup plan**:
```
[stale-worktree-cleaner] enumerated <N> worktrees
Class NO-COMMITS (<K> entries):
1. <path> — branch <name>, last commit <date>, status <merged|alive>
Action: git worktree remove <path>; git branch -d <name>
Class GONE (<K> entries):
...
Class UNCOMMITTED (<K> entries — flag only, no action proposed):
...
```
4. **Confirm each action individually** before running. Never bulk-execute.
## Discipline
- **NEVER `git worktree remove --force` without confirmation.** `--force` discards uncommitted work.
- **NEVER `git branch -D`** (force delete) — only `git branch -d` (allows refusal on unmerged).
- **Compose the existing `commit-commands:clean_gone` skill** when applicable (it handles GONE-class branches).
- **NetBird/Tailscale/CI worktrees** (if any) — leave alone unless explicitly named by the user.
- **Worktrees referenced by a running agent** must not be removed. Check `.claude/worktrees/*` against any active session metadata.
## Output format
```
[stale-worktree-cleaner] proposal:
Will remove (after your per-item confirmation):
- <path 1> [<class>] — <why>
- <path 2> [<class>] — <why>
Skipped (require manual review):
- <path 3> [UNCOMMITTED] — has unstaged changes
```
User confirms each removal individually before any destructive command runs.

3
.gitignore vendored
View File

@@ -191,3 +191,6 @@ services/*/load_tests/*.json
!services/*/load_tests/package.json
.playwright-mcp/
crates/ml/ml/
# Foxhunt audit hook dedup state (cleared at SessionStart)
.claude/.foxhunt-audit-state

1
Cargo.lock generated
View File

@@ -6243,6 +6243,7 @@ dependencies = [
"rayon",
"serde",
"serde_json",
"tempfile",
"tokio",
"toml",
"tracing",

View File

@@ -45,6 +45,7 @@ rand.workspace = true
tokio = { workspace = true, features = ["test-util", "macros"] }
approx.workspace = true
toml.workspace = true
tempfile = "3"
[lints]
workspace = true

View File

@@ -36,6 +36,7 @@ pub mod normalization;
pub mod ofi_calculator;
pub mod pipeline;
pub mod position_features;
pub mod predecoded;
pub mod price_features;
pub mod regime_adx;
pub mod statistical_features;

View File

@@ -0,0 +1,281 @@
//! Predecoded sidecar cache for parsed DBN files.
//!
//! Wraps `load_trades_sync` and `parse_mbp10_streaming` with an on-disk cache.
//! First call decodes zstd-DBN and writes a sidecar; subsequent calls
//! deserialize the sidecar directly. zstd decompression was 62% of
//! precompute_features wall time on the 2026-05-16 1Q ES profile — moving it
//! to a one-shot cache eliminates that cost for all repeat runs.
//!
//! # Cache key
//!
//! The sidecar header records the source file's mtime and size. A sidecar is
//! considered valid only when both match the source on the current call;
//! re-downloading a quarter (which bumps mtime + usually size) silently
//! invalidates the sidecar.
use std::fs::{self, File};
use std::io::{BufReader, BufWriter, Read, Write};
use std::path::{Path, PathBuf};
use std::time::SystemTime;
use bincode;
use data::providers::databento::dbn_parser::DbnParser;
use data::providers::databento::mbp10::Mbp10Snapshot;
use serde::{Deserialize, Serialize};
use crate::trades_loader::{load_trades_sync, DbnTrade};
use crate::MLError;
const MAGIC: u32 = u32::from_le_bytes(*b"PCD1");
const VERSION: u32 = 1;
fn sidecar_path(source: &Path, predecoded_dir: &Path, kind: &str) -> PathBuf {
let basename = source.file_name().unwrap_or_default().to_string_lossy();
predecoded_dir.join(format!("{basename}.{kind}.predecoded.bin"))
}
fn source_mtime_size(source: &Path) -> std::io::Result<(u128, u64)> {
let meta = fs::metadata(source)?;
let mtime = meta
.modified()?
.duration_since(SystemTime::UNIX_EPOCH)
.map(|d| d.as_nanos())
.unwrap_or(0);
Ok((mtime, meta.len()))
}
fn write_header<W: Write>(w: &mut W, mtime_nanos: u128, size: u64) -> std::io::Result<()> {
w.write_all(&MAGIC.to_le_bytes())?;
w.write_all(&VERSION.to_le_bytes())?;
w.write_all(&mtime_nanos.to_le_bytes())?;
w.write_all(&size.to_le_bytes())?;
Ok(())
}
fn read_header_and_validate<R: Read>(r: &mut R, source: &Path) -> std::io::Result<bool> {
let mut magic = [0u8; 4];
r.read_exact(&mut magic)?;
if u32::from_le_bytes(magic) != MAGIC {
return Ok(false);
}
let mut version = [0u8; 4];
r.read_exact(&mut version)?;
if u32::from_le_bytes(version) != VERSION {
return Ok(false);
}
let mut mtime_bytes = [0u8; 16];
r.read_exact(&mut mtime_bytes)?;
let mut size_bytes = [0u8; 8];
r.read_exact(&mut size_bytes)?;
let stored_mtime = u128::from_le_bytes(mtime_bytes);
let stored_size = u64::from_le_bytes(size_bytes);
let (current_mtime, current_size) = source_mtime_size(source)?;
Ok(stored_mtime == current_mtime && stored_size == current_size)
}
fn try_read_sidecar<T: for<'de> Deserialize<'de>>(
source: &Path,
sidecar: &Path,
) -> std::io::Result<Option<T>> {
if !sidecar.exists() {
return Ok(None);
}
let f = File::open(sidecar)?;
let mut r = BufReader::new(f);
if !read_header_and_validate(&mut r, source)? {
return Ok(None);
}
let payload: T = bincode::deserialize_from(r)
.map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidData, e))?;
Ok(Some(payload))
}
fn write_sidecar<T: Serialize>(
source: &Path,
sidecar: &Path,
payload: &T,
) -> std::io::Result<()> {
if let Some(parent) = sidecar.parent() {
fs::create_dir_all(parent)?;
}
let (mtime, size) = source_mtime_size(source)?;
let f = File::create(sidecar)?;
let mut w = BufWriter::new(f);
write_header(&mut w, mtime, size)?;
bincode::serialize_into(&mut w, payload)
.map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidData, e))?;
w.flush()?;
Ok(())
}
/// Load trades from a DBN file, using a predecoded sidecar when available.
///
/// On miss (no sidecar / stale sidecar): decode `.dbn.zst` via
/// `load_trades_sync`, persist `Vec<DbnTrade>` as a sidecar, return trades.
/// On hit: deserialize sidecar (skips zstd).
///
/// Sidecar write failures are logged and ignored — the caller still receives
/// the freshly-decoded `Vec<DbnTrade>`.
pub fn load_or_predecode_trades(
source: &Path,
predecoded_dir: &Path,
) -> Result<Vec<DbnTrade>, MLError> {
let sidecar = sidecar_path(source, predecoded_dir, "trades");
match try_read_sidecar::<Vec<DbnTrade>>(source, &sidecar) {
Ok(Some(trades)) => {
tracing::info!(
"Predecoded HIT: {} ({} trades)",
sidecar.display(),
trades.len()
);
return Ok(trades);
}
Ok(None) => {}
Err(e) => {
tracing::warn!(
"Predecoded trades sidecar read failed for {}: {} — falling back to zstd",
sidecar.display(),
e
);
}
}
let trades = load_trades_sync(source)?;
if let Err(e) = write_sidecar(source, &sidecar, &trades) {
tracing::warn!(
"Failed to write predecoded trades sidecar {}: {}",
sidecar.display(),
e
);
} else {
tracing::info!(
"Predecoded WRITE: {} ({} trades)",
sidecar.display(),
trades.len()
);
}
Ok(trades)
}
/// Load MBP-10 snapshots from a DBN file, using a predecoded sidecar when available.
///
/// On miss: streams the file via `DbnParser::parse_mbp10_streaming`, accumulates
/// `Vec<Mbp10Snapshot>`, persists a sidecar, returns the snapshots. On hit:
/// deserializes the sidecar (skips zstd).
pub fn load_or_predecode_mbp10(
source: &Path,
predecoded_dir: &Path,
) -> Result<Vec<Mbp10Snapshot>, MLError> {
let sidecar = sidecar_path(source, predecoded_dir, "mbp10");
match try_read_sidecar::<Vec<Mbp10Snapshot>>(source, &sidecar) {
Ok(Some(snapshots)) => {
tracing::info!(
"Predecoded HIT: {} ({} snapshots)",
sidecar.display(),
snapshots.len()
);
return Ok(snapshots);
}
Ok(None) => {}
Err(e) => {
tracing::warn!(
"Predecoded mbp10 sidecar read failed for {}: {} — falling back to zstd",
sidecar.display(),
e
);
}
}
let parser = DbnParser::new()
.map_err(|e| MLError::InsufficientData(format!("DbnParser init failed: {e}")))?;
let mut snapshots = Vec::new();
parser
.parse_mbp10_streaming(source, 100, |snap| {
snapshots.push(snap.clone());
})
.map_err(|e| MLError::InsufficientData(format!("parse_mbp10_streaming failed: {e}")))?;
if let Err(e) = write_sidecar(source, &sidecar, &snapshots) {
tracing::warn!(
"Failed to write predecoded mbp10 sidecar {}: {}",
sidecar.display(),
e
);
} else {
tracing::info!(
"Predecoded WRITE: {} ({} snapshots)",
sidecar.display(),
snapshots.len()
);
}
Ok(snapshots)
}
/// Remove all `*.predecoded.bin` files from `predecoded_dir`. Idempotent.
///
/// Used by the `--rebuild-predecoded` CLI flag to force a fresh decode of every
/// source file. Returns the number of files removed.
pub fn purge_predecoded_dir(predecoded_dir: &Path) -> std::io::Result<usize> {
if !predecoded_dir.exists() {
return Ok(0);
}
let mut removed = 0usize;
for entry in fs::read_dir(predecoded_dir)? {
let entry = entry?;
let path = entry.path();
if path
.file_name()
.and_then(|n| n.to_str())
.is_some_and(|n| n.ends_with(".predecoded.bin"))
{
fs::remove_file(&path)?;
removed += 1;
}
}
Ok(removed)
}
#[cfg(test)]
mod tests {
use super::*;
use tempfile::TempDir;
#[test]
fn sidecar_path_uses_kind_suffix() {
let p = sidecar_path(
Path::new("/data/ES.FUT_2024-Q1.dbn.zst"),
Path::new("/tmp/predecoded"),
"trades",
);
assert_eq!(
p,
PathBuf::from("/tmp/predecoded/ES.FUT_2024-Q1.dbn.zst.trades.predecoded.bin")
);
}
#[test]
fn header_round_trip_validates_source_metadata() {
let tmp = TempDir::new().unwrap();
let source = tmp.path().join("source.bin");
fs::write(&source, b"hello world").unwrap();
let sidecar = tmp.path().join("source.bin.test.predecoded.bin");
let payload: Vec<u32> = vec![1, 2, 3];
write_sidecar(&source, &sidecar, &payload).unwrap();
let read: Option<Vec<u32>> = try_read_sidecar(&source, &sidecar).unwrap();
assert_eq!(read, Some(vec![1, 2, 3]));
// Mutate source → sidecar must be invalidated.
fs::write(&source, b"different content here").unwrap();
let read: Option<Vec<u32>> = try_read_sidecar(&source, &sidecar).unwrap();
assert_eq!(read, None);
}
#[test]
fn purge_removes_only_predecoded_files() {
let tmp = TempDir::new().unwrap();
fs::write(tmp.path().join("a.trades.predecoded.bin"), b"x").unwrap();
fs::write(tmp.path().join("b.mbp10.predecoded.bin"), b"y").unwrap();
fs::write(tmp.path().join("unrelated.txt"), b"z").unwrap();
let removed = purge_predecoded_dir(tmp.path()).unwrap();
assert_eq!(removed, 2);
assert!(tmp.path().join("unrelated.txt").exists());
}
}

View File

@@ -10,11 +10,12 @@ use std::path::Path;
use dbn::decode::{DbnDecoder, DecodeRecordRef};
use dbn::RecordRefEnum;
use serde::{Deserialize, Serialize};
use crate::MLError;
/// A single parsed trade from a DBN file.
#[derive(Debug, Clone, Copy)]
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
pub struct DbnTrade {
/// Event timestamp in nanoseconds since Unix epoch
pub timestamp: u64,

View File

@@ -150,6 +150,17 @@ struct Opts {
/// Skip confirmation prompt
#[arg(long)]
yes: bool,
/// Purge the predecoded-sidecar cache before loading.
///
/// Predecoded sidecars (`<basename>.<kind>.predecoded.bin` under
/// `<output_dir>/predecoded/`) skip zstd-DBN decompression on subsequent
/// precompute runs. They self-invalidate when the source file's mtime or
/// size changes, so this flag is normally unnecessary — pass it only after
/// a format change to `Mbp10Snapshot` / `DbnTrade` (bumps fail to
/// deserialize) or when explicitly testing the cold path.
#[arg(long)]
rebuild_predecoded: bool,
}
// ---------------------------------------------------------------------------
@@ -183,6 +194,20 @@ async fn main() -> Result<()> {
let mbp10_dir = opts.mbp10_data_dir.as_ref().map(PathBuf::from);
let trades_dir = opts.trades_data_dir.as_ref().map(PathBuf::from);
// Predecoded-sidecar cache lives under output_dir/predecoded/ so it sits
// next to the .fxcache outputs (same writability assumptions). First call
// for any source `.dbn.zst` pays zstd; subsequent calls deserialize the
// sidecar and skip decompression entirely (zstd was 62% of wall time on
// the 2026-05-16 1Q ES profile).
let predecoded_dir = output_dir.join("predecoded");
std::fs::create_dir_all(&predecoded_dir)
.with_context(|| format!("Failed to create predecoded dir {}", predecoded_dir.display()))?;
if opts.rebuild_predecoded {
let removed = ml::features::predecoded::purge_predecoded_dir(&predecoded_dir)
.with_context(|| format!("Failed to purge {}", predecoded_dir.display()))?;
info!("--rebuild-predecoded: purged {} sidecar(s) from {}", removed, predecoded_dir.display());
}
// ── Validate inputs ──────────────────────────────────────────────────────
if !data_dir.exists() {
anyhow::bail!("Data directory not found: {}", data_dir.display());
@@ -289,23 +314,40 @@ async fn main() -> Result<()> {
// Load trades PER FILE and filter front-month within each file.
// Each quarterly DBN file has a different front-month contract (e.g. ESH24, ESM24).
// Filtering globally would select only ONE quarter's contract.
let mut all_front_month_trades: Vec<ml::features::DbnTrade> = Vec::new();
let mut total_raw = 0_usize;
for file in &trade_files {
match ml::features::load_trades_sync(file) {
Ok(trades) => {
let raw_count = trades.len();
total_raw += raw_count;
let filtered = ml::features::filter_front_month(&trades);
info!(" {} -> {} trades, {} front-month",
file.file_name().unwrap_or_default().to_string_lossy(),
raw_count, filtered.len());
all_front_month_trades.extend(filtered);
}
Err(e) => {
tracing::warn!(" Failed {:?}: {e}", file.file_name());
}
}
//
// Parallel across quarters via rayon; each file goes through the
// predecoded-sidecar cache so a re-run of `precompute_features` skips
// zstd decompression entirely.
let per_file: Vec<(usize, Vec<ml::features::DbnTrade>)> = {
use rayon::prelude::*;
trade_files
.par_iter()
.filter_map(|file| {
match ml::features::predecoded::load_or_predecode_trades(file, &predecoded_dir) {
Ok(trades) => {
let raw_count = trades.len();
let filtered = ml::features::filter_front_month(&trades);
info!(
" {} -> {} trades, {} front-month",
file.file_name().unwrap_or_default().to_string_lossy(),
raw_count,
filtered.len()
);
Some((raw_count, filtered))
}
Err(e) => {
tracing::warn!(" Failed {:?}: {e}", file.file_name());
None
}
}
})
.collect()
};
let total_raw: usize = per_file.iter().map(|(r, _)| r).sum();
let mut all_front_month_trades: Vec<ml::features::DbnTrade> =
Vec::with_capacity(per_file.iter().map(|(_, t)| t.len()).sum());
for (_, t) in per_file {
all_front_month_trades.extend(t);
}
all_front_month_trades.sort_by_key(|t| t.timestamp);
info!("Total trades: {} raw, {} front-month", total_raw, all_front_month_trades.len());
@@ -388,8 +430,6 @@ async fn main() -> Result<()> {
const OFI_DIM: usize = ml_core::state_layout::OFI_DIM;
let t2 = Instant::now();
let ofi: Vec<[f64; OFI_DIM]> = if let Some(ref mbp10_path) = mbp10_dir {
use ml::features::mbp10_loader::load_ofi_features_parallel;
use ml::features::trades_loader::load_trades_sync;
use ml::features::ofi_calculator::OFICalculator;
info!("Loading MBP-10 snapshots from {}...", mbp10_path.display());
@@ -400,21 +440,17 @@ async fn main() -> Result<()> {
info!("No MBP-10 files found, OFI will be zeros");
vec![[0.0; OFI_DIM]; n]
} else {
// Load MBP-10 snapshots (parallel)
use data::providers::databento::dbn_parser::DbnParser;
// Load MBP-10 snapshots (parallel, with predecoded sidecar)
let per_file: Vec<Vec<_>> = {
use rayon::prelude::*;
mbp10_files.par_iter().filter_map(|file| {
let parser = match DbnParser::new() {
Ok(p) => p,
Err(e) => { tracing::warn!("Parser init failed: {e}"); return None; }
};
let mut snapshots = Vec::new();
match parser.parse_mbp10_streaming(file, 100, |snap| {
snapshots.push(snap.clone());
}) {
Ok(_) => {
info!(" {} -> {} snapshots", file.file_name().unwrap_or_default().to_string_lossy(), snapshots.len());
match ml::features::predecoded::load_or_predecode_mbp10(file, &predecoded_dir) {
Ok(snapshots) => {
info!(
" {} -> {} snapshots",
file.file_name().unwrap_or_default().to_string_lossy(),
snapshots.len()
);
Some(snapshots)
}
Err(e) => {
@@ -430,14 +466,14 @@ async fn main() -> Result<()> {
all_snapshots.sort_by_key(|s| s.timestamp);
info!("Loaded {} MBP-10 snapshots in {:.1}s", all_snapshots.len(), t2.elapsed().as_secs_f64());
// Load trades (parallel)
// Load trades (parallel, with predecoded sidecar)
let all_trades = if let Some(ref tdir) = trades_dir {
let mut trade_files = collect_dbn_files_recursive(tdir);
trade_files.sort();
let per_file_trades: Vec<Vec<_>> = {
use rayon::prelude::*;
trade_files.par_iter().filter_map(|path| {
match load_trades_sync(path) {
match ml::features::predecoded::load_or_predecode_trades(path, &predecoded_dir) {
Ok(t) => { info!(" {} trades from {:?}", t.len(), path.file_name()); Some(t) }
Err(e) => { tracing::warn!(" Failed {:?}: {e}", path.file_name()); None }
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,37 @@
# Foxhunt Specialized Agents — Rollout Close-out
**Date:** 2026-05-10
**Branch:** sp15-trader-discipline-recovery
**Plan:** `docs/superpowers/plans/2026-05-10-foxhunt-specialized-agents.md`
## What was built (Tasks 113, 14 commits)
### Infrastructure
- `.claude/helpers/foxhunt-audit-router.sh` — warn-only PostToolUse hook router (~13 ms per invocation, deduped via state file)
- `.claude/helpers/foxhunt-audit-session-start.sh` — session-start banner
- `.claude/settings.json` — additive merge: preserved all claude-flow hooks, added foxhunt PostToolUse + SessionStart hooks
- `.claude/.foxhunt-audit-state` — dedup state file (gitignored)
### 5 Agents (`/.claude/agents/foxhunt/`)
| Agent | Fires on |
|-------|----------|
| `gpu-contract-auditor` | `*.cu`, `build.rs` in CUDA crates |
| `isv-discipline-auditor` | `isv_slots.rs`, `sp*_isv*.rs` |
| `reward-controller-auditor` | `experience_kernels.cu`, reward-related paths |
| `code-hygiene-auditor` | General crate `.rs` files |
| `sp-critical-reviewer` | Specs + plans under `docs/superpowers/` |
### 7 Skills (`/.claude/skills/foxhunt/`)
`sp-spec-writer`, `isv-slot-scaffolder`, `pearl-distiller`, `argo-deploy-helper`, `smoke-pilot`, `memory-curator`, `stale-worktree-cleaner`
## Acceptance results (Task 14)
All 8 acceptance steps passed:
- Hook router: 1113 ms per call (well under 200 ms target)
- All 9 routing rules fire correct agent or emit empty output
- 5 agent YAML files valid, names match
- 7 skill YAML files valid, names match
- Memory directory untouched (invariant held)
- `settings.json` additive merge verified
- State file gitignored, working tree clean (only pre-existing `.claude/scheduled_tasks.lock` modified)
- 14 commits, 5 agents, 7 skills, 2 helper scripts — all present and executable

View File

@@ -0,0 +1,272 @@
# Foxhunt Specialized Agents & Skills — Design
| | |
|---|---|
| **Date** | 2026-05-10 |
| **Author** | jgrusewski (with claude-opus-4-7) |
| **Scope** | Phase 1 — 12 foxhunt-aware agents/skills (5 agents + 5 workflow skills + 2 maintenance skills) |
| **Storage** | `.claude/agents/foxhunt/`, `.claude/skills/foxhunt/`, `.claude/helpers/foxhunt-audit-router.sh` |
| **Branch** | Separate from SP15 — design-and-tooling work, not part of any SP. |
## 1. Problem
The repository has accumulated, under `~/.claude/projects/-home-jgrusewski-Work-foxhunt/memory/`, a deep body of project-specific knowledge:
- **15 `feedback_*` rules** — hard behavioral constraints (no atomicAdd, no stubs, ISV-driven, no nvrtc, push-before-deploy, …).
- **30+ `pearl_*` pattern discoveries** — controllers, bootstrap, distributional Q, GPU mechanics, testing discipline, etc.
- **12+ `project_*` state files** — active hypotheses, regressions, audit findings.
- **30 SP design specs** in `docs/superpowers/specs/` (SP1 → SP15+ trader discipline).
The `.claude/agents/` and `.claude/skills/` directories contain ~64 generic items inherited from claude-flow and superpowers plugins. None are foxhunt-aware. Consequence: every spec, plan, kernel edit, and review must re-establish constraints already documented in memory. The user has to re-explain "no atomicAdd" or "this should be ISV" *every time*.
This design adds **foxhunt-namespaced agents and skills** that internalize the accumulated wisdom, with three goals:
1. **Drift prevention** — auditor agents catch violations on edits/PRs, citing the originating `feedback_*`/`pearl_*` file.
2. **Workflow acceleration** — skills templatize the SP lifecycle (spec → critical review → plan → ISV-slots → kernel-wire → smoke → close-out → pearl-distill).
3. **Anti-staleness** — skills curate `MEMORY.md` and `.claude/worktrees/` so accumulated wisdom does not rot.
## 2. Approach
**Pattern-cluster auditors + workflow-skill hybrid.**
- **Agents** (5) cluster pearls/feedback by *invariant being enforced*, not by file domain. E.g., the GPU-contract-auditor knows that `feedback_no_atomicadd`, `feedback_no_htod_htoh`, `pearl_no_host_branches_in_captured_graph`, and `pearl_cold_path_no_exception_to_gpu_drives` are all manifestations of one underlying invariant ("no CPU compute on the hot path; no host divergence inside graph capture"). That coherence is hard to get from per-pearl skills.
- **Skills** (7) split into workflow (5) and maintenance (2). Workflow skills templatize SP-lifecycle stages. Maintenance skills audit the meta-artifacts (memory + worktrees).
**Why agents for review, skills for templates.** Agents run in their own context window (via the Task tool), which protects the main session when reviewing 5-20 files. Skills are markdown checklists invoked from the main turn — appropriate when the work is procedural and you want it inline. The split mirrors how the existing claude-flow vs. superpowers tools are used.
**Hybrid trigger model.** A single `PostToolUse` hook (warn-only, <200 ms) emits a hint when an edit lands on a relevant file path. The hint is a system reminder; whether to spawn the agent stays Claude's call. Agents are also dispatchable explicitly. No PreToolUse blocking, no PreCommit-only — both would either over-friction or miss the editing window where the user gets the most value.
## 3. Phase-1 catalogue
### 3.1 Agents (5) — pattern-aligned auditors
#### `foxhunt-isv-discipline-auditor`
**Bundles**: `feedback_isv_for_adaptive_bounds`, `feedback_adaptive_not_tuned`, `pearl_controller_anchors_isv_driven`, `pearl_first_observation_bootstrap`, `pearl_wiener_optimal_adaptive_alpha`, `pearl_wiener_alpha_floor_for_nonstationary`, `pearl_blend_formulas_must_have_permanent_floor`, `pearl_zscore_normalization_for_magnitude_asymmetric_signals`, `pearl_per_branch_c51_atom_span`, `pearl_per_branch_iqn_tau_schedule`, `pearl_per_branch_loss_budget`, `pearl_per_branch_noisy_sigma`, `pearl_per_group_adam_hyperparams`, `pearl_kelly_cap_signal_driven_floors`, `pearl_trail_stop_signal_driven`, `pearl_adaptive_moe_lambda`, `pearl_l1_lambda_grad_direction_entropy_deficit`.
**Triggers on**: `crates/ml-*/**/*.rs`, `**/sp*_isv_slots.rs`.
**Verifies**:
- Hardcoded multipliers/caps/anchors → flag for ISV-slot conversion.
- EMA initializations use sentinel-bootstrap (`sentinel = 0; first observation replaces directly`).
- Wiener-α controllers in non-stationary loops carry a floor (≥ 0.4 default per `pearl_wiener_alpha_floor_for_nonstationary`).
- Blend formulas use `max(real, floor)`, never `α·real + (1-α)·floor`.
- Controller ratios over magnitude-asymmetric signals z-score-normalize first.
- Per-branch C51/IQN/CQL/Ens budgets derive from per-branch flatness; not shared.
#### `foxhunt-gpu-contract-auditor`
**Bundles**: `feedback_cpu_is_read_only`, `feedback_no_atomicadd`, `feedback_no_htod_htoh_only_mapped_pinned`, `feedback_no_cpu_test_fallbacks`, `feedback_no_nvrtc`, `feedback_cudarc_f64_f32_abi`, `pearl_no_host_branches_in_captured_graph`, `pearl_cold_path_no_exception_to_gpu_drives`, `pearl_fused_per_group_statistics_oracle`, `pearl_sp4_histogram_warp_tile_undercount`, `pearl_cublas_lt_vs_classic_sgemm`, `pearl_build_rs_rerun_if_env_changed`, `pearl_canary_input_freshness_launch_order`.
**Triggers on**: `**/*.cu`, `**/cuda/**/*.rs`, `crates/*/build.rs`.
**Verifies**:
- No `atomicAdd` (block tree-reduce only).
- No HtoD/HtoH outside mapped-pinned (tests not exempt).
- No `nvrtc` runtime compilation; only pre-compiled cubins.
- f64↔f32 explicit casts on cudarc kernel argument lists.
- No host branches inside CUDA-graph capture region.
- K groups × N stats → ONE fused kernel (per `pearl_fused_per_group_statistics_oracle`).
- Every `std::env::var()` paired with `cargo:rerun-if-env-changed` in build.rs.
- Producer kernels launched before any canary that consumes them.
#### `foxhunt-reward-controller-auditor`
**Bundles**: `pearl_audit_unboundedness_for_implicit_asymmetry`, `pearl_event_driven_reward_density_alignment`, `pearl_symmetric_clamp_audit`, `pearl_one_unbounded_signal_per_reward`, `pearl_bounded_modifier_outputs_require_structural_activation`, `pearl_loss_balance_controller`, `pearl_engagement_rate_self_correction`, `pearl_learned_gate_subsumes_handcoded`, `pearl_controller_amplifies_dominant_magnitude_trap`, `pearl_per_bar_vs_segment_pnl_signal_mismatch`, `pearl_thompson_for_distributional_action_selection`, `pearl_trail_fire_pre_vs_action_mag`, `pearl_adam_normalizes_loss_weights`, `pearl_separate_aux_trunk_when_shared_starves`, `pearl_imbalance_bar_ewma_washes_out_configured_threshold`, `pearl_reward_shape_drives_quality_over_quantity` (deprecated — for back-compat detection).
**Triggers on**: `**/cuda/experience_kernels.cu`, `**/reward*.{rs,cu}`, `**/controller*.rs`, `**/trader_*.rs`.
**Verifies**:
- Bilateral clamps (`fmaxf(lo, fminf(x, hi))`); one-sided forms flagged.
- Bounded modifier outputs use structural activations (sigmoid/tanh), not runtime clamps.
- Asymmetric bounded clamps where the unboundedness was implicitly providing behavioral asymmetry (loss > win caps for loss aversion).
- Event-driven reward density matches event density — no per-step shaping for trade-close objectives.
- Exactly one unbounded multiplicand per reward term.
- Thompson selector for action selection in train+eval; argmax only for Bellman target.
- Trail filter fires on `pre_mag`, not `action_mag`.
- Per-loss weight changes called out as Adam-normalized no-ops; suggest loss-function changes or per-group LR instead.
- Imbalance-bar samplers using `new_with_ewma()` flagged: data washes out the configured threshold within ~5 bars.
#### `foxhunt-code-hygiene-auditor`
**Bundles**: `feedback_no_stubs`, `feedback_no_todo_fixme`, `feedback_no_hiding`, `feedback_no_legacy_aliases`, `feedback_no_feature_flags`, `feedback_no_quickfixes`, `feedback_no_functionality_removal`, `feedback_no_partial_refactor`, `feedback_wire_everything_up`, `feedback_v7_gem_methodology`, `feedback_magnitude_must_be_useful`, `feedback_fix_everything_aggressively`, `pearl_tests_must_prove_not_lock_observations`, `pearl_no_deferrals_for_complementary_fixes`, `feedback_trust_code_not_docs`.
**Triggers on**: `crates/**/*.rs`, `services/**/*.rs`, `bin/**/*.rs`, `testing/**`.
**Verifies**:
- No stubs (return-zero, dead params, unused fields).
- No `TODO` / `FIXME` / `XXX` markers.
- No `_var` hiding or `#[allow]` suppression.
- No `*_legacy`, `*_v1`, `enable_*`, `use_*` flags.
- No partial refactors (kernel renamed, ≥1 caller un-migrated).
- New unused functions flagged for measure-or-wire-or-delete (v7-gem 3-step).
- Tests assert invariants (boundedness, monotonicity, fixed-point), not observed values.
- Plans/specs flagged when they sequence two complementary fixes that should ride together (`pearl_no_deferrals_for_complementary_fixes`).
#### `foxhunt-sp-critical-reviewer`
**Role**: Performs the "second/third critical review" pass on a freshly written SP spec or plan.
**Triggers on**: `docs/superpowers/specs/*-design.md` (on Edit), `docs/plans/*.md` (on Edit). Or explicit invocation.
**Verifies**: Every claim in the spec/plan grounds in a named pearl/feedback file. No-deferrals rule is enforced (asks "is there a sister fix that should ride along?"). Every kernel has a wire-up phase. Smoke gate has explicit kill conditions. ISV slots required are listed before scaffolding. Anti-patterns the spec is avoiding are enumerated.
**Output**: A numbered issues list in the same shape as the user's existing critical-review iterations (e.g., the `eb5e19d67` "fix all 16 reviewer-flagged issues" pattern). Each issue cites the originating pearl/feedback. Severity-tagged; non-blocking.
**Composes**: Reads findings from the four auditor agents when applicable (e.g., for ISV claims, asks `isv-discipline-auditor`; for kernel claims, asks `gpu-contract-auditor`).
### 3.2 Workflow skills (5)
#### `foxhunt-sp-spec-writer`
**Triggered by**: explicit (`/skill foxhunt-sp-spec-writer` or "write SP spec for X").
**Produces**: A new `docs/superpowers/specs/YYYY-MM-DD-spXX-<topic>-design.md` matching the established format:
1. Problem (with metrics or symptoms).
2. Pearl/feedback grounding (forced — every claim links to a memory file).
3. Approach (root-cause framing).
4. Phases A/B/C with bullet steps.
5. ISV slots required (count + index range).
6. Smoke gate with explicit kill conditions.
7. Anti-patterns avoided (enumerated).
8. Pearl-distillation hook for close-out.
Enforces `pearl_no_deferrals_for_complementary_fixes` by asking the user explicitly: "is there a sister fix?"
#### `foxhunt-isv-slot-scaffolder`
**Triggered by**: explicit (provide a list of new bounds/anchors/caps).
**Produces**:
- `crates/.../sp{N}_isv_slots.rs` with named constants `[START..END)`.
- `ISV_TOTAL_DIM` bumped at canonical location.
- Slot registration for fold-boundary reset.
- Commit message in the canonical shape: `feat(sp{N}): scaffold sp{N}_isv_slots.rs with K slots [S..E) — ISV_TOTAL_DIM OLD→NEW`.
#### `foxhunt-pearl-distiller`
**Triggered by**: explicit, post SP close-out.
**Produces**: A new `pearl_<topic>.md` in the memory directory with structured body — pattern statement / detection signal / fix / canonical commit:file:line / related pearls — and a `MEMORY.md` index entry placed in the right section. Critical: this skill is one of *only two* tools that write into `memory/`.
#### `foxhunt-argo-deploy-helper`
**Triggered by**: explicit (e.g., "deploy training X to argo").
**Produces**: A vetted invocation of `scripts/argo-train.sh` or `scripts/argo-compile-deploy.sh` with:
- Pre-flight: verify `git push` against `origin` is at HEAD (`feedback_push_before_deploy`).
- Default to `--gpu-pool ci-training-l40s` unless explicitly overridden (`feedback_default_to_l40s_pool`).
- Hard-error on missing `--mbp10-data-dir` + `--trades-data-dir` for SP-chain training (`feedback_mbp10_mandatory`).
- Always pass `--per-enabled` (`feedback_always_per`).
- When H100 is chosen, enforce maximum-utilization flags (`feedback_h100_gpu`).
#### `foxhunt-smoke-pilot`
**Triggered by**: explicit, paired with `argo-deploy-helper`.
**Produces**: A monitoring loop over a running smoke that:
- Tails the workflow log via `kubectl` / `argo logs`.
- Detects anomalies: NaN loss, magnitude collapse, EVAL_DIST anomalies, return-explosions, controller saturation, etc. (Anomaly detector list seeds from `feedback_stop_on_anomaly`, `feedback_kill_runs_on_anomaly_quickly`, and the `project_*` files documenting prior anomalies.)
- Auto-stops the workflow at first useful signal (`feedback_kill_runs_on_anomaly_quickly`).
- Does not duplicate Bash `run_in_background` Monitor (`feedback_no_redundant_monitor`) — uses argo's built-in stream.
### 3.3 Maintenance skills (2)
#### `foxhunt-memory-curator`
**Triggered by**: explicit, plus a SessionStart hint when `MEMORY.md` mtime > 30 days.
**Produces**: A curation report. For every memory entry:
- If it references a file/symbol/flag, verify it still exists.
- If it references a commit, verify the commit is still in history.
- Flag stale entries (e.g., `project_*` files where the project is now merged).
- Detect duplicate or fully-superseded pearls.
- Detect entries marked `DEPRECATED` that should be archived.
The skill **proposes** deletes and merges; it never auto-deletes. User confirms each action.
#### `foxhunt-stale-worktree-cleaner`
**Triggered by**: explicit (destructive op).
**Produces**: A per-worktree cleanup plan over `.claude/worktrees/*` and `git worktree list`:
- No commits in N days.
- Branch is `[gone]` (tracking ref deleted upstream).
- Branch is fully merged into `main`.
The skill prints the cleanup commands; user confirms each `git worktree remove` interactively. Composes the existing `commit-commands:clean_gone` skill where applicable.
## 4. Hook integration
### 4.1 Router contract
A single shell script — `bash .claude/helpers/foxhunt-audit-router.sh` — is wired to `PostToolUse` for `Edit|Write|MultiEdit`. It reads the tool input from stdin (Claude Code hook protocol), inspects the file path, and emits zero or one hint lines to stdout. It must:
1. Complete in <200 ms.
2. Never spawn agents itself — only suggest.
3. Never block. Exit code is always 0.
4. Coexist additively with existing hooks (existing `.claude/settings.json` is 619 bytes — not to be replaced; the new hook block is *added*, not overwritten).
### 4.2 Routing table
| Path glob | Suggested agent(s) |
|---|---|
| `**/*.cu`, `**/cuda/**/*.rs`, `crates/*/build.rs` | `gpu-contract-auditor` |
| `**/experience_kernels.cu`, `**/reward*.{rs,cu}`, `**/controller*.rs`, `**/trader_*.rs` | `reward-controller-auditor` (+ `gpu-contract-auditor` for `.cu`) |
| `**/sp*_isv_slots.rs`, `crates/ml-*/**/*.rs` (when diff adds numeric literals in suspicious contexts) | `isv-discipline-auditor` |
| `crates/**/*.rs`, `services/**/*.rs`, `bin/**/*.rs` (everything else) | `code-hygiene-auditor` |
| `docs/superpowers/specs/*.md`, `docs/plans/*.md` | `sp-critical-reviewer` |
| `MEMORY.md`, `memory/pearl_*.md`, `memory/feedback_*.md` | `memory-curator` |
### 4.3 Hint format
Single-line, parseable, prefixed for grep:
```
[foxhunt-audit] crates/ml-dqn/src/cuda/foo.cu — suggest: gpu-contract-auditor, reward-controller-auditor
```
### 4.4 Noise control
State file `.claude/.foxhunt-audit-state` (gitignored). One suggestion per (file path, agent) per session. Cleared at SessionStart.
### 4.5 Numeric-literal heuristic (ISV)
The router uses a regex over the diff snippet — `[a-zA-Z_]+\s*[:=]\s*-?\d+\.?\d*` plus name-context heuristics (assignment targets like `*_threshold`, `*_floor`, `*_cap`, `*_alpha`, `*_lambda`). False positives expected; the router only suggests, never blocks. The agent itself decides on the merits.
## 5. Memory authority
The four auditor agents and the critical-reviewer **read** memory. They do not write into `memory/`. Only two skills write into `memory/`:
- `foxhunt-pearl-distiller` (creates new pearls).
- `foxhunt-memory-curator` (deletes/archives stale entries — with explicit user confirmation).
This invariant prevents memory drift from auditor agents that merely *think* a pattern is established when in fact it isn't yet documented.
## 6. Acceptance criteria
A successful Phase-1 rollout means:
1. Five auditor agents are dispatchable via the Task tool. Each finding cites a specific `feedback_*.md` or `pearl_*.md` file by name (not a generic complaint).
2. The hook router emits hints in <200 ms and never blocks.
3. The seven skills produce artifacts in shapes identical to existing canonical examples:
- `foxhunt-isv-slot-scaffolder` → commit identical in shape to `c146c4fff` (SP15 ISV-slot scaffold).
- `foxhunt-sp-spec-writer` → spec identical in shape to `2026-05-06-trader-discipline-and-recovery-design.md`.
- `foxhunt-pearl-distiller` → pearl identical in shape to recent ones (e.g., `pearl_per_bar_vs_segment_pnl_signal_mismatch.md`).
- `foxhunt-sp-critical-reviewer` → numbered-issues output in the shape of the existing critical-review iterations.
- `foxhunt-argo-deploy-helper` → invocation matching `argo-train.sh` flag conventions and respecting all four `feedback_*` deploy rules.
4. The new `.claude/agents/foxhunt/` and `.claude/skills/foxhunt/` namespaces sit alongside (do not replace) the existing claude-flow / superpowers content.
5. No agent or skill writes into `memory/` other than `foxhunt-pearl-distiller` and `foxhunt-memory-curator`.
6. The new `PostToolUse` hook block is *added* to `.claude/settings.json`, not replacing existing content.
## 7. Phase-2 backlog (deferred)
| Item | Why deferred |
|---|---|
| `crate-graph-auditor` (agent) | Workspace-level orphan detection across 40 crates. Lower-frequency than per-file edits; defer until a concrete need surfaces. |
| Splitting `reward-controller-auditor` into `reward-shape-auditor` + `controller-design-auditor` | Only if the combined agent file grows past ~400 lines in practice. |
`web-dashboard-debugger` is **not** in the Phase-2 backlog (explicitly out-of-scope).
## 8. Implementation order
Implementation plan will be developed under `writing-plans` skill, but the dependency order is:
1. **Foundation**`.claude/helpers/foxhunt-audit-router.sh`, additive `.claude/settings.json` block, `.claude/.foxhunt-audit-state` gitignore entry.
2. **Auditor agents** (parallel) — `gpu-contract-auditor`, `isv-discipline-auditor`, `reward-controller-auditor`, `code-hygiene-auditor`.
3. **Workflow skills** (parallel) — `sp-spec-writer`, `isv-slot-scaffolder`, `pearl-distiller`.
4. **Deploy + smoke skills** (parallel) — `argo-deploy-helper`, `smoke-pilot`.
5. **Maintenance skills** (parallel) — `memory-curator`, `stale-worktree-cleaner`.
6. **Critical reviewer** (last) — `sp-critical-reviewer` composes the auditor agents from step 2.
## 9. Concerns flagged
- **Pearl-bundling overlap**: `pearl_one_unbounded_signal_per_reward` and `pearl_kelly_cap_signal_driven_floors` straddle `isv-discipline-auditor` and `reward-controller-auditor`. Both will be aware; the bundling above decides primary ownership but is not exclusive.
- **Numeric-literal detection is heuristic** — false positives expected. Mitigation: agents are permissive (flag for review, never block).
- **`settings.json` merge risk** — existing 619-byte file must be merged additively. Implementation plan must read it first and append, not replace.
- **Hook latency budget** — 200 ms is tight if the router does any disk I/O beyond reading the state file. Implementation must avoid `find`, `grep -r`, or any large-tree scan.