Closed general

Id-pinned dependencies: publish records the dep target's public_id, install resolves id-first

Colby Knox · 1mo ago ·closed by Colby Knox

Phase 2 of the public-id work, split out because it touches the publish/install contract and deserves both your eyes before it ships.

What already shipped (context)

Every page now carries a minted public_id (pg_ + 10 base32 chars, no confusables), backfilled fleet-wide and minted at both insert sites. It is never rewritten: renames and transfers are UPDATE-by-id, and the indexer's conflict SET excludes it. Live today:

  • /p/ 301s to the page's CURRENT owner/slug through any number of renames
  • pg_ refs work anywhere a bare slug does, API-wide (one branch in resolvePageBySlug, all 56 routes inherit), so the CLI takes them with no release: adom-wiki page delete pg_x --confirm worked on staging the day it shipped
  • the sidebar shows the permanent link

Names remain the presentation layer everywhere: URLs, deps, docs. slug_aliases remains the compat layer for name-shaped references.

The proposal: id-pinned dependencies

package.json deps stay exactly as authored (owner/slug, human-written). At PUBLISH, the registry resolves each dep and records the target's public_id alongside the name. At INSTALL, resolution goes id-first, name-fallback.

Effect: a rename can never break the dependency graph. The failure mode this kills is the silent one where a renamed dep keeps working through the alias 301 until someone retires the alias (the redirect-hits flatline gate), at which point installs break long after anyone remembers the rename.

Why now

The while-we're-small argument applies to exactly this: backfilling page ids cost the same at any size, but dep records are append-only history. Every publish that happens before pinning lands is a name-only record someone may need to migrate later. The graph can be id-clean almost from birth if this ships soon.

Open questions for you two

  1. Where the pin lives: a deps_resolved column on the release row vs a separate table. I lean release-row (immutable snapshot per publish, matches releases being immutable).
  2. Install fallback semantics when id and name DISAGREE (page deleted + slug reused by someone else): id-first means you get the ORIGINAL page's successor state (a deleted page 404s rather than resolving to a squatter, which reads like the safer default). Name-only today resolves to the squatter.
  3. Whether the CLI should print the pin (adom-wiki pkg info showing dep -> pg_x) or keep ids invisible until they matter.
  4. Hard delete: the id dies with the row by design (consistent with hard-delete-is-unrecoverable doctrine). Confirm that is the semantics you want for dep resolution too.

3 Replies

Colby Knox · 1mo ago

Server half is live on prod (tag aa1366a), with the four open questions answered by what shipped; John, Ray, object here if any default reads wrong and it moves.

What publish does now. pinDeps resolves each dependency ref as authored (owner-qualified first, alias-following for renamed targets, bare slugs only when exactly one owner holds the slug: a pin is never a guess) and records {"<ref>": "pg_..."} as deps_resolved on the release row. The publisher sees a DEPS_PINNED info hint. Verified end-to-end on staging: a package depending on adom/hydrogen-desktop-demo published with deps_resolved: {"adom/hydrogen-desktop-demo": "pg_dmcyjm4ny3"}, matching the target page's actual public_id.

The questions, as settled:

  1. Where the pin lives: releases.deps_resolved (JSON per release row). Immutable snapshot per publish, matching releases being immutable.
  2. Disagreement semantics: the registry records; the CLI decides at install, id-first with name fallback. Id-first means a deleted-then-squatted slug resolves to the ORIGINAL page's state (404 for a hard-deleted target) rather than the squatter, which is the safe default. Until the CLI arm lands, behavior is exactly today's name resolution, so nothing regresses in the meantime.
  3. Visibility: deps_resolved rides the served manifest (getManifest), so pkg info can print dep -> pg_x with no extra endpoint. Old CLIs ignore the unknown field.
  4. Hard delete: the pin dies with the row, by doctrine. A dead pin means name fallback, and if the name is also gone, the dead-dependency soft-failure path reports it loudly.

Edge behavior: a ref that predates the public_id backfill, does not resolve, or is ambiguous simply has no pin; the dead-dep gate still owns refusal of nonexistent required deps. Corrupt pin JSON never breaks a manifest.

Remaining: the CLI arm (id-first resolution at install, printing pins in pkg info). Spec is with the CLI thread; pg_ refs already resolve API-wide so it needs no server change. Leaving this open until that lands.

Colby Knox · 1mo ago

CLI arm shipped in adom-wiki 1.7.11 (5-platform gate, latest advanced). All three items from the spec, plus one finding that got its own issue.

(1) Install resolution, id-first with the squat check: for every package whose version changed in a run (the hook's steady-state tick stays zero-cost), the CLI reads the release's deps_resolved and compares each authored dep ref's CURRENT public_id against the pin, one GET per unique ref. Decision table, all live-proven on staging with throwaway pages: name still denotes the pinned page (aliases count, so plain renames stay silent) -> nothing; slug re-registered by a different page -> DEPS_PIN_DIVERGED warn naming the pin, with cmd 'adom-wiki pkg install pg_...'; name dark but pin alive -> DEPS_PIN_MOVED naming where the page lives now; both dark -> silent (the missing-dep machinery already spoke, matching your hard-delete doctrine). The full squat scenario was exercised for real: published a parent pinned to a throwaway dep, renamed the dep, re-registered its freed slug as a different page with its own package, fresh-installed the parent -> the squat installed and DEPS_PIN_DIVERGED fired with the correct pin.

(2) pkg info prints deps_pinned from the served manifest (verified: {"adom/hydrogen-desktop-demo": "pg_dmcyjm4ny3"} on the staging throwaway, matching the target page's actual public_id). No new endpoint, as you said.

(3) Manifests without the field: exactly today's behavior (the field's absence short-circuits everything; also silent against pre-pin servers).

Two implementation notes worth knowing server-side:

  • The pins ride the SERVED manifest only; the tarball's package.json is the authored one. The CLI fetches /api/packages/:seg/:ver/manifest for changed packages when the disk manifest lacks the field. If you ever bake deps_resolved into the tarball manifest at publish, the CLI already prefers the disk copy and the fetch disappears.
  • POST /api/packages/resolve does NOT accept pg_ refs as request keys ("Packages not found: pg_..."), unlike the page routes. The CLI now translates a pg_ install ref to its current owner/slug client-side before resolving, so 'pkg install pg_x' works and the recovery cmds are runnable. If resolve learns pg_ keys later, the translation becomes a harmless no-op.

One real pre-existing bug surfaced by proving the recovery path end-to-end, filed as adom/wiki#157: renamed packages fail signed installs (the seal binds the publish-time slug, resolve serves the current one, fail-closed). Re-sealing on rename closes it and also makes the pin recovery fully runnable for renamed targets.

Closing: both halves are live (server at aa1366a, CLI at 1.7.11).

Colby Knox · 1mo ago

Follow-up to Finding 1, now closed server-side (prod tag 3cc848c): POST /api/packages/resolve accepts pg_ request keys directly. resolveDependencies rewrites each pg_ key to the page's CURRENT owner/slug up front and reports the translation as id_refs (plus an ID_REF_RESOLVED info hint); an unknown id keeps its key and stays a loud miss under its own name, so a hard-deleted pin reports exactly what died. Verified on staging and on prod (resolved adom/adom-wiki-cli by its own pg_ id). The CLI's client-side translation keeps working and can be retired whenever convenient; it remains the right fallback against pre-3cc848c servers.

Log in to reply.