Targeted fixes per user direction (option 3 of "fix critical only,
accept rest as execution-time gaps"):
1. build.rs cubin manifest is 1:1 source-to-cubin (verified: list of
.cu filenames, NOT (source, name) tuples). Fixed Phase 3.1 Step 5
to use single manifest entry; both kernels load from SAME module
via get_function() with their symbolic names.
2. egf_anchor_p1 helper in sp4_histogram_p99.cuh: replaced the
`return 0.0f` stub with the full ~80-line body. Pass 1 + Pass 2
byte-identical to sp4_histogram_p99 (mirrored verbatim from the
existing sibling). Pass 3 walks cumulative-from-bottom for p1
instead of cumulative-from-top for p99. Returns lower-edge of the
first bin reaching 1% threshold.
3. Added Task 4.2.5: `fxt evaluate` subcommand (PREREQUISITE for 4.3).
Verified that bin/fxt/src/main.rs Commands enum has no Evaluate
variant. Without this task, eval-final-template.yaml fails at
runtime. Full subcommand shown: bin/fxt/src/evaluate.rs with 5
flags (--checkpoint-dir, --quarter, --seeds, --output-dir,
--report-card-md), report card markdown emitter per spec §10.5.
5 IMPORTANT and 2 NIT issues from review remain documented as
execution-time friction; subagent-driven-development's spec-compliance
reviewer between tasks is expected to catch them per-task.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Rewrite of 2026-05-06-sp15-trader-discipline-and-recovery.md from v1
(commit 0178a53ab) which had 16 reviewer-flagged issues including
5 critical compile-breakers and 8 important plan-failure violations.
CRITICAL fixes:
- Real GATE1_OPEN_STATE_INDEX (slot 391) — was wrong GATE1_STATE_INDEX
- Real PS_PEAK_EQUITY/PS_PREV_EQUITY (state_layout.cuh slots 7+9) —
no invented CUMULATIVE_EQUITY_INDEX
- Real sp4_histogram_p99<BLOCK_SIZE> block-tree-reduce pattern, no
atomicAdd_block (was feedback_no_atomicadd violation)
- Real evaluate_dqn_graphed pattern (gpu_backtest_evaluator.rs:1143) —
no nonexistent evaluate_on_val_slice
- Real services/ml_training_service/src/main.rs path — was wrong
IMPORTANT fixes:
- Fork from current main 0178a53ab, not stale 5417e2756
- Phase 0.B has explicit case-(a)/case-(b) branches driven by 0.A
diagnostic conclusion
- Phase 2B uses TEMPLATE + 17-row differential table — every test fully
specified, no compressed bullets
- Phase 3 each teaching gets full TDD: write test → run-fail → kernel
(full code) → launcher → consumer → run-pass → commit (5+ steps)
- Phase 3.5 each mechanism same TDD pattern with full kernel code
- Plasticity 3.5.4 specifies TWO-STEP recovery (Flat first, then
cooldown) + Kaiming-He init (not Xavier — architecturally appropriate)
- Phase 4.3 includes FULL eval-final-template.yaml (not 4 bullets)
- 4 EGF constants replaced (not 3 — verified all four in
alpha_grad_compute_kernel.cu lines 142,143,144,147)
- Behavioral_suite test target ordering fixed (Cargo.toml entry lands
before tests run against it)
Stats: 4947 lines, ~270 [- [ ]] step checkboxes, 0 todo!() in test
bodies, every kernel shown in compilable form.
Self-review section maps every spec section to implementing task,
verifies all cross-references against verified codebase facts.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two-sub-project plan:
- Layer A (4 tasks, ~50 LOC): C51 atom-floor, set_aux_weight clamp lift,
stagnation warmup gate, smoke validation
- Layer B (15 tasks, ~1180 LOC): 13 ISV slots, 4 kernels (q_disagreement,
alpha_grad, gradient_hack_detect, dir_concat_qaux), forward wire,
backward gradient gating, orchestrator wire-up, HEALTH_DIAG, tests, smoke
Each task has bite-sized TDD steps (write failing test, run fail,
implement, run pass, commit) per the writing-plans skill conventions.
Phase 0 verification tasks (A.0, B.0) anchor against current code at
HEAD 40e737a18+ before edits.
19 total tasks across 2 layers. Each layer commits independently as
an atomic feature; intermediate per-task commits during Layer B keep
the wire safety-protected (forward concat lands before backward gating
in B.9 → B.10 sequence).
8 explicit kill criteria for Smoke A2-B per spec B.7.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
While P0b smoke (train-sw4ws on bdc5cb8bb) runs, two prep items:
(A) scripts/argo-train.sh — add `ci-training` to the L40S sm_89 case
arm. Bare `ci-training` is an L40S pool alias in some clusters;
previously defaulted to sm_90 (Hopper), causing train-mnpf7 to
deploy with wrong-arch cubins (terminated + resubmitted manually).
Now both `*l40s*` and bare `ci-training` resolve correctly.
(C) docs/superpowers/plans/...sp13...md — Layer B section expanded
with concrete codebase locations discovered during P0a:
- aux_heads_kernel.cu, aux_heads_loss_ema_kernel.cu locations
- aux_nb_label_buf populated as column 0 of next_states (log_return)
- F1/F2 regression history note (don't alias the label buffer)
- aux_pred_to_isv_tanh_kernel.cu is a P0a placeholder per its own
header — Layer B should rewrite to read softmax logit-diff
- dir_acc kernel + oracle tests need softmax-read updates
- Layer B + P0b combined rationale post-P0a empirics
Saves the next implementer ~30 min of re-investigation.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
P0a.T3 v2 implementer's audit revealed `DirectionAction` enum doesn't exist; the
codebase uses an 8-variant fused `ExposureLevel` (ShortSmall/Half/Full, Hold,
LongSmall/Half/Full, Flat) with cross-crate consumers across 77 files and 32+
test files pinning the 8-variant invariant. Atomic Hold elimination would
cascade massively.
User insight (2026-05-04): Hold being FREE is the bug, not Hold itself. MFT
trading legitimately needs multi-bar holds; we want the model to use them
deliberately, not as a CQL-bias lazy default. Holding isn't free in the real
world — broker fees, margin interest, opportunity cost.
v3 reframes as Hold-pricing:
- 4-way action space stays; ExposureLevel::Hold stays; no cross-crate cascade
- 3 new ISV slots (380-382): HOLD_COST_INDEX, HOLD_RATE_TARGET_INDEX,
HOLD_RATE_OBSERVED_EMA_INDEX
- Hold-rate observer: small GPU kernel + Pearls A+D smoothing
- Hold-cost controller: 5-line deficit-driven formula
(excess > target → cost rises 1×→5× base; observed ≤ target → relax)
- Per-bar reward subtraction at action == DIR_HOLD site
- 2 GPU oracle tests for the controller
P0a.T3 cuts from ~250 LOC + 32-test cascade → ~120 LOC additive. T1+T2
already-staged work unchanged. T4/T5/Layer B/C/D structure preserved.
Tension with pearl_event_driven_reward_density_alignment acknowledged in
spec — per-bar Hold cost is exposure-NEGATIVE (away from Hold), models real
economic carry, ISV-bounded by controller. Inverse of the pearl's failure
mode. Faithful reward modeling, not artificial shaping.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
feedback_no_htod_htoh_only_mapped_pinned (tests not exempt):
- All test fixtures converted from htod_copy/dtoh_sync_copy to
MappedF32Buffer with host_slice / host_slice_mut access.
- Novelty hash buffer + projection matrix changed from
cudarc::CudaSlice<f32> + alloc_zeros to MappedF32Buffer.
feedback_no_cpu_forwards (CPU is read-only):
- Projection matrix initialization changed from host-side StdRng +
host_slice_mut writes to a one-shot GPU init kernel
(novelty_simhash_proj_init_kernel) using Philox seeded from
config.seed. No host RNG, no host writes.
feedback_no_cpu_compute_strict (saboteur multiplication):
- B1 step 5 reverted from Rust-side `read_isv_slot * scale` to
GPU-side: pass base scale + ISV pointer + slot index to the
saboteur perturbation kernel; multiplication happens on-device.
feedback_trust_code_not_docs (grad-ratio terminology):
- Spec §3.3.1 "per-component grad EMA" was wrong — SP4 grad-balancer
is per-branch (4 slots), not per-reward-component. Renamed:
reward_component_grad_ratio_compute_kernel
→ reward_component_mag_ratio_compute_kernel
REWARD_COMPONENT_GRAD_RATIO_BASE
→ REWARD_COMPONENT_MAG_RATIO_BASE
Source: existing REWARD_POPART_EMA_INDEX..+6 (per-component reward
magnitude EMAs from SP4 reward_component_ema_kernel). Semantic
equivalent for the controller's exploit/diversify blend.
feedback_no_stubs (dead parameter):
- Removed `eps_div_idx_unused` from mag_ratio kernel signature.
Saboteur engagement: missing producer specified
- Spec §3.3.1's two-reward-arrays formulation replaced with single
`saboteur_delta_reward_buf` produced by the saboteur perturbation
kernel itself (single reward computation, diff emitted as side
output). Engagement kernel signature simplified to one input array.
PNL_REWARD_MAGNITUDE_EMA_INDEX (slot 359): producer wired
- mag-ratio kernel mirrors `isv[REWARD_POPART_EMA_INDEX]` to
scratch_out[6]; chained apply_pearls_ad targets slot 359.
Replay sample kernel location specified
- graph_utility_kernels.cu:71 (gather_f32_scalar). New sibling kernel
`gather_replay_reward_with_curiosity` defined; replaces the existing
scalar gather (no legacy alias per feedback_no_legacy_aliases).
novelty_simhash_lookup runs before, novelty_simhash_update after.
A2 controller test placeholders → full GPU oracle assertions
- Three controller tests (z=0 midpoint, weight renorm, saboteur clamp)
have full mapped-pinned fixtures with assertions on weight sum,
individual values, post-clamp bounds.
Plan now passes:
- feedback_no_htod_htoh_only_mapped_pinned (tests + production)
- feedback_no_cpu_forwards (CPU never writes/computes for GPU)
- feedback_no_cpu_compute_strict (all multiplications GPU-side)
- feedback_no_atomicadd (race-tolerated non-atomic, safety documented)
- feedback_no_partial_refactor (Layer B atomic; saboteur kernel sig
change touches all callers in the same commit)
- feedback_no_stubs (no dead parameters)
- feedback_trust_code_not_docs (corrected spec terminology)
- feedback_wire_everything_up (every new field has init + producer)
- feedback_no_legacy_aliases (old gather_f32_scalar replaced, deleted)
1447 lines, +268 from previous version.
Critical re-review of v1 caught:
1. Task 6 retained Pearl 2 kernel signature with (void) no-op args
"to save 3-site cascade" — that's the partial-refactor anti-pattern
feedback_no_partial_refactor explicitly forbids. v2 does the full
contract change: signature shrinks 9→6 args, launcher migrates
atomically.
2. SCRATCH_PEARL_2_C51/_CQL/_ENS would become orphan constants —
feedback_wire_everything_up says delete or wire. v2 deletes them in
T6.
3. Audit-doc updates were separated into a single late commit — would
fail check_audit_doc_updates pre-commit gate on T1-T6. v2 folds Fix
31 stub into T1 and extends it commit-by-commit.
4. T5 referenced grad_decomp_*_result_dev_ptr field names that don't
exist — actual layout is one shared 27-float pinned buffer with
per-component byte offsets (iqn=0, cql_sx=24, c51=36). v2 uses
pointer arithmetic from grad_decomp_result_dev_ptr.
5. Tasks 1, 2, 4 had "if file shape X then Y, otherwise Z" placeholder
branches — writing-plans skill forbids these. v2 has concrete patches
based on reading the actual files.
Also corrected: T2 fixes the stale "regime_stability allocator"
description in state_reset_registry.rs alongside the new entries.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Three independent sub-projects (one per Pearl), each an atomic commit,
each running in its own git worktree (sp6-pearl-2/3/5). Pearl 2 expands
compute_adaptive_budgets() to per-branch [f32;4] arrays + trunk-mean
scalars, dispatches branch-correction SAXPY sub-launches. Pearl 3
replaces noise_sigma: f32 with noise_sigma_per_branch: [f32;4] in
ExperienceCollectorConfig and extends add_advantage_noise to per-branch
lookup. Pearl 5 adds 4-branch tau buffer triplets to GpuIqnHead, runs
4 IQN forward passes per step (Approach B), each contributing
iqn_trunk/4 to prevent 4x gradient accumulation.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Layer C close-out C1 redesigned. Original plan claimed
grad_norm_slow_ema_pinned was orphan post-Mech-6 migration; verification
surfaced a SECOND live consumer (fold_warmup_factor_update, commit
4ef1d8ebb) that legitimately reads the slow EMA as cross-fold
steady-state baseline.
The actual defect: the EMA UPDATE at update_adaptive_clip:22720-22737
was host-side `(1-α)*prev + α*obs` arithmetic — exactly the pattern
feedback_no_cpu_compute_strict (saved 2026-05-01) strictly forbids.
Migrated:
- New `update_grad_norm_emas_kernel.cu` — single-thread fused fast+slow
EMA update kernel. Reads `grad_norm_buf[0]`, updates two mapped-pinned
EMA scalars via dev_ptr. `__threadfence_system()` ensures the
`fold_warmup_factor_kernel` consumer sees freshly-written values.
- `launch_update_grad_norm_emas` Rust launcher chained on the
producer's stream — graph-capture-compatible, no host sync.
- update_adaptive_clip's host-side `unsafe { ... }` block replaced
with the GPU launcher call (warn-and-continue on launch failure
mirroring the launch_h_s2_rms_ema / launch_fold_warmup_factor
per-step ISV producer pattern at training_loop.rs:3450/3464).
Preserved:
- grad_norm_slow_ema_pinned mapped-pinned buffer (cross-fold persistent;
legitimate consumer is fold_warmup_factor_update).
- Fixed-α design (FAST_ALPHA=0.1, SLOW_ALPHA=0.001) — Pearls A+D
adaptive α would defeat the cross-fold-baseline semantic the warmup
factor depends on.
- Cold-start sentinel (`prev ≤ 0.0` ⇒ assign obs directly) — same
formula as the deleted host code.
- Host-side update_adaptive_clip early-return guard — kernel only
launches when observed_grad_norm is finite and > 0.
- grad_norm_emas_step_count host counter — scalar control-flow
metadata for warmup-window gating, not compute.
Plan/spec docs updated to remove stale "orphan" claim. State-reset
registry doc-comment + field doc-comments updated to reflect GPU-only
update path. fold_warmup_factor_kernel docstring no longer describes
its grad-norm EMA inputs as host-side-fed.
Build clean, sp4 + state_reset_registry lib tests pass (11/11), 16/16
SP4 producer GPU tests pass on RTX 3050 Ti. No behavior change — pure
architectural fix.
Refs: SP4 Layer C C1 redesigned (was: retire). Original plan
docs/superpowers/plans/2026-04-30-sp4-signal-driven-magnitude-control.md
lines 2184-2221.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Implementation plan for SP1 (Sub-project 1 of 3) of the numerical
stability investigation. Follows the γ + β methodology from the spec
at docs/superpowers/specs/2026-04-29-numerical-stability-investigation-design.md.
8 tasks across 4 phases:
- Phase A (Task 1): γ read-only audit producing docs/dqn-backward-nan-audit.md
- Phase B (Tasks 2-5): always-landing β instrumentation expanding
nan_flags_buf 24→48 with 12 new backward-kernel NaN check slots
+ 12 reserved slots for future coverage
- Phase C (Task 6): surgical fix(es), content-driven by audit + smoke
topology, ISV-driven for any dynamic bound (mandatory)
- Phase D (Task 7): multi-fold L40S smoke validation against 7 pass
criteria (F0 ≥ 95% baseline, F1+F2 monotone improvement, zero
NaN-CLAMPED-TO-ZERO, all 48 NaN flag slots remain at zero)
- Closure (Task 8): audit doc closure, memory entry, SP2/SP3 handoff
Operating principles (mandatory per spec):
- No deferrals — anomalies discovered during investigation get fixed
within SP1, not punted to SP2/SP3
- Combined RELATED fixes ship as rich commits (per
feedback_no_partial_refactor)
- ISV-driven design for any dynamic bound
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Plan C as authored assumed:
- c51_probs_dir = post-softmax probabilities (didn't exist on collector;
only raw exp_b_logits is materialised)
- atom_values = single global linear support (production uses per-sample
per-direction adaptive [v_min, v_max, delta_z] + optional atom_positions)
- iqn_quantiles_dir = rollout-side IQN inference (doesn't exist; IQN is
training-only)
Amended kernel signature uses the production architecture:
- b_logits_dir [N, b0_size, n_atoms] — raw direction-branch logits
- per_sample_support [N, b0_size, 3] — adaptive per-direction support
- atom_positions [b0_size, n_atoms] — non-linear positions (NULL = linear)
- n_atoms
Direction-branch Thompson is now single-distribution over C51 (with adaptive
support), not joint C51+IQN. Single-distribution still provides the
principled posterior sample that fixes the UCB selector/target asymmetry —
the goal of Plan C is preserved, just sourced from the existing rollout-time
distribution instead of a non-existent IQN inference path.
New device-inline helpers: softmax_c51_inline, compute_atom_values_inline.
Plan + audit doc updated. Phase 0 standalone test kernel gained two new
entry points (direction_thompson_v2_test, argmax_eq_v2_test) matching the
amended production API; original Plan A entry points retained for tests
0.B-0.F. Tasks 3+4 (buffer wiring) unblocked — collector/evaluator already
have per_sample_support_buf and exp_b_logits in production.
T1.6 implementer correctly identified that the gate input dim is
ml_core::state_layout::STATE_DIM=128, not the literal 42 the spec/plan
incorrectly stated. The 42-dim figure was the bar-feature subset; the
actual state vector is 128-dim (42 features + portfolio + MTF + OFI
padded to 128 for cuBLAS alignment).
Updated spec §3 architecture diagram, §4.1 gate subnetwork description
+ parameter count (3,272 → 8,776), and plan header architecture line.
Implementation in commit 28c707f6a is correct; this commit just makes
the spec match the implementation.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Per feedback_no_htod_htoh_only_mapped_pinned.md (newly recorded): every
CPU<->GPU path in this redesign uses mapped pinned memory exclusively.
No cudaMemcpy HtoD, no Vec-to-Vec defensive copies, including in test
code. CPU is strictly read-only on the production surface.
Plan changes:
- New Task 2.0 promotes MappedF32Buffer / MappedI32Buffer from
distributional_q_tests.rs local definitions to a shared
crates/ml/src/cuda_pipeline/mapped_pinned.rs module so all kernel
test wrappers (Test 0.F, upcoming MoE tests) share one
implementation. Adds write_from_slice helper for direct host_ptr
write (no memcpy).
- Task 2.1 test wrapper rewritten to allocate mapped pinned buffers
+ write to host_ptr + read GPU-written output via host_ptr. No more
memcpy_stod / memcpy_dtov in test code.
Spec: new section 6.4 codifies the mapped-pinned-only constraint and
references the shared module + reference implementation.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
A first dispatch of monolithic Task 2c was refused with a second scope
assessment that surfaced three architectural facts beyond what 2a/2b
had captured:
(d) attn_layer_norm_bwd_dx is a SIMPLIFIED element-wise approximation
(d_x = d_out * gamma / std), not the full LN Jacobian. Reusing
it in the GRN backward would propagate the approximation into
trunk gradients silently. Task 2c.1 must write a new full
Jacobian: (1/D) * rstd * (D * d_out_g - sum_d(d_out_g) -
normed * sum_d(d_out_g * normed)).
(e) IQN target-trunk migration is not a clean swap. iqn_compute_
target_h_s2 reproduces the legacy LeakyReLU trunk independently
in CUDA. Deleting it requires a target_encoder_forward_only
extraction on the target path (Task 4-style refactor on the
target side). Sub-task in itself.
(f) Three relu_mask removal sites have three different save-buffer
lifetimes (online trunk per-step, IQN-aux per-step in different
trainer, target-sync per-target-update). Three plumbing tasks,
not one.
Decomposition:
- 2c.1: grn_kernel.cu with full LN Jacobian backward + GLU + ELU +
residual-add. Module compiles standalone (additive, dead code).
- 2c.2: gpu_grn.rs Rust wrapper with 5 save-for-backward state
buffers per block (not 3 as the prior plan suggested).
- 2c.3+4: ATOMIC commit — compute_param_sizes 86→95 + fingerprint
rewrite + xavier init for 13 new tensors + all 98 padded_byte_
offset migrations + 3 relu_mask sites with different save-buffer
plumbing each + iqn target trunk delete + target_encoder_forward_
only + mag_concat RMS-match adaptive ISV.
- 2c.5: docs flip.
No code changes. Plan-doc only.
After a first dispatch attempt of monolithic Task 2 (GRN ADOPT) was
correctly refused with a thorough scope assessment, this revision
records the decomposition and the structural facts that drove it:
1. layout_fingerprint_seed() only fingerprints ISV slots, not
param-tensor layout. The "fingerprint will auto-update" assumption
in the original Task 2 spec was wrong for param-tensor reshuffles.
2. compute_param_sizes has 86 tensors (docstring saying 42 is stale).
Inserting GRN's 9 sub-tensors shifts 82 downstream tensors and
98 padded_byte_offset call sites.
3. At least 12 kernels consume h_s2 expecting ReLU non-negativity.
GRN's LayerNorm output is zero-mean (signed). Per-consumer
verification needed before swap.
4. crates/ml-supervised::tft::gated_residual is incompatible as a
port: different formula (sigmoid gate, not GLU split) and
incompatible tensor abstraction. New CUDA kernels from scratch.
Decomposition:
- Task 2a (research, no code): audit h_s2 consumers for ReLU vs
LayerNorm semantics. Output: per-consumer table.
- Task 2b (small, checkpoint break): extend fingerprint seed to
include param-tensor names + sizes. Pearl-aligned: complete
fingerprint coverage rather than partial.
- Task 2c (large, checkpoint break): GRN kernels + 98 call-site
migration + h_s2 consumer shims (per 2a). Blocked on 2a+2b.
Recommended order updated to thread 2a → 2b → 2c. Tasks 1, 6, 3
remain independent of 2c and can land in parallel where useful.
Pearl rules section added documenting the safety constraints
applied throughout the plan.
No code changes. Plan-doc only.
Comprehensive revision to match landed Plan 1+2+3 codebase state. Pattern
mirrors the Plan 3 second revision: per-task "Reality reconciliation"
blocks at the top of each task body identifying what's stale vs. landed,
with concrete file paths in the actual cuda_pipeline tree.
Added:
- Task dependency graph with recommended execution order:
5 (light ISV) → 4 (refactor) → 2 (GRN ADOPT) → 1 (Full VSN) →
6 (aux heads) → 3 (multi-Q IQN) → 7 (audit) → 8 (Argo)
- Per-task implementation surface with concrete trunk hooks:
- Task 1: pre-h_s1 VSN gate in batched_forward.rs::forward_online_raw
- Task 2: GRN audit confirms ADOPT branch (GRN absent from DQN trunk;
only in ml-supervised TFT). Replaces h_s1/h_s2 Linear blocks.
- Task 3: re-scoped — IQN already runs num_quantiles=32 with random τ.
Task is CONSTRAINING to fixed τ ∈ {.05, .25, .5, .75, .95}.
- Task 4: pure Rust API split (no kernel changes, no checkpoint break)
around existing forward_online_raw structure
- Task 5: split into Mode A (light, pre-Task-1, 3 ISV slots) and
Mode B (full, post-Task-1, 7 ISV slots). Mode A recommended first.
- Task 6: aux head loss scaled by ISV[LEARNING_HEALTH] per pearl
- Checkpoint-break consolidation note: Tasks 2/1/6/3 each break checkpoint
compatibility; land in sequence with no Argo run between (one fingerprint
shift per commit; final Argo at Task 8 amortizes retraining cost)
- Task 8 absorbs Plan 3's deferred Argo Tier 1 gate (combined validation)
No code changes.
Plan 4 was drafted 2026-04-24 against an earlier Plan 3 design. After
Plan 3 landed (commits 44539d8f4..3cb083f18), the pre-plan gate
referenced slot names that don't exist in the landed implementation:
- PLAN_PARAMS_0_EMA_INDEX → became READINESS_EMA_INDEX (Task 4)
- STATE_KL_THRESHOLD_EMA_INDEX → eliminated; Task 7 uses kernel-internal
trailing-EMA-of-self pattern (no separate threshold slot)
- TEMPORAL_REWARD_{PERSIST,REGIME_SHIFT,CONSISTENCY}_EMA_INDEX → consolidated
into rc[5] → ISV[68] REWARD_BONUS_EMA_INDEX via Plan 3 Task 6a/6b/6c
Updated:
- Pre-plan slot-existence check matches landed slot names
- Validation-doc gate softened: Plan 3 Argo Tier 1 PASS becomes OPTIONAL;
local 5-epoch multi-fold smoke is the de-facto gate (user choice
to proceed without burning Argo cycles)
- Status table at top shows landed Plan 3 baseline (ISV_TOTAL_DIM=87,
PS_STRIDE=43, fingerprint at [85,86]) and per-task difficulty estimate
- Reality-reconciliation note documents the rename trail
No task body changes; subsequent commits will revise individual task
specs as they become next-up for execution.
Tasks 1, 2, 3, 5 landed on main between a59e7599c and a0abc3da3. The
remaining Task 4/6/7/8/9 bodies in the plan had accumulated drift:
- Task 4: allocated slot 49 (already PLAN_THRESHOLD_INDEX); EMA'd
plan_params[0] (kernel compares against readiness).
- Task 6: allocated 59/60/61 (now collides with Plan 2 Q-quantile
[50..58) and Task 1 reward EMAs [63..69)); required 6 new PS memo
fields and included tuned 0.5e-4f penalty.
- Task 7: allocated 58/63/64 (63/64 collide with REWARD_TRAIL_EMA /
REWARD_MICRO_EMA from Task 1); amplification formula had tuned
2.0× trigger, 0.02 decay, 1.0/2.0 endpoints.
- Task 8: referenced trainer helpers that don't exist
(sample_state_feature_pair, step_scripted, replay_insert_with_
priority_scale); tuned priority_scale=0.5.
- Task 9: used pre-pivot CPU-compute AdaptiveMonitor pattern with
tuned 0.9/0.1 EMA rates.
This revision:
- Adds per-task "Reality reconciliation" sub-header flagging the
stale premise being fixed.
- Marks landed tasks with ✅ LANDED <SHA> and records actual outcomes
(vs. the planned outcomes the original text described).
- Rewrites Task 4/6/7/8/9 bodies to use tail-append slot allocation
(indices recomputed from ISV_TOTAL_DIM at impl time), GPU kernel
producer + read-only AdaptiveMonitor consumer pattern, and
ISV-derived adaptive coefficients in place of tuned constants.
- Splits Task 6 into 6a/6b/6c with independent PS-slot additions
(MIN_PNL / REGIME_SHIFT_BAR / PRE_ENTRY_CONVICTION_EMAs).
- Enumerates concrete trainer-helper prereqs for Task 8.
- Updates Task 10 metric bands to match actual landed ISV slot names.
- Updates exit criteria summary to check off what landed.
No code changes.
Aligns Plan 3 with post-Plan-2 reality:
1. AdaptiveController → AdaptiveMonitor throughout (spec §4.C.6
2026-04-24 revision). Tasks 4 (plan-threshold) and 9 (cql_alpha
seed-coupled) become GPU kernel + read-only CPU monitor pairs
instead of CPU-compute controllers — uniform with Plan 1's
tau/epsilon/gamma/kelly_cap and Plan 2's per-branch γ pattern.
2. Stale ISV slot indices [49..65) replaced with tail-append language.
Current post-Plan-2 ISV_TOTAL_DIM = 63; new slots start at 63 and
grow. Fingerprint at 61-62 auto-re-tails.
3. Architecture paragraph updated to document replay-seed orchestration
(Task 8 dqn_replay_seed.rs) as constructor-time cold-path pre-training
— not a GPU-drives violation.
4. Prerequisites section clarified: Plan 2 state — ISV_TOTAL_DIM 63,
TLOB at SL_TLOB_START, layout fingerprint auto-recomputes.
No scope changes to individual Tasks 1-10. Just terminology + slot
indexing aligned with current main state.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Revises Plan 2 to match the post-Plan-1 reality:
1. AdaptiveController → AdaptiveMonitor throughout (read-only observers
per spec §4.C.6 2026-04-24 revision; GPU kernels compute, CPU reads).
Applies to Task 3 (per-branch gamma) and Task 5 (liquid_mod audit).
2. Task 2 (D.1 Mamba2 backward) scope narrowed from "implement" to
"validate existing". Plan 1 A.5 audit confirmed
mamba2_scan_projected_bwd kernel + mamba2_backward host call are
already fully wired. Task 2 now adds a finite-difference grad-check
smoke + non-zero grad-propagation smoke; escalates if either fails.
3. Task 3 (D.2 per-branch gamma) rewritten for GPU-drives compliance:
- Replace scalar GAMMA_EFF_INDEX=43 with 4 per-branch slots at
43-46 (DIR/MAG/ORD/URG).
- New per_branch_gamma_update_kernel.cu reads v-range + health,
writes 4 slots. Deletes the Plan 1 gamma_update_kernel.cu.
- 4 read-only monitors (or one consolidated PerBranchGammaMonitor).
- ISV slots 44-48 shift downstream; fingerprint re-tails at 50-51;
ISV_TOTAL_DIM grows 49 → 52.
- c51_loss_kernel and iql_value_kernel read per-branch γ from ISV.
- No CPU-side γ computation anywhere.
4. Header architecture block updated: new ISV slot count target,
GPU-drives principle explicit, current Plan-1 layout table inline
for reference.
5. Pre-plan verification updated: expects AdaptiveMonitor trait (not
AdaptiveController); checks for 6 Plan-1 monitor files.
6. Removed "schema version bump 1 → 2" language — layout fingerprint
auto-recomputes from seed bytes; no integer version space exists.
Task 4 (D.5 soft fold transitions), Task 5 content (D.7 liquid audit),
Task 6 (D.3+D.6+D.8 coordinated state-layout migration), Task 7
(validation run) structure preserved. Internal slot numbers in those
tasks will reconcile to the new layout when they land (no pre-emptive
edits since those indices depend on whether Task 1 quantile slots or
Task 3 per-branch gamma slots land first — re-compute at exec time).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
User raised that the CPU-controller pattern (where CPU computes adaptive
values from ISV and writes back to ISV) violates the architectural
principle: GPU kernels should compute adaptive decisions; CPU-side code
is pure observation.
Replaces the AdaptiveController trait with read-only AdaptiveMonitor:
- No update() — CPU doesn't compute adaptive values.
- No write_output() — CPU doesn't write adaptive values to ISV.
- read() returns the GPU-computed value from ISV.
- diagnose() emits HEALTH_DIAG snapshot.
- observe() + fire_rate() track how often the GPU-computed value changes.
Reclassifies the 9 adaptive mechanisms:
- 6 reactive get GPU kernel + CPU monitor: atoms, gamma, kelly_cap, tau
(Polyak EMA), epsilon, grad_balancer (last is already GPU-driven).
- 3 static get ISV constructor-write, no monitor: cql_alpha,
conviction_floor, plan_threshold.
New ISV slots for GPU-written adaptive outputs (EPSILON_EFF, TAU_EFF,
GAMMA_EFF, KELLY_CAP_EFF) and CPU-born inputs (EPOCH_IDX, TOTAL_EPOCHS),
plus slots for the 3 static configs.
Rationale:
- Unified adaptive machinery, no CPU-side special cases.
- Per-sample granularity available (Expected SARSA τ in c51_loss_kernel
is already the exemplar — reads ISV q_gap + health per sample).
- Zero CPU→GPU config transfer in any path.
- ISV is single source of truth for every adaptive value.
Plan 1 Tasks 8-17 restructured:
- Task 8 creates AdaptiveMonitor trait (not AdaptiveController).
- Tasks 9, 10, 11, 13, 14, 17: GPU kernel + monitor pairs.
- Tasks 12, 15, 16: static ISV writes only.
Follow-up commits will revert Batch A (d76849f31) and Batch B (4189da563)
which implemented the old CPU-compute pattern, then re-implement under
this design.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Populated docs/dqn-wire-up-audit.md with every pub module and CUDA
kernel in the DQN path. Each entry classified Wired / Partial / Orphan /
Ghost / OUT-of-DQN-scope with the action plan linking to the plan+task
that resolves any non-Wired status.
No Orphan left unclassified. Orphans fall into three buckets:
1. Scheduled for wiring by a later Plan (gpu_statistics → Plan 2 D.2;
tlob_loader → Plan 2 D.8).
2. OUT-of-DQN-scope because supervised consumers exist (PPO kernels,
xLSTM, KAN trainable adapter, flash_attention, benchmarks).
3. Genuinely unused — escalated to user review in the task output,
not deleted autonomously (streaming_dbn_loader, unified_data_loader,
training/orchestrator, training_pipeline, inference_validator,
model_loader_integration, paper_trading/mod.rs,
portfolio_transformer, regime_detection/mod.rs).
Summary: 109 total modules/kernels, 74 wired, 7 partial, 11 orphan,
0 ghost, 17 OUT-of-DQN-scope.
Plan 1 Task 6. Spec §4.A.5, Invariant 2.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
User raised a backward-compat concern: integer "schema version"
semantically implies a family of coexisting versions with upgrade
paths between them, inviting the forbidden pattern
(feedback_no_legacy_aliases.md, feedback_no_partial_refactor.md).
Replace with a compile-time structural fingerprint:
- ISV[0..2) stores a u64 FNV-1a hash of the slot layout (split across
two f32 lanes preserving raw bits).
- The hash is computed by a const fn over the slot list; any slot
change automatically updates the fingerprint. No human decides
"what version is this now".
- Checkpoint load is fail-fast only. Error message does NOT mention
migration as an option.
- Pre-commit hook rejects any `fn migrate_isv|upgrade_isv` to make
the no-migration rule structurally enforced (landed as part of
Plan 1 Task 5 hook extension).
- StateResetRegistry entry renamed ISV_SCHEMA_VERSION →
ISV_LAYOUT_FINGERPRINT (Task 2's landed code touched in Task 5's
commit per no-partial-refactor).
Updated:
- spec §4.A.2 (fingerprint design + rationale)
- spec §5 landing-order note (fingerprint auto-updates on layout change)
- Plan 1 architecture line
- Plan 1 Task 2 StateResetRegistry test + entry naming
- Plan 1 Task 5 — full rewrite of implementation steps
- Plan 1 exit criteria #6
- Plan 2 pre-plan verification (grep fingerprint constants, not version==0)
- Plan 2 Task 6D.1 (update fingerprint seed, not bump version)
- Plan 2 Task 6D exit criteria #7
- Plan 3 pre-plan verification
- docs/isv-slots.md ISV[0..2) row
No code changed. Plan 1 Task 5 is not yet implemented — this commit
realigns the spec + plans so the implementer subagent works from the
corrected design.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Self-review caught that the CqlAlphaSeedCoupledController read_signals
example used unimplemented!("plumbed by caller"), which violates the very
Invariant 9 the plan enforces. Replaced with a concrete implementation
that reads SEED_FRACTION_EMA_INDEX from the ISV bus (a Plan 1 reserved
controller-signal slot).
No other plan had placeholder violations. The remaining TBD/TODO/FIXME
grep hits across Plans 1-5 are either the pre-commit hook's rejection
patterns (correctly quoted) or enforcement scans that DETECT the markers
in audit docs.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Fifth and final plan decomposing the DQN v2 unified spec
(docs/superpowers/specs/2026-04-24-dqn-v2-unified-design.md §2, §4.A.3,
§4.A.4, §4.A.4.1). No new policy mechanisms — orchestration, automation,
regression detection, and the final sign-off run.
Covers spec sections:
- §4.A.3 Multi-seed × multi-fold Argo harness (N=5, K=6 defaults via
--multi-seed / --folds flags on argo-train.sh)
- §4.A.4 Regression detection hard-stop (2N consecutive error-band
metrics → self-SIGTERM with TerminationReason)
- §4.A.4.1 nsys profile harness with --profile flag and
compare-nsys-profiles.py for 20% per-epoch regression detection
- §2 Tier 1 + Tier 2 + Tier 3 final exit criteria via scripts/validation/
suite (tiered tier1/tier2/tier3 check scripts + wrapper)
- §2 DQN v2 rollout close-out (spec marked LANDED, retrospective,
plan-level validation docs archived)
Plan structure: 6 tasks — 4 infrastructure (harness, regression-detect,
nsys, validation-scripts) + final-run task + rollout-closeout task. The
final-run task is the sole exit-gate for the DQN v2 rollout: ALL THREE
tiers must PASS on the first final-validation run (per stop-on-anomaly:
retrying with different seeds hoping for a different outcome is anti-
pattern).
Preserves all 9 invariants. Every mechanism wired, no stubs, no TODO/FIXME.
Retrospective written from actual implementation experience after Plans
1-5 all land.
This is the terminal plan. Plans 1-5 together constitute the complete
DQN v2 rollout. After Plan 5's final-run exit passes, the spec is marked
LANDED and the rollout is declared complete.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Fourth of five sequential plans decomposing the DQN v2 unified spec
(docs/superpowers/specs/2026-04-24-dqn-v2-unified-design.md §4.E).
Covers the six Part E IN decisions:
- §4.E.1 TFT Variable Selection Network across 6 feature groups
(market/OFI/TLOB/MTF/portfolio/plan_isv) with vsn_feature_selection kernel
- §4.E.2 Gated Residual Network — CONDITIONAL branch (ADOPT vs CANONICALISE)
based on docs/ml-supervised-to-dqn-concept-audit.md row
- §4.E.3 Multi-quantile IQN heads (5/25/50/75/95) replacing single CVaR output
- §4.E.4 Encoder-Decoder separation — explicit StateEncoder + per-branch
ValueDecoder with D.3 horizon-decomposed V_short/V_long sub-heads
- §4.E.5 Attention-weight interpretability — 7 new ISV slots [65..72) for
per-group attention focus EMAs + Mamba2 retention proxy
- §4.E.6 Multi-task auxiliary heads — next-bar return MSE + 5-bar regime CE,
ISV-coupled aux-weight schedule (sharpe-reactive)
ISV_TOTAL_DIM seals at 72 with this plan's allocations. Part E audit doc
closes out all rows (zero TBD/evaluate remain per Invariant 9).
Plan structure: 8 tasks (6 feature tasks + audit close-out + validation).
TDD-disciplined steps. Task 2 documents the CONDITIONAL branch decision
pathway explicitly (ADOPT vs CANONICALISE Branch A/B structure).
Plan 4 exit gate: Tier 1 convergence RETAINED (no regression vs Plan 3
baseline) + aux heads produce measurable signal. Tier 2 + Tier 3 checked
by Plan 5.
Preserves all 9 invariants. Zero stubs. Zero TODO/FIXME. Every Part E item
either lands fully or is explicit OUT (xLSTM, KAN); no third path.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Second of five sequential plans decomposing the DQN v2 unified spec
(docs/superpowers/specs/2026-04-24-dqn-v2-unified-design.md).
Covers spec sections:
- §4.C.1 Quantile-based atom support (8 new ISV slots, q_quantile_reduce kernel)
- §4.D.1 Mamba2 backward pipeline completion (no atomicAdd — per-sample arrays + host reduce)
- §4.D.2 Per-branch gamma via AdaptiveController (4 new ISV slots)
- §4.D.5 Soft fold-boundary transitions (extends StateResetRegistry with SoftReset category)
- §4.D.7 Liquid Time-constant audit (trace fire rate, decide wire-or-delete)
- §4.D.3 + §4.D.6 + §4.D.8 coordinated state-layout migration (horizon-decomposed V +
plan_isv[6] + TLOB integration with atomic ISV schema version bump 1→2)
Plan structure: 7 tasks + pre-plan verification, all TDD-disciplined with
bite-sized steps (test → fail → implement → pass → commit). Task 6 is
explicitly atomic to preserve Invariant 5 (state-layout consistency).
Plan 2 prerequisites: Plan 1 landed (StateResetRegistry, AdaptiveController
trait, ISV schema version, audit docs). Plan 2 exit: all 9 invariants
preserved; convergence-scaffolding run passes with 16 new ISV slots
populated; ISV schema version == 2.
Preserves all 9 invariants. No stubs. No TODO/FIXME. Every new module
wired to production path in the same task it lands in.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
First of 5 implementation plans for the DQN v2 unified integrated
policy system spec (d13b53586, 336ee40b9). Plan 1 covers:
Task 1: Audit doc scaffolding + pre-commit hook (Invariant 7, 9)
Task 2: StateResetRegistry definition (A.1)
Task 3: Wire StateResetRegistry into fold-boundary reset (A.1)
Task 4: Named-dimension refactor — ps[], plan_isv[], plan_params[],
branch indices, dir/mag sub-indices (Invariant 8)
Task 5: ISV schema version at ISV[0], fail-fast on checkpoint
mismatch (A.2)
Task 6: Orphan audit populated (A.5)
Task 7: Hot-path purity audit populated; MIGRATE calls fixed (A.6)
Task 8: AdaptiveController trait + harness (C.6)
Tasks 9–17: Migrate each adaptive controller to the trait in spec-
specified order (atoms → gamma → Kelly → cql_alpha → tau
→ epsilon → conviction_floor → plan_threshold → balancer)
Task 18: Plan 1 validation run (smoke + 3-epoch L40S)
Plan 1 landing criteria: all 9 invariants preserved across every
commit, all smoke tests pass, audit docs fully populated. Plan 2
(temporal core) starts only after Plan 1 passes exit criteria.
Plans 2–5 cover Parts B, C (minus C.6 already in Plan 1), D, E, and
final validation. Planned as separate documents in docs/superpowers/
plans/2026-04-24-dqn-v2-plan-{2..5}-*.md.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
User directive: "we don't remove functionality at all, we fix what's
broken". Standing rule captured in
~/.claude/projects/-home-jgrusewski-Work-foxhunt/memory/feedback_no_functionality_removal.md.
Phase 2 plan reframed:
* Task 2.3 (R5 micro-reward): was DELETE; now FIX — either (a) set a
non-zero scale that delivers real signal, or (b) document why it's
intentionally disabled while preserving the code path.
* Task 2.4 (R6 loss-aversion): unchanged — the relocation to C51
Bellman target is consolidation, not removal. Invariant preserved.
* Task 2.6 (E4 entropy-reg): was DELETE-CANDIDATE; now TUNE — if the
signal doesn't reach magnitude, re-route it rather than remove.
* Task 2.7 (C4 grad-clip): was DELETE-CANDIDATE; now
KEEP-AND-DOCUMENT — gradient clipping is a safety feature; run the
ablation for diagnostic, not for removal.
* H9 delete-magnitude-branch fallback: rejected permanently. On
magnitude convergence failure the fallback is per-magnitude reward
shaping / per-bin advantage weighting / curriculum / state enrichment.
Task 2.5 wiring-bug sweep unchanged (correctness fixes, not removal).
This commit updates only the planning narrative. Task execution hasn't
started on 2.3/2.6/2.7 yet, so no code changes required here. When
those tasks run, they will implement the fix-paths, not the
delete-paths.
Task 2.0 instrumentation (commits d60e5375a / 980f3b07f / 41b0c559c)
revealed two silent bugs in Task 0.4's grad_ratio_mag_dir accessor:
Bug A — readback size mismatch: pinned buffer allocated at
total_params, but grad_buf length is total_params+cutlass_tile_pad,
so size check always failed → Err silently coerced to 0.0 by proxy.
Bug B — readback timing wipe: estimate_avg_q_value_with_early_stopping
in process_epoch_boundary replays the forward graph, which zeros
grad_buf. Any subsequent readback sees all zeros.
Both fixed in 41b0c559c; snapshot hoisted to top of process_epoch_boundary.
With those bugs fixed, the measured gradient ratio is NOT 0.0000 — it is
50–400× mag/dir across 60 epochs. Magnitude branch is over-fed, not
starved. Direction gradient is small but non-zero (~2e-2 to 7e0).
Direction policy is observably healthy (Short/Hold/Long/Flat 38/12/42/14%).
Track 1 triage's H4 CONFIRMED verdict was a measurement artefact
produced by Bugs A+B. H4 as originally defined is now REJECTED.
Plan changes:
- Add new "Task 2.0 findings" section after cross-cutting concerns,
documenting bug A, bug B, observed data, and revised strategy.
- Task 2.1 marked DEFERRED (not deleted — kept for reference).
- Task 2.2 promoted to PRIMARY fix.
- New fallback: H9 delete-magnitude-branch as replacement for Task 2.1
if Task 2.2 alone is insufficient.
- Task 2.0 inventory row updated with LANDED status and commit chain.
Net effect: Phase 2 simplifies. The hardest task (2.1 three-branch
architectural fix) is skipped; primary path is Task 2.2 (~25 LOC).
First Task 2.0 dispatch escalated BLOCKED: the four loss-component
backward kernels are captured inside the fused training graph, so
host-side snapshot-between-components isn't possible mid-graph without
a force-ungraphed diagnostic step (~210 LOC + cross-stream sync risk).
Revised approach (chosen after cost analysis):
cudaMemcpyAsync(device → pinned host) IS captureable in a CUDA graph.
Even better: DtoD into per-component scratch buffers, then an in-graph
reduction kernel computes per-component (mag_norm, dir_norm) and writes
8 floats to a pinned result slot. Only the 8-float result crosses
PCIe (at epoch boundary), keeping per-step PCIe traffic to zero.
Changes to the plan's Step 2 + Step 3 + Step 4:
- Step 2: added 4 device-side scratch buffers (one per component,
~10 MB each = 40 MB device) + 8-float pinned result slot + new
reduction kernel grad_decomp_kernel.cu spec'd out.
- Step 3: clarified that DtoD snapshot + backward + reduction kernel
are ALL captured in the graph; graph replays them every step;
no force-ungraphed dance needed.
- Step 4: added refresh_grad_component_norms() accessor that reads
the 8-float pinned slot at epoch boundary (zero-copy) and populates
the host-side cache.
Approach matches Task 0.4 pattern (commit bb42c9963) extended four-fold.
No atomicAdd (reduction uses shared-mem tree), no non-captured replay,
no cross-stream sync risk.
LOC estimate: ~90 (was ~210 for the rejected option).
Three polish items from the plan review:
1. Task 2.4 Step 5 — replaced vague "must not regress" with a numeric
tolerance-band table: multi_fold best_val_metric ±15% per fold,
Best Sharpe ≥ 20 floor, and hard F_Half/F_Full ≥ 0.05 + cf_flip ≥ 0.1
BLOCKERS. Anchors to the baseline metrics doc values captured at
policy-quality-baseline.
2. Task 2.6 — converted the ad-hoc text decision at Step 1 into the same
five-row decision table format Task 2.1 uses (DELETE / KEEP / KEEP-as-
safety-net / INCONCLUSIVE / DELETE-because-inseparable). Step 2
ablation got its own 4-row outcome table keyed to ent_mag delta,
multi-fold Sharpe regression, and NaN appearance.
3. Task 2.7 Step 1 — added "Repeat 3× with different seeds" clause and
explicit total wall-clock note: ~75 min (3 × 25 min) for the ablation
pass before the Step 2 decision. Aligns with the Cross-cutting concern
#6 about sample-noise rejection.
No task count change (still 11: 2.0–2.10). Net code-delta estimate
unchanged. Standing-rule compliance unchanged (no stubs / no atomics
/ no quickfixes / no hiding / no feature flags / no push-per-task).
User preference: everything lands on main, ~500 LOC is small enough
that feature-branch isolation isn't needed.
Spec changes:
* §2.3 outcome paths: drop "unmerged branch" vocabulary
* §3 architecture: main-branch timeline, no feat/, no wip/
* §4 Phase 0: commits on main, tag policy-quality-baseline at end
* §5 Phase 1: experiments are worktree edits reverted after, only
triage docs commit (to main)
* §6 Phase 2: commits land on main, author chooses granularity,
each leaves smokes green
* §7.3 outcome handling: iterate on main, baseline tag for rollback
* §7.4 closing: tag policy-quality-v1, update status
Plan changes:
* Task 0.1: verify-starting-state (not branch creation)
* Task 1.x: temp-edit-then-revert (not wip branches)
* Task 1.5: triage docs commit directly to main
* Phase 4: tag + close (not merge + cleanup)
* Rollback: one git reset --hard command