Skip to content

fix(v0.4.1): 5 user-reported pain points (#3, #4, #6, #7, #8)#174

Merged
avrabe merged 5 commits intomainfrom
fix/v041-pain-points-3-8
Apr 22, 2026
Merged

fix(v0.4.1): 5 user-reported pain points (#3, #4, #6, #7, #8)#174
avrabe merged 5 commits intomainfrom
fix/v041-pain-points-3-8

Conversation

@avrabe
Copy link
Copy Markdown
Contributor

@avrabe avrabe commented Apr 22, 2026

Fixes a batch of v0.4.0 rough edges reported against the variant / hooks / sexpr surfaces. Each commit is self-contained with its own tests.

Summary of commits

# Commit Pain point
3 c4fcb00 docs+feat(variant): feature-model schema reference + init scaffolder Undocumented YAML schema; rivet variant init now scaffolds starter files with commented fields.
4 7a35861 fix(hooks): pre-commit hook walks up to find rivet.yaml Relocating rivet.yaml no longer breaks the pre-commit hook — marker discovery from $PWD.
6 01c244e feat(variant): check-all + optional --variant on validate --variant now optional for model+binding consistency validation; new rivet variant check-all iterates declared variants.
7 612bfe6 fix(sexpr): semantic notes on filter parse errors Positional-only FilterError messages now carry a semantic note for bare infix / missing parens / unknown head shapes.
8 c46a375 feat(variant-solve): per-feature origin tracking rivet variant solve labels each effective feature as (mandatory) / (selected) / (implied by X); JSON output adds an origins map and stays backward-compatible.

Tests

  • 3 new tests: rivet-cli/tests/variant_init.rs
  • 2 new tests: rivet-cli/tests/hooks_install.rs
  • 5 new tests: rivet-cli/tests/variant_scoped_api.rs
  • 2 new tests: rivet-cli/tests/variant_solve_origins.rs
  • 4 new unit tests in rivet-core/src/sexpr_eval.rs
  • 4 new unit tests in rivet-core/src/feature_model.rs

Total: 20 new tests. All 589 pre-existing rivet-core lib tests still pass; all 6 proptest_feature_model properties still hold; cargo clippy --workspace --all-targets -- -D warnings clean.

Scope limits respected

Per the brief's scope note on PR #156 (cross-tree constraints): fix #8 extends ResolvedVariant with a new origins field and populates it alongside the existing fixpoint loop — no refactor of the solve algorithm. Tests confirm PR #156's cross-tree semantics still hold.

Test plan

  • cargo test -p rivet-cli --test variant_init
  • cargo test -p rivet-cli --test hooks_install
  • cargo test -p rivet-cli --test variant_scoped_api
  • cargo test -p rivet-cli --test variant_solve_origins
  • cargo test -p rivet-core --lib feature_model
  • cargo test -p rivet-core --lib sexpr_eval
  • cargo test --workspace --lib (589 tests)
  • cargo clippy --workspace --all-targets -- -D warnings

🤖 Generated with Claude Code

avrabe and others added 5 commits April 22, 2026 00:27
Pain point: users reverse-engineer the feature-model YAML schema because
`rivet variant --help` has no field reference and `selects:` vs
`selected:` / group types / s-expression constraint syntax / bindings
file shape are undocumented.

Changes:
- Add docs/feature-model-schema.md: top-level reference for feature
  model YAML (root, features, group types, constraint syntax) with a
  worked example.
- Add docs/feature-model-bindings.md: dedicated binding file reference.
- Link both from docs/getting-started.md.
- Variant subcommand doc-comment now points at the schema reference so
  `rivet variant --help` surfaces it.
- Add `rivet variant init <name>` scaffolder that writes a starter
  feature-model.yaml + bindings/<name>.yaml with comments documenting
  every field.

Tests: 3 new integration tests in rivet-cli/tests/variant_init.rs
covering scaffolded file contents, overwrite protection, and that the
scaffolded template parses clean via `rivet variant list`.

Implements: REQ-042, REQ-043, REQ-044
Refs: REQ-046

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…very)

Pain point: `rivet init --hooks` emitted a pre-commit hook that ran
`rivet validate` at the git root. If the rivet project is relocated
inside the working tree (e.g. moved to subdir/), the hook either
silently validates the wrong directory or fails to find rivet.yaml.

Fix: the installed pre-commit hook now walks up from $PWD until it
finds a directory containing rivet.yaml, then cd's there before
invoking `rivet validate`. If no rivet.yaml exists in the ancestor
chain, the hook exits 0 silently so it does not block commits in
unrelated repositories.

Tests: rivet-cli/tests/hooks_install.rs adds 2 integration tests — one
verifies the hook body does not embed a hard-coded -p/--project flag and
uses the walk-up pattern; one stages a fresh project, moves rivet.yaml
into a subdirectory, and confirms the hook still discovers it when run
from a nested path.

Fixes: REQ-051

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…mics)

Pain point: variant-scoped validation required --model, --variant, and
--binding to be passed together — there was no way to validate just
model/binding consistency, and no single-invocation way to assert a
whole batch of variants is valid.

Changes:
- `rivet validate --model X --binding Y` (no --variant) now parses the
  model, parses the binding, and checks that every feature referenced
  in the binding exists in the model. Reports a clear diagnostic on
  unknown feature names instead of the old "must all be provided
  together" error. The full --model + --variant + --binding mode is
  unchanged.
- `rivet variant check-all --model M --binding B` iterates every
  variant declared under `variants:` in the binding file, prints a
  PASS/FAIL line per variant, and exits non-zero if any fail.
- `FeatureBinding` in rivet-core grows an optional `variants:` field
  (default empty) so the same file can carry bindings and declared
  variants without schema churn.

Tests: 5 new integration tests in rivet-cli/tests/variant_scoped_api.rs
cover the no-variant validate mode, the unknown-feature diagnostic,
check-all exit codes for mixed/all-pass fixtures, and JSON output shape.
Existing feature_model unit tests still pass (binding YAML is
backward-compatible — `variants:` defaults to empty).

Implements: REQ-044, REQ-045, REQ-046

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Pain point: `[FilterError { offset: 14, message: "unexpected atom at
top level" }]` exposed parser internals. Users writing `A and B`,
`and A B`, or `(bogus A B)` got a positional offset with no hint that
they were using the wrong syntax.

Fix: extend `FilterError` with an optional `note` field, populated by a
classifier that inspects the source before parsing. Three common shapes
get a semantic nudge:
  - bare infix (`A and B`) → suggest `(and A B)`.
  - missing outer parens (`and A B`) → suggest wrapping it.
  - unknown head (`(bogus …)`) → reference the supported form list.
`FilterError::Display` renders the positional detail followed by the
note on a new line. Feature-model constraint errors now format via
Display instead of Debug, so the note bubbles out through
`rivet variant check` / `rivet validate --model` paths.

Tests: 4 new unit tests in sexpr_eval covering the three error shapes
plus a success case that must carry no note. All pre-existing tests
unchanged.

Fixes: REQ-043
Implements: REQ-042

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ry vs implied)

Pain point: `rivet variant solve` output mixed user-picked features with
ones the solver added via mandatory-group propagation or constraint
implication. A flat list like `base, auth, oauth, token-cache, metrics`
didn't tell the user which features were their intent and which were
downstream effects.

Minimum-impact change per scope-limits brief (risk of conflict with
PR #156 cross-tree constraint work):
- Extend `ResolvedVariant` with a per-feature `origins: BTreeMap<String,
  FeatureOrigin>` where FeatureOrigin is UserSelected / Mandatory /
  ImpliedBy(name) / AllowedButUnbound.
- Populate origins alongside the existing selected-set fixpoint loop —
  no algorithmic changes. First-reason-wins on insertion so user
  selection beats later mandatory/implied discoveries.
- Text output of `rivet variant solve` prints one feature per line,
  prefixed with `+`, labeled (mandatory) / (selected) / (implied by X).
- JSON output is strictly additive: `effective_features` + `feature_count`
  preserved, new `origins` object keyed by feature name.

Tests:
- 4 new unit tests in rivet-core/src/feature_model.rs covering each
  origin variant.
- 2 new integration tests in rivet-cli/tests/variant_solve_origins.rs
  asserting text prefixes/labels and JSON backwards compatibility.
- All 15 pre-existing feature_model unit tests still pass; all 6
  proptest_feature_model properties still hold.

Implements: REQ-043, REQ-046
Refs: REQ-052

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@avrabe avrabe force-pushed the fix/v041-pain-points-3-8 branch from c46a375 to ce8a2d4 Compare April 22, 2026 05:27
@avrabe avrabe merged commit ec093ec into main Apr 22, 2026
1 check passed
@avrabe avrabe deleted the fix/v041-pain-points-3-8 branch April 22, 2026 05:32
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: ce8a2d4 Previous: 61bfc41 Ratio
store_lookup/100 2017 ns/iter (± 6) 1677 ns/iter (± 3) 1.20
store_lookup/1000 25058 ns/iter (± 185) 19262 ns/iter (± 54) 1.30
diff/100 64180 ns/iter (± 128) 53201 ns/iter (± 407) 1.21
query/100 777 ns/iter (± 4) 646 ns/iter (± 2) 1.20
query/1000 6861 ns/iter (± 112) 5450 ns/iter (± 12) 1.26

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

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