Deep dive · 2026-08-19 · HEAD c3c786ab

One Surface

Findings·and the path to 1.3

Wave 1 closed real defects and three of the premises the 1.3 plan rests on turn out to be false. Retrieval is three surfaces, not one, and the lifecycle gate reached exactly one of them. No trustworthy accuracy number exists at HEAD. And the benchmark gate we have been steering by targets a score of zero.

RELEASEDv1.2.2 FINDINGS RE-VERIFIED242 REAL BLOCKING SET9 CURRENT LAFS GAIN0.0000 SHORT OF THE CLIFF91 of 451
The Verdict

Three premises in the 1.3 docs do not survive contact with the code.

Nine ground-truth lanes re-verified the 2026-08-13 audit against HEAD, then five adversarial passes attacked the results. 242 findings came back where the audit counted about 120. Thirty were claimed closed. Fifty corrections survived review, and three of them re-sequence the release.

Premise 01 · false

The leaderboard is not empty, and the gate targets a zero.

The LongMemEval-V2 small tier has held three entries since 2026-08-09, four days before the audit asserted first-mover status. Both SOTA sweeps missed it because the static HTML ships hardcoded "coming soon" rows while the real entries live only in the deployed JavaScript. A fetch that does not execute JS cannot see them.

Worse, baseline-beat-gate aims at 42.8% at 10s. Under the published LAFS formula that scores exactly 0.0000. The reference floor across the whole integration window is 51.0%.

Premise 02 · false

Retrieval is three surfaces and #390 gated one.

Every call site of graph_metadata_recallable resolves inside context_search or pack admission. None lands in retrieval/hybrid.py, tools/search.py, or retrieval/naive.py. So sibyl search, REST /search, and MCP search all serve corrected memories as current.

And the correction action delete maps to a soft MemoryLifecycleState.DELETED, so the ungated lane serves memories a user explicitly deleted.

Premise 03 · overstated

The ranker is decontaminated. The measurement never happened.

#386 fully removed the corpus vocabulary. Zero hits across retrieval/ for the proper nouns or the dataset typo. What survives is a chat-transcript-shape lexicon, defensible for a conversational-memory product.

The live problem is the other half of the finding: the re-measurement never ran. #386's own commit body calls the anchor run "the planned next step", #395 then moved the tokenizer again, and the strategy skeleton still cites 30.38% unqualified.

What wave 1 genuinely closed, verified reachable on the default path: the metadata snapshot resurrection and the passage retire-sweep hole (#393), writable typed predicates end to end across CLI, REST, and MCP (#391), the CLI auth-URL precedence and 32 silent exit-0 paths (#392), the backups cross-org read (#384), MCP scope enforcement (#385), and the chart JWT-secret guard (#387). The typed-predicate walk now has a producer, so the traversal weight table is no longer a wish list.

The Core Defect

One request, two opposite treatments of one predicate.

A default /context/pack request at retrieval_mode=fast runs both halves of the system at once. The evidence block goes through _execute_context_evidence_search into hybrid_search, which supplies its own predicate table via default_factory where SUPERSEDES is a 1.1 boost. The pack sections come from context_search, where SUPERSEDES is excluded from the walk entirely.

The same retired row is dropped from the sections and promoted in the evidence, and the benchmark measures the composite. That is why no arm result is interpretable until the gate covers every surface. It is also why the fix is a lifecycle gate rather than a pipeline merge: two pipelines with different weight tables is a pre-1.0 architectural split that has shipped in every release and corrupts nothing on its own.

Read surfaceLifecycle gateSUPERSEDESServes deleted rows
/context/pack pack sectionsgatedexcluded from walkno
/context/pack evidence at fastungated1.1 boostyes
REST /api/searchungated1.1 boostyes
sibyl searchungated1.1 boostyes
MCP searchungated1.1 boostyes
/context/pack evidence at naive (#396)ungatedn/ayes

Cut criterion for 1.3: a two-row probe writes memory A, supersedes it with B, and asserts A is absent from all five read surfaces. Today four of five serve it.

The Blocking Set

Nine items, down from thirty-five claimed.

The adversarial passes deflated far more than they escalated. Of 16 tier-2/3/4 findings marked 1.3-blocking, 14 did not survive. Six of 29 tier-0 items had no named attacker and no boundary crossing. The CORS entry filed as blocking is a false alarm, because all eight set_cookie sites are SameSite=Lax so no cookie ever rides the cross-site fetch. What remains is smaller and much sharper.

BlockerClassWhy it cannot ship openSize
MCP writes carry no org-role gate security MCP mounts in the same process as REST, and load_access_token accepts any valid session JWT. The JWT path resolves org_role and hands it to the policy context, then nothing compares it to a write-role set. A VIEWER performs writes REST refuses. M
Lifecycle gate reaches one of five surfaces wrong data Corrected and explicitly deleted memories are served as current on every default read path except pack sections. M
Failed local job blocks its own retry for 24 hours wrong data Default backend. The entity id is content-derived, so retrying the same add reuses the dedup key and the write is silently swallowed. Not a latent variant, the ordinary user retry. S
create_entity returns success after losing its enqueues wrong data A create that fails to enqueue embedding backfill or memory extraction logs a warning and reports success, so the row exists and is unreachable by recall. S
Vector lane returns [] while the receipt says completed measurement Launders a broken HNSW lane into a completed search. Every accuracy number measured through it is unattributable. S
compile_context swallows retrieval exceptions measurement Arm asymmetry against #396: the naive control re-raises and drops the datapoint while the Sibyl arm silently degrades, so the race is biased before it runs. S
Both anchors are invalid and nothing says so measurement Nothing measured at or after #386. #395 additionally rewrote the adapter's client-side slicing, so the old anchor is unpairable for two independent reasons. L
Browser e2e suite can only skip or pass reliability All five tests follow redirects and accept 3xx while proxy.ts redirects unauthenticated requests to /login. Six dependabot PRs are queued for merge on that green. S
Readiness probe blind to a dead broker reliability All four startup handlers swallow into log.warning, the initialized flags are read nowhere, and check_readiness checks only Surreal. A pod that drops every background job reports ready. S

Deflated out of the blocking set with proof: the worker CrashLoopBackOff is cosmetic because the API pod runs the queue in-process under the local backend; the SurrealDB password cannot rotate out from under a working install because the chart has no SurrealDB dependency at all; the missing fsGroup fails loudly at first install rather than silently; helm template is executed by real subprocess tests inside the release gate; ordinal fusion discarding score magnitudes is Reciprocal Rank Fusion behaving as specified; and every "missing test" finding is coverage debt, not a shipped defect.

The Benchmark

51.0% is a cliff, not a slope.

LAFS gain is the integral of reachable accuracy over log-scaled latency budgets from 1s to 200s, scored against a fixed reference frontier. Because RAG: query to slice + notes sits at 51.0% and 0.2s, the entire region from 1s to 26.9s is already held at 51.0. Any system at or below that scores exactly zero no matter how fast it runs. There is no partial credit and no first-mover credit for showing up at 40%.

0.0000Sibyl LAFS gain today
31.26%best comparable, at 7.48s
230 / 451correct needed to start scoring
91answers short of the cliff
1.0955bar to beat, verifiably

The board, read live from the deployed JS

SystemSmall tierLAFS gainNote
subAgentRunbook-C v2 Online75.61% @ 130.54s2.8231not reproducible from its displayed point
subPerpetual Recall V353.22% @ 1.96s1.0955reproduced exactly; the real bar
subOPS-Agentic-Memory-v181.37% @ 153.64s0.3223accuracy leader, near-worthless gain
refAgentRunbook-C74.9% @ 108.3sfrontier413 lines, beats R by 16.3pp
refCodex69.9% @ 177.2sfrontierno memory architecture at all
refAgentRunbook-R58.6% @ 26.9sfrontier3,258 lines of classical memory
refRAG: query to slice + notes51.0% @ 0.2sthe cliffsets the floor; two embedding round trips
refRAG: query to slice42.8% @ 0.1sfrontiersame code, one boolean flipped
refNo retrieval1.3% @ 0sfrontierreader only

The top entry does not reconcile. Our reimplementation of LAFS reproduces the other two gains to six decimal places, but 2.8231 computes to 0.0572 from its own displayed operating point, a 49x discrepancy, and the ceiling at 130.54s even at 100% accuracy is 2.0211. Multi-point submission is officially permitted and the scorer takes the union, so the row is a lossy display of a submission whose gain comes from undisplayed faster points. Note also that 2.8231 is a hand-typed four-decimal literal while the other two carry full float64 precision.

Where we could land

Accuracy is exposure times conversion. Today that is 0.652 times 0.48 for 31.3%, against 31.26% measured, so the model is trustworthy enough to price levers with. The 82.6% figure is a measured ceiling on selection, so exposure cannot exceed it without new acquisition.

ExposureConversionAccuracyLAFS @7.5sLAFS @2sBoard position
65.2%48%31.3%0.00000.0000off board (today)
75.0%50%37.5%0.00000.0000off board (render wave lands)
82.6%48%39.7%0.00000.0000off board (full exposure ceiling)
82.6%55%45.4%0.00000.0000off board (best measured conversion)
82.6%62%51.2%0.05110.1040on the board, last
82.6%65%53.7%0.65091.32442nd on gain at 2s
82.6%70%57.8%1.64403.34561st outright at 2s

Read the third row twice. If we cash the entire un-cashed oracle exposure gap, every ingest lever in the strategy skeleton, all the way to the measured selection ceiling, and reader conversion does not move, we land at 39.7% and score zero. Retrieval work alone cannot put us on this board. The binding constraint is reader conversion, which the strategy skeleton ranks fifth.

And the empty square. Nobody on this board has put structured memory above roughly 53% at single-digit seconds. 58% at 2s scores 3.4337 and takes the top gain slot outright. 58% is AgentRunbook-R's accuracy, which R pays 26.9s of one thinking-mode LLM call and eight sequential embedding round trips to reach. Closing that gap is a memory-architecture problem, not an agent-loop problem, and it is exactly where Sibyl lives.

What the Winners Teach

We are the compact one by a factor of ten, and it buys us nothing.

The official harness ships every baseline's source, so this is a code read rather than a paper read. Five lanes read it against the real corpus and two adversarial passes killed 21 of 32 proposed transfers, including three the readers fabricated outright.

Lesson 01 · the big one

Reader payload is free on the scored axis.

LAFS latency is memory_query_avg_seconds, timed around memory.query() only. Reader time, ingest time, and post_query_hook are not counted. The 51.0% baseline spends 0.2s of scored time to hand its reader a mean of about 368,000 characters plus six screenshots. Sibyl delivers about 37,000 characters and 11,000 reader tokens against a 200,000-token allowance it grants itself, with zero truncation events.

We have been optimizing an axis the metric ignores, and paying for the restraint in accuracy.

Lesson 02 · the mechanism

Compaction does not scale with the budget.

compact_content_for_query emits at most four windows of eight lines, or eight of four in compact mode, roughly 32 lines regardless of allocation. The only path to whole-state exposure is len(content) <= max_chars returning mode full.

So raising the character total is not a geometry tweak. It is the switch between showing a state and showing 32 lines of it. That is where the exposure gap lives.

Lesson 03 · free density

The action spine carries half the gold for 508 tokens.

R annotates each action with the accessibility line of the element it consumed, turning click('a790') into click('a790') # [a790] menuitem 'Filters'. Pure AST parse plus a dict lookup, zero LLM. The whole trajectory spine is attached to every retrieved result.

Measured over both haystacks: a median 1,478 characters carrying 70% of procedure gold literals and 49% overall, riding on a 23,444-token slice. Sibyl has no equivalent.

Lesson 04 · the +8.2pp

Notes are a single flipped boolean, and strictly additive.

The two RAG leaderboard rows are the same module run against configs differing in one character. Two note kinds live in separate pools searched independently at top-3, so a procedure note and a hint note always both arrive, and the raw lane is byte-identical between arms. Notes add context and never evict a slice.

Sibyl's dedupe key collapses all three note kinds into one slot per trajectory, and its note lane is a reservation that evicts raw evidence.

Lesson 05 · the unattacked pool

The abstention block is four times bigger than we thought.

There are 128 -abs questions scored by llm_abstention_checker, rolling up into the static, dynamic, and procedure columns. The errors-gotchas column we have been measuring is only 29. That is 28.4% of the board, every reference system is weak there, and it points at exactly the product failure where a confidently surfaced memory is confidently wrong.

The catch: the judge converts only when the answer names the specific flaw. A generic UNKNOWN scores zero.

Lesson 06 · architecture

"Memory architecture is overkill" survives only in the weak form.

Codex has literally no memory architecture and reaches 69.9% by giving a frontier model shell access for 177 seconds. Its LAFS gain is zero, because AgentRunbook-C dominates it on both axes, and C's entire delta is a runbook, a deterministic corpus digest, and ingest-time symbol resolution. Structure bought 69 seconds and 5 points.

Read the other end too: the 3,258-line classical memory system loses to C's 413 lines by 16.3 points. Architecture does not buy top accuracy here. It buys the latency axis, and the latency axis is the entire metric.

Honest capability gap. Our corpus was downloaded in text-context mode, so the screenshots are absent. The 51.0% baseline's note distiller reads pixels and no accessibility text at all, and both RAG arms ship the centre state's screenshot to a vision reader. We have never had access to that channel, and any comparison should say so.

Ranked Levers

Ordered by expected gain per unit of work, survivors only.

Twenty-one of thirty-two proposed transfers died in review. These are what is left, with the killed ones and their reasons recorded in the traps section. Nothing above rank 13 requires a corpus rebuild.

#LeverMovesExpectSizeThe change
1Raise the reader character total so items reach mode=full exposure+5 to +12ppS DEFAULT_CONTEXT_TOTAL_CHARS 60,000 toward 400-600K, and the CLI default. Worth 0.0000 alone; it is the carrier that makes every other exposure lever cashable.
2Include note_kind in the typed-lane dedupe key exposure+1 to +3ppXS operational_evidence.py keys on (type, source_id) and all three kinds share type note, so only the top kind per trajectory survives. Already stamped in metadata, so a key change not a schema change.
3Make the note lane additive instead of an evicting reservation exposure+1 to +3ppS Grant notes their own lane the way neighbour-overflow and traversal already do. Additive in items is not additive in characters, so land it with lever 1.
4English lane labels, and group items by lane conversion+0 to +2ppXS A lane token already ships, it is just jargon. Production already emits proper section titles, so this also closes a bench-versus-production divergence.
5Render the annotated action spine once per trajectory multiple+1 to +3ppS At pack assembly, from a per-trajectory side artifact. Never in _trajectory_header, which is embedded and would flatten within-trajectory discrimination and force a re-index.
6Add an observed-absence key to note distillation premise+1 to +4ppS A fourth key for controls looked for and not found, actions attempted and blocked. Question-blind by construction, so inside the integrity boundary. Rides the campaign's only lever with a stable positive receipt.
7Un-starve the note digest: scale the line budget and add interactive roles exposure+1 to +3ppM 160 total lines is 1.6 lines per state on a 101-state trajectory, and 37% of literal-answer questions lose gold before the model is called. Both halves together or the role additions displace the content lines that were the web-gate NO-GO mechanism.
8Buy two fast operating points the week one point clears 51.0 selection+0.5 to +0.9 LAFSS Multi-point is permitted and the scorer takes the union. 53% at 2s plus 57% at 8s totals 1.8966. Stack only below 26.9s.
9Coarse per-source routing census on the retrieval side exposure+0 to +4ppM One of only two levers that can lift exposure past the 82.6% ceiling. Retrieval-side only, zero characters of reader budget, or it becomes the killed inventory-annotation chain wearing a hat.
10LLM surface decomposition with per-target reserved slots multipleunknownM A conditional revival of the killed planner, justified by two changed mechanisms: disjoint surface targets rather than PRF expansion, and reservation rather than RRF. Costs 1 to 3s, which is fatal to the 2s zone.
11Deterministic enumerative-absence detector on selected passages premise+0 to +3ppM Sibyl-original; claim no harness provenance. Needs a pre-registered detector-precision measurement first, because a false absence claim is worse than silence.
12Ship screenshots as image context items exposureunmeasuredL The one exposure channel never touched. Requires re-downloading the corpus with screenshots. Highest variance on the list; run it after lever 1 reports.
13The corpus rebuild wave selectionlow single digitsL Ranked last deliberately. Selection work against a bottleneck measured as exposure times conversion, carrying the campaign's most expensive item for its smallest expected gain.
The Plan

1.3 is "One Surface". Everything else waits for a real number.

Three separate agendas were each deferred into 1.3: the roadmap's "Lead It" with TeamMemBench and the coalescence engine, the benchmark chase moved whole from 1.2, and the rethink's own Phase 0 through 3 plus the debt ledger. They cannot all ship. Coalescence has zero lines of code in the repo and gets an explicit deferral with a written reason rather than silent omission.

Phase 0a · One surface, enforced

Mfirst

Every retrieval entry point drops the same retired rows and scores predicates from one table, so that any number produced afterward means something.

  • Lift the lifecycle half of the supersession gate into a surface-agnostic helper and call it from tools/search.py and retrieval/naive.py.
  • Exclude SUPERSEDES from the outgoing direction of the hybrid walk. Keep incoming: reaching the successor from the retired row is wanted.
  • Collapse the two predicate weight tables to the tuned 21-entry one, and add CONTRADICTS, which is declarable and writable on all three surfaces yet scores the untyped baseline at read.
  • Fix the exact-name rescue guard, which is true whenever the seed search succeeds so the lane never fires, and the fixture whose mock omits the metadata key production always sets.
  • Publish a per-predicate hop counter on the receipt, so a typed-predicate arm that contributed zero typed hops is distinguishable from one that contributed many and lost.
Gate · behavioralA two-row supersession probe returns zero retired rows across all five read surfaces, down from four of five today. Plus one test pinning predicate ordering identical across both call paths for all 21 entries.

Phase 0b · Rig repair and the first trustworthy number

L

One post-decontamination anchor that satisfies same-commit pairing, with a measured noise floor attached, on a stack whose latency receipts are citable. Until this lands every accuracy claim here is a hypothesis.

  • Flip the retrieval screen's --source-evidence-bundling default to match the adapter, since the naive conflict guard currently refuses the arm at construction.
  • Make the live smoke's typed-evidence check mode-aware, because it requires a selection origin the arm structurally cannot emit and so exits 1 on a healthy run.
  • Promote jitter_floor.py and the pack comparator out of gitignored cache into tools/bench/ with their self-tests on a moon task. The tooling that enforces the rig's integrity currently dies with the cache directory.
  • Bump the harness pin off the pre-metadata-leak revision and rewrite the adapter's set_query_context override, which raises TypeError against the new keyword-only signature.
  • Add a native-process mode to the Surreal runtime monitor, which resolves its target through docker compose and so guards CI only.
  • Fix the two receipt-integrity blockers first: the swallowed vector lane that reports completed, and the compile_context swallow that biases the arm.
Gate · pre-registeredRun an A/A pair first. The anchor is accepted only if the combined-accuracy span is at most 3pp, every row carries the run SHA with git_dirty false, and arm receipts are non-empty on every row. Publish the anchor with its measured A/A span attached as its noise floor. Do not reuse 30.38%: that is the deprecated accurate arm, and the comparable machine number is 31.26% at 7.48s.

Phase 0c · The naive-strong race

L

Find out whether the machine earns its latency. The fusion and pipeline-unification questions then resolve as consequences rather than as abstract design calls.

  • Pre-register the arm definition, the numbers, and the geometry confound in writing before the paired pass.
  • State plainly that the machine arm is hybrid evidence plus eight-lane pack sections plus client assembly, so a loss localizes to the whole machine and not to fusion. The rethink doc's framing is wrong on this.
  • Add one matched-geometry control pass at equal exposed characters, since the arm gets a materially larger payload by default.
  • Post-hoc arm-activity check before any number is quoted.
Gate · pre-registeredThe arm wins, meaning the machine becomes deletable, at mean paired delta at or above 0pp with consistent sign across 3 passes and arm latency at most 60% of machine latency. The machine survives at +3pp or better with consistent sign. A result between minus 3 and plus 3 resolves for the arm on latency grounds.

Phase 1 · The render wave

Mreprioritized

Levers 1 through 7, bundled as one arm with per-lever receipts. This is the phase the board arithmetic promoted: exposure alone cannot clear the cliff, and conversion is where the missing points are.

  • Raise the character total so selected items reach whole-state exposure.
  • Fix the per-kind note dedupe and make the note lane additive.
  • Label and group the lanes in English.
  • Render the action spine once per trajectory at assembly time.
  • Add the observed-absence key, and un-starve the digest with roles and budget together.
Gate · bundled, never soloEach lever is plausibly worth 0 to 3 points against a 3-point developmental noise floor, so a solo gate spends a paid run to learn nothing. One arm, per-lever receipts, multi-pass replay. If exposure stays near 0.652 after the character raise, the exposure hypothesis is falsified and levers 3, 5, and 7 lose their premise, which is a result worth having for one flag.

Phase R · The release surface

Mparallel

Make the shipped chart and the release path provable at review time rather than at cut time.

  • Add CI classifier arms for charts/, VERSION, install.sh, and the Tiltfile; widen the workflow arm; invert the unmatched-path default so it fails closed naming the file.
  • Gate a job on that flag reaching the twelve helm render tests that already exist and already pass on CI runners, plus a real helm template matrix with defaults and a production values set.
  • Fix the e2e suite so it can fail, since six dependabot PRs are merging on a green that has exactly two outcomes.
  • Clear the stock-chart papercuts: no value reaches the public URL settings, so every deploy emails password-reset links pointing at localhost.
  • Write the release runbook outside the YAML, and fix the three-way prerelease-label divergence where a beta tag passes the format check then dies inside version sync.
GateEvery one of charts/, VERSION, and the publish workflow triggers at least one non-skipped job on a PR. A stock-defaults render produces no worker Deployment unless the backend is redis.

Phase D · The debt that actually blocks

Mparallel

The nine blockers, and nothing else. Everything tier 4 goes to 1.4: the route-package helper sprawl, the divergent result normalizers, the MCP and REST convergence refactor, the dead archive tables.

  • MCP write role gate, and validate related_to targets on the MCP add path the way REST does.
  • Readiness that can see a dead broker, and coordination_backend=auto selecting redis when redis is configured.
  • The 24-hour retry block on the default backend, and the enqueue-loss subset of failure-looks-like-success.
  • Live 3.2.3 coverage for the attributes merge, the sentinel clear, and the snapshot fold, including a same-uuid rewrite. Every entity write's preservation semantics currently rest on behavior verified only against the bundled 2.x embedded engine.
  • Projection inheritance expressing removal as None rather than omission, since #393 inverted the semantics underneath it.
  • Issue #394 and the sibling bulk_create_direct drift one file over.

Phase M · MCP SDK 2.0

Mafter 1.3

Smaller than the issue implies. FastMCP became MCPServer and the session manager, custom routes, tool and resource decorators, the app builder, and the access-token hook all survive at their old paths. Two files import the old surface.

  • Move host, port, and stateless mode off the constructor onto the app builder. This is the risky edit: the builder defaults host to localhost, which auto-enables DNS-rebinding protection, and production binds 0.0.0.0.
  • Preserve the auth settings verbatim, because the lowlevel builder gates the entire auth middleware chain on their presence and dropping them silently disables #385.
  • Close #399 as superseded rather than trying to make it green. It contains only the pin and the lock, so it never can.
Gate · three assertions CI cannot makeA non-localhost Host header is accepted when bound to 0.0.0.0. A key scoped read-only is still refused a write tool, which is #385's invariant and is currently proven by nothing, because that test module is one of the thirteen that cannot collect under 2.0. All thirteen collect and pass.

Cut from 1.3, explicitly: the skeleton's sub-1K corpus rebuild, because the LME corpus declares zero relationships and the rebuild cannot start before the anchor exists; the coalescence engine and TeamMemBench, deferred to 1.4 with the roadmap's own sequencing logic intact; reader-side prompt work beyond the render wave, partly because nine readers disagree on the sign of an evidence intervention in a third of jointly affected cells and our levers are screened on one pinned reader while the product serves Claude and GPT.

Now Queue

Fourteen PRs, in dependency order, from a clean tree.

Each lands independently. The first four are uncontroversial and clear the queue the user asked to see handled.

  1. Merge #404 alone, then dry-run the publish workflow

    Its CLEAN status means nothing ran: the file it edits is dispatch-only, so no PR-triggered job can exercise it, and every other suite skipped. Both major-version breaking changes are default flips with nothing removed and the call site passes no inputs, so it is safe on the evidence. It lands in the irreversible publish job, so the dry-run is the only way to exercise it before it matters.

    merge as-is, then gh workflow run publish.yml -f dry_run=true

  2. Consolidate the three clean python bumps into one commit and one lock

    All three contend on uv.lock and two on the same pyproject, so serial merging buys three rebases and three CI cycles for three patch-to-minor moves. Before merging, run the check CI cannot: pydantic-ai 2.30 moved provider imports to construction time and build_model constructs all three. Add the uvicorn upper bound as ordinary pin hygiene, matching mcp, authlib, pyjwt, and surrealdb.

    nova/deps-python-lock-consolidation · closes #398, #400, #401

  3. Merge the web pair, #403 first, then rebase #402

    Both regenerate the pnpm lock with the same peer churn, so they conflict. #403 first because the Storybook job actually ran and passed on it, which is the strongest real evidence in the queue. Before #402, boot the app and load login plus an authenticated page by hand: 16.3.1 backports a fix restoring the live headers view, the server-side auth path reads cookies from that API, and the e2e suite cannot see a render break.

    merge #403, @dependabot rebase, merge #402

  4. Fix #394 and the sibling drift one file over

    The backfill calls a method removed from EntityManager and exits 0 with a generic warning. Do not reach for list_all: it hydrates, and the row builder promotes exactly the columns the detection tests for, so the repair would become a silent 100% already-set no-op. Add a raw-row pager instead. One file over, a probe reaches for a name that exists nowhere, so graph restore has silently lost its bulk write path while twelve mock sites assert against a method production cannot call.

    nova/fix-denormalized-backfill-and-bulk-probe

  5. Gate the lifecycle on every retrieval surface

    The worst live defect at HEAD and it is on every default path. It also unblocks measurement, because the benchmark hits the pack route whose evidence lane is the ungated one, so no arm result is interpretable until this lands.

    nova/gate-lifecycle-on-every-retrieval-surface

  6. One predicate table, plus CONTRADICTS, plus the rescue-lane guard

    One request currently exercises both tables with opposite treatment of the same predicate. CONTRADICTS is declarable and writable on all three surfaces and gated as suppressing at write, while scoring the untyped baseline at read, so #391's vocabulary is three predicates that pay rather than five.

    nova/one-predicate-table-plus-contradicts

  7. Gate MCP writes on org role

    A live intra-tenant authorization bypass. The role is already resolved on the JWT path and handed to the policy context; nothing anywhere compares it to a write-role set. While there, raise on the dead client-supplied-project fallback rather than leaving it as a trap for the first entry point that admits an org-only credential.

    nova/mcp-write-role-gate

  8. Repair the bench screen path and the two receipt liars

    Replay before paid is protocol law and both cheap entry points refuse the arm today. Bundle the swallowed vector lane that reports a completed search and the context-compile swallow that biases the arm, because both would poison the race before it runs.

    nova/bench-screen-path-repair

  9. Bump the harness pin and fix the adapter override

    The pin is three commits stale at a pre-metadata-leak revision whose baseline modules were then substantially rewritten, so an anchor cut on it gets thrown away. Not a one-line ref change: the upstream signature went keyword-only. Worth noting in the receipt that our adapter sanitized query context 25 days before upstream did.

    nova/lme-harness-pin-bump

  10. Classify the release surface in CI and render the charts

    Every asset needed already exists and already passes on CI runners. Only a trigger is missing. Fail-closed matters because the last fix added arms for five paths and still missed charts, VERSION, the installer, and six workflow files.

    nova/ci-classify-release-surface-and-render-charts

  11. Make the e2e suite capable of failing

    All five browser tests follow redirects and accept any 3xx while the proxy redirects unauthenticated requests to login, so the suite has two outcomes, skip or pass. Until this lands, "E2E green" means the server booted and nothing more.

    nova/e2e-can-actually-fail

  12. Readiness that sees a dead broker, and auto that selects redis

    Four startup handlers swallow into warnings, their flags are read nowhere, and readiness checks only Surreal. The default queue is in-process and persists nothing across a restart.

    nova/readiness-sees-dead-broker

  13. Inheritance expresses removal as None, plus quality-metadata precedence

    #393 inverted the semantics underneath these helpers: omission used to clear a key and now preserves it, and span ids are deterministic so reprojection lands on the duplicate path. Bundle the metadata precedence inversion, where legacy shadow names rank first so a patch that sets importance is silently reverted on the next read, on the ranking input that decides retention.

    nova/inheritance-expresses-removal-as-none

  14. Live 3.2.3 coverage for the merge semantics

    Every entity write's preservation semantics rest on duplicate-key resolution verified only on the bundled 2.x embedded engine while production runs 3.2.3, and the nightly live suite never writes the same uuid twice. If the production engine ever resolves duplicates the other way, every write inverts into stale-value-wins with no error anywhere.

    nova/live-merge-semantics-on-32

Open Decisions

Five forks, each with a recommendation attached.

What is 1.3?

Three agendas were each deferred into it. No Sibyl decision records which one wins; the fork was left open.

Recommend "One Surface". Collapse the disagreeing read surfaces onto one lifecycle rule and one predicate table, cut the first honest anchor, run the race, clear the nine blockers and the chart papercuts. Coalescence and TeamMemBench get an explicit written deferral to 1.4.

Does 1.3 chase a submission?

Keep chasing with the gate recalibrated, retarget to a different board, or ship with no benchmark claim.

Recommend no benchmark claim in 1.3, with the gate recalibrated as part of it. Every honest 1.3 outcome scores 0.0000, so a submission buys presence at rank last with a public number to defend. Buy the levers because they are defects in our own retrieval and let the board score them when the arithmetic pays.

Which tier?

The medium board is empty and its cliff is 5.1 points lower, which looks like the better target.

Recommend small, and only small, until we clear 51.0 there. Medium has 433 distinct haystacks averaging 487 trajectories against small's two shared haystacks of 100, so one run pair is 451 fresh memory builds. The empty board is an entry-cost artifact. Naive slicing loses exactly 5.1 points from small to medium, so the lower cliff is nominal. Worth measuring cheaply on a subsample: our own small-to-medium slope, since typed pools lose only 1.6 points where naive slicing loses 5.1.

If the naive arm wins the race, do we delete the machine?

Rip it out inside 1.3, ship the arm as a selectable non-default with a pre-committed deletion, or keep both behind the flag.

Recommend selectable non-default, deletion pre-committed to 1.4. A mid-release rip-out of the pipeline that also carries the supersession gate, the typed-predicate walk, and the pack section builder is how a release stops shipping. Reject "keep both" explicitly: two pipelines is the condition that produced this whole lane of defects.

Tags: index them or demote them honestly?

Add the index and expose a filter, state in the tool schemas that they are browse-only, or leave as-is.

Recommend honest demotion in 1.3, and revisit. Tags have no index, no WHERE clause, and a Python post-filter reachable only from the browse surface, so today they are write-only. Demoting the claim costs a docstring and stops the surface implying something false. Leaving as-is is the only wrong answer.

MCP SDK 2.0 in 1.3 or after?

Take the major with the release, or ship on the cap and land the port immediately after.

Recommend after. The port is mechanically small but both of its real failure modes are invisible to CI, and the test that would catch the worse one is currently proving nothing because it cannot collect. That is not a thing to verify under release pressure. Close #399 now either way.

Traps

Things that look transferable and are not.

Twenty-one of thirty-two proposed transfers died in adversarial review. Three were fabricated by the reading agents, which is worth recording as its own lesson about accepting a mechanism because it sounds like something a strong system would do.