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].
Artifacts + integrity pins
| Artifact | sha256 (prefix) | Note |
|---|---|---|
BTCUSDT-vgclust-oracle-sample-10k.csv | e70f3c69d2e44c33โฆ | 9,801 rows ยท [โ0.096, 0.146] |
ETHUSDT-vgclust-oracle-sample-10k.csv | e3e1ee7792db95d4โฆ | 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.