Repository navigation
v7.64.0
Pairs with engine-service-v0.1.137. That engine's reads show only chunks with a live owning document (RDR-192 Step 5, nexus-wbfpw.10), which older clients do not expect, so it deploys in step with this release rather than before it: conexus has it armed and flips it once this client is published, the live(c) census has been reviewed, and the gate corpus has been re-seeded through an owner-registering path. engine-service-v0.1.135 and v0.1.136 were tagged on the way and never deploy.
Added
-
nx catalog footnotes FILE.md— the in-place converter GH #896 asked for (nexus-sxiay, closes #896). Converts every[label](nx://catalog/<tumbler>)markdown link IN PLACE in the SOURCE file (not a.rendered.mdsidecar — GitHub/GitLab/VS Code preview render the source) into a stable GFM footnote marker[label][^tumbler-<slug>]— the label stays bracketed, which is what makes--to-linksexact regardless of what precedes or follows a citation on its line, or how many citations share one — with a## Footnotessection carrying title, content type, indexed date, link/merge/outbound-link data per unique tumbler. Idempotent: an unchanged catalog state re-run is a byte-for-byte no-op (trailing newline, or its absence, and CRLF-vs-LF preserved exactly); a drifted one (title edit, merge) rewrites only the footnote bodies — markers, once assigned, never move.--check(CI/pre-commit gate, exits non-zero on any needed change or dangling reference),--dry-run(unified diff, always shows the full picture, writes nothing),--to-links(reverse conversion, no catalog access needed),--refresh(only refresh existing footnote bodies, add no new markers),--style long|short(footnote body verbosity). GH #896's own acceptance criterion is held literally: a tumbler that fails to resolve means NOTHING is written for that file — not even the parts that would have converted cleanly — every failure is still reported (never silently dropped), and the run exits 1. It ships as a thin/conexus:tumbler-footnotesskill too. -
nx doctor --check-referencessamples reference-only catalog documents and stats each one's currently-recorded freshness (nexus-tb2yj, RDR-169 Gap 6 leg 3): the first production caller ofstat_source/staleness_signal. Reports fresh/stale/dangling/unknown counts per scheme (https://,obsidian://,x-devonthink-item://,nx-scratch://,chroma://), names the stale and dangling documents (capped), and exits 1 on any stale or dangling result —unknownnever fails the check alone.--references-sample(default 10) and--references-seedmirror--check-embeddings' sampling contract; up to 8 samples are stat'd concurrently sharing onehttpx.Client, and both the flag's help text and a run's own first printed line state the worst-case wall time, derived fromaspect_readers' HTTP retry constants and the actual concurrency. Not applicable on a box with no reference-only documents.nx catalog backfill-etagsis the named remedy for a document stuck atunknownbecause it predates ETag capture. -
https://catalog references record theirETagat register/refresh time and compare it when no usableLast-Modifiedis present (nexus-0ne1m,nexus.aspect_readers.HTTPS_ETAG_META_KEY):_stat_https_urinow takes arecorded_etag, strips a leadingW/weak-validator prefix from both sides before comparing, and readsStatOk(etag_stale=...)off it instead of an unconditionalStatFailnaming the gap.capture_https_etag/record_https_etagdo one best-effort HEAD (connect and read are separate timeout budgets, so worst case is ~5s, not ~3s; one attempt, never fails the caller) and merge the ETag intocatalog_documents.metadata; wired intonx catalog register/update --source-uri(refused in combination with--owner/--searchbatch mode) and theregisterMCP tool, all documented as a caller-visible side effect.NX_REFERENCE_ETAG_CAPTURE=0disables the HEAD entirely, checked incapture_https_etagitself so every caller (register/update and the bulkbackfill-etagssweep below) shares the same guarantee. -
nx catalog backfill-etagscaptures the ETag in bulk forhttps://references registered before ETag capture shipped:--owner/--dry-run/--limit(default 500, bounded), idempotent (a document that already has an ETag is excluded from candidates on every run), one sharedhttpx.Clientacross the run, honorsNX_REFERENCE_ETAG_CAPTURE=0the same asregister/update. -
nx collection aspects <name> [--enable|--disable]andnx collection aspects --from-config [--dry-run](nexus-l46pu, follow-up to nexus-kk4ut, pairs with the next engine-service tag). The docs__ aspect-extraction opt-in moves off each machine's localaspects.docs_collectionsconfig.yml onto a new tenant-widecatalog_collections.aspects_enabledengine row (BOOLEAN NULL, no default), so two machines indexing the same shared collection make the same decision instead of giving partialdocument_aspectscoverage. The engine is AUTHORITATIVE the moment it has an opinion — an explicittrue/falsewritten by one of these verbs (round-2 critic decision): the local config list is consulted only when the engine's attribute isNULL, meaning nobody has ever set it (a fresh install, an old engine, or an ordinary untouched row on a current one — all the same fallback).--from-configsyncs every locally-matched collection onto the engine in one pass.--enable/--disableand a changing--from-configrefuse a non-docs__ collection (also a 400 at the engine), print the tenant-wide blast radius and LLM-call cost before writing, and prompt for confirmation unless--yes/-yis given (--dry-runnever prompts); the engine logs a structured audit event on every write (tenant, collection, new value).nx doctor's default sweep gains adocs-aspects-configrow (N/A with an empty local list) warning when a local match still needs syncing. Nothing changes until an operator runs one of these — every existing row isNULL("no opinion"), the same "not opted in" behaviour as today. -
Windows subprocess trees are now contained by a Job Object (nexus-6y4e0), via
nexus.util.win_job(ctypes only, nopywin32) andnexus.util.process_group.contain/kill_tree.start_new_session=Trueis silently ignored on Windows, so a timed-out subprocess there previously killed only the direct child and every grandchild survived; the four same-process spawn-then-kill sites that actually reach a Windows client (bounded_subprocess.run_bounded,aspect_extractor._run_claude_isolated, the MinerU per-batch worker inpdf_extractor, andoperators.dispatch.claude_dispatch's timeout path) now assign the child to a job withJOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSEright after spawn, so closing the job reaches the whole tree. POSIX behaviour is unchanged (contain/kill_treeare no-ops there — the process group already contains the tree). Five further spawn sites were surveyed and left unwired, each for a stated reason: two are killed by pid from a wholly separate later CLI invocation (the mineru server and the storage-service supervisor spawn), and an in-process job handle cannot cross that boundary without a named job, a bigger design left for later; the storage-service supervisor class itself callsos.getuid()at construction and does not run on native Windows at all (RDR-218's Windows story is a WSL2 appliance);nx index's background spawn is fire-and-forget, with nothing that ever kills it; and the aspect worker daemon is retired by lease fencing, not by a kill. Measured on native Windows 11 (CPython 3.12.13 against the realkernel32): with a child that spawns three grandchildren, develop left all three running after every kill and every clean exit (120 of 120 across the four sites' timeout and success paths), and containment left none (0 of 120); closing the job on the success path also ends a grandchild the child left behind. On Windows, the uncontainedrun_boundedalso overran its timeout by the 5 s reap drain, because orphaned grandchildren held its output pipe open. Onlyrun_boundedran as the real function; the other three sites were reproduced in shape around the same primitives. Not covered: realclaude/nodeprocess trees, a native executable that spawns beforecontain()assigns it (the child is not started suspended, so that race is open), and a crash of the nexus process itself. -
safe_killpgno longer raisesAttributeErroron Windows,nx-session-end-launcherhas a Windows fast path, andstart_new_session=Truedegrades honestly there (nexus-34f7r).os.killpg/os.getpgid/signal.SIGKILLdo not exist on Windows;process_group.pynow imports and degrades cleanly, routing a Windows kill through the single surviving process (or, after nexus-6y4e0, through a job object when one was assigned at spawn). The SessionEnd hook spawns a detached child (DETACHED_PROCESS | CREATE_NEW_PROCESS_GROUP, with a breakaway-from-job retry) instead of running cleanup inline, closing the "Hook cancelled" timeout Windows previously reported. Landed on develop in an earlier session (6aa5990, 5276887, e1a0191).The bead's closing bar was exercised live on native Windows 11 (qwentescence, 2026-09-27; T2 nexus/nexus-34f7r-live-windows-closing-bar-2026-09-27) and found the fast path necessary but not sufficient: with no service reachable, Claude Code still cancelled the hook at ~1.6s, because the launcher's PARENT ran
_print_service_tier_summary()— a network read — AFTER the detached-child spawn, taking up to ~2.4s wheneverCLAUDE_CODE_SESSION_IDis set (always, under Claude Code). The resulting "Hook cancelled" kill walked the process tree and took the detached child down with it, even though itsCREATE_BREAKAWAY_FROM_JOBspawn had succeeded — with the service answering at once the hook instead completed in 356ms and the child outlivedclaude.exeby 52s. Fixed:main()'s no-fork branch never calls the tier summary now, in either the successful-spawn or inline-fallback case, so the parent does no network work after a successful spawn (no tier summary line at SessionEnd on native Windows — POSIX is unaffected). Two further leaks the same run caught: neither the POSIX grandchild nor the Windows detached child ever calledconfigure_logging, so no session_end log line could land in<config>/logs/hook.logon any platform (_run_session_end_synchronouslynow callsnexus._hook_runtime._io.configure_hook_logging()first); and the launcher parent's own_print_service_tier_summaryexception handler logged through the ambient, unconfiguredstructlog.get_logger(), which falls through to stdout — the hook's protocol channel (Claude Code logged "Hook output does not start with {"). That handler now uses a dedicated stderr-only emitter (_emit_parent_debug) instead. The no-breakaway spawn fallback arm was not exercised in the live run (breakaway succeeded every time). This fix has not yet been re-verified live on Windows; nexus-34f7r stays open until its closing bar is re-run there. -
nx doc renderandnx doc validateresolvenx://catalog/<tumbler>links (nexus-sevlu, refs #896). Render appends a Catalog References footnote block with each linked document's title, content type, owner and a link; a merged duplicate resolves to the surviving document; a machine-localfile://source or an absolute path is never emitted. Validate fails on every line citing a tumbler that no longer resolves, and exits 2 when the catalog is unreachable.
Fixed
-
The catalog link-safety helper (
nx doc render,nx catalog footnotes) is now one shared, hardened function (nexus-w715w, follow-up review on nexus-sevlu's landed fix).safe_link_targetis the single place in the codebase allowed to compose a markdown link fromentry.source_uri/entry.file_path(mechanically enforced by a lint test, including agetattr(entry, "source_uri"/"file_path", ...)bypass form). Hardening fixes: thefile://scheme check is now case- and slash-form-insensitive viaurlsplitplus an explicit link-scheme allowlist (FILE:///...,File:///...andfile:/Users/...all previously slipped past a bare.startswith("file://");nx-scratchwas removed from the allowlist too — T1 scratch is session-scoped and unresolvable by any later reader); the "is this path safe to treat as repo-relative" check is now content-based rather thanPath.is_absolute()(host-OS-native — a Windows drive-letter path, a UNC path,~, or an upward-escaping..sequence all read as "relative" on a POSIX host), and percent-decodes before classifying so%2e%2e%2fcan't hide a traversal. A safe, repo-relativefile_pathnow becomes a WORKING relative link — resolved against the citing file's own directory via the entry owner'srepo_root— only when the citing file is actually inside that repo; outside it (or with repo_root unknown), it falls back to the repo-relative string emitted as-is, which only happened to resolve when the citing file sat at the repo root. -
nx catalog footnotesreads its source file as strict UTF-8 and writes atomically (nexus-3ioz2, critical). The priorerrors="replace"read silently corrupted non-UTF-8 input and could write that corruption back over the original; invalid UTF-8 bytes are now refused outright (exit 2, nothing written, byte-identical file afterward) rather than "fixed" lossily. Writes go through a sibling.tmpfile plus an atomic rename (mirrorsnexus.commands.t3._save_backfill_state's pattern), so a crash mid-write can never leave the file half-written. GH #896's own "must not partially write the file" criterion is now held literally: any dangling reference — in either conversion direction — means nothing is written for that file at all, not just the parts that failed to resolve; every failure is still reported and the run exits 1. -
nexus.doc._common's shared markdown scanner now skips inline backtick code spans and 4-space-indented code blocks, not just triple-fenced blocks (nexus-3ioz2, follow-up review). A docs page explaining[label](nx://catalog/<tumbler>)syntax almost certainly quotes it inside inline backticks — this repo's owndocs/cli-reference.mddoes — and the old scanner treated that literal example as a real citation. Every consumer ofiter_plain_lines(catalog-link and chash-citation scanning, sonx doc render/validatetoo) benefits from the same fix, made once in the shared module. -
REGRESSION in the same scanner, fixed one round later: the first cut of the indented-code heuristic wrongly treated a blank-line-preceded indented line as code even when it was a LIST ITEM's own continuation — a further paragraph, a table, or a nested bullet indented to match the item's content column, separated by an ordinary CommonMark blank line.
nx doc render/validatesilently stopped resolving citations and tokens inside these shapes; real cases in this repo's own docs are named in the fix's commit.iter_plain_linesnow tracks open list context (a stack of each open level's content-indent) so a line still inside any open list's content zone is never misread as code, regardless of a preceding blank line. A full corpus diff over every*.mdunderdocs/(394 files) against the pre-fix baseline shows exactly 16 disappeared lines, every one independently confirmed to be genuinely indented code with no list context, and zero unexpected additions. -
nx catalog footnotes's atomic write preserves the original file's mode and cleans up its.tmpfile on any failure.Path.replace()renames the tmp file's OWN inode into place, so without an explicit mode copy a 0600 source file silently loosened to whatever umask gave the freshly-created tmp file (typically 0644); the mode is now copied onto the tmp file before the replace. Any failure between creating the tmp file and the replace succeeding now removes the tmp file and re-raises, rather than leaving a stray.tmpbehind. -
Engine (next engine-service tag): a bulk owner-agnostic file_path lookup backs
nx index repo's batched cross-owner mint announcement (nexus-1vc0n). nexus-yzij1 closed the cross-owner path-duplication class for every per-document catalog writer, but left the batchednx index reporegistrar out on purpose: wiring its existing per-documentannounce_cross_owner_mintthere would cost one owner-agnostic/list?file_path=per file inside aregister_manyloop, turning one round trip into N+1. New engine routePOST /v1/catalog/list_by_file_paths(CatalogRepository.documentsByFilePaths, partial indexidx_catalog_documents_file_path_live) answers every live document for N file_paths in one call, made once perregister_manypage, immediately before it;HttpCatalogClient.find_all_by_file_pathspages it at 300 paths (QUOTAS.MAX_RECORDS_PER_WRITE) and degrades to the existing per-path lookup against an engine that predates the route.indexer._catalog_hooknow runs one bulk lookup perregister_manypage, immediately before it, and feedsannounce_cross_owner_mint/announce_cross_owner_resolvefrom it, so a cross-owner mint or resolve in a batched run is reported exactly like the per-file writers already do, at one extra round trip per page instead of one per file. -
A board subscription starts at now and folds a wait's posts into one notification (nexus-zxthy). A fresh
tuple_subscribeto a busy topic replayed every retained post as its own notification, 282 in 80 seconds on 2026-09-27, because the engine's per-subscriber stamp had no start position and the waiter asked for one row per wait. The subscription set now records the subscribe time per topic and restores it on/resume; the waiter asks for up to 100 rows per wait, drops rows created before the subscribe time (with a 30 second margin for clock skew between the engine and the client), and sends one notification per topic per wait naming the count, the first and last tuple id and thetuple_rdcall that reads them; after board activity it settles 3 seconds so a cluster lands in one wait. The notification content stays identifiers only; the batch's ids, kinds and senders ride in the notification meta.tuple_subscriptionsshows each board entry'ssince, and the channel status carriesboard_batchesandboard_backlog_dropped. Posts older than the subscription are read withtuple_rd, never pushed; a subscription persisted before this release has no start position and keeps delivering everything. A resumed session keeps its original start position, so a long absence comes back as folded batches of at most 100 posts each, the way a durable consumer replays its own gap. This is still the LIVE mechanism until the client's pinned engine carries the start-position change (engine-service-v0.1.137; see docs/wire-contract-pending.md) — see the pairing below. -
Engine + client: a board subscriber's start position is now honoured by the engine itself, so a fresh subscription never walks the topic's backlog at all (nexus-n36sw, follow-up to nexus-zxthy). The fix above still dropped excluded rows client-side, after the engine had already selected and stamped them for this subscriber — a few extra waits per fresh subscription, no notifications, but still a walk. A per-subscriber
announcespec (the board shape) may now also carrysince;TupleRepository.queryOnceAnnounceSubscriberfolds it into its own candidate predicate as a barecreated_at > sincetimestamp compare (never the(created_at, id)row comparerd's cursor uses — a board subscriber's watermark is a subscribe time with no id to pair it with), so a row at or before the watermark is never selected, stamped, or returned.sincealongside a row-level (mailbox)announceis still refused, unchanged. The waiter sends the subscribe time, widened backwards by the same 30 second clock-skew margin the old client-side compare applied. An engine predating this bead refusessincealongside anyannounce; the waiter detects that refusal on first contact, falls back to the pre-nexus-n36sw client-side drop for the rest of its life, and logs once, so a new client against an old engine still works.channel_waiter status()gainsboard_since_supported. Requires the next engine-service tag; seedocs/wire-contract-pending.md. -
nx collection re-embedworks on a quarantine sibling (nexus-ny7j4). The write path's registration step derived registration fields from the collection name, and aquarantine-<type>first segment is not a content type, so the command refused withunknown content_type 'quarantine-code'. The client never registers a quarantine sibling (the engine does, from the origin's row), so the step now skips it. -
nx doctor'staxonomy.discover healthrow reconciles against the engine's own discover stamp (nexus-l3dg2, the du6d0 residual). A best-effort T2 write failure right after a real engine-side discover success kept the row warning forever, because it only ever read the client-recorded outcome. The engine (next engine-service tag) gains a batchedPOST /v1/taxonomy/meta/last_discover_batch(cap 300 collections;taxonomy_meta's existing primary key answers the lookup, no new index or changeset); the client'sHttpTaxonomyStore.get_last_discover_stampsreads it in one round trip per 300-collection page for every collection the row is about to warn on, and a collection whose enginelast_discover_atis newer than the recorded failure is dropped from the warning. Fully additive: an engine that predates the route 404s and the row degrades to its prior behaviour, with one "(engine reconcile unavailable)" note. -
The last two direct
register_collectioncallers now route through the registration seam (nexus-aotql): the RDR-103 post-rename registration innx index repo's legacy-collection migration, andnx catalog migrate-fallback's registration loop. Two behaviour changes fall out of that: a register-time 409 (another process won the race to register the same name) is now tolerated as already-registered instead of propagating as a crash, and an embedding-profile mismatch (the engine's profile still reflects an olderlocal.embed_model/voyage_api_keydecision) now fails early with aClickExceptionnaming the collection, the engine's actual profile model, and the remedy (--target-model <profile model>, or a service restart), before any document is re-pointed — rather than surfacing later as a confusing write-time 422 or an escaped raw exception mid-loop. -
.nxexpimport registers an owner document before storing chunks, so an import stays live under RDR-192 Step 5's live(c) predicate (nexus-wbfpw.31). Export now resolves each live chunk's own catalog document (source URI, title, content type, manifest position) and carries it as an optionalownerrecord field; import groups chunks by that identity across the whole file, registers or reconciles the document once every batch is upserted, and writes its manifest explicitly with the recorded positions — never through the per-batch manifest hook, whose position numbering restarts on every 300-chunk batch and would corrupt a multi-batch document's manifest. A record with no owner (an older export, or a live-but-unmanifested chunk) is grouped under one document per import file instead, keyed by the target collection and file name, so re-importing the same file stays idempotent. Importing into a collection other than the one the documents live in (--collection) copies: the source collection's documents are left untouched and stay live, and the target gets its own documents undernxexp://<target>/<original source URI>. A legacy record carryingdoc_idkeeps that document when it is live in the target collection, and otherwise gets a new one per original document (nxexp://<target>/<file>#<doc_id>).--skip-existingstill ends every record owned; if some owner documents cannot be registered, the rest still are and the import fails naming them. Export fails loud if the catalog cannot answer rather than silently writing owner-less records. -
nx doctorgains ataxonomy.discover healthrow, and the "no files changed — skipping discovery" line names a failing collection (nexus-du6d0). qgc4b's self-heal guard re-runs taxonomy discovery on a no-changenx index reporun only while a collection has zero topics; once a collection has topics, a later discover failure (credential expiry, quota, schema drift) had no operator-visible signal on a repo whose files stopped changing. Every discover attempt now records its outcome in T2, and the doctor row warns on a collection whose most recent attempt failed, naming the collection, failure time, error class and thenx taxonomy discover --collection <name>remedy — visible independent of whether the repo indexes again. A collection deleted or renamed since a failed attempt is dropped from the row rather than warning forever. -
Engine (engine-service-v0.1.137): quarantine restore no longer overwrites a live row's metadata (nexus-brxnp, catalog-043).
gc_restore_rereferencedand its bounded sibling copied a quarantine row back onto its origin withON CONFLICT DO UPDATE, so a chash the current run had already re-inserted live was overwritten with the older quarantine copy's metadata — and, since catalog-042, itscreated_attoo. The copy now usesON CONFLICT DO NOTHINGand still deletes the quarantine row; each restore'sgc_auditrow recordsdetails.already_live(candidates skipped because the origin row was already live). The client's quarantine legs now sample up to 5000 chashes into that audit instead of 20. -
Engine (engine-service-v0.1.137): a bounded
gc_restore_rereferenced_boundedsweep (nexus-e8h5x, catalog-042), mirroring catalog-037's bounded quarantine-in sweep for the opposite direction. The unbounded restore ran in one transaction per (tenant, quarantine, origin); about 36,000code__1-1rows left quarantine in a single unbounded call on 2026-09-16, the same edge-deadline exposure catalog-037 fixed for quarantine-in after a 41,032-row call took 58s against the ~30s edge deadline.nexus.gc_restore_rereferenced_bounded(row_limit)restores at mostrow_limitrows per call, returns(restored, remaining), and writes onegc_auditrow per batch;POST /v1/vectors/gc/restore-rereferencedtakes an optionalrow_limit. The client now drives both bounded routes — restore, and catalog-037's quarantine route, which no client had ever called, so its 41k-row edge-cut exposure was still reachable — looping at a 2000-row default until drained, with an older-engine fallback to the unbounded call. -
The marketplace plugin set is now derived from
marketplace.json, not hand-kept lists (nexus-smsau, RDR-221 prerequisite). Adding a plugin to.claude-plugin/marketplace.jsonpreviously had to be echoed by hand into three places —plugin_lockstep.py'sPLUGINStuple, the stdlibversion_lockstep_hook.py's own copy, androuting_stats.py's fixed loop — so a plugin present only in the marketplace silently fell out of lockstep and routing-stats checks.nexus.plugin_registry.known_plugins()now derives the set frommarketplace.json(env override, dev checkout, or installed clone, requiring thenexus-pluginsname; a fallback is logged), andplugin_lockstep,routing_stats, and the stdlib hook all read it instead of their own copy. A fourth plugin added to a copy of the marketplace with no other edit now fails at least one test naming each site that still needed it. -
A subagent's RDR-205 ledger REPORT tuple can no longer be dropped by
claude -pteardown (nexus-egm7p, nexus-wgalh). The RDR-205 tuple-projection hooks (hook_subagent_start_tuple,hook_subagent_stop_tuple) shared the MCP-outage drop class nexus-5l8i8 fixed for the RDR-184 ledger — they were stillmcp_toolentries depending on a live MCP connection — and once moved to command-tiernx-hookverbs (subagent-start-tuple,subagent-stop-tuple) they still ranasync:true, which Claude Code kills mid-flight atclaude -pteardown, exactly the moment a subagent's SubagentStop fires. Both verbs now run synchronously (about 0.16s warm and 0.4s cold added latency per SubagentStart/SubagentStop; during an engine outage, two 5s posts plus interpreter start, under the hooks' 20s timeout). Along the way: the stop hook now passesagent_transcript_pathso REPORT rows carry a real VERIFY field instead of always readingabsent, and the VERIFY extractor readsSendMessage's actual field,message, notcontent.
Changed
- Engine (next engine-service tag): search and get return only chunks with a live owner in their own collection (RDR-192 Step 5, nexus-wbfpw.10). Plain, hybrid and topic-scoped search,
store_get,store_list, the/getfamily andnexus.live_chunksall use one predicate, live(c) (changeset vectors-019). A chunk with no manifest row in its collection, or whose only owners are trashed, is no longer returned; before, a manifest-less chunk stayed visible, andlive_chunkscounted a chunk as live when its owner was in another collection. Nothing is deleted. The collection listing is split from liveness: every collection that stores chunks keeps its row,countis the live count and a newstored_countcounts every stored chunk, so a quarantine collection or one whose chunks are all unowned still lists, withcount0.existing_idsasks the engine for stored ids with the newstore-getinclude_non_livefield, so verify, skip-existing and the multi-piecestore_putdelete guard still see a stored chunk without an owner. The PDF-stub health check now reports only stubs with an owner, since a stub with none is hidden and is the RDR-192 reaper's to remove. Not additive: deploy this engine only after the client release that carries its client half and nexus-wbfpw.31 (.nxexpimports register an owner first), and after the RDR-192 census reads zero legacy-unmanifested chunks. scripts/ci_status.pyhas acancelledverdict and exit code 4 (nexus-lgx93). Every row of a run whose own run post iscancelledreadscancelled, the aggregator job that reportsfailurebecause its shards never reported included, so a run a newer push superseded no longer reads as failed. Acancelledjob inside a run GitHub did not cancel was not superseded and still readsfailed; one whose run has no post readscancelled, since the run post is a separate delivery. A genuine failure or a pending job outranks a cancellation.