Skip to content

feat(iid-spec, iid, iid-ladder, iid-registry): register provider-local identifiers as class D with the reference's grammar, typing and P1 floor (P16f) - #40

Merged
kamescg merged 6 commits into
update/v1.1.0-alphafrom
codex/p16f-provider-local-iids
Oct 20, 2026
Merged

kamescg merged 6 commits into
update/v1.1.0-alphafrom
codex/p16f-provider-local-iids

Conversation

@jonathanprozzi

@jonathanprozzi jonathanprozzi commented Oct 19, 2026 •

Copy link
Copy Markdown
Member

Important

@kamescg — review ready. Stacked on #35 (codex/p16g-weak-account-floor); review after #35 and retarget as #35 and #36 land.

To ratify here: the new identity class, class D (provider-local identifiers), for podcast-index, letterboxd and apple-podcasts: byte-exact grammar (no repair), unambiguous typing, valid but never anchor-eligible (P1 floor), category positions as the reference. The reference's ladder already mints these; this makes the spec and registry agree with it.

Coordination: the provider-only apple-podcasts input follows #37's alias-only filter once this branch rebases after #37; this PR and #41 both touch packages/primitives/src/types.ts (whichever lands second takes IdentityClass | null).

Receipt: corpus 209 vectors across 29 schemes; harness 1068 pass / 0 fail; no ledger entry touched.

Child PR of the v1.1.0-alpha umbrella (#15). Part of the parity series: each PR in the series makes a train package behave exactly as the reference implementation's in-tree copy does today, so the published packages can replace those copies without a behaviour change.

Current (train)

The reference implementation's ladder mints three provider-local namespaces, int:podcast-index:<id>, int:letterboxd:film:<slug> and int:apple-podcasts:<id>, through its provider path (byte-exact row grammars, the handle rung), while its own iid package still rejects them as unknown schemes and its spec never defines them. The train refused them outright: the ladder returned unregistered-provider and the registry had no rows, so the published packages could not reproduce identifiers the reference already stores.

Need

The three namespaces legal in the spec and the registry, with the grammar, typing, rung position and eligibility the reference's ladder gives them, and every pre-existing scheme unchanged.

Reconciliation

  • @0xintuition/iid-spec: a new identity class, class D, provider-local identifiers (an identifier assigned by one provider, stable within that provider, not governed by any registry outside it); one scheme page per namespace; a dated record under §9.7 (pre-publication amendments); the closed-registry rule is unchanged. Corpus: schemes.json rows and 39 vectors (valid, invalid, canonicalization, eligibility); the fixture validator now pins 29 schemes and gains a regression proving the pin still bites.
  • Grammar is byte-exact and canonicalization is the identity function: ^[1-9]\d*$ for podcast-index and apple-podcasts, ^film:[a-z0-9]+(?:-[a-z0-9]+)*$ for letterboxd; zero, leading zeros, uppercase, padding and malformed hyphen runs are invalid and never repaired.
  • @0xintuition/iid: registry rows, typing (podcast feed, podcast feed, film) and eligibility: valid and typed but never anchor-eligible, with the new reason provider-local (a P1 floor, the same mechanism as the weak account form in feat(iid, iid-spec): floor weak account handles below anchor eligibility, with the spec amendment and conformance rule (P16g) #35).
  • @0xintuition/iid-ladder: the three namespaces resolve instead of returning unregistered-provider; the reference fixtures come out byte for byte on rung handle; category positions follow the reference (podcast-index second after podcastguid for podcast-series and after rssitem for podcast-episode; apple-podcasts matching-only for podcast-series; letterboxd has no movie rung).
  • @0xintuition/iid-registry: the typed namespaces classify to podcast-series and movie.
  • @0xintuition/primitives: the anchor's class field is typed from the iid union so class D flows through the builder (one type, no behaviour change).

Parity

Matches the reference implementation's ladder for these namespaces (provider-prefixes.ts rows, category rungs, fixtures) and makes the spec and registry agree with what that ladder ships. One path is deliberately left to #37: a provider-only apple-podcasts input for podcast-series still projects to handle through the generic provider fallback, exactly as the reference does on that path; the alias-only filter that keeps it from ever being primary is the ladder-selection PR's, and this branch picks it up when it rebases after #37 lands.

Held out

  • No version bump; the release PR freezes versions for the whole family at once.
  • The spec could carry a section on reproducible canonicalization and hashing for provider-local values; noted, not written here.

Evidence

  • packages/iid-spec: corpus OK, 209 vectors across 29 schemes; 8 validator tests (the 28/30 count regression included).
  • packages/iid 345 tests, packages/iid-ladder 168, packages/iid-registry 52, packages/primitives 140, packages/classifications 38; typecheck and lint green in each; root registry:check (14 packages), guard:supply-chain, pack:dry-run pass.
  • Conformance harness on top of test(integration): reconcile merged conformance gates and enforce them in CI #36: 1068 pass, 23 todo, 0 fail; no divergence entry touched (the root inventories did not move; the new vectors are consumed by iid's own conformance test).
  • Independent review: the conformance pass found every item conforming (reference runtime parity confirmed by source inspection; the sandbox could not execute the reference ladder). The adversarial pass probed the grammar edges per namespace, eligibility through the anchor builder (P1, class D), collateral on the 26 pre-existing schemes, the ladder positions and the vacuity mutations. No findings; the provider-only apple-podcasts path is recorded as the fix(iid-ladder): select identifiers like the reference on normalized evidence, with the alias-only and typed-wd filters pinned by a differential grid (P16h) #37 dependency above.

Review notes

Base codex/p16g-weak-account-floor (#35), which sits on fix/pr15-integration-gates (#36); retarget as those land. Read the class definition and the three scheme pages first, then packages/iid/src/schemes.ts, then packages/iid-ladder/src/provider-prefixes.ts and ladder.ts. The class definition is the newest ruling to ratify.

…the spec amendment and vectors (P16g)

The weak form of the acct scheme (<platform>:@<handle>) stays a valid,
unambiguously typed identifier but is no longer anchor-eligible: handles
are renamed and recycled, so it ranks one rung below the strong form, as
the reference implementation already enforces.

- iid: isAnchorEligible returns false for the weak form; the inspect
  result carries a reason for it. Canonical bytes are unchanged.
- iid-spec: schemes/acct.md and the representation-profiles section state
  the rule; recorded as a pre-publication correction in a new
  "Pre-publication amendments" subsection of the governance section, not
  as an additive extension, since it changes eligibility of an existing
  valid form before first publication.
- Conformance corpus: strong-form and weak-form acct vectors with
  explicit eligibility (170 vectors validate).
- Tests: weak-account cases in iid (282 tests), corpus validator green;
  ladder, registry and classifications suites unchanged and green.
…eligibility exactly (P16g fix round)

Review found that the standalone corpus validator only checked that a
vector declared eligible was valid, outside Class C and unambiguously
typed, so a weak account vector declared eligible passed. The rule is now
data-driven and enforced both ways:

- schema.json: optional anchorIneligibleValuePattern on a scheme entry,
  a value pattern that is valid but never anchor-eligible; schemes.json
  sets it on acct to the weak form.
- validate-fixtures.mjs: expected eligibility is computed for every
  validation vector (valid, not Class C, unambiguous typing, value does
  not match the scheme's ineligible pattern) and must equal the declared
  flag; a mismatch names the vector. The validator is exported as a
  function and keeps its CLI behaviour.
- validate-fixtures.test.mjs: the shipped corpus passes; the weak vector
  declared eligible and the strong vector declared ineligible both fail
  by name. Wired into the package test script.
…the public packages floor weak handles (P16g)
…l identifiers (podcast-index, letterboxd, apple-podcasts) as class D with the reference's grammar, typing and P1 floor (P16f)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants