W6 RUN — the vector-sum dipole model is VOID by its own control, and the stranded stratum is empty for a structural reason - #940
Conversation
…un (dipole = neighbor + bow-wave deconvolution)
Per weather-w-probes-v1.md SS3. Tests the report SS10.2 vector-sum model:
D = c_geo*P_geo (far-field neighbor high) + c_bow*P_bow (relative-motion
bow wave) on CT-F14/F16's 19 stored storms, read from comet_tail_f16.json.
Ported spine() VERBATIM from l4_rail_probe.py (the brief names
comet_tail_f16.py; verified against the tree, the actual constrained
2-parameter dipole fit lives in l4_rail_probe.py -- ported from the real
location instead of the brief's file name). Every other primitive
(wrap_deg/err_deg/geom_ll/disk_mean_uv/circular) is verbatim from
comet_tail_f16.py / the SS0 statistics standard, named at each site.
Motion bearing recovered by algebra (no tracking): exact inversion of
CT-F16's stored err_deg = wrap(lp-(mth+pi/2)). v_rel = v_storm - v_env850
carries the bow term; the annulus neighbor search (600-2500km, lat 20-80N)
finds the strongest positive zonal-anomaly cell.
Bars pre-registered, controls FIRST:
B0 CONTROLS -- joint fit with per-storm PERMUTED P_bow ((i+7)%19) and
P_bow rotated +90deg; either control's joint R2 must stay <=
single-geo R2 + 0.03, else VOID.
B1 IDENTIFIABILITY -- joint R2_vec >= best single R2_vec + 0.10.
B2 SIGN -- c_bow > 0 AND c_geo > 0 (both in physically-predicted
directions).
B3 (descriptive) -- resultant (R_bar, mu, p) of residual bearings,
overall AND stratified by |v_storm| < / >= 8 m/s -- the stranded
stratum: if v_rel genuinely carries the bow term for weak-motion
storms, the stranded residuals should not be worse than the moving
ones.
B4 (descriptive) -- per-storm bearing(D) vs bearing(Dhat) table.
Checkpointed per storm (comet_tail_w6.partial.jsonl, resume-skip on t0),
tag-file heartbeat every 5 storms, per SS0's stranded-rescue protocol.
Offline smoke-tested before commit (no network): r2_vec/fit_joint recover
exact synthetic coefficients (c_geo=2, c_bow=3 -> R2=1.0 to 1e-9);
bearing_deg/wrap_deg/the +90deg rotation matrix/circular() all verified
against hand-computed expectations. Fixed one real bug found by the
smoke pass: an operator-precedence error in the annulus lat-mask (`*`
binds tighter than `&` in Python, silently producing a wrong boolean
combination) -- rewritten to compose the lat mask separately before
combining with `&`.
Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CcpLeEC3XK8Eye53GKBVvi
… storm, not just derived bearings The first W6 run completed (comet_tail_w6.json committed separately below) with a near-zero c_bow and wrong-signed c_geo. Manually audited one storm by re-fetching it (t0=54358): D=[-0.561,-0.469] Pa, P_bow=[-43.6,-19.3] Pa (dynamic pressure of an 8.9 m/s v_rel -- a physically sane magnitude), P_geo=[-0.340,0.237] Pa/km (A_H=906 Pa at d_H=2187 km -- also physically sane). No implementation bug found -- but the per-storm checkpoint row already carried these fields and the final JSON's per_storm table dropped them, so the finding above could only be verified by a live re-fetch, not from the committed artifact. Same lesson codex kept catching this session for other probes, self-caught here before it needed a reviewer to find it. Fix: per_storm now carries D/P_geo/P_bow/v_rel_ms/A_H_Pa/d_H_km/ theta_H_rad for every storm, not only the derived bearings. Re-running (the partial checkpoint from the first run self-deleted on success, so this refetches all 19 storms -- ~3 min per the brief's own cost estimate, not a burden). Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CcpLeEC3XK8Eye53GKBVvi
…ntrol, plus a reusable sample-composition lesson The dipole vector-sum model (report SS10.2: D = c_geo*P_geo + c_bow*P_bow) was fit on CT-F14/F16's 19 stored storms per weather-w-probes-v1.md SS3's pre-registered bars. Result: B0 VOIDS. Single-geo R2=-0.104 (worse than predicting the mean); both anti-vacuity controls (permuted P_bow, P_bow rotated +90deg) score -0.071/-0.062, both clearing the <=single-geo+0.03 ceiling of -0.074 -- two deliberately WRONG references score as well as or better than the real geo predictor. c_geo carries the wrong sign throughout (-0.41, predicted positive); c_bow ~= 0.0006 -- no measurable weight from the bow predictor at all. B1/B2 correctly report VOID per the pre-registered rule, not their own numbers. Checked for an implementation bug before calling this a clean negative (measurement-skeptic discipline): none found. One storm independently re-fetched and hand-audited; extended to the full sample by fixing a self-caught audit gap (the first run shipped only derived bearings, not the raw predictors -- fixed in 3436918, re-run bit-identical, confirming determinism and that the fix changed nothing but auditability). Every committed value is physically sane: A_H>0 always, d_H inside the 600-2500km annulus always, v_rel 2.8-27.9 m/s. |P_bow| averages 147x |D|, |P_geo| averages 0.61x -- a real Pa-vs-Pa/km scale disparity, but lstsq is scale-invariant per column, so the near-zero c_bow reflects a genuine absence of correlation, not a units artifact. The reusable lesson, filed as E-THE-DISPLACEMENT-FILTER-ATE-THE-STRANDED- STRATUM-1: B3's stranded stratum (|v_storm|<8 m/s) came back n=0 -- every storm has |v_storm|>=12.54 m/s. This is arithmetic, not physics: CT-F14's own qualifying filter (displacement_km>=250 over 6h) implies |v_storm|>=250km/6h=11.574 m/s for ANY admitted storm. A filter selected for one purpose (fast, cleanly-displaced storms) silently excludes exactly the storms a LATER, differently-motivated test needs -- knowable from the filter's own arithmetic before a single fetch, not checked until the stratum came back empty. Consequence: the vector-sum model AS SPECIFIED is disconfirmed on this sample, void by its own control. CT-F17's gate (W6 + independent audit) is now moot for THIS form of the model -- a fresh-sample verdict on a model that fails identifiability on the stored sample is not the next useful step. A revised model form needs its own W6-shaped test; a genuine stranded-rescue test needs a sample built without the displacement floor. Board homes: weather-w-probes-v1.md SS3 RUN section; EPIPHANIES new entry; PR_ARC_INVENTORY results entry (RUN complete, awaiting a PR); STATUS_BOARD D-W6 updated. Note: an earlier commit (afec59c) incorrectly claimed the first run's JSON was "committed separately below" -- it was never committed (superseded by this audited re-run before that could happen), corrected here rather than left standing. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CcpLeEC3XK8Eye53GKBVvi
Bugbot couldn't run - usage limit reachedBugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit. A user or team admin can review and increase usage limits in the Cursor dashboard. (requestId: serverGenReqId_d38ebd24-b3cb-4628-b7b5-ae03481841db) |
📝 WalkthroughWalkthroughThe PR adds and runs the W6 dipole-deconvolution weather probe. It stores ERA5-derived predictors and per-storm diagnostics. The run fails B0 anti-vacuity controls, marks B1/B2 as VOID, finds no stranded storms, and records the model as disconfirmed. ChangesW6 Weather Probe
Estimated code review effort: 4 (Complex) | ~45 minutes Sequence Diagram(s)sequenceDiagram
participant Probe as W6 probe
participant ERA5 as ERA5 Zarr
participant Models as model evaluation
participant Records as result records
Probe->>ERA5: fetch pressure and wind fields
ERA5-->>Probe: return weather data
Probe->>Models: provide measured bearings and predictors
Models-->>Probe: return coefficients and control metrics
Probe->>Records: write JSON, log, and board findings
Possibly related PRs
Suggested reviewers: Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 9affdc5828
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| coef, *_ = np.linalg.lstsq(X, resid, rcond=None) | ||
| return coef |
There was a problem hiding this comment.
Flip the fitted gradient before testing low-pole signs
The least-squares coefficient points toward increasing pressure because resid = a*x + b*y, while both P_geo and P_bow are constructed as low-pole directions (away from the high and behind relative motion). Thus D should be -coef before fitting, or B2's expected signs must be reversed; as written, the published claim that the negative c_geo has the physically wrong sign is backwards (the committed F16 low-pole bearings are approximately bearing(coef) + 180°).
Useful? React with 👍 / 👎.
| UNITS. c_geo and c_bow are dimensionless least-squares coefficients that | ||
| absorb P_geo's [Pa/km] and P_bow's [Pa] units into themselves -- their SIGN | ||
| is what B2 tests, not their magnitude, and no unit conversion is performed | ||
| or needed. |
There was a problem hiding this comment.
Preserve the bow coefficient's inverse-length unit
Because spine() regresses pressure in Pa on coordinates in km, D has units Pa/km; consequently c_geo is dimensionless but c_bow, which multiplies a Pa-valued dynamic pressure, has units km⁻¹. Calling both coefficients dimensionless enables the subsequent invalid interpretation of 0.0006 as intrinsically near zero and the dimensionally meaningless raw |P_bow|/|D| ratio; assess the fitted bow contribution or normalized effect instead.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Actionable comments posted: 4
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.claude/board/EPIPHANIES.md:
- Around line 15-16: Update the discussion of c_bow in EPIPHANIES.md to avoid
inferring absent correlation from its raw magnitude. Replace the claim that
c_bow ≈ 0.0006 indicates no measurable weight with a scale-aware measure, such
as a standardized coefficient, |c_bow·P_bow|, or an incremental fit statistic,
while preserving the separate c_geo sign observation.
In `@probes/weather-p1/comet_tail_w6.py`:
- Around line 230-234: Move the load_completed() call before the tag_path START
write so the checkpoint is successfully read before any run metadata is
appended. Preserve the existing done value and subsequent partial/tag file
handling.
- Around line 243-278: Wrap the per-storm processing block containing fetch(),
spine(), and predictor calls in try/except Exception. On any exception, append
an ERROR t0=<value> record to tf, flush the file, then re-raise the original
exception; preserve existing NO-VERDICT handling for valid predictor results.
- Around line 17-20: Correct the coefficient documentation and related
conclusions: in probes/weather-p1/comet_tail_w6.py lines 17-20, state that c_geo
is dimensionless while c_bow has units km⁻¹; in
.claude/plans/weather-w-probes-v1.md line 564, correct D to Pa/km and remove any
inference based solely on c_bow’s magnitude; in
.claude/board/PR_ARC_INVENTORY.md lines 21-24, remove the same magnitude-based
inference and rely on failed R² and B0 controls for the conclusion.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 332a2aa4-beaf-49b1-be5c-2d113b593ef6
📒 Files selected for processing (7)
.claude/board/EPIPHANIES.md.claude/board/PR_ARC_INVENTORY.md.claude/board/STATUS_BOARD.md.claude/plans/weather-w-probes-v1.mdprobes/weather-p1/comet_tail_w6.jsonprobes/weather-p1/comet_tail_w6.pyprobes/weather-p1/exec-runs/comet_tail_w6.txt
…nvention, unit-consistent contribution metric, error handling, checkpoint ordering All four real, verified before fixing: 1. (P2, both bots) SIGN CONVENTION. spine()'s raw coef is the gradient of INCREASING residual (toward the storm's HIGH side), while P_geo/P_bow both point toward the LOW side -- exactly the convention low_pole_bearing() makes explicit via its "(ph + pi) % (2*pi)" flip (comet_tail_f16.py:160). D = -spine(...) now, with the convention documented at both the module docstring and the call site. Verified offline before committing: negating the fit target flips c_geo/c_bow's signs EXACTLY and leaves R2 (hence every B0/B1 verdict) and the B3 residual-bearing resultant COMPLETELY UNCHANGED (both bearing(D) and bearing(Dhat) shift by 180 deg, canceling in their difference) -- so this fix changes the reported COEFFICIENT SIGNS and any prose asserting them, but not a single pass/fail verdict already on record. 2. (P2, both bots) UNITS. spine()/P_geo are Pa/km, P_bow is Pa -- so c_geo is dimensionless but c_bow carries km^-1, and OLS coefficients rescale inversely under column rescaling while leaving R2/fitted-values unchanged. The prior "c_bow~=0, no measurable weight" claim inferred absence of correlation from a raw coefficient magnitude that was never comparable across differently-unitted columns in the first place. Fixed with a dimensionally valid metric: |c_bow*P_bow| vs |D|, both now consistently Pa/km (the fitted CONTRIBUTION, not the raw coefficient), committed in the output JSON alongside a units_note on B2_sign. 3. (Major, CodeRabbit) No per-storm error handling -- a dead fetch/step would raise uncaught, violating SS0's "record it in the tag-file and stop" rule. Wrapped the per-storm block in try/except, appending ERROR t0=... before re-raising. 4. (Minor, CodeRabbit) load_completed() ran AFTER the tag file's START line was already written -- if the checkpoint read itself failed, the tag file would misleadingly show a successfully-started run. Reordered: load_completed() first, START written with the resumed count. Re-running with all four fixes -- R2/B0/B1/B3 verdicts are algebraically guaranteed unchanged (verified above), only B2's reported coefficient values and the new contribution metric are expected to differ from the prior commit's numbers. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CcpLeEC3XK8Eye53GKBVvi
…rative everywhere it appeared
Rerun confirms the algebra verified before committing the fix: R2 and every
B0/B1 verdict are bit-identical to the pre-fix run (r2_single_geo=
-0.10381057418374096, r2_permuted=-0.07139097664756067, r2_rotated90=
-0.062400718434540536, joint R2=-0.08608330921952212, B3 R_bar=
0.15311408050017475 -- all unchanged). Only the coefficient signs and the
new fitted-contribution metric differ, exactly as predicted:
c_geo: -0.407 (claimed wrong sign) -> +0.407 (CORRECT, matches the
physically predicted positive)
c_bow: +0.0006 (claimed near-zero) -> -0.0006 km^-1 (wrong sign, small)
fitted contribution (Pa/km, the unit-consistent metric): mean |D|=0.745,
geo contribution 25% of |D|, bow contribution 9% of |D| -- modest, not
"no weight"
Corrected the same two errors everywhere they had propagated:
weather-w-probes-v1.md's RUN table + prose (dated correction block, prior
text kept for the record), EPIPHANIES.md's E-THE-DISPLACEMENT-FILTER-ATE-
THE-STRANDED-STRATUM-1 entry (revised in place -- still unmerged, not yet
protected by the append-only rule), PR_ARC_INVENTORY.md's pending entry
(same). STATUS_BOARD.md checked and needed no change -- it never stated a
coefficient sign.
The headline finding is completely unchanged by either correction: B0 VOID,
the vector-sum model as specified is disconfirmed on this sample, the
stranded stratum is empty by CT-F14's own filter arithmetic. What changed
is only the supporting narrative around WHY c_geo/c_bow look the way they
do -- exactly the kind of correction this session's own falsifiability
discipline exists to catch before merge, caught here by codex + CodeRabbit
rather than missed.
Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CcpLeEC3XK8Eye53GKBVvi
#940 was substantial (real probe code + real disconfirming results + a same-PR sign/units correction round caught by codex+CodeRabbit), so it gets its own MERGED entry per the mandatory board-hygiene rule -- not covered by the termination clause (that's for hygiene-only PRs). The entry records only what changed AFTER the pre-existing RUN-results entry was drafted (unmerged-PR content composed in place per the PR-878 allowance, since it predated this PR's existence): the sign-convention fix (D = -spine(...), verified R2/B0/B1/B3-invariant offline before committing, confirmed bit-identical on the actual re-run), the units fix (c_bow is km^-1, not dimensionless -- replaced with the dimensionally valid |c_bow*P_bow| vs |D| contribution metric), and two CodeRabbit robustness fixes (per-storm try/except with an ERROR tag-file record; load_completed() before the START line). The headline finding (B0 VOID, model disconfirmed, stranded stratum structurally empty) is unchanged by either correction -- stated explicitly rather than left implicit. LATEST_STATE shipped-table row added. STATUS_BOARD's D-W6 row was already checked earlier and confirmed to state no coefficient sign -- verified again here, no change needed. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CcpLeEC3XK8Eye53GKBVvi
…arvest-rfii13 Board hygiene for merged #940 — arc entry + shipped row
…lifier on the 25%/9% contribution figures The source RUN entry and the committed JSON's fitted_contribution_Pa_per_km keys report MEANS over the 19 storms (mean |c_geo*P_geo| / mean |c_bow*P_bow| vs mean |D|); the #940 summary rows dropped the qualifier. PR_ARC's merged entry gets an appended dated correction line (append-only rule -- the entry merged via #941 before the finding landed, not edited in place); LATEST_STATE's living shipped-table row is fixed in place (precedent: the N-vs-n_qualifying fix on #939). Fourth instance of the same summarization defect class this week: a qualifier or operand pairing true in the source, lost in the summary. The figure itself was never wrong -- its aggregation scope went unstated. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CcpLeEC3XK8Eye53GKBVvi
The last exploratory W-probe runs, and it's a real, honest negative
comet_tail_w6.pytested the report §10.2 vector-sum model (D = c_geo·P_geo + c_bow·P_bow) on CT-F14/F16's 19 stored storms, perweather-w-probes-v1.md§3's pre-registered bars, controls first.B0 VOIDS — the model has nothing to identify
Two deliberately-wrong references score as well as or better than the real geo predictor alone. B1/B2 correctly report VOID per the pre-registered rule — not their own numbers.
⚠ Corrected during review (codex + CodeRabbit P2/Major, both real, both fixed before merge)
The first version of this PR asserted
c_geohad the physically wrong sign andc_bow ≈ 0("no measurable weight"). Both claims were backward:spine()'s raw coefficient points toward the storm's HIGH side (the gradient of increasing residual pressure), whileP_geo/P_boware both constructed pointing toward the LOW side — exactly the conventionlow_pole_bearing()makes explicit via its own(ph + π) % (2π)flip.Dis now−spine(...). Corrected:c_geo = +0.407— the physically predicted positive sign, correct.c_bow = −0.0006km⁻¹ — predicted positive, wrong sign, but small. Verified algebraically and numerically (bit-identical rerun) that this flip changes nothing about R² or the B0/B1 VOID verdicts — OLS is odd-symmetric in the fit target.D/P_geoare [Pa/km];P_bowis [Pa] — soc_geois dimensionless butc_bowcarries km⁻¹, and OLS coefficients rescale inversely under column rescaling while R²/fitted-values stay fixed. Raw|c_bow|(and the original|P_bow|/|D|≈ 147× comparison) was never valid evidence of "no weight." The dimensionally valid measure is the fitted contribution|c_bow·P_bow|vs|D|, both in Pa/km: mean|D|= 0.745, geo contribution = 25 % of|D|, bow contribution = 9 % of|D|— modest, not absent, and consistent throughout with R² < 0.Also fixed: no per-storm error handling (a dead fetch would raise uncaught, violating §0's "record it, then stop" rule — wrapped in try/except now) and the checkpoint being read after the tag file's START line was written.
None of this changes the headline finding. B0/B1's VOID verdicts, every R² value, and B3's residual-bearing resultant are bit-identical before and after — only the coefficient-sign narrative around B2 was wrong and is now corrected everywhere it appeared (this PR body, the plan's RUN section, the EPIPHANIES entry, the arc entry).
The reusable lesson —
E-THE-DISPLACEMENT-FILTER-ATE-THE-STRANDED-STRATUM-1B3's stranded stratum (
|v_storm| < 8 m/s) came back n=0 — every storm has|v_storm| ≥ 12.54 m/s. Arithmetic, not physics: CT-F14's own qualifying filter (displacement_km ≥ 250over 6h) implies|v_storm| ≥ 250 km/6h = 11.574 m/sfor any admitted storm. A filter selected for one purpose (fast, cleanly-displaced storms) silently excludes exactly the storms a later, differently-motivated test needs — knowable from the filter's own arithmetic before a single fetch. Filed as a reusable pre-flight check for any future probe reusing an existing filtered sample.Consequence for the report and CT-F17
The vector-sum model as specified is disconfirmed on this sample — void by its own control, not merely "unproven." CT-F17's gate (W6 + independent adversarial audit) is now moot for this form of the model. A revised model form needs its own W6-shaped mechanistic test first; a genuine stranded-rescue test needs a sample built without the displacement floor.
Board homes
weather-w-probes-v1.md§3 — full RUN section + dated correction blockEPIPHANIES.md— entry (revised in place; still unmerged when the fix landed)PR_ARC_INVENTORY.md— results entry (revised in place, same reason)STATUS_BOARD.md— D-W6 moved from Queued to the RUN verdict (checked — it never asserted a coefficient sign, needed no correction)Probe + docs — zero product code.
🤖 Generated with Claude Code
https://claude.ai/code/session_01CcpLeEC3XK8Eye53GKBVvi