You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
cargo deny check advisories fails on the merge path (guard-cargo m70/m71) since the advisory db picked up RUSTSEC-2024-0449: unsafe-libyaml 0.2.11 is unmaintained (repo archived 2024-03-25; no patched version exists).
It enters the lock three ways:
serde_yaml 0.9.34+deprecated: workspace dep (Cargo.tomlserde_yaml = "0.9"), direct in 13 crates; 861 references in 179 .rs files.
serde_yaml_ng 0.10.0: workspace dep, direct in 15 crates (apr-cli aliases it as serde_yaml); 164 references in 49 files.
bashrs 6.66.1 (registry; also 7.4.2, as a build dependency): declared by aprender-compute-xtask and as the optional transpilers feature of aprender-orchestrate. Neither crate calls into it.
Interim
One deny.toml ignore entry for RUSTSEC-2024-0449, same form as the existing unmaintained-transitive entries, so every other advisory still blocks.
Close condition
Delete the ignore entry after the serde-saphyr swap and a bashrs release without serde_yaml_ng (or dropping the unused bashrs deps) leave unsafe-libyaml out of Cargo.lock. This issue closes when deny.toml no longer names RUSTSEC-2024-0449 and cargo deny check advisories is green on main.
Staged swap continues under this ticket, one PR per crate group.
Notes for the swap. All figures were read from main 8c12fca; nothing here is built yet.
bashrs is never called.aprender-compute-xtask declares bashrs = "6.35", and nothing in its src/ uses it. In aprender-orchestrate it is only the optional transpilers = ["bashrs", "native"] feature; the one bashrs:: occurrence there is inside a string template (oracle/knowledge_graph/integrations.rs:135), and no cfg(feature = "transpilers") exists. Dropping both deps removes the bashrs path without waiting for an upstream release. bashrs 7.4.2 still has serde_yaml_ng ^0.10 as a build dependency.
A cheaper candidate than serde-saphyr:serde_norway 0.9.42, a serde_yaml 0.9 fork on unsafe-libyaml-norway (no RustSec advisory today). It is API-compatible with serde_yaml 0.9 (Value, Mapping, from_str, to_string), so a dependency-level rename (serde_yaml = { package = "serde_norway", ... }) needs no .rs edits for the serde_yaml crates. serde-saphyr has no Value type, and 92 files use serde_yaml{,_ng}::Value. Risk: both norway crates were last released 2024-12, so they could draw the same advisory later. Measure it before choosing.
Declaration sites to rewrite: 2 workspace entries plus about 36 per-crate lines, several of which pin "0.9" or "0.10" directly instead of using workspace = true.
contracts 357;34; delete the RUSTSEC-2024-0449 entry from deny.toml
357
34
Total: about 1,107 path references and 96 Value/Mapping files.
What makes it more than a rename
serde-saphyr has no Value or Mapping type. Untyped YAML goes into serde_json::Value instead. Every Value site has to be checked for things serde_json cannot represent: non-string mapping keys, YAML tags, !!binary, and key-order assumptions (serde_json needs preserve_order wherever order is printed or hashed). The error types also change (serde_yaml::Error becomes serde_saphyrDeserializeError/SerializeError), and so does serialized output formatting. Any test that compares emitted YAML byte for byte has to be re-baselined, and each re-baseline is reviewed. S1 to S3 are mostly typed from_str/to_string. S4 and S5 hold most of the Value sites.
Owners
One owner per stage, and each owner opens a child issue under #5205 for that stage. S1 and S5 (contracts, plus deleting the deny.toml entry) stay with this issue's owner, who also holds the merge order. S2, S3 and S4 each have their own owner, tracked on that stage's child issue. Every stage carries the same proof: old = new on the real YAML corpus (all three parsers into serde_json::Value, with a planted diff that must turn the check red). S5 also proves that pv parse output is identical across all of contracts/.
Parser options, required at every serde-saphyr call in every stage:
serde-saphyr defaults to YAML 1.1 inference: no/n parse as false and << merges are expanded. serde_yaml 0.9 read both literally, and S1's old = new run caught 20 files that changed. serde-saphyr also keeps comments as budgeted events, which fails .github/workflows/ci.yml ("too many consecutive comments"); the old parsers discard comments, hence emit_comments: false. Over the whole tree, serde-saphyr also rejects 6 files that the old parsers accept: docs/roadmaps/entries/PMAT-3699.yaml, -3731, -3732, -3740, -3741 and the aggregated docs/roadmaps/roadmap.yaml. No Rust code in the workspace parses them (git grep finds only a path string in a test; make roadmap-aggregate builds the aggregate outside the crates), so no stage reads them through serde-saphyr. They are recorded here, not fixed in this swap. The details are in the S1 result comment below. In S5, a value that still changes (leading-zero 048 becoming 48.0, 5_000_000 becoming an integer) gets a quoted YAML literal or a typed field. A diff is never ignored.
PRs and dates
One PR per group, in order. Each PR goes through CI, two seats and the queue, with cargo deny check advisories run on its head.
S5: 10-17. It deletes the deny.toml entry, which needs unsafe-libyaml out of Cargo.lock, and this issue closes then.
Dated finish: 2026-10-17. A red on main or a queue stall moves the date, and each move gets a cause line here.
bashrs
bashrs 7.4.2 still has serde_yaml_ng ^0.10 as a build dependency. In this repo, neither crate that declares bashrs calls it, so S1 drops both declarations, which takes bashrs out of aprender's lock without waiting on upstream. Separately, a bashrs PR will replace its serde_yaml_ng build dependency so the next bashrs release is clean for its other consumers. That PR goes through bashrs's own owner and is not on aprender's critical path.
S1 result: old = new needs two serde-saphyr options, and every stage uses them.
I parsed all 2,881 YAML files under contracts/ and crates/ with serde_yaml 0.9, serde_yaml_ng 0.10 and serde-saphyr 1.3, each into serde_json::Value. A planted change fires as expected. With default options, 20 files differ, because serde-saphyr defaults to YAML 1.1 inference:
no and n become false (compute-parity-v1.yamltolerance_table.dot_product.n, apr-format-leaf-sovereignty-v1.yamlformal: no).
<< merge keys are expanded (both docker-compose.test.yml files and a pv fixture).
With those options, 13 files still differ, and all of them are in contracts/ or its staging copy:
a leading-zero kaizen: 048 parses as 48.0 where it used to be the string "048";
max_files: 5_000_000 parses as an integer where it used to be a string.
No option restores the old reading. These files are S5's scope. S5 settles them with its pv parse-output identity check, by quoting the values or by typed fields.
Activity
noahgift commentedon Oct 12, 2026
Notes for the swap. All figures were read from main 8c12fca; nothing here is built yet.
aprender-compute-xtaskdeclaresbashrs = "6.35", and nothing in itssrc/uses it. Inaprender-orchestrateit is only the optionaltranspilers = ["bashrs", "native"]feature; the onebashrs::occurrence there is inside a string template (oracle/knowledge_graph/integrations.rs:135), and nocfg(feature = "transpilers")exists. Dropping both deps removes the bashrs path without waiting for an upstream release. bashrs 7.4.2 still hasserde_yaml_ng ^0.10as a build dependency.serde_norway0.9.42, a serde_yaml 0.9 fork onunsafe-libyaml-norway(no RustSec advisory today). It is API-compatible with serde_yaml 0.9 (Value,Mapping,from_str,to_string), so a dependency-level rename (serde_yaml = { package = "serde_norway", ... }) needs no.rsedits for the serde_yaml crates. serde-saphyr has noValuetype, and 92 files useserde_yaml{,_ng}::Value. Risk: both norway crates were last released 2024-12, so they could draw the same advisory later. Measure it before choosing."0.9"or"0.10"directly instead of usingworkspace = true.noahgift commentedon Oct 12, 2026
Swap plan: serde_yaml / serde_yaml_ng → serde-saphyr
Measured size (main 8c12fca, 2026-10-12 12:05Z)
git grepcounts ofserde_yaml::/serde_yaml_ng::paths per crate, plus the files that useValue/Mapping:transpilers); compute 1, db 2, rag 1, viz-ttop 1, present-cli 1, present-core 2, present-terminal 3, qa-cli 2, qa-report 1, test 1, train-distill 3, data 2;1, mcp 6;1, cgp 6;1, facades 1, root tests 2, qa-gen 8, test-cli 6, contracts-staging 7deny.tomlTotal: about 1,107 path references and 96 Value/Mapping files.
What makes it more than a rename
serde-saphyr has no
ValueorMappingtype. Untyped YAML goes intoserde_json::Valueinstead. Every Value site has to be checked for things serde_json cannot represent: non-string mapping keys, YAML tags,!!binary, and key-order assumptions (serde_jsonneedspreserve_orderwherever order is printed or hashed). The error types also change (serde_yaml::Errorbecomesserde_saphyrDeserializeError/SerializeError), and so does serialized output formatting. Any test that compares emitted YAML byte for byte has to be re-baselined, and each re-baseline is reviewed. S1 to S3 are mostly typedfrom_str/to_string. S4 and S5 hold most of the Value sites.Owners
One owner per stage, and each owner opens a child issue under #5205 for that stage. S1 and S5 (contracts, plus deleting the
deny.tomlentry) stay with this issue's owner, who also holds the merge order. S2, S3 and S4 each have their own owner, tracked on that stage's child issue. Every stage carries the same proof: old = new on the real YAML corpus (all three parsers intoserde_json::Value, with a planted diff that must turn the check red). S5 also proves thatpvparse output is identical across all ofcontracts/.Parser options, required at every serde-saphyr call in every stage:
serde-saphyr defaults to YAML 1.1 inference:
no/nparse asfalseand<<merges are expanded. serde_yaml 0.9 read both literally, and S1's old = new run caught 20 files that changed. serde-saphyr also keeps comments as budgeted events, which fails.github/workflows/ci.yml("too many consecutive comments"); the old parsers discard comments, henceemit_comments: false. Over the whole tree, serde-saphyr also rejects 6 files that the old parsers accept:docs/roadmaps/entries/PMAT-3699.yaml,-3731,-3732,-3740,-3741and the aggregateddocs/roadmaps/roadmap.yaml. No Rust code in the workspace parses them (git grepfinds only a path string in a test;make roadmap-aggregatebuilds the aggregate outside the crates), so no stage reads them through serde-saphyr. They are recorded here, not fixed in this swap. The details are in the S1 result comment below. In S5, a value that still changes (leading-zero048becoming48.0,5_000_000becoming an integer) gets a quoted YAML literal or a typed field. A diff is never ignored.PRs and dates
One PR per group, in order. Each PR goes through CI, two seats and the queue, with
cargo deny check advisoriesrun on its head.deny.tomlentry, which needsunsafe-libyamlout ofCargo.lock, and this issue closes then.Dated finish: 2026-10-17. A red on main or a queue stall moves the date, and each move gets a cause line here.
bashrs
bashrs 7.4.2 still has
serde_yaml_ng ^0.10as a build dependency. In this repo, neither crate that declares bashrs calls it, so S1 drops both declarations, which takes bashrs out of aprender's lock without waiting on upstream. Separately, a bashrs PR will replace itsserde_yaml_ngbuild dependency so the next bashrs release is clean for its other consumers. That PR goes through bashrs's own owner and is not on aprender's critical path.noahgift commentedon Oct 12, 2026
S1 result: old = new needs two serde-saphyr options, and every stage uses them.
I parsed all 2,881 YAML files under
contracts/andcrates/with serde_yaml 0.9, serde_yaml_ng 0.10 and serde-saphyr 1.3, each intoserde_json::Value. A planted change fires as expected. With default options, 20 files differ, because serde-saphyr defaults to YAML 1.1 inference:noandnbecomefalse(compute-parity-v1.yamltolerance_table.dot_product.n,apr-format-leaf-sovereignty-v1.yamlformal: no).<<merge keys are expanded (bothdocker-compose.test.ymlfiles and a pv fixture).Every call now passes:
With those options, 13 files still differ, and all of them are in
contracts/or its staging copy:kaizen: 048parses as48.0where it used to be the string"048";max_files: 5_000_000parses as an integer where it used to be a string.No option restores the old reading. These files are S5's scope. S5 settles them with its
pvparse-output identity check, by quoting the values or by typed fields.fix(deps): untyped YAML slots keep what serde_yaml kept (#5205)