โ€บNavigation

iteration 10 ยท card 60 S1 oracle ยท batch-6 implementation ยท 2026-07-02

Card 60 started โ€” the time-arrow detector; two traps sprung before any Rust exists S1 DONE

← campaign board ยท previous: iter 09 (card 59 shipped, PR #569)

In plain language โ€” what happened this iteration

Feature 3 of 4 began: a detector for the arrow of time in price data. Play a video of truly random noise backwards and nobody can tell. Play real markets backwards and subtle things look wrong โ€” crashes are sharper than recoveries, quiet grinds differ from panicked unwinds. Card 60 measures one precise slice of that: turn the last 200 closes into a network (bars are nodes; two bars "see" each other if nothing between them closes as high โ€” the horizontal visibility graph this family is named after), then ask each bar: how tightly clustered are the neighbours you can see BEHIND you versus AHEAD of you? The feature is the average gap. Zero = time-symmetric; on real data it sits slightly positive (~+0.055) โ€” the past really does look different from the future.

An honest renaming ships with it. The research card called it "_kld" (Kullbackโ€“Leibler divergence), but a true KL divergence needs a density estimator with tuning knobs this project's parameterless rule forbids. What was actually evaluated โ€” and what ships โ€” is a signed mean gap. The column is named โ€ฆ_meangap, matching what the number IS.

The paranoid answer-key process earned its keep twice today, before a single line of engine code:

โ€ข Trap 1 โ€” the tie bug. The first draft used a textbook "monotonic stack" to build the graph fast. The reference library (the exact one the research evaluation used) was wired in as a mandatory cross-check on every window โ€” and instantly vetoed the draft: 3 phantom connections on the very first window. Root cause: when several bars close at exactly the same price (common โ€” prices live on a discrete grid), the stack version lets a bar "see" through an equal-height wall that should block it. The fix is a simpler scan that applies the visibility rule literally. Without the cross-check, those phantom edges would have poisoned every downstream number while looking completely plausible.

โ€ข Trap 2 โ€” perfection that floating point can't keep. Mathematically, reversing time flips this feature's sign exactly, so the generator asserts that on every window. It failed โ€” by one part in 10ยนโถ. Reversing the window changes the ORDER numbers are added in, and computer arithmetic rounds differently for different orders. The check now allows a hair's width (10โปยนยฒ) and documents why โ€” a lesson that transfers straight into how the Rust tests must be written.

The frozen answers: 19,602 windows across Bitcoin and Ethereum, every one passing four independent checks (fast scan โ‰ก reference library โ‰ก brute force on samples, plus the reversal flip).

Grounded this iteration

Worktree batch6-60-vg-clustering stacked on the card-59 branch (e8f92cce). Rename applied from the start: production column bar_vg_time_directed_clustering_meangap. scripts/gen_vgclust_oracle.py โ€” quadruple-leg fail-closed: running-max left-scan HVG (primary), ts2vg edge-set EXACT equality on all windows (the probe's own builder โ€” probe parity by construction), naive O(nยณ) pairwise on 21 windows/symbol, 1e-12 time-reversal anchor on every window; I3 drift guard (drift=0). Oracle CSVs: 9,801 rows/symbol, values โˆˆ [โˆ’0.096, 0.146].

featurecard 60
stage1/16 oracle
stack78 โœ… 59 โœ… โ†’ 60 โ†’ 50
legs4 ยท all green
traps caught2 (pre-Rust)
mean asym BTC+0.0551
mean asym ETH+0.0589

Artifacts + integrity pins

Artifactsha256 (prefix)Note
BTCUSDT-vgclust-oracle-sample-10k.csve70f3c69d2e44c33โ€ฆ9,801 rows ยท [โˆ’0.096, 0.146]
ETHUSDT-vgclust-oracle-sample-10k.csve3e1ee7792db95d4โ€ฆ9,801 rows ยท [โˆ’0.069, 0.124]
input fixtures (reused)08725310โ€ฆ / 09060aecโ€ฆcommitted close-only fixtures

Two pitfalls recorded for the Rust kernel (S3): (P1) a monotonic-stack HVG admits spurious edges on equal-height runs (the [2,1,1,2] class) โ€” the kernel MUST use the tie-safe left-scan, and the in-Rust brute-force leg must include tie-dense inputs; (P2) the time-reversal flip is exact only in real arithmetic โ€” the Rust reversal property test needs ~1e-12 tolerance, NOT to_bits equality.

Next fire picks up here

S2/S3 kernel: compute_bar_vg_time_directed_clustering_meangap(closes) โ€” tie-safe left-scan HVG adjacency (THE shared VG helper, landing where it's used) + the probe-mirror clustering; in-Rust naive O(nยณ) leg + tie battery (incl. [2,1,1,2], [3,3,1,3]) + ~1e-12 reversal property + anchors; expected oracle residual ~1e-15 (float means). Standard walk 13โ†’14 (selector churn + BarClose13โ†’14). Then S4 surfaces (tripwires 87โ†’88 / 92โ†’93 / compat 67โ†’68 & 14โ†’15), S10, S12 ADR, S13 vgclust:*, S14, S15, S16 PR (base = feat/bar-orth/batch6-59-hvg-horizon). Card 50 after: still GATED (window_mean_ratified=false) โ€” if unchanged when 60 completes, STOP with the morning-handoff. Worktree: ~/eon/odb-worktrees/batch6-60-vg-clustering, ledger is truth.