CANP-WP-0002 T07: semver precedence and strict packaging

Two defects from the original review of the seed.

Prerelease ordering was worse than first recorded. parse_semver returned
(major, minor, patch, raw_string), so 1.0.0-rc1 and 1.0.0 tied on the numeric
fields and then compared as strings — "1.0.0-rc1" > "1.0.0". A release
candidate therefore shadowed its own release for `newest` and for `>=`, not
just for the no-version case.

parse_semver now returns a SemVer section 11 precedence key: numeric fields, a
release/prerelease rank, then dot-separated prerelease identifiers with
numeric ones compared numerically. Build metadata is ignored. Beyond ordering,
prereleases are excluded from `any`, `newest` and `>=` entirely; only an exact
pin selects one, so publishing a release candidate never changes what existing
consumers resolve to. A package holding only prereleases now says so rather
than reporting a bare not-found.

Packaging copied the whole source directory, so a stray .git, virtualenv or
scratch file landed in the catalog and registry. Section 2 already required
otherwise — tools MUST ignore unknown non-reserved files unless a manifest
field references them — so this is conformance rather than a new rule. What is
new is that omissions are reported instead of silent:

  not packaged (not a reserved path, not referenced by the manifest): .git/, .venv/, notes.txt

LICENSE joins the reserved paths. Strict packaging would otherwise drop a
package's license text while faithfully copying its `license` field, which
contradicts section 14's instruction to surface licensing on publish and
install.

Spec: 2 (LICENSE, packaging obligation, reporting), 17.1 new, 10.3 note.

Reference CLI: parse_semver rewritten with is_prerelease; select_version and
pick_version updated; copy_package and report_skipped replace copy_immutable.
Tests 65 -> 78.

examples/pqrst-estimate carries a LICENSE and a license field, exercising the
new reserved path.

Also fixes a leaked loop variable in package_members that would have reported
a bad `template` path as an `evals` error.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Bjefh8NUiEiahN4JLwoSKM

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 388925@bnt-lap001
Assistant-Session: 3507023f-e0fd-4a1e-9d90-a0d4217d1502
This commit is contained in:
tegwick 2026-09-06 09:32:42 +02:00
parent e885eb7b05
commit 95a8bb31d2
8 changed files with 306 additions and 34 deletions

View file

@ -53,12 +53,24 @@ my-prompt/
| `prompt.yaml` | Required package manifest |
| `prompt.md` | Default prompt template unless overridden by `template` |
| `README.md` | Optional human documentation |
| `LICENSE` | Optional license text |
| `examples/` | Optional examples/fixtures |
| `evals/` | Optional evaluation specifications |
| `assets/` | Optional supporting text/data artifacts |
Tools MUST ignore unknown non-reserved files unless a manifest field explicitly references them.
This governs packaging as well as reading: a tool that copies a package into a
catalog or registry MUST copy the reserved paths and the files a manifest
field references, and nothing else. A working directory's `.git`, virtualenv or
scratch files are not part of the package and MUST NOT be published with it. A
tool that omits files SHOULD say which, so that an author is never silently
surprised by what did not ship.
`LICENSE` is reserved because § 14 asks tools to surface licensing on publish
and install, and because dropping the license text while faithfully copying the
`license` field would misrepresent the package.
## 3. Manifest
The canonical manifest is UTF-8 YAML named `prompt.yaml`.
@ -680,6 +692,9 @@ reported as ambiguous rather than chosen.
| `newest` | the newest available version |
| `>= 1.2.0` | the newest available version that is at least `1.2.0` |
Prereleases are excluded from `any`, `newest` and `>=` (§ 17.1); name one
exactly to select it.
**An exact pin is the expected form.** The other three are explicit opt-ins,
visible in the manifest, so that looseness is always something an author wrote
rather than something absence implied.
@ -887,6 +902,17 @@ both is not in a conflict — it is holding two things whose names happen to
coincide. Only a repeat publication *within one registry* violates
immutability.
### 17.1 Precedence and prereleases
Where versions are compared, precedence follows Semantic Versioning: numeric
fields first, and a prerelease version ranks **below** its own release, so
`1.0.0-rc1` precedes `1.0.0`. Build metadata does not affect precedence.
A prerelease is **not selected** by `any`, `newest`, or a `>=` lower bound
(§ 10.3). Only an exact pin selects one. Publishing a release candidate
therefore never changes what existing consumers resolve to — which is the
point of marking it a candidate.
Suggested versioning guidance:
- PATCH: wording/metadata correction with intended behavior unchanged;