Repository navigation
Description
id(publications): Registry::load restores records publish() would refuse — duplicates, empty worlds, non-canonical shadow paths
Summary
Registry::publish (pkgs/id/src/publications.rs:195) enforces the registry's invariants: non-empty world name (:200), canonical mount path via canonicalize_mount_path (:204), one publication per placement (:207), and the #2147 shadow-acknowledgement gate (:216). Registry::load (:297) enforces none of them — it is a bare serde_json::from_str, so a persisted publications.json that is syntactically valid but semantically invalid is restored verbatim at boot.
That breaks the module's own stated contract: the module doc (publications.rs:40-49) and the boot call site (pkgs/id/src/commands/serve.rs, the #2147 slice-1 comment) both say a registry that cannot be honoured must fail loudly rather than silently restore the wrong set. Today only unparseable JSON fails; parseable-but-invalid records sail through.
Verified behaviour (laptop probe, main 464467b)
Probe crate ~/git/fleet/probe-2026-11-03-laneB-pubload (path-dep on pkgs/id, Registry public API only):
- Control —
publish()refuses all three: empty world name, non-canonical path//world, duplicate placement/arena. - A hand-written
publications.jsoncontaining exactly those violations (duplicate/arenamounts for worlds a+b, an empty-world mount, an unacknowledged//worldmount, an unacknowledged/api/trailing-slash mount) loads withOkand restores all 5 records. boot_warnings(KNOWN_EXPLORER_ROUTES)on the loaded registry returns 2 warnings, and the one for the//worldmount names the wrong route:shadows()(publications.rs:151) compares the raw stored string, so//worldfails to match the/worldroute it denotes and instead matches route/via the"//"prefix — the warning claims the mount shadows/. The id(serve): dynamic publish/unpublish — bind and unbind programs and pages to ports, interfaces, and paths at runtime #2147 duty is to warn loudly naming the shadowed route; a non-canonical spelling makes the warning name a route the mount does not denote (and any future route-matching at apply time faces the same raw-string mismatch).unpublishcanonicalises its argument (publications.rs:239), so a stored/api/record can never be removed by naming/api— the lookup compares canonical/apiagainst stored/api/and reports "nothing published at path /api" (probe:unpublish(/api)against the loaded registry errors).
Why it matters
- Slice 1 is the persisted half of id(serve): dynamic publish/unpublish — bind and unbind programs and pages to ports, interfaces, and paths at runtime #2147; later slices apply this set to live listeners. Duplicate placements will collide at bind time, an empty world name will fail (or worse, mis-target) at apply time, and an unacknowledged shadow mount that spells its path non-canonically overrides an Explorer route with no warning anywhere — exactly the failure id(serve): dynamic publish/unpublish — bind and unbind programs and pages to ports, interfaces, and paths at runtime #2147's binding rule exists to prevent.
- The file is operator-editable JSON by design (it lives in the store directory /
ID_PUBLICATIONS_FILE); "only publish() writes it" is not a safe assumption for a persistence format, and the module doc already promises fail-loud behaviour for a file that cannot be read as a valid registry.
Acceptance criteria
Registry::loadvalidates the parsed set against the same invariantspublish()enforces: every world name non-empty, every path-mount path already canonical (or canonicalised on load with the change surfaced), no duplicate placements, and duplicate/invalid records produce an error naming the file — matching the existing malformed-JSON behaviour.- Boot warnings fire for every unacknowledged persisted mount that shadows an Explorer route and name the route actually shadowed, for any spelling of the mount path.
- A stored record can always be removed by
unpublishnaming its placement in canonical form. - Tests: load-rejection (or load-canonicalisation) cases for duplicate placement, empty world, and non-canonical path alongside the existing
save_load_round_trips_and_missing_file_is_emptytest; a boot-warning case for a non-canonical shadow spelling. - Related: id(serve): dynamic publish/unpublish — bind and unbind programs and pages to ports, interfaces, and paths at runtime #2147 (slice 1 introduced the registry, PR id(serve): persisted publications registry, boot restore + shadow warnings (#2147 slice 1) #5114); Design: reconstruction is admission — validate authority-bearing inputs where they are rebuilt (pkgs/id) #2898 (the reconstruction-is-admission design theme this is a concrete instance of).
Activity
dezren39 commentedon Nov 3, 2026
🤖 Agent note · filed
dezren39 commentedon Nov 3, 2026
agent-note:v1
event: picked_up
task: 8859 (repo-health-code-pilot; worker repo-worker-code; lane code-c)
state: claimed; implementing Registry::load validation in a laptop worktree on branch queue/8859-5179-publications-load-validation.
dezren39 commentedon Nov 3, 2026
agent-note:v1
event: parked
task: 8859 (repo-health-code-pilot; worker repo-worker-code; lane code-c)
state: implementation complete, PR open, parked on Codex review.
PR: #5190 (branch queue/8859-5179-publications-load-validation, commit 97bbf55, pkgs/id/src/publications.rs only, +192/-12)
Tests: cargo test --lib publications on the laptop — 14 passed, 0 failed (9 pre-existing + 5 new load/boot-warning/unpublish cases).
Parked because: Codex review on PR #5190 still running at park time (and the production-line-limit check pending). Resume: read Codex findings, disposition each (fix or reply), merge under standing criteria, close out task 8859.
fix(id/publications): validate the persisted registry at load against…
dezren39 commentedon Nov 3, 2026
🤖 Agent note · done
dezren39 commentedon Nov 4, 2026
🤖 Agent note · done
already_done. Fresh verification this claim (2026-11-04T05:05Z): issue id(publications): Registry::load restores records publish() would refuse — duplicates, empty worlds, non-canonical shadow paths #5179 is CLOSED (stateReason COMPLETED, closed 2026-11-03T22:55:30Z) because the fix already landed via task 8859: PR fix(id/publications): validate the persisted registry at load against publish() invariants (#5179) #5190 MERGED 2026-11-03T22:55:27Z, merge commit 7a85004, branch queue/8859-5179-publications-load-validation. Task 9088 was a duplicate coverage-pass packet for the same issue; no new work, no competing PR.