Skip to content

fix: prevent repeated session lineage resolution - #3

Merged
ss251 merged 2 commits into
mainfrom
fix/lineage-resolve-perf
Jul 20, 2026
Merged

ss251 merged 2 commits into
mainfrom
fix/lineage-resolve-perf

Conversation

@ss251

@ss251 ss251 commented Jul 20, 2026 •

Copy link
Copy Markdown
Owner

Intent

Fix the session-lineage startup performance fire: the app sat in 'Preparing session index' for 30+ seconds because lineage resolution re-ran over the entire corpus on every incremental scan publish and every keystroke; resolution must run once per corpus rebuild (memoized), key each session exactly once, and sort without deep-copying subtrees or hashing long keys per comparison — with no behavior change to the resolved forest

What Changed

  • Memoize lineage resolutions by session, evidence, and heuristic revisions, sharing cancellable in-flight work across incremental rebuilds and filter updates.
  • Compute stable session keys once and reuse them during projection; sort roots before materialization to avoid repeated path normalization and subtree copying.
  • Preserve the existing resolved forest while adding coverage for indexed key reuse, cancellation, and revision-based snapshot wiring.

Risk Assessment

🚨 High: The prior three blockers are fixed, but one explicit performance acceptance criterion remains unmet, so the change should not merge without correction or explicit human approval.

Testing

Diff inspection, focused lineage and memo-contract tests, and the production release build all passed; a seven-run 10,000-session comparison improved median resolution from 377.941 ms to 206.355 ms (45.4%) while producing byte-identical forests, and the production snapshot-store trace showed exactly one resolution for nine same-source publishes plus rapid typing, one key per session, memo reuse for later keystrokes, and exactly one new resolution after a real corpus revision. No screenshot was captured because this is a transient performance-only change with no visual or copy change; the readiness timing and resolution-count trace directly demonstrate removal of the visible stall.

Evidence: Complete lineage validation report
Trifola session-lineage performance validation
Base:   1d1c0bac2bf8252641a908fb96d6613afcafd189
Target: a5abb62c68ce1801d01b21f5015023298ae8af80

RESULT: PASS

1. Production build and focused behavior tests

  swift test --filter SessionLineageTests
  PASS: deterministic edge kinds, heuristic visibility, cycles/orphans/depth,
        duplicate transport IDs, indexed key reuse, cancellation, parent timing,
        detached metadata, and evidence parsing.

  swift test --filter NavigationSnapshotShapeTests
  PASS: bounded projection shape and revision-owned/in-flight lineage memo contract.

  swift build -c release --product Trifola
  PASS: production executable compiled and linked.

2. Base/target forest equivalence and resolver performance

Fixture: 10,000 sessions, 2,000 rooted Codex spawn trees, long stable-key paths.
Each commit was compiled in release mode and run seven times over identical input.

Base elapsed_ms samples:
  395.239, 374.808, 386.004, 369.555, 372.074, 394.940, 377.941
Base median: 377.941 ms

Target elapsed_ms samples:
  206.390, 209.362, 193.811, 195.466, 198.459, 206.355, 206.667
Target median: 206.355 ms

Median improvement: 45.4%

Both outputs:
  roots=2000 nodes=10000 transcript=10000 metadata=0
  SHA-256=e100387acef707834a9556555a0bd353a344a7e27cd0a399f09b87d885afb70b
  cmp exit status=0 (byte-identical canonical forests)

3. End-to-end NavigationSnapshotStore memoization trace

The instrumented evidence build drives the production snapshot store with a
10,000-session corpus, nine same-revision rebuild calls, seven rapid query
keystrokes before readiness, six additional memoized keystrokes, then a real
corpus revision adding a distinct 10,001st session.

  [lineage-resolve] build sessions=10000
  [lineage-resolve] complete sessions=10000 keyed_sessions=10000 roots=2000
  [lineage-resolve] build sessions=10001
  [lineage-resolve] complete sessions=10001 keyed_sessions=10001 roots=2001
  [lineage-store] same_source_rebuilds=9 rapid_keystrokes=7 cold_ready_ms=937.696 rows=0
  [lineage-store] memoized_keystrokes=6 ready_ms=331.507
  [lineage-store] revised_corpus=10001 ready_ms=373.314 rows=400
  [lineage-store] expected_resolution_builds=2 (one for revision 1, one for revision 2)

Observed contract:
  - exactly one resolve for revision 1 despite repeated publishes/typing;
  - every session keyed exactly once in each resolve;
  - later keystrokes reuse the memo;
  - exactly one new resolve after the corpus revision changes;
  - the Sessions snapshot becomes ready in under one second on this 10k fixture,
    clearing the UI's "Preparing session index…" shell.

Visual evidence note: no screenshot was captured because this is a transient
performance-only change with no altered layout or copy. A still image cannot show
the removed stall; the production-store timing and resolution-count trace directly
captures the user-visible readiness transition.
Evidence: Base canonical 10,000-node forest
0	0	codex:local:root-0:/fixture/very/long/session/path/used/to/exercise/stable/key/hashing/project-0/root-0.jsonl	root-0	-	-	-	-	4
1	1	codex:local:child-0-4:/fixture/very/long/session/path/used/to/exercise/stable/key/hashing/project-0/child-0-4.jsonl	child-0-4	codexSpawn	deterministic	-	Worker 4	0
1	1	codex:local:child-0-3:/fixture/very/long/session/path/used/to/exercise/stable/key/hashing/project-0/child-0-3.jsonl	child-0-3	codexSpawn	deterministic	-	Worker 3	0
1	1	codex:local:child-0-2:/fixture/very/long/session/path/used/to/exercise/stable/key/hashing/project-0/child-0-2.jsonl	child-0-2	codexSpawn	deterministic	-	Worker 2	0
1	1	codex:local:child-0-1:/fixture/very/long/session/path/used/to/exercise/stable/key/hashing/project-0/child-0-1.jsonl	child-0-1	codexSpawn	deterministic	-	Worker 1	0
0	0	codex:local:root-1440:/fixture/very/long/session/path/used/to/exercise/stable/key/hashing/project-1440/root-1440.jsonl	root-1440	-	-	-	-	4
1	1	codex:local:child-1440-4:/fixture/very/long/session/path/used/to/exercise/stable/key/hashing/project-1440/child-1440-4.jsonl	child-1440-4	codexSpawn	deterministic	-	Worker 4	0
1	1	codex:local:child-1440-3:/fixture/very/long/session/path/used/to/exercise/stable/key/hashing/project-1440/child-1440-3.jsonl	child-1440-3	codexSpawn	deterministic	-	Worker 3	0
1	1	codex:local:child-1440-2:/fixture/very/long/session/path/used/to/exercise/stable/key/hashing/project-1440/child-1440-2.jsonl	child-1440-2	codexSpawn	deterministic	-	Worker 2	0
1	1	codex:local:child-1440-1:/fixture/very/long/session/path/used/to/exercise/stable/key/hashing/project-1440/child-1440-1.jsonl	child-1440-1	codexSpawn	deterministic	-	Worker 1	0
0	0	codex:local:root-1441:/fixture/very/long/session/path/used/to/exercise/stable/key/hashing/project-1441/root-1441.jsonl	root-1441	-	-	-	-	4
1	1	codex:local:child-1441-4:/fixture/very/long/session/path/used/to/exercise/stable/key/hashing/project-1441/child-1441-4.jsonl	child-1441-4	codexSpawn	deterministic	-	Worker 4	0
1	1	codex:local:child-1441-3:/fixture/very/long/session/path/used/to/exercise/stable/key/hashing/project-1441/child-1441-3.jsonl	child-1441-3	codexSpawn	deterministic	-	Worker 3	0
1	1	codex:local:child-1441-2:/fixture/very/long/session/path/used/to/exercise/stable/key/hashing/project-1441/child-1441-2.jsonl	child-1441-2	codexSpawn	deterministic	-	Worker 2	0
1	1	codex:local:child-1441-1:/fixture/very/long/session/path/used/to/exercise/stable/key/hashing/project-1441/child-1441-1.jsonl	child-1441-1	codexSpawn	deterministic	-	Worker 1	0
0	0	codex:local:root-1:/fixture/very/long/session/path/used/to/exercise/stable/key/hashing/project-1/root-1.jsonl	root-1	-	-	-	-	4
1	1	codex:local:child-1-4:/fixture/very/long/session/path/used/to/exercise/stable/key/hashing/project-1/child-1-4.jsonl	child-1-4	codexSpawn	deterministic	-	Worker 4	0
1	1	codex:local:child-1-3:/fixture/very/long/session/path/used/to/exercise/stable/key/hashing/project-1/child-1-3.jsonl	child-1-3	codexSpawn	deterministic	-	Worker 3	0
1	1	codex:local:child-1-2:/fixture/very/long/session/path/used/to/exercise/stable/key/hashing/project-1/child-1-2.jsonl	child-1-2	codexSpawn	deterministic	-	Worker 2	0
1	1	codex:local:child-1-1:/fixture/very/long/session/path/used/to/exercise/stable/key/hashing/project-1/child-1-1.jsonl	child-1-1	codexSpawn	deterministic	-	Worker 1	0
0	0	codex:local:root-1442:/fixture/very/long/session/path/used/to/exercise/stable/key/hashing/project-1442/root-1442.jsonl	root-1442	-	-	-	-	4
1	1	codex:local:child-1442-4:/fixture/very/long/session/path/used/to/exercise/stable/key/hashing/project-1442/child-1442-4.jsonl	child-1442-4	codexSpawn	deterministic	-	Worker 4	0
1	1	codex:local:child-1442-3:/fixture/very/long/session/path/used/to/exercise/stable/key/hashing/project-1442/child-1442-3.jsonl	child-1442-3	codexSpawn	deterministic	-	Worker 3	0
1	1	codex:local:child-1442-2:/fixture/very/long/session/path/used/to/exercise/stable/key/hashing/project-1442/child-1442-2.jsonl	child-1442-2	codexSpawn	deterministic	-	Worker 2	0
1	1	codex:local:child-1442-1:/fixture/very/long/session/path/used/to/exercise/stable/key/hashing/project-1442/child-1442-1.jsonl	child-1442-1	codexSpawn	deterministic	-	Worker 1	0
0	0	codex:local:root-2:/fixture/very/long/session/path/used/to/exercise/stable/key/hashing/project-2/root-2.jsonl	root-2	-	-	-	-	4
1	1	codex:local:child-2-4:/fixture/very/long/session/path/used/to/exercise/stable/key/hashing/project-2/child-2-4.jsonl	child-2-4	codexSpawn	deterministic	-	Worker 4	0
1	1	codex:local:child-2-3:/fixture/very/long/session/path/used/to/exercise/stable/key/hashing/project-2/child-2-3.jsonl	child-2-3	codexSpawn	deterministic	-	Worker 3	0
1	1	codex:local:child-2-2:/fixture/very/long/session/path/used/to/exercise/stable/key/hashing/project-2/child-2-2.jsonl	child-2-2	codexSpawn	deterministic	-	Worker 2	0
1	1	codex:local:child-2-1:/fixture/very/long/session/path/used/to/exercise/stable/key/hashing/project-2/child-2-1.jsonl	child-2-1	codexSpawn	deterministic	-	Worker 1	0
0	0	codex:local:root-1443:/fixture/very/long/session/path/used/to/exercise/stable/key/hashing/project-1443/root-1443.jsonl	root-1443	-	-	-	-	4
1	1	codex:local:child-1443-4:/fixture/very/long/session/path/used/to/exercise/stable/key/hashing/project-1443/child-1443-4.jsonl	child-1443-4	codexSpawn	deterministic	-	Worker 4	0
1	1	codex:local:child-1443-3:/fixture/very/long/session/path/used/to/exercise/stable/key/hashing/project-1443/child-1443-3.jsonl	child-1443-3	codexSpawn	deterministic	-	Worker 3	0
1	1	codex:local:child-1443-2:/fixture/very/long/session/path/used/to/exercise/stable/key/hashing/project-1443/child-1443-2.jsonl	child-1443-2	codexSpawn	deterministic	-	Worker 2	0
1	1	codex:local:child-1443-1:/fixture/very/long/session/path/used/to/exercise/stable/key/hashing/project-1443/child-1443-1.jsonl	child-1443-1	codexSpawn	deterministic	-	Worker 1	0
0	0	codex:local:root-3:/fixture/very/long/session/path/used/to/exercise/stable/key/hashing/project-3/root-3.jsonl	root-3	-	-	-	-	4
1	1	codex:local:child-3-4:/fixture/very/long/session/path/used/to/exercise/stable/key/hashing/project-3/child-3-4.jsonl	child-3-4	codexSpawn	deterministic	-	Worker 4	0
1	1	codex:local:child-3-3:/fixture/very/long/session/path/used/to/exercise/stable/key/hashing/project-3/child-3-3.jsonl	child-3-3	codexSpawn	deterministic	-	Worker 3	0
1	1	codex:local:child-3-2:/fixture/very/long/session/path/used/to/exercise/stable/key/hashing/project-3/child-3-2.jsonl	child-3-2	codexSpawn	deterministic	-	Worker 2	0
1	1	codex:local:child-3-1:/fixture/very/long/session/path/used/to/exercise/stable/key/hashing/project-3/child-3-1.jsonl	child-3-1	codexSpawn	deterministic	-	Worker 1	0
0	0	codex:local:root-1444:/fixture/very/long/session/path/used/to/exercise/stable/key/hashing/project-1444/root-1444.jsonl	root-1444	-	-	-	-	4
1	1	codex:local:child-1444-4:/fixture/very/long/session/path/used/to/exercise/stable/key/hashing/project-1444/child-1444-4.jsonl	child-1444-4	codexSpawn	deterministic	-	Worker 4	0
1	1	codex:local:child-1444-3:/fixture/very/long/session/path/used/to/exercise/stable/key/hashing/project-1444/child-1444-3.jsonl	child-1444-3	codexSpawn	deterministic	-	Worker 3	0
1	1	codex:local:child-1444-2:/fixture/very/long/session/path/used/to/exercise/stable/key/hashing/project-1444/child-1444-2.jsonl	child-1444-2	codexSpawn	deterministic	-	Worker 2	0
1	1	codex:local:child-1444-1:/fixture/very/long/session/path/used/to/exercise/stable/key/hashing/project-1444/child-1444-1.jsonl	child-1444-1	codexSpawn	deterministic	-	Worker 1	0
0	0	codex:local:root-4:/fixture/very/long/session/path/used/to/exercise/stable/key/hashing/project-4/root-4.jsonl	root-4	-	-	-	-	4
1	1	codex:local:child-4-4:/fixture/very/long/session/path/used/to/exercise/stable/key/hashing/project-4/child-4-4.jsonl	child-4-4	codexSpawn	deterministic	-	Worker 4	0
1	1	codex:local:child-4-3:/fixture/very/long/session/path/used/to/exercise/stable/key/hashing/project-4/child-4-3.jsonl	child-4-3	codexSpawn	deterministic	-	Worker 3	0
1	1	codex:local:child-4-2:/fixture/very/long/session/path/used/to/exercise/stable/key/hashing/project-4/child-4-2.jsonl	child-4-2	codexSpawn	deterministic	-	Worker 2	0
1	1	codex:local:child-4-1:/fix

... [1687416 bytes truncated] ...

-1430-3.jsonl	child-1430-3	codexSpawn	deterministic	-	Worker 3	0
1	1	codex:local:child-1430-2:/fixture/very/long/session/path/used/to/exercise/stable/key/hashing/project-1430/child-1430-2.jsonl	child-1430-2	codexSpawn	deterministic	-	Worker 2	0
1	1	codex:local:child-1430-1:/fixture/very/long/session/path/used/to/exercise/stable/key/hashing/project-1430/child-1430-1.jsonl	child-1430-1	codexSpawn	deterministic	-	Worker 1	0
0	0	codex:local:root-1431:/fixture/very/long/session/path/used/to/exercise/stable/key/hashing/project-1431/root-1431.jsonl	root-1431	-	-	-	-	4
1	1	codex:local:child-1431-4:/fixture/very/long/session/path/used/to/exercise/stable/key/hashing/project-1431/child-1431-4.jsonl	child-1431-4	codexSpawn	deterministic	-	Worker 4	0
1	1	codex:local:child-1431-3:/fixture/very/long/session/path/used/to/exercise/stable/key/hashing/project-1431/child-1431-3.jsonl	child-1431-3	codexSpawn	deterministic	-	Worker 3	0
1	1	codex:local:child-1431-2:/fixture/very/long/session/path/used/to/exercise/stable/key/hashing/project-1431/child-1431-2.jsonl	child-1431-2	codexSpawn	deterministic	-	Worker 2	0
1	1	codex:local:child-1431-1:/fixture/very/long/session/path/used/to/exercise/stable/key/hashing/project-1431/child-1431-1.jsonl	child-1431-1	codexSpawn	deterministic	-	Worker 1	0
0	0	codex:local:root-1432:/fixture/very/long/session/path/used/to/exercise/stable/key/hashing/project-1432/root-1432.jsonl	root-1432	-	-	-	-	4
1	1	codex:local:child-1432-4:/fixture/very/long/session/path/used/to/exercise/stable/key/hashing/project-1432/child-1432-4.jsonl	child-1432-4	codexSpawn	deterministic	-	Worker 4	0
1	1	codex:local:child-1432-3:/fixture/very/long/session/path/used/to/exercise/stable/key/hashing/project-1432/child-1432-3.jsonl	child-1432-3	codexSpawn	deterministic	-	Worker 3	0
1	1	codex:local:child-1432-2:/fixture/very/long/session/path/used/to/exercise/stable/key/hashing/project-1432/child-1432-2.jsonl	child-1432-2	codexSpawn	deterministic	-	Worker 2	0
1	1	codex:local:child-1432-1:/fixture/very/long/session/path/used/to/exercise/stable/key/hashing/project-1432/child-1432-1.jsonl	child-1432-1	codexSpawn	deterministic	-	Worker 1	0
0	0	codex:local:root-1433:/fixture/very/long/session/path/used/to/exercise/stable/key/hashing/project-1433/root-1433.jsonl	root-1433	-	-	-	-	4
1	1	codex:local:child-1433-4:/fixture/very/long/session/path/used/to/exercise/stable/key/hashing/project-1433/child-1433-4.jsonl	child-1433-4	codexSpawn	deterministic	-	Worker 4	0
1	1	codex:local:child-1433-3:/fixture/very/long/session/path/used/to/exercise/stable/key/hashing/project-1433/child-1433-3.jsonl	child-1433-3	codexSpawn	deterministic	-	Worker 3	0
1	1	codex:local:child-1433-2:/fixture/very/long/session/path/used/to/exercise/stable/key/hashing/project-1433/child-1433-2.jsonl	child-1433-2	codexSpawn	deterministic	-	Worker 2	0
1	1	codex:local:child-1433-1:/fixture/very/long/session/path/used/to/exercise/stable/key/hashing/project-1433/child-1433-1.jsonl	child-1433-1	codexSpawn	deterministic	-	Worker 1	0
0	0	codex:local:root-1434:/fixture/very/long/session/path/used/to/exercise/stable/key/hashing/project-1434/root-1434.jsonl	root-1434	-	-	-	-	4
1	1	codex:local:child-1434-4:/fixture/very/long/session/path/used/to/exercise/stable/key/hashing/project-1434/child-1434-4.jsonl	child-1434-4	codexSpawn	deterministic	-	Worker 4	0
1	1	codex:local:child-1434-3:/fixture/very/long/session/path/used/to/exercise/stable/key/hashing/project-1434/child-1434-3.jsonl	child-1434-3	codexSpawn	deterministic	-	Worker 3	0
1	1	codex:local:child-1434-2:/fixture/very/long/session/path/used/to/exercise/stable/key/hashing/project-1434/child-1434-2.jsonl	child-1434-2	codexSpawn	deterministic	-	Worker 2	0
1	1	codex:local:child-1434-1:/fixture/very/long/session/path/used/to/exercise/stable/key/hashing/project-1434/child-1434-1.jsonl	child-1434-1	codexSpawn	deterministic	-	Worker 1	0
0	0	codex:local:root-1435:/fixture/very/long/session/path/used/to/exercise/stable/key/hashing/project-1435/root-1435.jsonl	root-1435	-	-	-	-	4
1	1	codex:local:child-1435-4:/fixture/very/long/session/path/used/to/exercise/stable/key/hashing/project-1435/child-1435-4.jsonl	child-1435-4	codexSpawn	deterministic	-	Worker 4	0
1	1	codex:local:child-1435-3:/fixture/very/long/session/path/used/to/exercise/stable/key/hashing/project-1435/child-1435-3.jsonl	child-1435-3	codexSpawn	deterministic	-	Worker 3	0
1	1	codex:local:child-1435-2:/fixture/very/long/session/path/used/to/exercise/stable/key/hashing/project-1435/child-1435-2.jsonl	child-1435-2	codexSpawn	deterministic	-	Worker 2	0
1	1	codex:local:child-1435-1:/fixture/very/long/session/path/used/to/exercise/stable/key/hashing/project-1435/child-1435-1.jsonl	child-1435-1	codexSpawn	deterministic	-	Worker 1	0
0	0	codex:local:root-1436:/fixture/very/long/session/path/used/to/exercise/stable/key/hashing/project-1436/root-1436.jsonl	root-1436	-	-	-	-	4
1	1	codex:local:child-1436-4:/fixture/very/long/session/path/used/to/exercise/stable/key/hashing/project-1436/child-1436-4.jsonl	child-1436-4	codexSpawn	deterministic	-	Worker 4	0
1	1	codex:local:child-1436-3:/fixture/very/long/session/path/used/to/exercise/stable/key/hashing/project-1436/child-1436-3.jsonl	child-1436-3	codexSpawn	deterministic	-	Worker 3	0
1	1	codex:local:child-1436-2:/fixture/very/long/session/path/used/to/exercise/stable/key/hashing/project-1436/child-1436-2.jsonl	child-1436-2	codexSpawn	deterministic	-	Worker 2	0
1	1	codex:local:child-1436-1:/fixture/very/long/session/path/used/to/exercise/stable/key/hashing/project-1436/child-1436-1.jsonl	child-1436-1	codexSpawn	deterministic	-	Worker 1	0
0	0	codex:local:root-1437:/fixture/very/long/session/path/used/to/exercise/stable/key/hashing/project-1437/root-1437.jsonl	root-1437	-	-	-	-	4
1	1	codex:local:child-1437-4:/fixture/very/long/session/path/used/to/exercise/stable/key/hashing/project-1437/child-1437-4.jsonl	child-1437-4	codexSpawn	deterministic	-	Worker 4	0
1	1	codex:local:child-1437-3:/fixture/very/long/session/path/used/to/exercise/stable/key/hashing/project-1437/child-1437-3.jsonl	child-1437-3	codexSpawn	deterministic	-	Worker 3	0
1	1	codex:local:child-1437-2:/fixture/very/long/session/path/used/to/exercise/stable/key/hashing/project-1437/child-1437-2.jsonl	child-1437-2	codexSpawn	deterministic	-	Worker 2	0
1	1	codex:local:child-1437-1:/fixture/very/long/session/path/used/to/exercise/stable/key/hashing/project-1437/child-1437-1.jsonl	child-1437-1	codexSpawn	deterministic	-	Worker 1	0
0	0	codex:local:root-1438:/fixture/very/long/session/path/used/to/exercise/stable/key/hashing/project-1438/root-1438.jsonl	root-1438	-	-	-	-	4
1	1	codex:local:child-1438-4:/fixture/very/long/session/path/used/to/exercise/stable/key/hashing/project-1438/child-1438-4.jsonl	child-1438-4	codexSpawn	deterministic	-	Worker 4	0
1	1	codex:local:child-1438-3:/fixture/very/long/session/path/used/to/exercise/stable/key/hashing/project-1438/child-1438-3.jsonl	child-1438-3	codexSpawn	deterministic	-	Worker 3	0
1	1	codex:local:child-1438-2:/fixture/very/long/session/path/used/to/exercise/stable/key/hashing/project-1438/child-1438-2.jsonl	child-1438-2	codexSpawn	deterministic	-	Worker 2	0
1	1	codex:local:child-1438-1:/fixture/very/long/session/path/used/to/exercise/stable/key/hashing/project-1438/child-1438-1.jsonl	child-1438-1	codexSpawn	deterministic	-	Worker 1	0
0	0	codex:local:root-1439:/fixture/very/long/session/path/used/to/exercise/stable/key/hashing/project-1439/root-1439.jsonl	root-1439	-	-	-	-	4
1	1	codex:local:child-1439-4:/fixture/very/long/session/path/used/to/exercise/stable/key/hashing/project-1439/child-1439-4.jsonl	child-1439-4	codexSpawn	deterministic	-	Worker 4	0
1	1	codex:local:child-1439-3:/fixture/very/long/session/path/used/to/exercise/stable/key/hashing/project-1439/child-1439-3.jsonl	child-1439-3	codexSpawn	deterministic	-	Worker 3	0
1	1	codex:local:child-1439-2:/fixture/very/long/session/path/used/to/exercise/stable/key/hashing/project-1439/child-1439-2.jsonl	child-1439-2	codexSpawn	deterministic	-	Worker 2	0
1	1	codex:local:child-1439-1:/fixture/very/long/session/path/used/to/exercise/stable/key/hashing/project-1439/child-1439-1.jsonl	child-1439-1	codexSpawn	deterministic	-	Worker 1	0
- Evidence: Target canonical 10,000-node forest (local file: /var/folders/nc/j5wdfhws2q56jzdcj_jkdvww0000gn/T/no-mistakes-evidence/01KXYHH6CTBV43ZATGCMSVB5S7/target-forest.tsv)

Pipeline

Updates from git push no-mistakes

✅ **intent** - passed

✅ No issues found.

✅ **Rebase** - passed

✅ No issues found.

⚠️ **Review** - 1 error
  • 🚨 Sources/Trifola/NavigationSnapshotStore.swift:392 - Required intent says “resolution must run once per corpus rebuild (memoized),” but the new if let memo, memo.source == source cache uses sourceGeneration, which increments on every rebuild(). Search-index progress calls rebuild() every 250 ms, and a normal refresh calls it twice, so unchanged lineage inputs repeatedly miss the memo and rerun the full resolver. Key the memo by a lineage-specific corpus/evidence revision.
  • 🚨 Sources/Trifola/NavigationSnapshotStore.swift:378 - The same required once-per-rebuild invariant is violated while resolution is in flight. Each filter update captures a nil completed memo, cancels only the outer task, and launches another Task.detached; canceled tasks cannot publish their completed forest into the memo. Startup keystrokes can therefore create overlapping full-corpus resolves. Memoize and share an in-flight lineage task, canceling it only when lineage inputs change.
  • 🚨 Sources/Trifola/NavigationSnapshotStore.swift:394 - Required intent says “key each session exactly once,” but even the new memo-hit path immediately calls projectLineage, which rebuilds summaryByKey using SessionLineage.key for every summary at line 621. Thus every keystroke and same-corpus projection still re-standardizes all session paths. Carry the keyed lookup alongside the memoized forest instead of recreating it per projection.

🔧 Fix: Fix lineage memoization and keyed projection reuse
1 error still open:

  • 🚨 Sources/TrifolaKit/SessionLineage.swift:714 - Required intent says sorting must occur “without … hashing long keys per comparison,” but the changed child materialization still sorts sibling keys using nodes[$0] and nodes[$1]. Those dictionary probes hash full stable-key strings for every comparison. Precompute lightweight (key, date, stable) child-order records once, sort those records, then recurse by key.
✅ **Test** - passed

✅ No issues found.

  • git diff --find-renames --find-copies --unified=80 1d1c0bac2bf8252641a908fb96d6613afcafd189..a5abb62c68ce1801d01b21f5015023298ae8af80
  • swift test --filter SessionLineageTests
  • swift test --filter NavigationSnapshotShapeTests
  • swift build -c release --product Trifola
  • Release-build and run the same LineageProbe 10000 <canonical-path> harness seven times against base and target
  • cmp -s <base-forest.tsv> <target-forest.tsv> and shasum -a 256 <base-forest.tsv> <target-forest.tsv>
  • Instrumented production-store check: CFFIXED_USER_HOME=<evidence-home> .build/release/Trifola --evidence-lineage-store --evidence-search-db <evidence-db>
  • git status --short after removing transient probe sources, builds, databases, and sandbox data
✅ **Document** - passed

✅ No issues found.

✅ **Lint** - passed

✅ No issues found.

✅ **Push** - passed

✅ No issues found.

Summary by CodeRabbit

  • Bug Fixes

    • Improved navigation updates when session lineage evidence changes.
    • Preserved stable session identifiers during lineage processing for more consistent navigation results.
    • Improved cancellation handling for long-running lineage calculations.
    • Reused in-progress lineage calculations to reduce redundant processing.
  • Tests

    • Added coverage for stable session keys, cancellation behavior, and lineage revision tracking.

ss251 added 2 commits July 20, 2026 06:45
…out deep copies

Cold-scan batch publishes re-resolved lineage over the full corpus every
250ms, pinning cores for the whole 'Preparing session index' phase: key()
re-standardized every file URL four-plus times per pass, heuristic pools
copied and sorted whole SessionSummary values, and the roots sort hashed
long string keys inside its comparator (then deep-copied materialized
subtrees). Sessions are now keyed exactly once per resolve, pools and
sorts carry small value tuples, and the snapshot store memoizes the
forest per rebuild generation so keystrokes and same-source refreshes
never resolve again. Live corpus: startup burst settles to 0% idle
instead of oscillating 20-80% indefinitely.
@coderabbitai

coderabbitai Bot commented Jul 20, 2026 •

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Lineage resolution now returns stable-key indexes with cancellation support. Session and evidence revisions drive navigation snapshot cache invalidation and shared resolution tasks, while projections derive session filtering and display state from lineage references.

Changes

Lineage and navigation snapshot updates

Layer / File(s) Summary
Indexed lineage resolution and cancellation
Sources/TrifolaKit/SessionLineage.swift, Tests/TrifolaKitTests/SessionLineageTests.swift
Lineage resolution computes stable keys once, returns them with the forest, adds cancellation checks, and preserves deterministic tree materialization.
Revision-aware snapshot resolution
Sources/TrifolaKit/Stores.swift, Sources/Trifola/AppServices.swift, Sources/Trifola/NavigationSnapshotStore.swift
Session and evidence revisions are passed into snapshot rebuilding, invalidate stale lineage results, and coordinate shared cancellable resolutions.
Resolution-backed navigation projection
Sources/Trifola/NavigationSnapshotStore.swift, Tests/TrifolaKitTests/NavigationSnapshotShapeTests.swift
Projection logic uses indexed resolutions and reference fields for eligibility, activity, context weight, and stable-key construction. The snapshot shape test verifies the revised memo wiring.

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

Sequence Diagram(s)

sequenceDiagram
  participant SessionStore
  participant AppServices
  participant NavigationSnapshotStore
  participant SessionLineage
  SessionStore->>AppServices: publish session and evidence revisions
  AppServices->>NavigationSnapshotStore: rebuild with revision inputs
  NavigationSnapshotStore->>SessionLineage: start or share cancellable resolution
  SessionLineage-->>NavigationSnapshotStore: return indexed resolution
  NavigationSnapshotStore-->>AppServices: project updated navigation snapshot
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 14.29% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main change: memoizing and sharing session lineage resolution to avoid repeated work.
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 fix/lineage-resolve-perf

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.

@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.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
Sources/TrifolaKit/SessionLineage.swift (1)

710-745: 🚀 Performance & Scalability | 🟠 Major | ⚡ Quick win

Sibling materialization still hashes long keys per comparator call — this is the PR's own flagged unmet criterion.

childKeys.sorted calls nodes[$0]! and nodes[$1]! inside the comparator (Lines 715-716), i.e. two dictionary probes (each hashing the full standardized-path stable key) per comparison, for every parent's sibling list. The RootOrder pattern introduced just below (Lines 750-767) solves exactly this by precomputing (key, activity, stable) once per node before sorting — the same fix should be applied here.

♻️ Apply the RootOrder pattern to child materialization
     func materialize(_ nodeKey: String, depth: Int) throws -> LineageNode {
         try checkForCancellation()
         let value = nodes[nodeKey]!
         let candidate = value.candidate
-        let childKeys = (childrenByParent[nodeKey] ?? []).sorted {
-            let lhs = nodes[$0]!.session
-            let rhs = nodes[$1]!.session
-            let ld = lhs.startedAt ?? lhs.lastActivity ?? .distantPast
-            let rd = rhs.startedAt ?? rhs.lastActivity ?? .distantPast
-            return ld == rd ? lhs.stableKey < rhs.stableKey : ld < rd
-        }
+        let childOrder = (childrenByParent[nodeKey] ?? []).map { childKey -> ChildOrder in
+            let session = nodes[childKey]!.session
+            return ChildOrder(
+                key: childKey,
+                activity: session.startedAt ?? session.lastActivity ?? .distantPast,
+                stable: session.stableKey)
+        }.sorted {
+            $0.activity == $1.activity ? $0.stable < $1.stable : $0.activity < $1.activity
+        }
         var children: [LineageNode] = []
-        children.reserveCapacity(childKeys.count)
-        for childKey in childKeys {
-            children.append(try materialize(childKey, depth: depth + 1))
+        children.reserveCapacity(childOrder.count)
+        for child in childOrder {
+            children.append(try materialize(child.key, depth: depth + 1))
         }

Add a small ChildOrder struct (key/activity/stable) alongside RootOrder, mirroring its shape.

This confirms the exact gap called out in the PR description itself.

🤖 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 `@Sources/TrifolaKit/SessionLineage.swift` around lines 710 - 745, Optimize
sibling ordering in materialize by introducing a ChildOrder value containing
each child’s key, activity date, and stable key, populated before sorting. Sort
the precomputed ChildOrder collection without repeated nodes lookups, then
iterate its keys when recursively materializing children; define ChildOrder
alongside RootOrder and preserve the existing ordering behavior.
🤖 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.

Outside diff comments:
In `@Sources/TrifolaKit/SessionLineage.swift`:
- Around line 710-745: Optimize sibling ordering in materialize by introducing a
ChildOrder value containing each child’s key, activity date, and stable key,
populated before sorting. Sort the precomputed ChildOrder collection without
repeated nodes lookups, then iterate its keys when recursively materializing
children; define ChildOrder alongside RootOrder and preserve the existing
ordering behavior.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 243a60f4-0b90-490e-b10c-efb3334e5007

📥 Commits

Reviewing files that changed from the base of the PR and between 1d1c0ba and a5abb62.

📒 Files selected for processing (6)
  • Sources/Trifola/AppServices.swift
  • Sources/Trifola/NavigationSnapshotStore.swift
  • Sources/TrifolaKit/SessionLineage.swift
  • Sources/TrifolaKit/Stores.swift
  • Tests/TrifolaKitTests/NavigationSnapshotShapeTests.swift
  • Tests/TrifolaKitTests/SessionLineageTests.swift

@ss251
ss251 merged commit f3b3d81 into main Jul 20, 2026
3 checks passed
@ss251
ss251 deleted the fix/lineage-resolve-perf branch July 20, 2026 02:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant