Skip to content

test(temporal_spine): G2 top-rank invariance evidence for the #1064 flip gate - #1074

Merged
github-actions[bot] merged 2 commits into
mainfrom
test/issue-1064-g2-rank-invariance
Jul 4, 2026
Merged

test(temporal_spine): G2 top-rank invariance evidence for the #1064 flip gate#1074
github-actions[bot] merged 2 commits into
mainfrom
test/issue-1064-g2-rank-invariance

Conversation

@robotrocketscience

@robotrocketscience robotrocketscience commented Jul 4, 2026

Copy link
Copy Markdown
Owner

What

Part of #1064 (temporal-spine flip gate). Produces the missing G2
top-rank invariance
evidence and records the result. The lane + writer
already landed default-off in #1069; this is flip-gate evidence tooling, not
a product change — both flags stay default-off and the issue stays open.

The G2 question

G2 requires, at the production hook budget (1500 tokens), both a coverage
delta ≥ +3pp and no top-rank regression. The coverage half was already
measured; the "no top-rank regression" half had no measurement. This adds it.

The spine lane appends its hits after the [locked, l25, l1, hrr] core
and before BFS (retrieval.py § temporal-spine lane), so the claim under
test is: turning the lane on must never displace or reorder a core belief — it
may only insert its own hits below the core (and, under budget, evict
BFS-tail items, which is by-design since BFS is the lowest-priority lane).

Change

  • benchmarks/temporal_spine_ablation.py gains --rank-invariance: a paired
    lane-off/lane-on retrieval per question on the same store. It reads
    last_lane_telemetry() to locate the core boundary
    (locked + l25 + l1 + hrr_expand) exactly, then asserts the core prefix
    is identical between arms and that no dropped baseline belief sits inside the
    core. RankInvarianceAccumulator carries the aggregates + a passed() gate.
  • tests/test_temporal_spine_ablation.py (8 tests): pins the accumulator math
    — head invariance, core displacement, core reorder, BFS-tail eviction,
    core-length mismatch, empty-guard, LCP tracking — on hand-built id lists, no
    dataset needed.
  • docs/design/feature-temporal-spine.md: records the result and splits G2
    into the completed bench half and the still-open shadow-eval half.

Result — LoCoMo10, --budget 1500 --l1-limit 50 --rank-invariance

Coverage (production trim):

arm coverage Δ vs baseline
baseline 0.3457
+spine 0.5402 +19.45pp
shuffled-control 0.3504 +0.47pp

The gain survives the production trim and in fact exceeds the wide-budget
dev figure; the seeded shuffled control recovers almost nothing, so the value
is the chronology, not the added density.

Top-rank invariance (1,986 questions):

What this does not close

  • G2 shadow-eval half — aggregate-only run on a real hook-ingested
    backfilled store (open question 1: chain-length distribution under
    production session_id semantics).
  • G3 latency — p50/p95 at ≥10k beliefs; needs a quiet box (the p95 gate
    false-negatives under concurrent load).
  • The default-ON flip itself is the operator's call once G2-shadow + G3
    clear.

Reproduce

uv run python -m benchmarks.temporal_spine_ablation \
  --data /path/to/locomo10.json --out /tmp/g2.json \
  --budget 1500 --l1-limit 50 --rank-invariance

Summary by CodeRabbit

  • New Features

    • Added an optional rank-invariance check to the temporal-spine benchmark, with summary metrics shown in the console and included in the JSON report.
    • Improved benchmark output to make core-prefix stability easier to inspect.
  • Documentation

    • Updated the temporal-spine design notes with current evaluation status and results.
  • Tests

    • Added coverage for ranking stability, prefix behavior, and regression cases to help prevent changes that alter the expected result order.

@robotrocketscience robotrocketscience added the author-Setr PR coordination mutex label Jul 4, 2026

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry @robotrocketscience, you have reached your weekly rate limit of 500000 diff characters.

Please try again later or upgrade to continue using Sourcery

@coderabbitai

coderabbitai Bot commented Jul 4, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

This PR adds an optional G2 top-rank invariance evaluation mode to the temporal spine ablation benchmark via a new --rank-invariance CLI flag, introducing a RankInvarianceAccumulator and paired retrieval logic, refactors imports to be lazy, updates the design doc's flip-gate status, and adds corresponding unit tests.

Changes

G2 Rank Invariance Benchmark

Layer / File(s) Summary
Lazy import refactor
benchmarks/temporal_spine_ablation.py
Moves LoCoMoConversation behind TYPE_CHECKING and relocates locomo_adapter imports into ingest_with_evidence_map() and main().
RankInvarianceAccumulator and core-boundary logic
benchmarks/temporal_spine_ablation.py
Adds _lcp_len, RankInvarianceAccumulator class, and run_rank_invariance_on_store for paired lane-off/on retrieval and telemetry-based core boundary tracking.
CLI flag and report/print wiring
benchmarks/temporal_spine_ablation.py
Adds --rank-invariance flag, wires accumulator into the per-conversation loop, and extends JSON report and console output with invariance metrics.
Accumulator and LCP tests
tests/test_temporal_spine_ablation.py
New pytest module verifying _lcp_len and RankInvarianceAccumulator behavior across invariant, displacement, reorder, tail-eviction, mismatch, and empty/multi-question scenarios.
G2 gate documentation update
docs/design/feature-temporal-spine.md
Marks the bench-pool half of the G2 flip gate DONE with measured results, leaves the shadow-eval half OPEN.

Estimated code review effort: 2 (Simple) | ~15 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Main
  participant MemoryStore
  participant RankInvarianceAccumulator

  Main->>MemoryStore: retrieve (lane-off, use_temporal_spine=False)
  MemoryStore-->>Main: baseline_ids, core_len_base
  Main->>MemoryStore: retrieve (lane-on, use_temporal_spine=True)
  MemoryStore-->>Main: spine_ids, core_len_spine
  Main->>MemoryStore: last_lane_telemetry()
  MemoryStore-->>Main: core boundary telemetry
  Main->>RankInvarianceAccumulator: add(baseline_ids, spine_ids, core_len_base, core_len_spine, n_spine_added)
  RankInvarianceAccumulator-->>Main: updated invariance metrics
  Main->>Main: print/report passed()
Loading

Possibly related issues

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title is concise and clearly names the G2 top-rank invariance evidence added by this PR.
Description check ✅ Passed The description is detailed and covers the goal, implementation, results, and reproduction steps; it only omits some template sections.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch test/issue-1064-g2-rank-invariance

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions

github-actions Bot commented Jul 4, 2026

Copy link
Copy Markdown

PR-size soft cap

This PR is over the advisory size threshold:

  • 346 changed lines (limit: 200)
  • 3 changed files (limit: 3)

Bigger PRs collide with more open work, which under the parallel-session workflow tends to produce repeated attn:merge-conflict cycles (see #602). When practical, split into smaller PRs that each touch a focused surface.

This is advisory only — nothing is blocked. If the size is intentional (large refactor, module removal, generated code), apply the size:override label and this comment will be removed on the next push.

@robotrocketscience

Copy link
Copy Markdown
Owner Author

[claim:review:Gylf:2026-07-04T07:59:54Z]

@robotrocketscience

Copy link
Copy Markdown
Owner Author

Review: APPROVE

Test + docs evidence for the #1064 G2 flip gate. Verified end-to-end:

Refactor (lazy imports). locomo_adapter (→ nltk) is now imported inside the functions that need it, so the pure logic is importable under the ordinary pytest matrix. Confirmed from __future__ import annotations is present and the TYPE_CHECKING-only LoCoMoConversation is used solely as a string annotation — no eager-evaluation NameError. Module imports clean; no runtime use of a deferred symbol leaks out.

Accumulator semantics are correct. RankInvarianceAccumulator.add compares baseline_ids[:core] == spine_ids[:core] (catches both drops and reorders inside the core), counts core-belief displacements separately, and excludes BFS-tail eviction (rank ≥ core) from regressions — matching the retrieval lane order [locked, l25, l1, hrr] → temporal_spine → BFS. passed() requires all-invariant ∧ 0 displacements ∧ 0 core-length mismatch ∧ n>0. Core boundary is read from last_lane_telemetry(), not inferred.

API surface verified against the branch. retrieve_v2(budget, include_locked, l1_limit, use_temporal_spine) and LaneTelemetry.{locked,l25,l1,hrr_expand,temporal_spine} + last_lane_telemetry() all exist.

Tests. All 8 pass locally (pure-append invariant, core drop/reorder = regression, BFS-tail eviction = not a regression, core-length mismatch fails, empty-acc fails, LCP tracking). Correctly mirror the implementation.

Scope. Docs mark the bench-pool half DONE and correctly leave shadow-eval + G3 + the flip OPEN; PR body is "Part of #1064", not "Closes" — issue stays open. Correct.

CI green (CodeRabbit/Sourcery rate-limited, no substantive automated findings). Discretion grep clean. FF-able, both commits signed. Over the advisory 200-line soft cap but cohesive — non-blocking.

Adding ready-to-merge.

@robotrocketscience robotrocketscience added the ready-to-merge Trigger merge-train: FF main to this PR's head label Jul 4, 2026
@robotrocketscience

Copy link
Copy Markdown
Owner Author

[release:review:Gylf:2026-07-04T08:04:12Z]

@github-actions

github-actions Bot commented Jul 4, 2026

Copy link
Copy Markdown

merge-train: blocked

branch is not fast-forward on main (branch base fe3f29c45952426501b97cc8366ed7ccc0b3a5f5, current main 3f1a66ab24be8f05768dba4975e3daad82d77a26). Rebase locally (git rebase github/main), force-push, and re-add the label.

The ready-to-merge label has been removed. Address the issue above and re-add the label when you're ready for another attempt.

@github-actions github-actions Bot removed the ready-to-merge Trigger merge-train: FF main to this PR's head label Jul 4, 2026
…1064)

Adds a `--rank-invariance` mode to `temporal_spine_ablation.py` that pairs
lane-off vs lane-on retrieval per question on the same store and verifies the
flip-gate's "no top-rank regression" criterion: the spine lane appends after
the [locked, l25, l1, hrr] core and before BFS, so the core prefix must stay
identical between arms. The pass reads `last_lane_telemetry()` to locate the
core boundary exactly, then asserts no core belief is displaced or reordered;
BFS-tail eviction (the lane spending budget above the lowest lane) is reported
separately as by-design, not a regression.

`RankInvarianceAccumulator` carries the aggregates and a `passed()` gate.
`test_temporal_spine_ablation.py` pins the accumulator math (head invariance,
core displacement, core reorder, BFS-tail eviction, core-length mismatch, empty
guard) on hand-built id lists — no LoCoMo dataset needed, runs in the pytest
matrix.

The LoCoMo dataset stack (`locomo_adapter` -> `nltk`) is now imported lazily
inside the functions that run the bench, so this module's pure logic imports —
and unit-tests — without the benchmark dependency set.
The bench-pool half of the temporal-spine flip gate's G2 criterion is done:
LoCoMo10 at budget 1500 / l1-limit 50 gives +19.45pp coverage (survives the
production trim, exceeding the wide-budget dev figure) with 1,986/1,986
questions core-prefix invariant and 0 top-rank displacements. Records the
result and the reproduce command, and splits G2 into the completed bench half
and the still-open shadow-eval half (aggregate-only run on a real backfilled
store).
@robotrocketscience
robotrocketscience force-pushed the test/issue-1064-g2-rank-invariance branch from e3fbc7a to 33b7ddd Compare July 4, 2026 09:31
@robotrocketscience robotrocketscience added the ready-to-merge Trigger merge-train: FF main to this PR's head label Jul 4, 2026
@github-actions github-actions Bot removed the ready-to-merge Trigger merge-train: FF main to this PR's head label Jul 4, 2026
@github-actions
github-actions Bot merged commit 33b7ddd into main Jul 4, 2026
34 of 35 checks passed
@github-actions

github-actions Bot commented Jul 4, 2026

Copy link
Copy Markdown

merge-train: merged 33b7dddmain via FF push.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
benchmarks/temporal_spine_ablation.py (1)

218-303: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Add a bounds guard for the telemetry-derived core boundary.

core = core_len_base is used directly to slice baseline_ids/spine_ids. This relies on the invariant that core_len_base <= len(baseline_ids) (and similarly for spine) always holding, per the LaneTelemetry contract where packed counts should equal what lands in result.beliefs. If that invariant is ever violated by an upstream change, slicing silently truncates instead of erroring, and the check could silently report head_invariant=True for a boundary that doesn't actually reflect the intended core — precisely the kind of false-positive this accumulator exists to prevent.

🛡️ Proposed defensive check
         self.n_questions += 1
         if core_len_base != core_len_spine:
             self.core_mismatch += 1
         core = core_len_base
+        assert core <= len(baseline_ids) and core <= len(spine_ids), (
+            "core boundary exceeds retrieved belief count — telemetry and "
+            "result list are out of sync"
+        )
         if baseline_ids[:core] == spine_ids[:core]:
             self.head_invariant += 1

Since this depends on the packed-count contract of LaneTelemetry/retrieve_v2 in src/aelfrice/retrieval.py, worth confirming that invariant can never be violated in practice before deciding whether to add the guard.

🤖 Prompt for 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.

In `@benchmarks/temporal_spine_ablation.py` around lines 218 - 303, The
`RankInvarianceAccumulator.add` method uses `core = core_len_base` directly to
slice `baseline_ids` and `spine_ids`, which can hide upstream contract
violations by silently truncating. Add a defensive bounds check before computing
`baseline_ids[:core]` and `spine_ids[:core]` so the telemetry-derived core
boundary is validated against both lists. If the boundary is invalid, fail fast
or otherwise mark the sample as bad rather than letting `head_invariant` be
computed on an out-of-range core; keep the fix localized to
`RankInvarianceAccumulator.add` and its `core_len_base`/`core_len_spine` inputs.
🤖 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.

Nitpick comments:
In `@benchmarks/temporal_spine_ablation.py`:
- Around line 218-303: The `RankInvarianceAccumulator.add` method uses `core =
core_len_base` directly to slice `baseline_ids` and `spine_ids`, which can hide
upstream contract violations by silently truncating. Add a defensive bounds
check before computing `baseline_ids[:core]` and `spine_ids[:core]` so the
telemetry-derived core boundary is validated against both lists. If the boundary
is invalid, fail fast or otherwise mark the sample as bad rather than letting
`head_invariant` be computed on an out-of-range core; keep the fix localized to
`RankInvarianceAccumulator.add` and its `core_len_base`/`core_len_spine` inputs.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: bb9caf78-c67f-47da-9bdd-43ace03f2ec6

📥 Commits

Reviewing files that changed from the base of the PR and between 3f1a66a and 33b7ddd.

📒 Files selected for processing (3)
  • benchmarks/temporal_spine_ablation.py
  • docs/design/feature-temporal-spine.md
  • tests/test_temporal_spine_ablation.py

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

attn:review Needs review (PR open, awaiting reviewer) author-Setr PR coordination mutex

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant