Skip to content

fixture-composition: one fragment per module, merged by the run - #42

Merged
antoinetoussaint-byte merged 5 commits into
mainfrom
fixture/module-fragments
Oct 27, 2026
Merged

antoinetoussaint-byte merged 5 commits into
mainfrom
fixture/module-fragments

Conversation

@antoinetoussaint-byte

@antoinetoussaint-byte antoinetoussaint-byte commented Oct 27, 2026 •

Copy link
Copy Markdown
Collaborator

What

One definition per module for the local fixture. Each composed module ships a fragment of the fixture-composition document next to its code (<module>/configurations/<profile>/fixture-composition.yaml): the grants of the fixture's standard roles at that module's kinds. A composition's own module ships only what is truly its own (a seeded collection's id). The run merges them (codefly-dev/cli, codefly run / codefly generate fixture-composition); this repository owns the document and the merge rules.

  • libraries/fixture-composition/go (new): the document's one parser (Decode) and its merge (Merge). Every statement has exactly one source: a role's grant on one (organization, role, kind), one installation (organization, target), one stand-in module. A second source is ErrConflict, naming both, even when the two agree (a composition narrowing a module's grant to a seeded id must not be silently widened by it; a module's grant copied into a playground is the drift this ends). The merge is order-independent (sources sorted by name; duplicate names refused).
  • local service: state.ApplyComposition decodes with the library (the old decodeComposition/checkScope/grammar are gone); the fixture types Scope, ResourceMatch, FixtureModule, FixtureInstallation are aliases of the library's, so the fixture and the document share one spelling. Fixture-dependent checks (role defined, developer role, organisation/owner) stay in the host.
  • docs: services/local/README.md "What a composition adds to the fixture", root README, the authority module's own (empty) fragment, service.codefly.yaml and work.go comments.

Reuse: the delivery is core's existing composition of every module's configurations/<profile>/*; the CLI's generated workspace document resolves the name through core's existing precedence (the workspace's own declaration). No new delivery path, no schema change (v1 unchanged).

Evidence

  • libraries/fixture-composition/go: go vet + go test -race — merge of two module fragments + a composition's own (order-independent, round-trips through Encode/Decode, keeps the \x1f id); conflict refused naming both sources (narrowed grant, agreeing grant, installation, stand-in, duplicate source name); every refusal names the fragment.
  • modules/authority/services/local/code: go vet ./..., go test -race ./... all green; tools/noproxygate, tools/packagegate green.
  • Live (branch CLI, authority pinned at main de0deb68, which reads the merged v1 document unchanged): see the document-store and robin PRs.

Fix or hack

Fix at the owner: the merge rules live with the schema's owner; the CLI only finds fragments.

Follow-ups

  • Migrate playground/configurations/local/fixture-composition.yaml in this repo (and core-solutions wiki/proposals) to module fragments; until then a run reads a hand-written copy as it stands and warns.

🤖 Generated with Claude Code

antoinetoussaint-byte and others added 4 commits October 27, 2026 10:37
…fixture fragments

Each composed module now states what the fixture's standard roles may do at
its kinds in a fragment next to its code, and a composition states only what
is its own. libraries/fixture-composition/go is the document's one parser
(Decode) and its merge (Merge): every grant on a (organization, role, kind),
every installation and every stand-in module has exactly one source, and a
second source is refused naming both. The run (codefly-dev/cli) merges the
fragments into the workspace document the local authority reads; the local
authority decodes it with the same library and checks it against its fixture.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@antoinetoussaint-byte antoinetoussaint-byte changed the title fixture-composition: one fragment per module, merged by the run fixture-composition fragments merged by the run, and ListHeld (supersedes #41) Oct 27, 2026
@antoinetoussaint-byte antoinetoussaint-byte changed the title fixture-composition fragments merged by the run, and ListHeld (supersedes #41) fixture-composition: one fragment per module, merged by the run Oct 27, 2026
@antoinetoussaint-byte
antoinetoussaint-byte merged commit bfe4876 into main Oct 27, 2026
17 checks passed
antoinetoussaint-byte added a commit to codefly-dev/cli that referenced this pull request Oct 27, 2026
codefly-dev/authority#42 merged as bfe4876a. The pseudo-version this PR
carried, e2fba9d058e1, exists only on that PR's branch: a squash-merge and
branch delete would orphan it and break `go mod download` here. The library
tree is byte-identical between the two.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
crisfiorini-oss pushed a commit to crisfiorini-oss/cli that referenced this pull request Oct 27, 2026
…ered where no module is named (codefly-dev#1197)

* feat(run): merge every composed module's fixture-composition fragment before the local authority boots

Each module states once, next to its code, what the local authority's standard
fixture roles may do at its kinds (configurations/<profile>/fixture-composition.yaml
in the module); a composition's own module states only what is its own. `codefly
run` now merges those fragments into the workspace's fixture-composition document
before anything reads the configuration, when a composed service declares the
group, and `codefly generate fixture-composition --env local` does the same on
its own. The merge rules are the authority's
(codefly-dev/authority libraries/fixture-composition): every grant on one
(organization, role, kind), installation and stand-in module has one source, and
a second is refused naming both. The written file says it is generated; a
hand-written one is never overwritten (generate refuses it; a run reads it as it
stands and warns that no module's fragment is applied).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(generate): fixture-composition materializes pinned modules first, as a run does

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(fixture-composition): the lint gate's four findings on the new code

golangci-lint v2.14.0 (`make lint`): the generated document is written 0600
like every other file the run writes, the three shadowed `err` declarations are
named for what they report, and the unused cobra parameter is `_`.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* fix(orchestration): a service declaring hop-routes with no module named gets the group empty, not nothing

A host that stands a hop on some compositions and not others declares
`hop-routes` always — saas's auth-gateway is the hop on a cell and stands none
where the composition names no module. `generatedHopRoutes` returned no group
at all for that composition, and core's reader
(GetWorkspaceDependenciesConfigurations) refuses a declared group that resolves
to nothing: the service failed at runtime-init, from the declaration, before
any of its code ran, so it could not degrade — and it could not stop declaring
the group either, since generation requires a declaring service.

The group is now delivered with an empty ROUTES. Nothing decodes it: the
gateway stands its hop only where `hop/MODULES` names modules, and
freezeDeliveredHopAddresses skips an empty value, so DecodeRouting is never
reached. The routing snapshot of an empty value equals that of no group, so no
run is told its routes changed. A composition that names modules still gets its
generated routes, and a workspace no service of which declares the group still
gets nothing.

`codefly doctor workspace` is the same seam, not a second fix: it takes
generated groups from this inventory ("never from a name exemption"), so it now
reports the group as supplied by the invocation instead of
`configuration_missing`. The baselined refusal that asserted the old failure
("no selected modules") is replaced by a test of the new behaviour rather than
kept green.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* chore: drop the go.sum entries a local golangci-lint install added

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* build: pin the fixture-composition library to its merged commit

codefly-dev/authority#42 merged as bfe4876a. The pseudo-version this PR
carried, e2fba9d058e1, exists only on that PR's branch: a squash-merge and
branch delete would orphan it and break `go mod download` here. The library
tree is byte-identical between the two.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* build: drop the orphaned fixture-composition pseudo-version from go.sum

The branch-only e2fba9d058e1 is no longer required; only the merged
bfe4876a is.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* build: pin the fixture-composition library past authority#43

codefly-dev/authority#43 merged as 5178f016, adding the intra-document half of
the one-statement-one-source rule: Decode refuses the same (organisation, role,
kind), installation or stand-in module stated twice within ONE fragment.

The bump is about the diagnostic, not correctness. Without it a duplicate
inside one fragment is still refused — but at the authority's boot-time Decode
of the MERGED document, which names the merged file. The developer is then told
the wrong file is wrong: the generated workspace document rather than the module
fragment that owns the duplicate. With this pin the merge refuses it where it is
written, naming that fragment.

The orphaned bfe4876a pseudo-version is dropped from go.sum: `go get` leaves it
behind, and it names a ref that is no longer required.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

---------

Co-authored-by: Antoine Toussaint <312536060+antoinetoussaint-byte@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant