Skip to content

fix(yaml): rowan parser handles flush-left block sequences + links loud-fail (REQ-091, v0.13.1)#329

Merged
avrabe merged 3 commits into
mainfrom
fix/req-091-rowan-yaml-link-loss
May 25, 2026
Merged

fix(yaml): rowan parser handles flush-left block sequences + links loud-fail (REQ-091, v0.13.1)#329
avrabe merged 3 commits into
mainfrom
fix/req-091-rowan-yaml-link-loss

Conversation

@avrabe
Copy link
Copy Markdown
Contributor

@avrabe avrabe commented May 24, 2026

Summary

Two REQ-091 fixes that together close the F2 silent-failure where the default `rivet validate` (salsa + rowan-yaml) silently graded zero links for artifacts written in YAML's flush-left list style.

The CST bug — flush-left sequences silently dropped

`yaml_cst::parse_mapping_entry` required `child_indent > entry_indent` when descending into a same-line `value`. YAML allows the zero-indent block sequence form where the dashed list sits at the same column as the parent key:

```yaml
artifacts:

  • id: A # <-- dash at column 0, same column as "artifacts:"
    type: req
    ```

For this shape the parser silently treated the value as empty and dropped the entire sequence from the tree. `extract_schema_driven` then returned 0 artifacts + 0 diagnostics, while `rivet list` (which routes through the legacy `parse_generic_yaml`) returned the artifact correctly — the link graph the validator should grade was invisible.

The fix: when the next-line content is a `Dash`, accept `child_indent >= entry_indent` (YAML 1.2's zero-indent block-sequence rule).

CST probe — both shapes now produce identical trees:
`Mapping → MappingEntry → Value → Sequence → SequenceItem → Mapping`.

The F2 silent-failure — `extract_links_via_serde` silently returned empty

The fallback for `links:` values the CST doesn't recognise as a block `Sequence` (most importantly flow-style `links: [{type:X, target:Y}]`) silently returned `Vec::new()` when `serde_yaml::from_str` rejected the value text. The outer cardinality validator then graded that as "links field present but empty."

Threads `&mut Vec` through `extract_links` and `extract_links_via_serde`. On serde parse error, emit an ERROR diagnostic naming the underlying error and the value's span.

Tests

Test plan

  • `cargo test -p rivet-core` — all tests pass (1068 lib + 83 yaml-test-suite + 2 new integration tests)
  • `rivet validate` smoke test on this repo's corpus — PASS
  • CST probe before vs after — flush-left now produces the same tree as indented
  • CI

What this does NOT do

Acceptance #5 — re-grading the parallel agent's sphinx-needs port corpus against the Python reference's ~2500 warnings — requires their `feat/import-results-sphinx-needs` branch. Once this lands they can rebase and re-grade.

Fixes: REQ-091
Refs: REQ-051, REQ-082

🤖 Generated with Claude Code

@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 24, 2026

📐 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: 798c5d7 Previous: 54d609a Ratio
store_lookup/100 2178 ns/iter (± 10) 1688 ns/iter (± 3) 1.29
store_lookup/1000 26885 ns/iter (± 136) 19128 ns/iter (± 836) 1.41
traceability_matrix/1000 60540 ns/iter (± 238) 41367 ns/iter (± 727) 1.46
query/100 797 ns/iter (± 4) 650 ns/iter (± 19) 1.23
query/1000 7621 ns/iter (± 35) 5409 ns/iter (± 32) 1.41

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

@avrabe avrabe force-pushed the fix/req-091-rowan-yaml-link-loss branch from 6ef067f to 798c5d7 Compare May 25, 2026 04:20
@codecov
Copy link
Copy Markdown

codecov Bot commented May 25, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

avrabe and others added 3 commits May 25, 2026 07:23
…ud-fail (REQ-091)

Two REQ-091 fixes that together close the F2 silent-failure where the
default `rivet validate` (salsa + rowan-yaml) silently graded zero
links for artifacts written in YAML's flush-left list style.

## The CST bug — flush-left sequences silently dropped

`yaml_cst::parse_mapping_entry` required `child_indent > entry_indent`
when descending into a same-line `value` of a mapping. YAML allows the
"zero-indent" block sequence form where the dashed list sits at the
same column as the parent key:

    artifacts:
    - id: A           <- dash at column 0, same as `artifacts:`
      type: req

For this shape the parser silently treated the value as empty and
dropped the entire sequence from the tree. `extract_schema_driven` then
returned 0 artifacts + 0 diagnostics, but `rivet list` (which routes
through `parse_generic_yaml`) returned the artifact correctly — the
link graph the validator should grade was invisible.

The fix: when the next-line content is a Dash, accept
`child_indent >= entry_indent`. This is YAML 1.2's zero-indent
block-sequence rule (the parser already handled `child_indent >
entry_indent` for the indented form; the equal-indent case is
necessary at the document root where there is no shallower indent to
go to).

CST probe before vs after — both shapes now produce identical trees:
`Mapping → MappingEntry → Value → Sequence → SequenceItem → Mapping`.

## The F2 silent-failure — `extract_links_via_serde` returned empty without signal

`extract_links_via_serde` (the fallback for `links:` values the CST
doesn't recognise as a block Sequence — most importantly flow-style
`links: [{type:X, target:Y}]`) silently returned `Vec::new()` when
`serde_yaml::from_str` rejected the value text. The outer cardinality
validator then graded that as "links field present but empty" — the
F2 ethos violation REQ-091 calls out.

The fix: thread `&mut Vec<ParseDiagnostic>` through `extract_links`
and `extract_links_via_serde`. On serde parse error, emit an ERROR
diagnostic naming the underlying `serde_yaml` error and the value's
span. The two call sites (`extract_section_item`,
`extract_artifact_from_item`) both have `result: &mut ParsedYamlFile`
in scope, so they pass `&mut result.diagnostics` — no surface API
change beyond these internal helpers.

## Tests

- `rivet-core/tests/req_091_flush_left_yaml.rs` — REQ-091 Acceptance
  #1 + #4: drives `extract_schema_driven` directly with both fixture
  shapes, asserts 1 artifact + 1 link parity. Also a structural-parity
  test across multi-link inputs.
- `rivet-core/src/yaml_hir.rs::tests::extract_links_via_serde_emits_diagnostic_on_parse_error`
  — REQ-091 Acceptance #3: a malformed `links:` value produces a
  diagnostic naming the field, not a silent empty list.

All rivet-core tests pass (1068 lib + 83 yaml-test-suite + 2 new
integration tests). `rivet validate` on the in-tree corpus: PASS.

## What this does NOT do

Acceptance #5 — re-grading the parallel agent's sphinx-needs port
corpus against the Python reference's ~2500 warnings — requires
their `feat/import-results-sphinx-needs` branch and runs outside
this PR. Once this lands they can rebase and re-grade.

Fixes: REQ-091
Refs: REQ-051, REQ-082

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Pure cosmetic — assert_eq! macros expand onto one line where rustfmt
prefers and the test's two-line assertion on artifact.id/title is
joined back to one line.

Refs: REQ-091
…rename

REQ-091 fixed the rowan-yaml CST so the salsa validate path now sees
artifacts written in flush-left list style — which is exactly what
the dev → aspice migration emits via serde_yaml::to_string. The
existing test `apply_rewrites_dev_to_aspice_and_validate_passes` was
green only because the validator could not see the migrated artifacts
at all; with REQ-091 fixed, validate correctly reports that the
migrated REQ-001 / FEAT-001 have no `derives-from` / `allocated-from`
links — exactly the cardinality obligations the aspice schema
declares.

The migration is intentionally structural-only: it renames types in
place, it does not invent semantic links to system-level artifacts
the dev preset doesn't ship. The right test contract is therefore
that validate FAILS with exactly those two cardinality errors —
documenting the migration's actual behaviour rather than hiding it.

Rename the test to `apply_rewrites_dev_to_aspice_and_validate_reports_expected_link_gaps`,
assert `!val.status.success()`, and pin both expected error strings
(`derives-from` for the migrated sw-req, `allocated-from` for the
migrated sw-arch-component). Add a comment block tying this back to
the pre-REQ-091 silent-success it replaces.

Refs: REQ-091

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@avrabe avrabe force-pushed the fix/req-091-rowan-yaml-link-loss branch from 798c5d7 to ab2ef58 Compare May 25, 2026 05:23
@avrabe avrabe merged commit c362795 into main May 25, 2026
21 of 39 checks passed
@avrabe avrabe deleted the fix/req-091-rowan-yaml-link-loss branch May 25, 2026 09:14
avrabe added a commit that referenced this pull request May 26, 2026
Bumps workspace + vscode-rivet to 0.13.1. CHANGELOG captures the
v0.13.1 theme — silent-failure closeout (REQ-091, rowan-yaml
flush-left link loss + extract_links_via_serde loud-fail),
release-pipeline standardization to the cross-repo synth pattern
(SBOM + SLSA + build-env), spar `84a7363` → tag bump, and the
MODULE.bazel `rules_wasm_component` git_override that unblocked
Rocq Proofs CI on every PR.

This is the release-prep commit; the substantive PRs (#328, #329,
#330, #327) merge first, this branch is rebased onto the merged
main, then merged + tagged.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
avrabe added a commit that referenced this pull request May 27, 2026
Bumps workspace + vscode-rivet to 0.13.1. CHANGELOG captures the
v0.13.1 theme — silent-failure closeout (REQ-091, rowan-yaml
flush-left link loss + extract_links_via_serde loud-fail),
release-pipeline standardization to the cross-repo synth pattern
(SBOM + SLSA + build-env), spar `84a7363` → tag bump, and the
MODULE.bazel `rules_wasm_component` git_override that unblocked
Rocq Proofs CI on every PR.

This is the release-prep commit; the substantive PRs (#328, #329,
#330, #327) merge first, this branch is rebased onto the merged
main, then merged + tagged.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
avrabe added a commit that referenced this pull request May 27, 2026
Bumps workspace + vscode-rivet to 0.13.1. CHANGELOG captures the
v0.13.1 theme — silent-failure closeout (REQ-091, rowan-yaml
flush-left link loss + extract_links_via_serde loud-fail),
release-pipeline standardization to the cross-repo synth pattern
(SBOM + SLSA + build-env), spar `84a7363` → tag bump, and the
MODULE.bazel `rules_wasm_component` git_override that unblocked
Rocq Proofs CI on every PR.

This is the release-prep commit; the substantive PRs (#328, #329,
#330, #327) merge first, this branch is rebased onto the merged
main, then merged + tagged.

Co-authored-by: Claude Opus 4.7 <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