chore(main): release blueprint 1.0.0 - #2
Open
github-actions[bot] wants to merge 1 commit into
Open
Conversation
github-actions
Bot
force-pushed
the
release-please--branches--main--components--blueprint
branch
from
August 9, 2026 01:23
bb8ddac to
b497d6c
Compare
7 tasks
github-actions
Bot
force-pushed
the
release-please--branches--main--components--blueprint
branch
from
August 9, 2026 02:53
b497d6c to
0287d27
Compare
This was referenced Aug 9, 2026
github-actions
Bot
force-pushed
the
release-please--branches--main--components--blueprint
branch
from
August 9, 2026 04:42
0287d27 to
347d021
Compare
This was referenced Aug 9, 2026
Closed
justinmerrell
added a commit
that referenced
this pull request
Aug 9, 2026
…nd issue #9 (#25) * docs(conformance): ADR 0002, extend the fixture contract to a case tree Eight of the twenty-four diagnostic codes the three spec.md files declare are about a document's surroundings rather than its contents — a directory with a name, a sibling document, a file on disk. A case was one `case.yaml`, so none of them had a fixture, and they reached `main` as prose with CI green. That is the failure mode CONTRIBUTING.md ground rule 2 exists to prevent. GOVERNANCE.md lists "Changing the conformance fixture contract" among the changes needing an ADR. This is that change. A case directory MAY now carry a `tree/` instead of a `case.yaml`, with `metadata.document` naming the file under test inside it. `tree/` is the parent of the item root rather than the item root itself: ERR_SLUG_MISMATCH tests the item directory's *name*, so a fixture for it needs a directory that has one. `case.yaml` is not thereby the legacy form, and the ADR says so. It asserts that the document has no item root — a real state, per blueprint §3.1, and the one an API payload arrives in. Making `tree/` the only form would have traded one blind spot for another. Symlinks are declared in metadata rather than committed. ERR_PATH_ESCAPE is reachable only by symlink now that listing §5's grammar makes `..` unspellable, and a committed link does not survive a checkout without `core.symlinks`, is invisible in a diff, and would ship inside a release tarball pointing outside the archive. The ADR also corrects tools/src/validator.ts, which asserted that the semantic phase was out of scope "per ADR 0001". ADR 0001 §6 forbids *publishing* a reference validator and is untouched; §7 describes tools/ as one non-normative adapter. Implementing more phases inside it publishes nothing. The alternative was a corpus in which every semantic clause was checked by nothing at all. Refs: #9 Signed-off-by: Justin Merrell <merrelljustin@gmail.com> * fix(tools): reject YAML anchors and aliases in the parser phase Component §7 has said since bootstrap that the parser phase MUST reject anchors and aliases. Nothing did. `parseDocument` set `uniqueKeys`, `merge: false` and `strict`, none of which touches an alias — a document spelling `&b` / `*b` parsed cleanly and the alias expanded. The parser now walks the tree and rejects both. `maxAliasCount` would have covered the alias and not the lone anchor, which is inert and rejected anyway: an author who writes one is reaching for a feature this contract withholds, and §2 already settled that a field which does nothing is an error rather than a silence. Two codes rather than one. `ERR_ANCHOR_OR_ALIAS` is new. `ERR_INVALID_YAML` is not — `validator.ts` has emitted it since bootstrap and no spec.md declared it, so a fixture citing it would have been rejected by the runner's own registry check as an unknown code. Both are now in component §8, and the two say different things to an author: one means the document is malformed, the other means it is well-formed and uses something this contract does not have. Also switches the parser from `parse` to `parseDocument`, which reports every problem instead of throwing on the first and carries machine-readable error codes. The previous implementation matched on message text — the one thing this specification explicitly declares non-normative. The parser lives in a new document.ts so that semantic.ts can read an item's other documents without closing an import cycle. Fail-before evidence: all six anchor/alias cases validate cleanly against tools/ at be77e19 and fail after. The three malformed-YAML cases are green beforehand and are the regression pin for the code registration. This tightens the parser phase — a document with an alias validates today and will not after. It is free while no tag exists and release PRs #1/#2/#3 are open; after they merge it is a v2 directory plus a migration note. Refs: #9 Signed-off-by: Justin Merrell <merrelljustin@gmail.com> * feat(tools): implement the semantic phase and the case-tree runner Every semantic clause PR #13 wrote — acyclicity, the floating-tag blocklist, ERR_CONFLICTING_INPUT_SCHEMA, the media rules — was prose that nothing in CI ever executed. Five fixtures existed and all five were skipped. New semantic.ts, split the way the prose splits the rules rather than the way this runner would find convenient. An **in-document** rule is decided by reading the document: a floating image tag, a probe naming an endpoint that is not declared, a `fromRole` naming no node, a cycle, two screenshots sharing a basename. An **item-scoped** rule is measured against the item root, and is reported only when the caller supplies one. That is blueprint §3.1's rule, not a concession: "an implementation handed a document rather than a directory MUST NOT report any of them … a diagnostic it cannot substantiate is worse than a silence." It is also why `check:examples` stays green — `examples/` is not an item root, and its documents reference `./components/postgres.yaml` and `media/icon.png`, neither of which exists beside them. PR #13's body flagged that as latent breakage; this is the shape that makes it not one. Cycle reporting follows §4.2's canonical form exactly: strongly connected components, each reported as a closed walk from the lexicographically smallest node, choosing the smallest successor at each step. Verified stable across the three key orderings of one three-node cycle — the walk is a property of the graph, not of the traversal, which is the whole point of pinning it. conformance.ts gains the ADR 0002 case-tree form: `tree/` materialised to a scratch directory with its declared symlinks created there, validated with the item root set to the document's own directory. A case must declare exactly one of `case.yaml` or `tree/`; declaring both leaves it ambiguous which one the diagnostics describe. `IMPLEMENTED_PHASES` gains `semantic`. `capability` stays out — it needs an account, a region and a quota, which is a server. Refs: #9 Signed-off-by: Justin Merrell <merrelljustin@gmail.com> * fix(listing): correct the stale media-path description `$defs.ListingSpec.description` still told readers that `icon` and `screenshots[].file` are "paths relative to the listing document" — the exact wording §5 now singles out as wrong: "`media/` is a stronger rule than 'relative to the listing document'. That was this section's earlier wording, and it is not what anything enforces." PR #13 updated the two per-property descriptions and missed the containing object's, so the schema contradicted itself and the prose in one file. Refs: #9 Signed-off-by: Justin Merrell <merrelljustin@gmail.com> * test(conformance): the item-scoped semantic cases, against ADR 0002 Thirteen new cases covering every diagnostic code that needed a directory to exist. All are trees; none was expressible before. blueprint 003–009 take the identity, reference and merge rules one at a time — slug against the directory name, version against the sibling listing, a component document nothing references, a reference resolving to nothing, a reference escaping the item root, a `fromOutput` naming no output of the component it resolves to, and two nodes declaring one input key with differing schemas. Three deserve their reasons stated: 007 the reference is `../shared/postgres.yaml` and the document it lands on exists. A leading `../` is legal spelling — it is how a blueprint reaches a sibling directory — so only a containment check on the resolved path catches this one. 009 `api` and `db` both declare `adminPassword`, STRING against INTEGER. `api` sorts first, so `api`'s declaration stands and `db`'s is reported. The case exists because silent first-wins is defensible until you write down which schema the second component's value was validated against. 011 a three-node cycle written as queue, db, cache. semantic-002 cannot make this case: with two nodes, "smallest first" and "first declared" can coincide by luck. Here they do not, so an implementation reporting from wherever its traversal started produces the wrong walk and fails. listing 002–005 mirror the identity rules and add the two media rules that need the filesystem. 005 is the symlink case, declared in metadata rather than committed per ADR 0002 §3. blueprint 010 and listing 006 are the positive cases, and they are not padding. Each satisfies every item-scoped rule of its family at once, which makes them the regression pin for the class: a careless containment check or a merge rule that forgets to absorb an identical redeclaration rejects a well-formed item, and nothing else in the corpus would notice. `ERR_UNKNOWN_COMPONENT` remains uncovered. It is `capability` — resolving a published reference needs the catalog, and no phase a client runs may reach the network. Refs: #9 Signed-off-by: Justin Merrell <merrelljustin@gmail.com> * docs(conformance): record coverage as a check rather than a claim The runner enforced fixture → spec: every code a case declares must exist in a reachable diagnostics table, at the right phase. Nothing enforced spec → fixture, which is how ten codes reached `main` untested with CI green. Two checks close it. Every `ERR_*` row in a family's own diagnostics table must be exercised by an indexed case or appear in `UNCOVERED` with a reason — after this branch that list holds one entry. And every case directory on disk must be named by `cases.json`, since the runner reads the index rather than walking the tree: an unindexed directory is not a failing fixture, it is an invisible one. Both were verified by injecting a violation and watching the check fail. The README's "Coverage status" described a limitation ADR 0002 removes, so it is rewritten around what is now true: three phases covered, one code uncovered, and a check that derives the list instead of a paragraph someone has to remember to update. It also documents the `tree/` form, `document`, and `symlinks`. CONTRIBUTING gains the obligation this creates: adding a diagnostic code to a spec.md means adding a case for it. Refs: #9 Signed-off-by: Justin Merrell <merrelljustin@gmail.com> --------- Signed-off-by: Justin Merrell <merrelljustin@gmail.com>
github-actions
Bot
force-pushed
the
release-please--branches--main--components--blueprint
branch
from
August 9, 2026 09:25
347d021 to
9669831
Compare
7 tasks
justinmerrell
added a commit
that referenced
this pull request
Aug 9, 2026
…lock (#27) * feat(component): define metadata.version semantics 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> * feat(component): define endpoints, and address the public URL per endpoint 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> --------- Signed-off-by: Justin Merrell <merrelljustin@gmail.com> Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
7 tasks
github-actions
Bot
force-pushed
the
release-please--branches--main--components--blueprint
branch
from
August 11, 2026 11:07
9669831 to
8faba5d
Compare
7 tasks
github-actions
Bot
force-pushed
the
release-please--branches--main--components--blueprint
branch
from
August 17, 2026 00:28
8faba5d to
0282424
Compare
7 tasks
github-actions
Bot
force-pushed
the
release-please--branches--main--components--blueprint
branch
from
August 17, 2026 01:05
0282424 to
fc5a658
Compare
github-actions
Bot
force-pushed
the
release-please--branches--main--components--blueprint
branch
from
August 17, 2026 02:30
fc5a658 to
d07fe6d
Compare
7 tasks
github-actions
Bot
force-pushed
the
release-please--branches--main--components--blueprint
branch
from
August 17, 2026 03:15
d07fe6d to
72c2cdf
Compare
Merged
7 tasks
github-actions
Bot
force-pushed
the
release-please--branches--main--components--blueprint
branch
from
August 17, 2026 16:38
72c2cdf to
4328304
Compare
github-actions
Bot
force-pushed
the
release-please--branches--main--components--blueprint
branch
from
August 18, 2026 00:04
4328304 to
92e8bca
Compare
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.
🤖 I have created a release beep boop
1.0.0 (2026-08-18)
⚠ BREAKING CHANGES
Additions
Corrections
Specification prose
This PR was generated with Release Please. See documentation.