Seats are a closed set, a seat's holder answers for what it delivers, and a build source may live on the git seat #63

Merged
jschoubben merged 2 commits from feat/seats-are-a-closed-set into main 2026-09-26 12:31:16 +00:00
Owner

Implements novox/hq ADR 0110 and 0111. It is one of three PRs on feat/seats-are-a-closed-set, with novox/hq and novox/mesh-catalog.

What changes

  • The seat set is in internal/catalogue/seats.go: 14 seats, each with a scope, what it delivers, and its record. A test asserts the count and a decision per entry.

  • Validation. ParseManifest refuses three things:

    • a claim on a seat the mesh does not define;
    • a seat claimed at another scope;
    • a delivering seat claimed by a module that does not provide what it delivers.

    A malformed claim is refused once, for being malformed.

  • Resolution. Among several providers of a mesh provision the order is pin, then the seat's holder, then the only provider, then refusal. Provider gains Module, because a provider is a (node, module) pair.

  • Planner fix. The second pass now receives the first pass's holdings. Before, a node consuming a seat-delivered provision was refused in that pass. Its own claims then silently dropped out of what the mesh holds, so a second holder of one of its seats would pass unrefused.

  • seats [--json] lists every seat with what it delivers and each holder. Holders are derived from assignments on every call and never stored. Unheld seats are listed. A stored claim outside the set is shown, not hidden: stored manifests aren't re-validated, so a live mesh keeps working.

  • build --self <owner>/<repo> composes the clone URL from the git seat's holder at build time. It records the path and the seat (migration 0032), never an address. With no holder it refuses and says why. External URLs are unchanged. An address passed with --self is refused rather than recorded as a path.

Tests

Seat-set closure; the three refusals; every catalogue manifest plus this repo's own claims a seat in the set; the network module's code-composed claim; holder versus pin versus unheld; holder told apart from a same-node neighbour; the overview; URL composition, including a moved port; the refusal paths; and the inventory round-trip of a seat source against Postgres.

This PR also replaces three tests already failing on main. They defended the builder's hand-written package binding, which the catalogue has since removed. They now assert the builder requires what the npm seat delivers, and that the forge holds the npm and git seats.

Verified

  • go vet is clean.
  • The whole suite passes against a throwaway Postgres (make postgres), and the new inventory tests were confirmed to run, not skip.
  • gofmt is clean except cmd/mesh-builder/stdout_test.go, which fails on main too.
  • Not run: the lab.
Implements **novox/hq ADR 0110 and 0111**. It is one of three PRs on `feat/seats-are-a-closed-set`, with novox/hq and novox/mesh-catalog. ## What changes - **The seat set** is in `internal/catalogue/seats.go`: 14 seats, each with a scope, what it delivers, and its record. A test asserts the count and a decision per entry. - **Validation.** `ParseManifest` refuses three things: - a claim on a seat the mesh does not define; - a seat claimed at another scope; - a delivering seat claimed by a module that does not provide what it delivers. A malformed claim is refused once, for being malformed. - **Resolution.** Among several providers of a mesh provision the order is pin, then the seat's holder, then the only provider, then refusal. `Provider` gains `Module`, because a provider is a (node, module) pair. - **Planner fix.** The second pass now receives the first pass's holdings. Before, a node consuming a seat-delivered provision was refused in that pass. Its own claims then silently dropped out of what the mesh holds, so a second holder of one of its seats would pass unrefused. - **`seats [--json]`** lists every seat with what it delivers and each holder. Holders are derived from assignments on every call and never stored. Unheld seats are listed. A stored claim outside the set is shown, not hidden: stored manifests aren't re-validated, so a live mesh keeps working. - **`build --self <owner>/<repo>`** composes the clone URL from the git seat's holder at build time. It records the path and the seat (migration `0032`), never an address. With no holder it refuses and says why. External URLs are unchanged. An address passed with `--self` is refused rather than recorded as a path. ## Tests Seat-set closure; the three refusals; every catalogue manifest plus this repo's own claims a seat in the set; the network module's code-composed claim; holder versus pin versus unheld; holder told apart from a same-node neighbour; the overview; URL composition, including a moved port; the refusal paths; and the inventory round-trip of a seat source against Postgres. **This PR also replaces three tests already failing on `main`.** They defended the builder's hand-written package binding, which the catalogue has since removed. They now assert the builder requires what the npm seat delivers, and that the forge holds the npm and git seats. ## Verified - `go vet` is clean. - The **whole suite passes against a throwaway Postgres** (`make postgres`), and the new inventory tests were confirmed to run, not skip. - `gofmt` is clean except `cmd/mesh-builder/stdout_test.go`, which fails on `main` too. - Not run: the lab.
jschoubben added 1 commit 2026-09-25 18:49:14 +00:00
Implements novox/hq ADR 0110 and 0111.

The seat set lives in internal/catalogue/seats.go: fourteen seats, each with a scope, what occupying
it delivers, and the record that made it one. A test asserts the count and a decision per entry, so
changing the set means finding the argument, as the host's vocabulary test does. The first set is
every seat already claimed — including the-private-network, which the network module claims from a
manifest composed in this repository's code, not from any module.json — plus npm-package-registry
(ADR 0109) and git (ADR 0111). A test parses every catalogue manifest and this repository's own and
fails on any refused claim, so closing the set refuses nothing in use.

ParseManifest now refuses a claim on a seat the mesh does not define, a seat claimed at another
scope, and a delivering seat claimed by a module that does not provide what it delivers. A
malformed claim is refused once, for being malformed.

Resolution: among several providers of a mesh provision, a pin still wins; then the holder of the
seat that delivers it; then the only provider; otherwise refused as before. ADR 0009's "never
guessed" holds — the seat is the choice made once, mesh-wide, rather than a pin per consumer node.
A provider now carries the module it came from, because a provider is a (node, module) pair and the
pair is what tells a holder from a neighbour on the same machine.

The planner's second pass is now given the first pass's holdings. Without them, a node consuming a
seat-delivered provision was refused there, and a refused node's own claims dropped out of what the
mesh holds — letting a second holder of one of its seats pass unrefused.

`seats [--json]` lists every seat, what it delivers, and each holder, derived from assignments
every time and never stored. Unheld seats are listed. A stored claim outside the set — possible
for a manifest registered before the set closed, since stored manifests are not re-validated — is
shown rather than hidden.

`build --self <owner>/<repo>` builds from a repository on the git seat's holder. The clone URL is
composed at build time from the holder's node and what it serves for git; the recorded source is the
path and the seat (migration 0032), never an address, so a moved forge changes nothing recorded.
Nobody holding the seat refuses self-hosted builds and says so; external URLs are unchanged. An
address passed with --self is refused rather than recorded as a path.

Replaces three foundation tests that defended the builder's carried package binding. The catalogue
removed that binding when the builder began requiring the registry through a real grant, so the
tests were already failing on main; they now assert the builder requires what the npm seat delivers
and carries no copy of its own, and that the forge holds the npm and git seats.

Verified: go vet clean; the whole suite passes against a throwaway Postgres (make postgres), the new
inventory tests included; gofmt clean apart from cmd/mesh-builder/stdout_test.go, which fails on
main too.
jschoubben added 1 commit 2026-09-26 12:30:51 +00:00
Author
Owner

ADRs 0110 and 0111 are accepted on hq's main (hq #118), and 26 — The seats is in-progress naming the files in this PR, so the design side no longer blocks this.

Verified before merge, with main merged into the branch (pushed, so what lands is what was tested):

  • go build ./... clean; go test ./... 19 packages pass, no failures.
  • These tests only pass with the catalogue half checked out too. internal/catalogue/foundation_manifests_test.go reads ../../../mesh-catalog/modules/<m>/module.json from the sibling working tree. With mesh-catalog on main, four tests fail — the forge claims neither seat and the builder requires artifact-store alone. So this PR and mesh-catalog #69 are one change and must land together; between the two merges, a suite run against main will fail either way round.

One thing a reviewer should see rather than discover later: this PR deletes the three tests issue 121 named as deliberately left failing — TestTheBuildersCarriedPackageBindingTakesThePortFromTheNode, TestTheBuildersCarriedBindingStartsWhereTheForgeServes, TestTheBuildersPackageBindingKeepsItsIdentity — and replaces them with TestTheBuilderRequiresTheRegistryTheNpmSeatDelivers and TestTheForgeHoldsTheNpmAndGitSeats. The failing tests go away, but the condition they pinned is not obviously answered: builder still requires a registry provision (now npm-package-registry) that only the forge provides, and the forge's own runtime image is what builder builds. Issue 121's open questions — whether builder carries both a real requirement and a fallback, or genesis sequences around it — are still open.

ADRs 0110 and 0111 are `accepted` on hq's `main` (hq #118), and `26 — The seats` is `in-progress` naming the files in this PR, so the design side no longer blocks this. Verified before merge, with `main` merged into the branch (pushed, so what lands is what was tested): - `go build ./...` clean; `go test ./...` **19 packages pass, no failures**. - **These tests only pass with the catalogue half checked out too.** `internal/catalogue/foundation_manifests_test.go` reads `../../../mesh-catalog/modules/<m>/module.json` from the sibling working tree. With mesh-catalog on `main`, four tests fail — the forge claims neither seat and the builder requires `artifact-store` alone. So this PR and **mesh-catalog #69** are one change and must land together; between the two merges, a suite run against `main` will fail either way round. One thing a reviewer should see rather than discover later: this PR **deletes** the three tests issue 121 named as deliberately left failing — `TestTheBuildersCarriedPackageBindingTakesThePortFromTheNode`, `TestTheBuildersCarriedBindingStartsWhereTheForgeServes`, `TestTheBuildersPackageBindingKeepsItsIdentity` — and replaces them with `TestTheBuilderRequiresTheRegistryTheNpmSeatDelivers` and `TestTheForgeHoldsTheNpmAndGitSeats`. The failing tests go away, but the condition they pinned is not obviously answered: `builder` still requires a registry provision (now `npm-package-registry`) that only the forge provides, and the forge's own runtime image is what `builder` builds. Issue 121's open questions — whether `builder` carries both a real requirement and a fallback, or genesis sequences around it — are still open.
jschoubben merged commit 0944311f86 into main 2026-09-26 12:31:16 +00:00
jschoubben deleted branch feat/seats-are-a-closed-set 2026-09-26 12:31:16 +00:00
Sign in to join this conversation.
No Reviewers
No labels
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: novox/mesh-controller#63