Skip to content

feat: temporal spine — ingest-time chronological edges + dedicated retrieval lane, default-off (#1064) - #1069

Merged
github-actions[bot] merged 5 commits into
mainfrom
feat/issue-1064-temporal-spine-writer
Jul 4, 2026
Merged

feat: temporal spine — ingest-time chronological edges + dedicated retrieval lane, default-off (#1064)#1069
github-actions[bot] merged 5 commits into
mainfrom
feat/issue-1064-temporal-spine-writer

Conversation

@robotrocketscience

@robotrocketscience robotrocketscience commented Jul 4, 2026

Copy link
Copy Markdown
Owner

Lands deliverables 1–4 of #1064 — the temporal spine, everything default-OFF. Four atomic commits:

  1. feat(temporal_spine) — spine writer. After each belief insert, link to the session predecessor (created_at order, insertion-order tie-break) with TEMPORAL_NEXT, src = successor, dst = predecessor, weight 0.8. New store.session_predecessor_id() does the indexed O(log n) lookup. Wired into _ingest_turn_ids (covers both ingest_turn and ingest_jsonl) behind AELFRICE_TEMPORAL_SPINE_WRITE env → kwarg → [ingest] write_temporal_spine TOML → False, mirroring the feat(ingest): build the semantic-edge substrate via deterministic relationship-detection at ingest (default-off) — the real LoCoMo lever; reframes #981/#977 #988 writer's posture: off-path ingest is byte-identical. Idempotent per (src, dst, type); soft-deleted predecessors stay eligible so chains never sever under GC.

  2. feat(cli)aelf spine backfill + doctor row. Idempotent per-session chain build over existing stores (--dry-run supported), hidden subcommand (migration surface, registered in HIDDEN_SUBCOMMANDS). aelf health / doctor graph scope reports temporal spine: present (N edges) / absent. Equivalence pinned by test: backfill output == writer output on the same corpus. Live-store dry-run: 21,476 edges across 521 sessions ≈ 0.98 edges/belief, matching the issue's ~1.0 estimate.

  3. feat(retrieval) — dedicated lane + telemetry + ablation bench. Additive candidate source after L1 in retrieve_with_tiers: traverses TEMPORAL_NEXT from the top-5 packed L1 seeds via spine_neighbors (bidirectional, depth 1, node budget 32; deterministic ordering; valid_to skip-but-continue). Appended after L1, never displacing it pre-packing; no-op guard via count_edges_by_type() keeps spineless stores byte-identical. Flag: AELFRICE_TEMPORAL_SPINEuse_temporal_spine kwarg → [retrieval] use_temporal_spine → False; budget via AELFRICE_TEMPORAL_SPINE_BUDGET / temporal_spine_budget. LaneTelemetry gains temporal_spine (packed survivors) + temporal_spine_candidates (pre-pack) so the G2 trim-loss question is readable per call. benchmarks/temporal_spine_ablation.py scores gold-evidence coverage on LoCoMo (baseline / +spine / seeded shuffled-control — the chronology-vs-density isolate).

  4. docsdocs/design/feature-temporal-spine.md (mechanism, evidence, lane-not-gate rationale vs Adaptive expansion-gate: skip BFS/HRR-expensive lanes on broad prompts #741/Benchmark the gated retrieval lanes ON; revisit conservative default-off flags (BFS, type-aware compression, γ/ζ rerank) #977/decide: denser/typed semantic-edge substrate beyond CONTRADICTS-only for HRR-expand/BFS lanes #998-A4, pre-registered flip-gate criteria G1–G5), CONFIG.md keys, CHANGELOG [Unreleased] entry.

Two deliberate deviations from strict #981-lane symmetry, both documented in code: spine hits do not seed BFS (the confirmatory evidence measured depth-1 append-after-L1 with BFS untouched; feeding BFS is unmeasured surface), and the lane lives in retrieve_with_tiers / retrieve_v2 only (same scope as the #981 lane — hook-path wiring is G2 flip-time work).

Deliverable 5 (default-ON flip) is intentionally not here: gated on G2–G5 per the issue.

Tests: 35 new in tests/test_temporal_spine.py (flag precedence, ordering contract incl. rowid tie-break, writer/backfill idempotency + equivalence, traversal budget/depth/soft-delete, lane on/off/no-op-guard/env/budget via retrieve_v2); full suite 5601 passed locally.

Part of #1064 — deliberately not Closes: the issue stays open tracking the G2–G5 flip gate (deliverable 5). If the operator prefers close-on-merge + a fresh flip-gate issue, say the word and I'll file it.

Summary by CodeRabbit

  • New Features

    • Added an optional temporal-spine experience for ingest and retrieval, linking related items in chronological order and allowing retrieval to extend results from that chain.
    • Added a new health check signal and a backfill command to help surface whether temporal-spine data is present and ready.
    • Expanded configuration docs with new settings and defaults for enabling and tuning temporal-spine behavior.
  • Bug Fixes

    • Improved handling for existing data by supporting backfill, dry runs, and no-op behavior when no matching chain data exists.

@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

@robotrocketscience robotrocketscience added the attn:review Needs review (PR open, awaiting reviewer) label Jul 4, 2026
@coderabbitai

coderabbitai Bot commented Jul 4, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@robotrocketscience, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 11 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 58b3aceb-d3be-4f54-879a-47cf33301f31

📥 Commits

Reviewing files that changed from the base of the PR and between af34a83 and fe3f29c.

📒 Files selected for processing (11)
  • CHANGELOG/v3.md
  • benchmarks/temporal_spine_ablation.py
  • docs/design/feature-temporal-spine.md
  • docs/user/CONFIG.md
  • src/aelfrice/cli.py
  • src/aelfrice/ingest.py
  • src/aelfrice/retrieval.py
  • src/aelfrice/store.py
  • src/aelfrice/temporal_spine.py
  • tests/test_slash_commands.py
  • tests/test_temporal_spine.py
📝 Walkthrough

Walkthrough

This PR introduces a "temporal spine" feature: ingest-time chronological belief chaining via TEMPORAL_NEXT edges, a default-off retrieval lane, a backfill/doctor CLI subcommand, config flags, an ablation benchmark, design documentation, and unit tests.

Changes

Temporal Spine Feature

Layer / File(s) Summary
Core module and store helpers
src/aelfrice/temporal_spine.py, src/aelfrice/store.py
New module resolves write/enable flags, writes/backfills TEMPORAL_NEXT edges, and traverses the spine; MemoryStore gains session_predecessor_id and session_belief_ids_ordered.
Ingest-time wiring
src/aelfrice/ingest.py
_ingest_turn_ids conditionally calls write_temporal_spine for newly inserted beliefs.
Retrieval lane
src/aelfrice/retrieval.py
Adds flags, env overrides, LaneTelemetry counters, and a spine-traversal lane appended after L1 in retrieve_with_tiers/retrieve_v2.
CLI backfill and health reporting
src/aelfrice/cli.py, tests/test_slash_commands.py
Adds aelf spine backfill hidden subcommand and TEMPORAL_NEXT status in aelf health.
Config, design docs, changelog
docs/user/CONFIG.md, docs/design/feature-temporal-spine.md, CHANGELOG/v3.md
Documents new flags, feature design, flip-gate criteria, and changelog entry.
Ablation benchmark
benchmarks/temporal_spine_ablation.py
Adds LoCoMo-based coverage benchmark comparing baseline, spine, and shuffled-control arms.
Unit tests
tests/test_temporal_spine.py
Covers flag resolution, writer, backfill, traversal, and retrieval integration.

Estimated code review effort: 4 (Complex) | ~60 minutes

Possibly related issues

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely summarizes the main change: a default-off temporal spine feature spanning ingest and retrieval.
Description check ✅ Passed The description covers the summary, linked issue stance, verification, tests, and reviewer notes with enough detail for the template.
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 unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/issue-1064-temporal-spine-writer

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:

  • 1740 changed lines (limit: 200)
  • 11 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.

@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 (4)
src/aelfrice/cli.py (1)

4590-4618: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Consider adding --json for scriptability.

Other operator-facing subcommands (doctor, health, review) support --json for machine consumption. Since spine backfill is meant to be invoked by the default-ON flip release (per the docstring), a JSON output mode would make it easier to script/verify programmatically.

🤖 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 `@src/aelfrice/cli.py` around lines 4590 - 4618, Add a machine-readable output
mode to `_cmd_spine` so it matches other operator-facing commands like `doctor`,
`health`, and `review`. Update the `aelf spine backfill` CLI wiring to accept
`--json`, and in `_cmd_spine` emit the `backfill_temporal_spine` report as JSON
when that flag is set instead of the current human-readable summary. Keep the
existing text output as the default, and make sure the new option is
discoverable alongside the existing `dry_run` behavior.
src/aelfrice/temporal_spine.py (1)

302-318: 🚀 Performance & Scalability | 🔵 Trivial | ⚖️ Poor tradeoff

Backfill commits one edge at a time; consider a single transaction for large stores.

backfill_temporal_spine calls store.insert_edge per consecutive pair, and each insert_edge runs its own commit() (plus invalidation-callback fire). On a large pre-existing store this is O(N) fsync-bearing commits for a one-shot migration, which can make aelf spine backfill noticeably slow. Since this is MemoryStore-internal, batching all inserts into one transaction/commit would need a small store-side helper — worth considering if the migration is expected to run against sizable on-disk DBs.

🤖 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 `@src/aelfrice/temporal_spine.py` around lines 302 - 318, The backfill in
backfill_temporal_spine is issuing store.insert_edge for each adjacent belief
pair, which triggers a commit and callback per edge. Update the migration to
batch these inserts into a single store-side transaction/commit path for large
runs, ideally by adding or using a MemoryStore helper that can insert multiple
Edge records at once. Keep the existing logic in backfill_temporal_spine that
iterates session_belief_ids_ordered, but route writes through the batch helper
instead of calling insert_edge repeatedly.
benchmarks/temporal_spine_ablation.py (1)

160-168: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Add explicit strict= to zip (B905).

srcs/dsts/weights are built from the same rows so lengths match today; strict=True documents that invariant and fails loudly if the extraction ever diverges.

♻️ Suggested tweak
-    for src, dst, weight in zip(srcs, dsts, weights):
+    for src, dst, weight in zip(srcs, dsts, weights, strict=True):

The other Ruff/ast-grep hints here (random S311, /tmp default S108, path-traversal on --out) are expected for a deterministic, dev-only benchmark and don't warrant changes.

🤖 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 160 - 168, Add an
explicit strict argument to the zip loop in the temporal spine ablation
benchmark. In the edge-writing loop that iterates over srcs, dsts, and weights,
update the zip call to use strict=True so the invariant is enforced and any
length mismatch fails loudly. Use the existing loop in the benchmark function
that writes into edges to locate the change.

Source: Linters/SAST tools

src/aelfrice/retrieval.py (1)

3281-3286: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Avoid counting all edge types just to check for TEMPORAL_NEXT.
store.count_edges_by_type() runs SELECT type, COUNT(*) FROM edges GROUP BY type, and there’s no edges(type) index, so this guard still scans the whole table. Use an EXISTS/LIMIT 1 probe on type = EDGE_TEMPORAL_NEXT here instead.

🤖 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 `@src/aelfrice/retrieval.py` around lines 3281 - 3286, The temporal spine guard
in retrieval logic is still doing a full grouped edge count through
store.count_edges_by_type() just to verify EDGE_TEMPORAL_NEXT exists. Update the
condition in the retrieval path to use a direct existence probe against the
edges table for type = EDGE_TEMPORAL_NEXT (for example via an EXISTS or LIMIT 1
helper) so the check avoids scanning all edge types. Keep the change localized
around the is_temporal_spine_enabled/query/l1_packed guard and reuse the
existing EDGE_TEMPORAL_NEXT symbol.
🤖 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 160-168: Add an explicit strict argument to the zip loop in the
temporal spine ablation benchmark. In the edge-writing loop that iterates over
srcs, dsts, and weights, update the zip call to use strict=True so the invariant
is enforced and any length mismatch fails loudly. Use the existing loop in the
benchmark function that writes into edges to locate the change.

In `@src/aelfrice/cli.py`:
- Around line 4590-4618: Add a machine-readable output mode to `_cmd_spine` so
it matches other operator-facing commands like `doctor`, `health`, and `review`.
Update the `aelf spine backfill` CLI wiring to accept `--json`, and in
`_cmd_spine` emit the `backfill_temporal_spine` report as JSON when that flag is
set instead of the current human-readable summary. Keep the existing text output
as the default, and make sure the new option is discoverable alongside the
existing `dry_run` behavior.

In `@src/aelfrice/retrieval.py`:
- Around line 3281-3286: The temporal spine guard in retrieval logic is still
doing a full grouped edge count through store.count_edges_by_type() just to
verify EDGE_TEMPORAL_NEXT exists. Update the condition in the retrieval path to
use a direct existence probe against the edges table for type =
EDGE_TEMPORAL_NEXT (for example via an EXISTS or LIMIT 1 helper) so the check
avoids scanning all edge types. Keep the change localized around the
is_temporal_spine_enabled/query/l1_packed guard and reuse the existing
EDGE_TEMPORAL_NEXT symbol.

In `@src/aelfrice/temporal_spine.py`:
- Around line 302-318: The backfill in backfill_temporal_spine is issuing
store.insert_edge for each adjacent belief pair, which triggers a commit and
callback per edge. Update the migration to batch these inserts into a single
store-side transaction/commit path for large runs, ideally by adding or using a
MemoryStore helper that can insert multiple Edge records at once. Keep the
existing logic in backfill_temporal_spine that iterates
session_belief_ids_ordered, but route writes through the batch helper instead of
calling insert_edge repeatedly.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 7ba172a1-a0d8-40d3-ac7a-309bad92a706

📥 Commits

Reviewing files that changed from the base of the PR and between 5c02067 and af34a83.

📒 Files selected for processing (11)
  • CHANGELOG/v3.md
  • benchmarks/temporal_spine_ablation.py
  • docs/design/feature-temporal-spine.md
  • docs/user/CONFIG.md
  • src/aelfrice/cli.py
  • src/aelfrice/ingest.py
  • src/aelfrice/retrieval.py
  • src/aelfrice/store.py
  • src/aelfrice/temporal_spine.py
  • tests/test_slash_commands.py
  • tests/test_temporal_spine.py

@robotrocketscience

Copy link
Copy Markdown
Owner Author

[claim:review:Kulili:2026-07-04T07:05:14Z]

@robotrocketscience

Copy link
Copy Markdown
Owner Author

Reviewed the full diff (4 atomic signed commits, 11 files).

Verified:

  • Spine writer: predecessor lookup is (created_at, rowid)-ordered and session-scoped (idx_beliefs_session exists on main), idempotent per (src,dst,type), dedupes input ids, and is gated on inserted inside ingest — off-path ingest is byte-identical (pinned by test_ingest_off_path_writes_no_spine_edges).
  • TEMPORAL_NEXT already lives in models.EDGE_TYPES with src=successor semantics; edge weight 0.8 is a distinct traversal-tier constant, correctly documented as unrelated to EDGE_VALENCE[TEMPORAL_NEXT]=0.2.
  • Backfill: single ordered scan, idempotent, --dry-run writes nothing (pinned), and test_backfill_matches_writer_output pins writer/backfill equivalence — the migration-correctness property that matters.
  • Lane: default-off via the established tri-state resolver precedence; no-op guard on count_edges_by_type(); seeds = top-5 packed L1; appended post-L1 within the relevance budget (never displaces); deduped against locked/L2.5/L1/HRR-expand ids and folded into BFS seen_ids; deterministic traversal ordering; soft-deleted skip-but-continue. Telemetry split packed-vs-candidates supports the G2 trim-loss question.
  • No touch of derive()/raw_meta — replay-soak-safe by construction.
  • spine registered in HIDDEN_SUBCOMMANDS; CI fully green (pytest 3.12/3.13, CodeQL, scans, release-docs-check); no unresolved review threads (one bot rate-limited itself; the other left 4 trivial nitpicks, no inline threads).
  • Bench: retrieval query is qa.question (no gold in the retrieval path), no reader, seeded shuffle control, arm-independent ingest.

Non-blocking notes:

  1. Doc/code mismatch: docs/design/feature-temporal-spine.md §Mechanism-2 and the Temporal spine: ingest-time chronological edges + dedicated retrieval lane (confirmed +14.6pp coverage on LoCoMo) #1064 issue body say the spine presence row lands in aelf doctor; the implementation puts it in aelf health (where edges-by-type already prints — arguably the better home). Suggest a one-line doc fix in a follow-up, or duplicating the row in doctor at flip time.
  2. resolve_temporal_spine_budget reads the TOML knob through the float reader and truncates — a TOML 32.9 silently becomes 32. Harmless; noting for the flip-release pass.
  3. CodeRabbit's --json suggestion for spine backfill is reasonable to fold into the flip release, since that release scripts the backfill.

FF on main, all commits signed. Labeling 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:Kulili:2026-07-04T07:09:33Z]

@robotrocketscience

Copy link
Copy Markdown
Owner Author

[claim:review:garsecg:2026-07-04T07:10:00Z]

@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 5c020676e74920bfc3e0aeb8433414d7d0ea5e46, current main 1d13b94f523837589299e19d0163e71f7af0bf58). 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

@robotrocketscience robotrocketscience left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Reviewed the full diff, verifying claims at call sites:

  • Lane guard order is right: is_temporal_spine_enabled(...) short-circuits before any store call, so the default-off path pays zero; the no-op guard (count_edges_by_type) keeps spineless stores byte-identical when the flag IS on. Seeds are the top-5 packed L1 beliefs; spine hits append under the token budget after L1 (never displacing), dedup against locked/L2.5/L1/HRR-expand, and deliberately don't seed BFS — all matching the #1064 lane-not-gate rationale.
  • Bidirectional traversal is sound: edges_for_beliefs (pre-existing) matches src OR dst, so both successor and predecessor directions resolve; ordering (successors-then-predecessor, id-sorted within group) is deterministic; budget counts emitted only.
  • Writer/backfill: idempotent per (src,dst,type); session_predecessor_id orders by (created_at, rowid) with correct tie-break SQL; backfill==writer equivalence is pinned by test; --dry-run touches nothing.
  • Wiring complete: env → kwarg → TOML → False on both flags (unrecognised env falls through, tested); HIDDEN_SUBCOMMANDS registration present; doctor row; CONFIG.md keys; telemetry carries candidates-vs-packed so the G2 trim question is readable per call.
  • Independent verification: fetched the PR head; test_temporal_spine.py + test_slash_commands.py pass locally in a fresh dev venv (194 passed, 1 skipped). CI matrix green; 0 unresolved review threads; discretion grep on the diff clean (one hit is a pre-existing context line).

Two non-blocking notes for the flip-time revisit, not this PR:

  1. At the default depth-1, skip-but-continue can't actually traverse past a soft-deleted neighbour (it joins the next frontier but the loop ends). Chain integrity across GC'd segments effectively needs depth ≥2 — worth one line in the design doc when the budget/depth knobs get revisited at flip time.
  2. benchmarks/temporal_spine_ablation.py imports the private ingest._ingest_turn_ids; fine for a bench, but if that seam ever changes the bench is the only breakage surface — a public re-export would decouple it.

LGTM on content — but hold the label: main moved 4 commits under this branch (the #1068 hook landed), so the FF-only merge-train will bounce it as-is. After a rebase onto current github/main + green rerun, add ready-to-merge; happy for anyone to label it then, no re-review needed for a clean rebase.

@robotrocketscience

Copy link
Copy Markdown
Owner Author

[release:review:garsecg:2026-07-04T07:14:09Z]

@robotrocketscience

Copy link
Copy Markdown
Owner Author

[claim:review:Gylf:2026-07-04T07:18:01Z]

@robotrocketscience

Copy link
Copy Markdown
Owner Author

Incremental review of 0110e5e (pushed one minute before the prior review landed, so pinning down that the delta is covered):

  • The guard swap count_edges_by_type().get(EDGE_TEMPORAL_NEXT, 0) > 0has_edge_type(EDGE_TEMPORAL_NEXT) is semantically identical (presence-only check), and the LIMIT-1 probe is strictly cheaper than the full GROUP BY.
  • has_edge_type has no direct unit test, but the spineless byte-identical guard test (tests/test_temporal_spine.py:507) now exercises it on the flag-on / zero-edges path, which is the path the swap changes.
  • Ran tests/test_temporal_spine.py + tests/test_slash_commands.py at this exact head (0110e5e) in a fresh env: 194 passed, 1 skipped.

One flip-time note (non-blocking, same bucket as the prior review's two): edges is indexed on src/dst only, so the probe's miss path (flag on, zero spine edges) is a full table scan per retrieval call. Cheap at current store sizes and strictly better than the GROUP BY it replaces, but if the flag ever defaults ON without a guaranteed backfill, an index on edges(type) or caching the probe result would bound it.

Status: content approved — prior review plus this delta covers the full branch. mergeable is CONFLICTING; a dry-run merge shows the only conflict is CHANGELOG/v3.md (overlap with the changelog entries that landed on main since branch). Setting attn:merge-conflict for the author. After rebase + green rerun, anyone can add ready-to-merge per the prior review — no re-review needed.

@robotrocketscience robotrocketscience added attn:merge-conflict PR branch needs rebase and removed attn:review Needs review (PR open, awaiting reviewer) labels Jul 4, 2026
@robotrocketscience

Copy link
Copy Markdown
Owner Author

[release:review:Gylf:2026-07-04T07:22:20Z]

@robotrocketscience

Copy link
Copy Markdown
Owner Author

[claim:review:Kulili:2026-07-04T07:25:30Z]

@robotrocketscience
robotrocketscience force-pushed the feat/issue-1064-temporal-spine-writer branch from 0110e5e to d4897d5 Compare July 4, 2026 07:28
@robotrocketscience robotrocketscience removed the attn:merge-conflict PR branch needs rebase label Jul 4, 2026
…t, default-off (#1064)

Ingest-time chronological spine: each newly inserted belief links to its
session predecessor (created_at order, insertion-order tie-break) with a
TEMPORAL_NEXT edge, src=successor dst=predecessor weight 0.8. New
store.session_predecessor_id() accessor does the O(log n) indexed lookup.
Wired into _ingest_turn_ids behind is_temporal_spine_write_enabled
(AELFRICE_TEMPORAL_SPINE_WRITE env > kwarg > [ingest] write_temporal_spine
TOML > False) so the off-path is byte-identical to today. Idempotent per
(src,dst,type); soft-deleted predecessors stay eligible so chain integrity
survives GC.
backfill_temporal_spine walks every session's beliefs in (created_at,
rowid) order and links consecutive pairs with the same TEMPORAL_NEXT
edge the ingest writer produces — idempotent per (src,dst,type), with a
--dry-run counting mode. Hidden 'aelf spine' subcommand (migration
surface, not a workflow verb; registered in HIDDEN_SUBCOMMANDS). aelf
health / doctor graph scope gains a 'temporal spine: present (N
edges)/absent' row. Equivalence test pins backfill output == writer
output on the same corpus.
…bench, default-off (#1064)

Additive candidate source after L1 in retrieve_with_tiers: traverses
TEMPORAL_NEXT chains from the top-5 packed L1 seeds via
temporal_spine.spine_neighbors (bidirectional, depth 1 default, node
budget 32 default; deterministic ordering, soft-deleted beliefs
skip-but-continue). Appended after L1, never displacing it pre-packing;
no-op guard skips the traversal on stores with zero TEMPORAL_NEXT edges
so spineless output stays byte-identical. Spine hits do not seed BFS
(unmeasured surface; the #1064 evidence ran depth-1 append-after-L1).
Flag: AELFRICE_TEMPORAL_SPINE env > use_temporal_spine kwarg >
[retrieval] use_temporal_spine TOML > False; node budget tunable via
AELFRICE_TEMPORAL_SPINE_BUDGET / temporal_spine_budget. LaneTelemetry
gains temporal_spine (packed survivors) + temporal_spine_candidates
(pre-pack discoveries) so the G2 trim-loss question is readable per
call. benchmarks/temporal_spine_ablation.py scores gold-evidence
coverage on LoCoMo across baseline / +spine / seeded shuffled-control
arms (the chronology-vs-density isolate).
docs/design/feature-temporal-spine.md records the mechanism, the
dev/confirmatory coverage evidence, the lane-not-gate rationale vs
G1-G5. CONFIG.md documents [ingest] write_temporal_spine and
[retrieval] use_temporal_spine / temporal_spine_budget (schema block +
detail sections). CHANGELOG [Unreleased] entry for the feature wave.
…1064)

count_edges_by_type() is a full GROUP BY over the edges table — 4.15ms
per call measured on a 24k-edge production store, nearly the whole G3
p50 gate (5ms) spent before the lane does any work. New
store.has_edge_type() is a LIMIT-1 existence probe: ~0ms when a spine
edge exists (the lane's common on-path), bounded by one scan when none
does. Paired alternating min-of-7 measurement on a 26k-belief store
copy: lane delta within ±5ms on the heaviest-firing queries (8-10
packed spine hits).
@robotrocketscience
robotrocketscience force-pushed the feat/issue-1064-temporal-spine-writer branch from d4897d5 to 5dac4de Compare July 4, 2026 07:33
@robotrocketscience robotrocketscience added the ready-to-merge Trigger merge-train: FF main to this PR's head label Jul 4, 2026
@robotrocketscience
robotrocketscience force-pushed the feat/issue-1064-temporal-spine-writer branch from 5dac4de to fe3f29c Compare July 4, 2026 07:33
@github-actions

github-actions Bot commented Jul 4, 2026

Copy link
Copy Markdown

merge-train: blocked

branch head moved during merge-train queue (event=5dac4de0209c5f1e2003b2f0a7f00584ba9a4708, current=fe3f29c45952426501b97cc8366ed7ccc0b3a5f5). Re-add ready-to-merge to retry against the new head.

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
@github-actions

github-actions Bot commented Jul 4, 2026

Copy link
Copy Markdown

merge-train: merged fe3f29cmain via FF push.

@github-actions
github-actions Bot merged commit fe3f29c into main Jul 4, 2026
26 checks passed
@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:Kulili:2026-07-04T07:37:48Z]

@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 commented Jul 4, 2026

Copy link
Copy Markdown

merge-train: merged fe3f29cmain via FF push.

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

Labels

author-Setr PR coordination mutex

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant