CANP-WP-0006 T03: read API
Search, versions, manifests, archives and the index, over HTTP.
The route shape is the decision worth recording. Package ids contain `/`, so
the obvious /packages/{id}/{version} is ambiguous under a greedy path
parameter. Rather than invent an HTTP-specific identifier, the routes speak the
format's own <registry>:<id>@<version> syntax and parse it — `:` and `@` are
both legal in a path segment, and each route keeps a distinct prefix so
greediness cannot swallow a neighbouring one. The API therefore exercises
section 3.2's reference notation instead of working around it.
A bare id present in more than one registry returns 409 with the candidates,
never a guess. 409 rather than 300 because the request is answerable once the
caller says which registry they meant. Omitting a version applies section
17.1's selector rules, so a prerelease is never chosen implicitly.
Validation is delegated to reference/, installed into the service environment
rather than reimplemented. One validator means the service and the CLI cannot
disagree about what a valid package is; a service accepting something the CLI
rejects would be the divergence this project exists to prevent. Importing it is
not changing it — reference/ stays the dependency-light conformance witness.
Storage keeps the format's distinctions: an immutable package version, its
files as content rather than parsed rows, and an index entry recording arrival.
Only reserved paths and manifest-referenced files are stored (section 2), and a
re-publish of identical content is accepted while different content under the
same id@version is a conflict (section 17).
Handles a real test-vs-production difference: SQLite autoincrements INTEGER
PRIMARY KEY only, never BIGINT, so the SQLite-backed tests could not insert a
row. BigInteger().with_variant(Integer, "sqlite") keeps BIGINT on PostgreSQL
while letting the tests exercise the same models and migration.
Verified live against a seeded store holding this repo's examples and four
helix-forge prompt packages. Service tests 11 -> 22.
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:
parent
d5782d4d04
commit
5f47da8036
15 changed files with 641 additions and 11 deletions
|
|
@ -114,7 +114,7 @@ consolidation time. Uniqueness is `(tenant, registry, package_id, version)`.
|
|||
|
||||
```task
|
||||
id: CANP-WP-0006-T03
|
||||
status: todo
|
||||
status: done
|
||||
priority: high
|
||||
state_hub_task_id: "0ae11e65-68b0-543c-b8fa-f3188a4d239a"
|
||||
```
|
||||
|
|
@ -127,6 +127,30 @@ state_hub_task_id: "0ae11e65-68b0-543c-b8fa-f3188a4d239a"
|
|||
Ambiguity is reported, never guessed (§ 3.2): a bare id matching packages from
|
||||
more than one registry returns the candidates, not a choice.
|
||||
|
||||
**Done**, with the route shape worth recording. Package ids contain `/`, so the
|
||||
obvious `/packages/{id}/{version}` is ambiguous under a greedy path parameter.
|
||||
Rather than invent an HTTP-specific identifier, the routes speak the format's
|
||||
own `<registry>:<id>@<version>` syntax and parse it — `:` and `@` are both legal
|
||||
in a path segment, each route keeps a distinct prefix, and the API therefore
|
||||
tests § 3.2's reference notation rather than working around it.
|
||||
|
||||
Ambiguity returns **409 with the candidates**, not 300: the request is
|
||||
answerable once the caller says which registry they meant. Omitting a version
|
||||
applies § 17.1's selectors, so a prerelease is never chosen implicitly.
|
||||
|
||||
Validation is delegated to `reference/`, installed into the service
|
||||
environment rather than reimplemented. One validator means the service and the
|
||||
CLI cannot disagree about what a valid package is — a service accepting
|
||||
something the CLI rejects would be exactly the divergence this project exists to
|
||||
prevent. Importing it is not changing it; `reference/` stays the
|
||||
dependency-light conformance witness.
|
||||
|
||||
**Test-vs-production difference found and handled.** SQLite autoincrements
|
||||
`INTEGER PRIMARY KEY` only, never `BIGINT`, so the SQLite-backed tests could not
|
||||
insert a row. `BigInteger().with_variant(Integer, "sqlite")` keeps `BIGINT` on
|
||||
PostgreSQL, where this actually runs, while letting the tests exercise the same
|
||||
models and the same migration.
|
||||
|
||||
## Publish API
|
||||
|
||||
```task
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue