feat(component): define metadata.version semantics and the endpoint block - #27
Merged
Merged
Conversation
Section 4 was a heading and a TODO, while metadata.version was already load-bearing in three rules: blueprint §4.1 deploys "the referenced document's own metadata.version", blueprint §3 and listing §3 pin an item's two halves to each other, and the published reference form carries its own componentVersion. Answers the three questions #15 asks. Monotonicity: each publication MUST carry a version strictly greater than the highest already published for that component. Gaps are permitted. The phase is capability, because deciding it needs the catalog and §7 forbids the earlier phases from reaching the network — so offline validation is exactly as strict as it was and this is a minor release rather than a narrowing one. Reuse: a version is used once. componentVersion: 3 is the whole of what a published node deploys, and a registry that let 3 mean two documents would make the pin name nothing. Whether an identical re-submission is a no-op rather than a publication stays outside this contract, which orders publications without defining when two YAML files are the same document. Relationship to the item: none. The two numbers count different things, and in the published form one component is deployed by many items at once. A component release SHOULD be accompanied by an item release, without a diagnostic — the disagreement is visible only across two revisions and a validator is handed one. ERR_VERSION_NOT_MONOTONIC is the family's first capability code, so §8's closing paragraph no longer says the semantic codes alone are its own. It can have no fixture for the same reason ERR_UNKNOWN_COMPONENT cannot, and is recorded in the runner's UNCOVERED list with that reason. Section 4 also states what metadata carries — version and nothing else, no slug — which nothing said before. Refs #15 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Signed-off-by: Justin Merrell <merrelljustin@gmail.com>
…point Section 5.2 was a heading and a TODO, while the endpoint block was already constrained from the outside: §5 forbids endpoints on a WORKER, JOB and CRON, and §5.4 makes readiness REQUIRED for a SERVICE exposing a PUBLIC endpoint. Answers the three questions #16 asks, and the two things §5.2 turned out to owe that #16 does not name. Port range. containerPort is an integer from 1 to 65535, structural. A port below 1024 is a SHOULD NOT and not a rejection: whether the container can bind one depends on a capability the runtime grants, which this document cannot see, and a rule that rejects on a fact it cannot check is guessing. structural-026 is what keeps that a SHOULD NOT rather than drifting into a MUST. Protocol x visibility. A PUBLIC endpoint is published at a URL by something that speaks the HTTP family, so its protocol MUST be HTTP, HTTPS, WS or GRPC and a TCP or UDP endpoint MUST be PRIVATE. §5.4 is the second argument: a probe polls an HTTP path, so a PUBLIC TCP endpoint would compel a readiness probe it has no way to express. Multiple PUBLIC endpoints are permitted, and each publishes its own URL. The consequence is a rule rather than a caveat — anything naming a public address MUST name the endpoint it means — so platformDefault becomes an object carrying `source` and `endpoint` rather than a bare enum. That field had no prose at all before; §6.1 now has it. Note this is not what #16 assumed: capping PUBLIC at one is not expressible structurally, since JSON Schema has no keyword that counts mapping members matching a condition. Two things §5.2 owed and nothing had paid. "The primary endpoint" was used by §5.4 and by ComponentProbe.endpoint and defined nowhere; it is now the sole endpoint, failing that the sole PUBLIC one, failing that nothing — an error rather than a sort-order tiebreak, because a tiebreak lets a new endpoint named `api` silently re-point a probe that already works. And endpoint names were bare mapping keys, so `web.api` was legal despite being referenced by probes and platform defaults and becoming a DNS label; they now take the slug grammar blueprint §4.1 quotes. Three semantic codes, all fixture-covered: ERR_AMBIGUOUS_ENDPOINT, ERR_ENDPOINT_NOT_PUBLIC, and ERR_UNKNOWN_ENDPOINT broadened past probes. checkProbeEndpoints becomes checkEndpointReferences, which walks probes and platform defaults through one resolver. BREAKING CHANGE: bounding containerPort, restricting a PUBLIC endpoint's protocol, constraining endpoint names, and reshaping platformDefault each reject documents that validate today. No v1.0.0 has been published — every family reads 0.0.0 in the release-please manifest and no tag exists — so §3's guarantee has no released version to run from and this lands free. It does not once #1/#2/#3 merge. Refs #16 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Signed-off-by: Justin Merrell <merrelljustin@gmail.com>
This was referenced Aug 11, 2026
Closed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What changes
Closes the two
component/v1/spec.mdsections that were a heading and aTODOwith no normative content: §4 Metadata and §5.2 Endpoints. Both were left open by #13, and #9 supplied an answer for neither.§4 now states what
metadatacarries —versionand nothing else, noslug, because a component is not the item and its name is the stem of the file holding it — and answers #15's three questions. §5.2 defines the endpoint block and answers #16's three, plus two things §5.2 turned out to owe that #16 does not name.platformDefaultgains prose and an endpoint selector; it had no prose at all before.Two commits, one per issue, so the scoping survives review.
Why
Closes #15. Closes #16.
metadata.versionwas already load-bearing in three rules — blueprint §4.1 deploys "the referenced document's ownmetadata.version", blueprint §3 and listing §3 pin an item's two halves to each other, and the published reference form carries its owncomponentVersion. The endpoint block was constrained from the outside (§5 forbidsendpointson aWORKER,JOBandCRON; §5.4 makesreadinessREQUIRED for aSERVICEexposing aPUBLICendpoint) and undefined on the inside.§4 —
metadata.version(#15)The phase is
capability, because deciding it needs the catalog and §7 forbids the earlier phases from reaching the network. Offline validation is therefore exactly as strict as it was, which is what makes this a minor release rather than a narrowing one. Whether a registry treats an identical re-submission as a no-op rather than a publication stays outside this contract — that sidesteps having to define when two YAML files are the same document.ERR_VERSION_NOT_MONOTONICcan have no fixture for the same reasonERR_UNKNOWN_COMPONENTcannot, and is recorded in the runner'sUNCOVEREDlist with that reason.This does not answer listing §3's
COMPONENT-item question. That is #21 and itsTODOstands.§5.2 — Endpoints (#16)
1–65535. Below 1024 is a SHOULD NOT, not a rejection.structuralPUBLICMUST beHTTP/HTTPS/WS/GRPC;TCP/UDPMUST bePRIVATE.structuralPUBLICA privileged port stays advice because whether the container can bind one depends on a capability the runtime grants, which this document cannot see — and a rule that rejects on a fact it cannot check is guessing.
structural-026-privileged-container-portis what keeps that a SHOULD NOT rather than drifting into a MUST.Permitting several
PUBLICendpoints is what forcesplatformDefaultfrom a bare enum to an object carryingsourceandendpoint.Three findings that differ from what the issues assume
self.publicUrldoes not exist in this repository. component §5.2: endpoint port range, protocol/visibility interaction, multiple PUBLIC endpoints #16 cites it as component §6.2, but §6.2 only says "its public URL" in prose. The real surface wasComponentInput.platformDefault, which had no prose in anyspec.mdand whose description said "the component's own public endpoint" — singular.PUBLICat one would not have beenstructural. JSON Schema 2020-12 has no keyword that counts mapping members matching a condition (maxContainsis arrays only). component §5.2: endpoint port range, protocol/visibility interaction, multiple PUBLIC endpoints #16 says all three answers are structural; only the port bound and protocol × visibility are.ComponentProbe.endpointand defined nowhere. It is now the sole endpoint, failing that the solePUBLICone, failing that nothing — an error rather than a sort-order tiebreak, because a tiebreak lets a new endpoint namedapisilently re-point a probe that has worked for a year.web.apiwas legal despite being referenced by probes and platform defaults and becoming a DNS label. They now take the slug grammar blueprint §4.1 quotes.One rejection neither issue asked for
Defining the primary endpoint means a probe on a workload declaring no endpoint now fails — it polls a port that does not exist. §5 permits a
SERVICEto declare no endpoint and that is unchanged; a probe on one is what stops validating. It has explicit prose in §5.2 and its own fixture rather than being left for a reader to derive.Compatibility
Four narrowings: bounding
containerPort, restricting aPUBLICendpoint's protocol, constraining endpoint names, and reshapingplatformDefault.No new
v<N>directory, and the reason is that v1 has never been published.git tag -lis empty and.github/release-please/manifest.jsonreads0.0.0for all three families. §3's guarantee — "a document that validated againstv1.0.0MUST validate against every laterv1.x.y" — has no releasedv1.0.0to run from, so there is no document anywhere that was validated against a published v1 and would now fail. A migration note would have nothing to migrate from.That argument expires when #1, #2 and #3 merge. #24 sets the same deadline for its divergence 3. If the release PRs land first, this becomes a
v2change.The §4 half is additive on its own:
ERR_VERSION_NOT_MONOTONICiscapability, so no document that validates offline today stops validating.Corpus impact — verified
Nothing in the repository breaks. Ports in use were only
8080and5432; endpoint names onlywebandprimary; no example or fixture pairedPUBLICwithTCP/UDP, declared more than one endpoint, or usedplatformDefaultat all.Checklist
task checkpasses locallyschemas/dist/regenerated withtask bundleand committed (never edited by hand)clausespec.md— schemadescriptions are explanatory, not normativegit commit -s)12 new fixtures, 76 → 88 cases, 0 skipped.
ERR_AMBIGUOUS_ENDPOINT,ERR_ENDPOINT_NOT_PUBLICand the broadenedERR_UNKNOWN_ENDPOINTare each exercised;ERR_VERSION_NOT_MONOTONICis inUNCOVEREDwith a reason. 9 examples validate, including a newmulti-endpoint.yaml— the only place a reader sees the per-endpoint addressing.🤖 Generated with Claude Code