Skip to content

fix(upgrade-plan): never introduce an advisory; the only fix across a major raises the row, flagged (parity decisions 2, 3) - #994

Merged
runyourempire merged 3 commits into
mainfrom
fix/parity-decisions
Oct 18, 2026
Merged

runyourempire merged 3 commits into
mainfrom
fix/parity-decisions

Conversation

@runyourempire

@runyourempire runyourempire commented Oct 18, 2026 •

Copy link
Copy Markdown
Collaborator

What

This PR covers parity decisions 2 and 3: the Rust engine now matches the MCP (fix/final-audit-625 @ a1a2aeb). It is rebased onto main 44d98e7ba, so #992's is_malware(id, aliases, cwe_ids, summary) and #993's DirectFix::Refresh are already in.

Decision 2: a move never introduces an advisory. The rule applies on every path:

  • Parent, copy, lever and newest-release text. A release cannot be named when ANY advisory (fixed or not) affects it but not the installed version (Advisories::usable takes the installed version). "remains affected by (no fix published)" stays only for advisories the installed version already has.
  • Blocked only by introduced advisories. If a release is blocked ONLY by advisories the installed version lacks, fixed or not (Advisories::clean_but_introduced, the MCP's cleanButIntroduced; malware still blocks outright), nothing is named. The row reads "every fixing release brings in , which the installed version does not have". <ids> is the sorted, deduped union over every such candidate release (S2, S4).
  • The copy's own fix target (B2). The fix-target walk in fourda-depengine names no target when it lands inside an advisory with no way out that the installed version does not have. The line fix is not offered in its place either. This matches the MCP's smallestCleanVersion(installed). The ids travel as MatchedDependency.introduces and LineTarget.introduces. All three surfaces agree:
    • Plan row: "No clean fix for X" plus the reason.
    • Preemption: "No release is named: every fixing release ...".
    • Brief: FixPath::Introduces.
  • Levers (S3). A declared lever whose only releases that bring a fixed parent in are blocked solely by introduced advisories is Introduces, and both lever texts word it. The lever_move arm is now reachable.
  • "Already the newest" (B3). A parent with newer releases that are ruled out only by advisories it lacks is Introduces, no longer "already the newest X release 4DA has seen".

Decision 3: when the only fix is across a major, the row is raised and flagged.

  • row_target raises the parent's own row across a major. Lifts and lever hops follow.
  • The plan, Preemption ("BREAKING: the only release of that fixes is , a major upgrade") and the Brief ("a major upgrade") all say so.
  • Scope (S1): the cross-major raise is live for npm. Cargo is pinned at the rule level only (pr-009 runs decide()). Wiring Cargo needs per-version crates.io dependency records; that is a follow-up.

Vectors

  • parent_release.jsonl:
    • pr-001 is main's (npm update express).
    • pr-009 is the MCP terminal's line, copied verbatim.
    • pr-010, pr-011 (lever release: "introduces", graph mode) and pr-012 (blocked only by a fixed advisory the installed version lacks) are SYNTHETIC and marked as such.
  • fix_target.jsonl: ft-069 is SYNTHETIC, with a new optional field introduces[] holding sorted record ids.
    • Its records are osv/SYNT-0000-0001.json and osv/SYNT-0000-0002.json. Each record's id matches its file name, and the package is an invented one (4da-synthetic-ft069).
    • Both Rust corpus loaders and the conformance suite accept records that no case references. The suite only matches packages a case holds, and all conformance tests pass.
    • scripts/conformance-corpus.mjs keeps SYNT-* files on a re-pin.
  • The ids were agreed with the MCP terminal, and its runner checks pr-011 and ft-069 too.

Review of #994 (B1, S5)

  • B1: the Brief's direct fix and its major wrap moved to brief_facts_clause.rs, so brief_facts_build.rs is 962 lines.
  • B1: the hop code moved to parent_release_hop.rs, so parent_release.rs is 669 lines.
  • S5: doc fixed.

Touched outside my lanes (one line each):

  • morning_audit.rs and evidence/stack_change.rs: an exhaustive FixPath::Introduces arm.
  • upgrade_plan_tests.rs (claimed by fix/severity-parity): one added test.
  • release_advisories is unchanged here.

Follow-ups, not in this PR:

  • Cargo cross-major raise in production (S1).
  • An alias-aware "already has" check, so an alias record with a slightly different range isn't counted as introduced. Both engines have this gap.
  • A malware record with no way out is not told apart inside the walk. It still names no clean target there, but it isn't reported as "introduces".

Tests

  • New tests:
    • depengine a_target_never_brings_in_an_advisory_the_installed_version_lacks
    • depengine a_line_whose_fixes_bring_in_an_advisory_has_no_target
    • plan + Preemption a_fix_that_brings_in_an_advisory_is_not_named
    • B3 newer_releases_that_bring_in_an_advisory_are_not_the_newest
    • a_release_never_introduces_an_advisory
    • every_fixing_release_bringing_in_an_advisory_names_none
    • a_raise_across_a_major_says_why
  • Updated tests:
    • a_target_that_cannot_clear_everything_still_clears_every_fixable_window: from 1.0.0 it now names nothing; from 1.6.0, which already has the open advisory, it names 2.5.0.
    • row_target now raises across a major.
    • The Brief's bump expectations now say "a major upgrade".
  • Rule vectors: fix_target 69/69, parent_release 12/12, fix_command 61/61, and every other suite passes.
  • cargo test --lib -- osv:: evidence:: brief_facts preemption scoring:: morning db::: 2035 passed, 0 failed.
  • depengine: 43 tests pass, clippy --lib -D warnings is clean, and the wasm build has 0 imports.
  • cargo clippy --lib -D warnings, rustfmt and the file-size gate are clean, and the pre-push gate passed.

🤖 Generated with Claude Code

https://claude.ai/code/session_018hjX3pEMmC7CHfG41PiPj7

runyourempire added a commit that referenced this pull request Oct 18, 2026
…e text never bring in an advisory (review of #994: B1-B3, S2-S5)

B2: the fix-target walk (fourda-depengine) names no target, and no line
fix, when it lands inside an advisory with no way out that the installed
version does not have (the MCP's smallestCleanVersion(installed)). The
ids travel as MatchedDependency/LineTarget.introduces (sorted, deduped),
and the plan row (No clean fix), Preemption and the Brief
(FixPath::Introduces) say every fixing release brings them in.
B3: a parent with newer releases only advisories it lacks rule out is
Introduces, not "already the newest release".
S2/S4: a release blocked ONLY by advisories the installed version lacks,
fixed or not, introduces (Advisories::clean_but_introduced); the ids are
the union over every such candidate. S3: a lever stuck only so is
Introduces too, and row_move words it. S5: doc. B1: the Brief's direct
fix and major wrap move to brief_facts_clause.rs; the hop code moves to
parent_release_hop.rs (parent_release.rs under 700 lines).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018hjX3pEMmC7CHfG41PiPj7
runyourempire added a commit that referenced this pull request Oct 18, 2026
… fixed advisory the installed version lacks), ft-069 (a copy's own target never brings one in); README scope of the cross-major raise (review of #994)

pr-011, pr-012 and ft-069 are SYNTHETIC (no live case across the 11 audit
projects; ids agreed with the MCP terminal). ft-069's records live under
osv/SYNT-*.json; the corpus pin script keeps them on a re-pin. Unit tests:
B2 on the plan row and the Preemption sentence, B3 (newer releases that
bring an advisory in are not "the newest"), and the walk test updated for
decision 2. README: the cross-major raise is live for npm; Cargo is pinned
at the rule level only (follow-up).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018hjX3pEMmC7CHfG41PiPj7
@runyourempire
runyourempire added this pull request to the merge queue Oct 18, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to a conflict with the base branch Oct 18, 2026
runyourempire and others added 3 commits October 19, 2026 03:13
… major raises the row, flagged (parity decisions 2, 3)

Decision 2: a release cannot be named when ANY advisory (fixed or not)
affects it that does not also affect the installed version
(Advisories::usable takes the installed version, for the parent, the
copy, the lever and the "newest release" text). When a release would
admit a clean copy but every one brings in such an advisory, the row says
"every fixing release brings in <id>, which the installed version does
not have" (Standing / ParentStanding::Introduces) and names nothing.
"remains affected by <id> (no fix published)" stays for advisories the
installed version already has.

Decision 3: the parent's own row is raised to the release a copy needs
even across a major, and says so ("BREAKING: the only release of <parent>
that fixes <child> is <ver>, a major upgrade"); rows and a lever's hop
lift to it across a major too. The plan line's upgrade type is major (its
title and step flag it), Preemption's alert gets the major sentence and
the note, and the Brief's bump clause says "a major upgrade".

Vectors: pr-009 (xsv's crossbeam-channel 0.2.4 -> 0.5.0 for
crossbeam-utils, live crates.io and OSV, the MCP terminal's line verbatim;
the runner gains a Cargo project shape and dialect) and pr-010 (SYNTHETIC
"introduces" rule line, marked as such).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018hjX3pEMmC7CHfG41PiPj7
…e text never bring in an advisory (review of #994: B1-B3, S2-S5)

B2: the fix-target walk (fourda-depengine) names no target, and no line
fix, when it lands inside an advisory with no way out that the installed
version does not have (the MCP's smallestCleanVersion(installed)). The
ids travel as MatchedDependency/LineTarget.introduces (sorted, deduped),
and the plan row (No clean fix), Preemption and the Brief
(FixPath::Introduces) say every fixing release brings them in.
B3: a parent with newer releases only advisories it lacks rule out is
Introduces, not "already the newest release".
S2/S4: a release blocked ONLY by advisories the installed version lacks,
fixed or not, introduces (Advisories::clean_but_introduced); the ids are
the union over every such candidate. S3: a lever stuck only so is
Introduces too, and row_move words it. S5: doc. B1: the Brief's direct
fix and major wrap move to brief_facts_clause.rs; the hop code moves to
parent_release_hop.rs (parent_release.rs under 700 lines).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018hjX3pEMmC7CHfG41PiPj7
… fixed advisory the installed version lacks), ft-069 (a copy's own target never brings one in); README scope of the cross-major raise (review of #994)

pr-011, pr-012 and ft-069 are SYNTHETIC (no live case across the 11 audit
projects; ids agreed with the MCP terminal). ft-069's records live under
osv/SYNT-*.json; the corpus pin script keeps them on a re-pin. Unit tests:
B2 on the plan row and the Preemption sentence, B3 (newer releases that
bring an advisory in are not "the newest"), and the walk test updated for
decision 2. README: the cross-major raise is live for npm; Cargo is pinned
at the rule level only (follow-up).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018hjX3pEMmC7CHfG41PiPj7
@runyourempire
runyourempire added this pull request to the merge queue Oct 18, 2026
Merged via the queue into main with commit 629decd Oct 18, 2026
25 checks passed
@runyourempire
runyourempire deleted the fix/parity-decisions branch October 18, 2026 17:51
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