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.
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.
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%.
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.
#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.
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 surface | Lifecycle gate | SUPERSEDES | Serves deleted rows |
|---|---|---|---|
/context/pack pack sections | gated | excluded from walk | no |
/context/pack evidence at fast | ungated | 1.1 boost | yes |
REST /api/search | ungated | 1.1 boost | yes |
sibyl search | ungated | 1.1 boost | yes |
MCP search | ungated | 1.1 boost | yes |
/context/pack evidence at naive (#396) | ungated | n/a | yes |
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 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.
| Blocker | Class | Why it cannot ship open | Size |
|---|---|---|---|
| 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.
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%.
| System | Small tier | LAFS gain | Note | |
|---|---|---|---|---|
| sub | AgentRunbook-C v2 Online | 75.61% @ 130.54s | 2.8231 | not reproducible from its displayed point |
| sub | Perpetual Recall V3 | 53.22% @ 1.96s | 1.0955 | reproduced exactly; the real bar |
| sub | OPS-Agentic-Memory-v1 | 81.37% @ 153.64s | 0.3223 | accuracy leader, near-worthless gain |
| ref | AgentRunbook-C | 74.9% @ 108.3s | frontier | 413 lines, beats R by 16.3pp |
| ref | Codex | 69.9% @ 177.2s | frontier | no memory architecture at all |
| ref | AgentRunbook-R | 58.6% @ 26.9s | frontier | 3,258 lines of classical memory |
| ref | RAG: query to slice + notes | 51.0% @ 0.2s | the cliff | sets the floor; two embedding round trips |
| ref | RAG: query to slice | 42.8% @ 0.1s | frontier | same code, one boolean flipped |
| ref | No retrieval | 1.3% @ 0s | frontier | reader 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.
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.
| Exposure | Conversion | Accuracy | LAFS @7.5s | LAFS @2s | Board position |
|---|---|---|---|---|---|
| 65.2% | 48% | 31.3% | 0.0000 | 0.0000 | off board (today) |
| 75.0% | 50% | 37.5% | 0.0000 | 0.0000 | off board (render wave lands) |
| 82.6% | 48% | 39.7% | 0.0000 | 0.0000 | off board (full exposure ceiling) |
| 82.6% | 55% | 45.4% | 0.0000 | 0.0000 | off board (best measured conversion) |
| 82.6% | 62% | 51.2% | 0.0511 | 0.1040 | on the board, last |
| 82.6% | 65% | 53.7% | 0.6509 | 1.3244 | 2nd on gain at 2s |
| 82.6% | 70% | 57.8% | 1.6440 | 3.3456 | 1st 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.
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.
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.
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.
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.
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.
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.
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.
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.
| # | Lever | Moves | Expect | Size | The change |
|---|---|---|---|---|---|
| 1 | Raise the reader character total so items reach mode=full |
exposure | +5 to +12pp | S | 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. |
| 2 | Include note_kind in the typed-lane dedupe key |
exposure | +1 to +3pp | XS | 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. |
| 3 | Make the note lane additive instead of an evicting reservation | exposure | +1 to +3pp | S | 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. |
| 4 | English lane labels, and group items by lane | conversion | +0 to +2pp | XS | A lane token already ships, it is just jargon. Production already emits proper section titles, so this also closes a bench-versus-production divergence. |
| 5 | Render the annotated action spine once per trajectory | multiple | +1 to +3pp | S | 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. |
| 6 | Add an observed-absence key to note distillation | premise | +1 to +4pp | S | 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. |
| 7 | Un-starve the note digest: scale the line budget and add interactive roles | exposure | +1 to +3pp | M | 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. |
| 8 | Buy two fast operating points the week one point clears 51.0 | selection | +0.5 to +0.9 LAFS | S | 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. |
| 9 | Coarse per-source routing census on the retrieval side | exposure | +0 to +4pp | M | 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. |
| 10 | LLM surface decomposition with per-target reserved slots | multiple | unknown | M | 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. |
| 11 | Deterministic enumerative-absence detector on selected passages | premise | +0 to +3pp | M | Sibyl-original; claim no harness provenance. Needs a pre-registered detector-precision measurement first, because a false absence claim is worse than silence. |
| 12 | Ship screenshots as image context items | exposure | unmeasured | L | The one exposure channel never touched. Requires re-downloading the corpus with screenshots. Highest variance on the list; run it after lever 1 reports. |
| 13 | The corpus rebuild wave | selection | low single digits | L | 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. |
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.
Every retrieval entry point drops the same retired rows and scores predicates from one table, so that any number produced afterward means something.
tools/search.py and retrieval/naive.py.SUPERSEDES from the outgoing direction of the hybrid walk. Keep incoming: reaching the successor from the retired row is wanted.CONTRADICTS, which is declarable and writable on all three surfaces yet scores the untyped baseline at read.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.
--source-evidence-bundling default to match the adapter, since the naive conflict guard currently refuses the arm at construction.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.set_query_context override, which raises TypeError against the new keyword-only signature.compile_context swallow that biases the arm.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.Find out whether the machine earns its latency. The fusion and pipeline-unification questions then resolve as consequences rather than as abstract design calls.
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.
Make the shipped chart and the release path provable at review time rather than at cut time.
charts/, VERSION, install.sh, and the Tiltfile; widen the workflow arm; invert the unmatched-path default so it fails closed naming the file.helm template matrix with defaults and a production values set.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.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.
related_to targets on the MCP add path the way REST does.coordination_backend=auto selecting redis when redis is configured.None rather than omission, since #393 inverted the semantics underneath it.bulk_create_direct drift one file over.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.
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.
Each lands independently. The first four are uncontroversial and clear the queue the user asked to see handled.
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
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
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
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
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
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
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
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
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
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
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
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
#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
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
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.
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.
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.
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.
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.
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.
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.