fix(modify): insert new base fields after trailing block mappings, not inside#600
Conversation
…t inside
`rivet modify --set-release` (and any `set_field` inserting a NEW base field)
corrupted the artifact when its last field was a block MAPPING — e.g.
`provenance:` with `created-by`/`model` children, which every AI-stamped
artifact has. `find_insert_position` treated `provenance:` as a base field but
used `block_scalar_end` to find its extent, which only skips block *scalars*
(`|`/`>`), not nested mappings. The new key was spliced BETWEEN `provenance:`
and its children:
provenance:
release: v0.22.0 # <- invalid: splits the block
created-by: ai-assisted
making the file unparseable so the artifact vanished on reload (a #573-class
write-path corruption, in the #516 release-field setter).
Fix: add `field_block_end`, which consumes a field's full extent including
nested block-mapping children, and use it in `find_insert_position`. The new
field now lands AFTER the whole block.
Surfaced by dogfooding the release field while planning v0.22 (a real
`--set-release` on rivet's own provenance-stamped requirements.yaml corrupted
it). Regression test added; affects released v0.21.0 → warrants a v0.21.1 patch.
Fixes: REQ-004
Verifies: REQ-004
📐 Rivet artifact deltaNo artifact changes in this PR. Code-only changes (renderer, CLI wiring, tests) don't touch the artifact graph. |
There was a problem hiding this comment.
⚠️ 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: d784881 | Previous: 11db466 | Ratio |
|---|---|---|---|
store_lookup/1000 |
24987 ns/iter (± 39) |
19362 ns/iter (± 110) |
1.29 |
validate/10000 |
1119785830 ns/iter (± 33988932) |
914370645 ns/iter (± 5183624) |
1.22 |
diff/1000 |
726155 ns/iter (± 9442) |
590833 ns/iter (± 3936) |
1.23 |
query/100 |
1301 ns/iter (± 23) |
837 ns/iter (± 4) |
1.55 |
query/1000 |
15243 ns/iter (± 170) |
11489 ns/iter (± 47) |
1.33 |
This comment was automatically generated by workflow using github-action-benchmark.
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
|
Merging past the single Kani Proofs failure — exit 143 (SIGTERM during the Kani build, the #567/#590 resource kill; no proof runs). Diff is |
P0 — write-path corruption in released v0.21.0
rivet modify --set-release(and anyset_fieldthat inserts a new base field) corrupts an artifact whose last field is a block mapping — most importantlyprovenance:, which every AI-stamped artifact has. The new key is spliced betweenprovenance:and its children:→ the file fails to parse → the artifact (and its file-mates) vanish on reload. Same class as #573, this time in the #516 release-field setter shipped in v0.21.0.
Root cause
find_insert_positiontreatsprovenance:as a base field but measured its extent withblock_scalar_end, which only skips block scalars (|/>), not nested block mappings. So "after the last base field" landed right after theprovenance:line — inside its block.Fix
Add
field_block_end, which consumes a field's full on-disk extent including nested block-mapping children, and use it infind_insert_position. The inserted field now lands after the whole block.How it surfaced
Dogfooding: I ran a real
rivet modify --set-release v0.22.0on rivet's own provenance-stampedrequirements.yamlwhile planning v0.22 — and it corrupted the file. Exactly the friction the methodology is meant to catch.Verification
set_field_inserts_after_trailing_provenance_block: was RED (parse failure), now GREEN — output parses,releaseset,provenanceblock intact.yaml_edittests pass; fmt + clippy clean.Fixes #573-adjacent corruption in the release-field setter. Recommend a v0.21.1 patch since
--set-releaseon any provenance-stamped artifact corrupts it.🤖 Generated with Claude Code