Skip to content

feat: SPEC-001 package identity — name is one segment, wire key is the literal (0.0.106)#280

Merged
Sunrisepeak merged 2 commits into
mainfrom
feat/spec001-package-identity
Jul 25, 2026
Merged

feat: SPEC-001 package identity — name is one segment, wire key is the literal (0.0.106)#280
Sunrisepeak merged 2 commits into
mainfrom
feat/spec001-package-identity

Conversation

@Sunrisepeak

Copy link
Copy Markdown
Member

Why

0.0.105 fixed #278 by requiring package.name to be the fully-qualified <namespace>.<name>. That rule was an encoding constraint dressed up as a design rule.

Its only cause: mcpp threw away the literal package.name it had already read, and re-rendered <ns>.<short> to address the package. The two only matched when the descriptor repeated its namespace inside name.

Use the literal instead and the constraint disappears — the canonical form goes back to what the identity model always said: namespace carries the hierarchy, name is a single atomic segment.

Paired with xlings 0.4.69 (openxlings/xlings#381), which keys its index by (namespace, name) so two packages sharing a short name in ONE index are both addressable. mcpp's part is sending <namespace>:<literal name> rather than a reconstructed string.

What changed — three subtractions, one addition

Identity normalization no longer splits on the last dot. It used to infer a namespace nobody declared: ns="a" + name="a.b.c" silently became (a.b, c). Identity is now exactly the two declared fields; a leftover dot is rejected rather than reinterpreted.

The install target is the literal package.name, prefixed with the package namespace (xlings' effective namespace) — not the index name. That prefix is what disambiguates two same-short-name packages in one index. Namespace-less upstream packages keep their bare name, no prefix.

The store dir formula {ns}-x-{name} is first-class for both spellings instead of buried among six guesses.

Discovery no longer stops at a fixed candidate-filename list. Recommended names stay the fast path; when they all miss, mcpp scans by declared identity. This finally delivers the "filename is not a key" half that identity-first resolution only ever promised — verification was implemented, discovery was not. Conforming indices pay zero scan cost.

Compatibility

The legacy fully-qualified spelling is still accepted (the prefix is stripped before judging), so every published descriptor keeps working unchanged.

A full-registry sweep — 245 descriptors, mcpp indices and xlings-native ones alike — reports zero form violations. Notably that includes all 30 descriptors that needed --allow-split-name under 0.0.105: the short-name rule is what the wider ecosystem already does, and 0.0.105's FQN requirement was the odd one out. That flag is now obsolete, kept accepted only so 0.0.105-era index CI keeps running.

legacy FQN descriptor short-name descriptor
mcpp ≤ 0.0.105 E_NOT_FOUND
mcpp ≥ 0.0.106

Only one broken cell, so indices migrate package by package; an index adopting short names raises its min_mcpp to 0.0.106.

New: docs/spec/

A home for normative documents — semantics, constraints, matching rules — distinct from usage docs (docs/*.md) and per-change design docs (.agents/docs/*.md). Each rule carries an implementation-status tag, so it doubles as "can I write this today?".

SPEC-001 covers identity, the four [dependencies] spellings and their candidate ladders, the full matching pipeline, derived-value formulas, and worked end-to-end examples.

Verification

  • 37/37 unit binaries; XpkgNameForm + CanonicalIdentity cases rewritten for the new semantics.
  • e2e 161 inverted — short/nested/legacy forms accepted; a short name still carrying a dot rejected with both corrected fields named; runtime fail-fast asserted to fire before any download.
  • e2e 163 (new) — short-name install → acme-x-widget; legacy FQN install → acme-x-acme.widget; descriptor at pkgs/z/totally-unrelated-name.lua still discovered; two same-short-name packages in ONE index installing to alpha-x-widget / beta-x-widget.
  • Full-registry xpkg parse sweep: 231 pass / 0 violations; the 14 non-zero exits are byte-identical on 0.0.104 (pre-existing, mostly xlings' own test fixtures).
  • xlings pin → 0.4.69 across kXlingsVersion, release.yml, ci-linux-e2e.yml, cross-build-test.yml.

Spec: docs/spec/package-identity.md
Plan: .agents/docs/2026-07-25-spec001-implementation-plan.md

…e literal (0.0.106)

0.0.105 fixed #278 by REQUIRING `package.name` to be the fully-qualified
`<namespace>.<name>`. That rule was an encoding constraint dressed up as a
design rule. Its only cause: mcpp threw away the literal `package.name` it had
already read and re-rendered `<ns>.<short>` to address the package, so the two
only matched when the descriptor repeated its namespace inside `name`.

Use the literal instead and the constraint disappears. The canonical form goes
back to what the identity model always said: `namespace` carries the hierarchy,
`name` is a single atomic segment.

Paired with xlings 0.4.69 (openxlings/xlings#381), which keys its index by
(namespace, name) so two packages sharing a short name in ONE index are both
addressable. mcpp's part is sending `<namespace>:<literal name>` rather than a
reconstructed string.

Three subtractions, one addition:

  - identity normalization no longer splits on the last dot. It used to infer a
    namespace nobody declared — `ns="a"` + `name="a.b.c"` silently became
    `(a.b, c)`. Identity is now exactly the two declared fields; a leftover dot
    is rejected rather than reinterpreted.
  - the install target is the descriptor's literal `package.name`, prefixed with
    the package NAMESPACE (xlings' effective namespace), not the index name.
    Namespace-less upstream packages keep their bare name.
  - the store dir formula `{ns}-x-{name}` is first-class for both spellings
    instead of buried among six guesses.
  - discovery no longer stops at a fixed candidate-filename list. Recommended
    names stay the fast path; when they all miss, mcpp scans by declared
    identity. This finally delivers the "filename is not a key" half that
    identity-first resolution only ever promised — verification was implemented,
    discovery was not.

Compatibility: the legacy fully-qualified spelling is still accepted (the
prefix is stripped before judging), so every published descriptor keeps working
unchanged. A full-registry sweep — 245 descriptors, mcpp indices and
xlings-native ones alike — reports zero form violations. Notably this includes
all 30 descriptors that needed `--allow-split-name` under 0.0.105; that flag is
now obsolete and kept accepted only so 0.0.105-era index CI keeps running.

Adds docs/spec/ for normative documents, with SPEC-001 covering identity,
the four [dependencies] spellings and their candidate ladders, the matching
pipeline, derived-value formulas and worked examples — each rule tagged with
its implementation status.

Verified: 37/37 unit binaries; e2e 161 inverted; new e2e 163 covers short-name
install, legacy FQN install, arbitrary filename discovery, and two same-short-name
packages in one index resolving to alpha-x-widget / beta-x-widget.

Spec:  docs/spec/package-identity.md
Plan:  .agents/docs/2026-07-25-spec001-implementation-plan.md
…xlings 0.4.69

Two CI failures from PR #280, both real and both worth fixing rather than
working around.

1. Windows e2e ran the PREVIOUS release's binary.

   `MCPP_SELF=$(find target -name "mcpp.exe" -path "*/bin/*" | head -1)`

   `target/` is restored from cache and keeps one directory per build
   fingerprint. A version bump changes the fingerprint, so the freshly built
   0.0.106 binary landed next to the cached 0.0.105 one and `head -1` returned
   whichever the directory walk hit first — hence `Version mismatch:
   mcpp.toml=0.0.106, --version='mcpp 0.0.105'`.

   Latent all along; the version bump is just what made two candidates exist.
   The Linux workflows already sort by mtime and take the newest — this brings
   the five Windows/release sites in line with them.

2. e2e 163 step 4 asserted a capability the sandbox cannot have yet.

   That step covers two same-short-name packages in ONE index, which needs
   xlings >= 0.4.69 (openxlings/xlings#381). The sandbox xlings comes from
   bootstrapping the PREVIOUS mcpp release, so CI runs 0.4.68 until 0.0.106
   ships bundling 0.4.69 — a genuine chicken-and-egg, not a defect.

   Step 4 now detects the sandbox xlings version and skips with a stated
   reason when it is older. Properties 1-3 (short-name install, legacy FQN
   install, arbitrary-filename discovery) are what this change is responsible
   for and stay asserted unconditionally.

Verified both branches locally by swapping the sandbox xlings: 0.4.69 runs
step 4, 0.4.68 skips it with the reason printed.
@Sunrisepeak
Sunrisepeak merged commit 97e1185 into main Jul 25, 2026
15 checks passed
@Sunrisepeak
Sunrisepeak deleted the feat/spec001-package-identity branch July 25, 2026 10:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant