Skip to content

feat(variant): cross-repo feature model composition core (REQ-085 v1)#321

Merged
avrabe merged 1 commit into
mainfrom
feat/req-085-cross-repo-composition
May 24, 2026
Merged

feat(variant): cross-repo feature model composition core (REQ-085 v1)#321
avrabe merged 1 commit into
mainfrom
feat/req-085-cross-repo-composition

Conversation

@avrabe
Copy link
Copy Markdown
Contributor

@avrabe avrabe commented May 24, 2026

Summary

Core API for REQ-085 (cross-repo feature model composition). v1
delivers the rivet-core API + tests; CLI threading (so `rivet
variant` automatically builds the externals map from
`rivet.yaml`'s `externals:`) is the v2 follow-on, same
core-first-CLI-later shape as REQ-086 v1.

What's new

```rust
FeatureModel::load_composed_with_externals(binding, externals_map)
FeatureModel::load_with_externals(path, externals_map)
```

A mount's `model:` is now either a local relative path
(REQ-083 behaviour, unchanged) or `:`
resolved via `externals[prefix].join(inner-path)`.

```yaml

binding.yaml — the binding names the prefix, NOT the git source

compose:

  • parent: vehicle.yaml
    mount:
    powertrain:
    model: acme-pwt:powertrain.yaml # rivet.yaml's externals[acme-pwt]
    prefix: pwt
    ```

This is the design REQ-085 captured: `rivet.yaml` is the single
source of truth for "where external repos come from"; the binding only
says "which file, mounted where, under what prefix" — and composition
rides the existing `rivet sync` plumbing entirely.

Fail-loud (F2 ethos inherited from REQ-083)

`resolve_model_path` rejects a prefix-shaped reference that doesn't
match any declared external — never a silent fall-back to local-path
resolution that won't find the file. Error names the offending prefix
and the declared set.

Test plan

  • Three new tests in `feature_model::tests`:
    `compose_with_externals_resolves_prefixed_mount` (happy path),
    `compose_with_externals_unknown_prefix_is_error` (loud failure),
    `compose_with_empty_externals_treats_paths_as_local` (backward
    compat). All pass.
  • All 51 existing REQ-083 composition tests still green (refactor
    preserves backward compat — `load_composed` is now a thin
    wrapper).
  • CI green.

🤖 Generated with Claude Code

Adds the core API for mounting feature-model sub-models from external
repositories without the binding file re-declaring git sources — the
binding only names a prefix the consumer's rivet.yaml already declares
and `rivet sync` has fetched. Per the REQ-085 design, rivet.yaml stays
the single source of truth for "where external repos come from"; the
binding only says "which file, mounted where, under what prefix."

New API:
  FeatureModel::load_composed_with_externals(binding_path, externals_map)
  FeatureModel::load_with_externals(path, externals_map)

A mount's `model:` field is now either a local relative path (REQ-083
behaviour, unchanged) OR `<external-prefix>:<inner-path>` resolved via
externals[prefix].join(inner-path). The path-resolution helper
(`resolve_model_path`) fails loudly when a prefix-shaped reference does
not match any declared external — no silent fall-back to local-path
resolution that won't find the file (F2 ethos inherited from REQ-083).

Three new tests cover: a happy-path mount resolving through an external
prefix, an unknown prefix erroring with the declared set named, and
backward compat (`load_composed_with_externals` with an empty externals
map behaves identically to `load_composed` for local-only bindings).
All 51 existing REQ-083 composition tests still green.

CLI threading (so `rivet variant` commands automatically build the
externals map from the project's `rivet.yaml` via ProjectContext) is
the v2 follow-on increment — same boundary as REQ-086 v1's
core-first-CLI-later shape.

Implements: REQ-085
Verifies: REQ-085
@github-actions
Copy link
Copy Markdown

📐 Rivet artifact delta

No artifact changes in this PR. Code-only changes (renderer, CLI wiring, tests) don't touch the artifact graph.

Copy link
Copy Markdown

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Performance Alert ⚠️

Possible performance regression was detected for benchmark 'Rivet Criterion Benchmarks'.
Benchmark result of this commit is worse than the previous benchmark result exceeding threshold 1.20.

Benchmark suite Current: 951f458 Previous: 86bf482 Ratio
traceability_matrix/1000 62715 ns/iter (± 684) 44577 ns/iter (± 158) 1.41
query/10000 119122 ns/iter (± 1010) 98968 ns/iter (± 405) 1.20

This comment was automatically generated by workflow using github-action-benchmark.

@codecov
Copy link
Copy Markdown

codecov Bot commented May 24, 2026

Codecov Report

❌ Patch coverage is 99.01961% with 1 line in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
rivet-core/src/feature_model.rs 99.01% 1 Missing ⚠️

📢 Thoughts on this report? Let us know!

@avrabe avrabe merged commit eef9732 into main May 24, 2026
21 of 40 checks passed
@avrabe avrabe deleted the feat/req-085-cross-repo-composition branch May 24, 2026 09:09
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