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

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

Development

Successfully merging this pull request may close these issues.

descriptor spec 与安装目标口径不一致:package.name 写成 split 形式(namespace + 短名)的索引包无法被任何消费端安装,且 lint 静默放行

1 participant