Skip to content

feat(primitives): accept a candidate IID on the anchor builder, export the reference's validators and the buildAtomAnchor name (P05c) - #42

Merged
kamescg merged 11 commits into
update/v1.1.0-alphafrom
codex/p05c-candidate-iid-anchor
Oct 20, 2026
Merged

kamescg merged 11 commits into
update/v1.1.0-alphafrom
codex/p05c-candidate-iid-anchor

Conversation

@jonathanprozzi

@jonathanprozzi jonathanprozzi commented Oct 19, 2026 •

Copy link
Copy Markdown
Member

Important

@kamescg — review ready. Stacked on #41 (codex/p05b-anchor-builder-parity); review after #41 and retarget as #41, #39 and #36 land.

To ratify here: the public anchor builder now accepts a caller-supplied candidate IID (options.candidateIid), validates it exactly as the reference does (scheme registered, value canonical, URL-class values under the 220-character cap) and uses it in place of ladder derivation. The reference's validators are exported (validateCandidateAnchorIid, validateDerivedAnchorIid, isOverCapUrlIid) and buildAtomAnchor is exported as the reference's name for buildIidAnchor, so the in-tree callers that pass a candidate IID (create-flow preview, canonical page, classification plugin, seed projection) can switch to the package without a behaviour change.

Coordination: this PR, #40 and #41 all touch packages/primitives/src/anchor.ts and types.ts; land it last of the three.

Receipt: the 23 anchor cases the harness held OPEN now run against the public builder: anchor proof 75 agreements / 6 divergences / 0 OPEN; harness 1050 pass / 0 fail / 0 todo; primitives 229 tests; classifications 257.

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 shipped callers anchor with a candidate identifier: buildAtomAnchor(slug, values, { candidateIid }), where the candidate comes from the provider ladder. The train's builder, buildIidAnchor in @0xintuition/primitives, had no candidate option, did not export the two validators the reference exposes (validateDerivedAnchorIid, isOverCapUrlIid), and carried a different name. The conformance harness held 23 anchor cases OPEN for exactly this reason.

Need

The candidate path with the reference's validation and bytes, the validators public, and the reference's helper name available, so a consumer switching from the in-tree copy changes only the package path.

Reconciliation

  • src/anchor.ts: options.candidateIid. Validation follows the reference: the candidate must be a valid IID; a typed Wikidata candidate must carry an active binding and belong to the classification; a weak account handle is excluded from P0; an over-cap canonical URL candidate is rejected; there is no general scheme-membership check beyond those (the reference has none). Serialization follows the reference: an anchor-eligible candidate serializes as bare P0 bytes; any other candidate uses the validated values envelope with identifier immediately after @type and declared string arrays normalized.
  • The candidate's profile follows its eligibility alone (p0 when anchor-eligible, otherwise p1); options.profile is not consulted on this path, so a candidate always yields the atom the reference mints for it.
  • A rejected candidate returns the reference's message inside the builder's existing structured result instead of throwing (the builder's established contract); the two exported validators throw exactly as the reference does (AtomAnchorError, code INVALID_IID, same message).
  • buildAtomAnchor is exported as the same function object as buildIidAnchor; the README documents the option, the validators and the alias.
  • Conformance harness: support/proofs.ts passes the candidate option through to the public builder, drops the OPEN markers and compares the validator results in full. The 23 cases now run and all agree; no ledger entry added.

Parity

Matches the reference implementation's candidate path (atom-anchor.ts: buildCandidateAnchor, validateCandidateAnchorIid, validateDerivedAnchorIid, isOverCapUrlIid) on every harness vector and on the reference's own fixtures, which src/__tests__/anchor-candidate.test.ts pins literally. The one intended difference is error delivery (structured result instead of a throw) for the builder; the validators throw as the reference does.

Held out

  • No version bump; the release PR freezes versions for the whole family at once.
  • Whether both helper names stay after the first cut is open; both ship now so the swap is a package-path change.

Evidence

  • packages/primitives: typecheck, lint and build green; 229 tests (37 new candidate and validator cases, red-first, plus the 52 parity pins unchanged and the pre-existing suite).
  • Conformance harness on top of feat(primitives): bring the anchor builder to byte parity with the reference implementation, including the no-identifier fallback envelope (P05b) #41: 1050 pass, 0 fail, 0 todo; anchor proof 75 agreements / 6 divergences / 0 OPEN (was 52 / 6 / 23). README enumeration equals the harness's own table.
  • Root registry:check (14 packages), guard:supply-chain, pack:dry-run pass.
  • Independent review: a conformance pass and an adversarial pass each found one difference, the same one: the candidate path let an explicit profile option override the reference's eligibility-based choice, so a caller could mint different bytes (and a different atom id) than the reference would for the same candidate. The third commit makes the candidate path select the profile from eligibility alone, as the reference's buildCandidateAnchor does, documents it in the options type and the README, and pins both reproductions (eligible ISBN requested as P1 → bare P0 bytes and the reference's atom id; ineligible TMDB requested as P0 → the reference's P1 envelope). The combined re-review confirms the difference resolved with no new finding; the derived path's profile handling is unchanged.

Review notes

Base codex/p05b-anchor-builder-parity (#41), on #39, on #36; retarget as those land. Read src/anchor.ts candidate branch against the reference's atom-anchor.ts, then the harness adapter in conformance/support/proofs.ts. The judgement call is the structured error return for a rejected candidate; it is in effect by default and open to ratification here.

… to parity with the reference implementation (P16d)

Application code imports helpers and data from the classifications
package that the train lacked, and the train's spec data had drifted from
the reference since August. This ports all of it onto the train's
declarative spec shape.

- Helpers: getClassificationLabel, getClassificationByType,
  getClassificationByReference (plus the reference resolver tiers),
  deriveClassificationIdentifier, buildClassificationSearchText,
  visibility presets incl. PRIMARY_ITEM_CATALOGUE_PRESET, native atom
  types, identity field aliases, DISCOVER_ACTIVITY_GROUPS; all exported
  from the package root.
- Spec shape: optional typed commonName and atomCategory fields (the
  reference's label and engine category); every spec populates
  atomCategory. schema, metadataPredicates, inline identity and the
  creation-profile shape stay canonical.
- Identity data: every rung order, wdSlug, recipe and identifies value
  audited against the reference for all 37 classifications; podcast GUID
  derived through normalizePodcastFeedUrl; person accepts a mononym
  (familyName optional) as the reference does.
- Schema contexts: four specs move to the reference's context values
  (ethereum-account and ethereum-smart-contract to
  https://schemas.intuition.systems/v1, social-media-account and one more
  to the reference value); no spec references schema.intuition.systems
  any more. Creation profiles regenerated through the repo generator.
- Tests: 256 pass, including inline parity oracles for labels, ladders
  and the catalogue (rung tokens, schema type and context per type) built
  from the reference data at authoring time; all 67 reference test cases
  present.

Six ladders still carry slugless wd rungs (brand, local-business,
location, event, thing, music-group); the ladder already refuses bare wd
minting unless the plain-wd policy allows it, matching the reference.
…ta (P16d)

The classification parity port changed data the primitives tests pinned:
the Ethereum classifications' JSON-LD context is now the reference value,
a person accepts a mononym (familyName optional, givenName required), and
the movie ladder carries an active typed wd binding so it no longer
serves as the polymorphic bare-P0 example (brand does). Eight
expectations updated; 140 tests pass.
…interpreter so every entry point derives one GUID (P16d fix round)
…lassification data resolves (P16d ledger round)
…ference, including the no-identifier fallback envelope (P05b)
…, now that the builder returns no-identifier envelopes (P05b)
…t the reference's validators and the buildAtomAnchor name (P05c)
… the public builder instead of holding them OPEN (P05c)
@kamescg
kamescg changed the base branch from codex/p05b-anchor-builder-parity to update/v1.1.0-alpha October 20, 2026 16:22
@kamescg
kamescg merged commit 9c5a8f7 into update/v1.1.0-alpha Oct 20, 2026
1 check passed
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