|
| 1 | +# preferred_semver.rs Decision Tree |
| 2 | + |
| 3 | +```mermaid |
| 4 | +flowchart TD |
| 5 | + Start["visit(dependency)"] --> InvalidLocal{"has invalid<br/>local instance?"} |
| 6 | +
|
| 7 | + %% ── Invalid local ── |
| 8 | + InvalidLocal -->|Y| ForEachInvalid["for each instance"] |
| 9 | + ForEachInvalid --> IsLocalInvalid{"is_local?"} |
| 10 | + IsLocalInvalid -->|Y| S_InvalidLocalVersion["⚠️ InvalidLocalVersion"] |
| 11 | + IsLocalInvalid -->|N| E_DependsOnInvalid["❌ DependsOnInvalidLocalPackage"] |
| 12 | +
|
| 13 | + %% ── Valid local ── |
| 14 | + InvalidLocal -->|N| HasLocal{"has local<br/>instance?"} |
| 15 | + HasLocal -->|Y| ForEachLocal["for each instance"] |
| 16 | + ForEachLocal --> IsLocalValid{"is_local?"} |
| 17 | + IsLocalValid -->|Y| V_IsLocalAndValid["✅ IsLocalAndValid"] |
| 18 | + IsLocalValid -->|N| IsLink{"is link<br/>specifier?"} |
| 19 | +
|
| 20 | + IsLink -->|Y| LinkResolves{"link resolves to<br/>local package?"} |
| 21 | + LinkResolves -->|Y| V_SatisfiesLocal_Link["✅ SatisfiesLocal"] |
| 22 | + LinkResolves -->|N| F_DiffersToLocal_Link["🔧 DiffersToLocal"] |
| 23 | +
|
| 24 | + IsLink -->|N| IsWorkspace{"is workspace<br/>protocol?"} |
| 25 | + IsWorkspace -->|Y| StrictMode{"strict mode?"} |
| 26 | + StrictMode -->|N| V_SatisfiesLocal_WS["✅ SatisfiesLocal"] |
| 27 | + StrictMode -->|Y| LocalVersionCheck |
| 28 | + IsWorkspace -->|N| LocalVersionCheck |
| 29 | +
|
| 30 | + LocalVersionCheck{"same version<br/>as local?"} |
| 31 | + LocalVersionCheck -->|N| F_DiffersToLocal["🔧 DiffersToLocal"] |
| 32 | + LocalVersionCheck -->|Y| LocalSemverGroup{"semver group prefers<br/>range ≠ Exact?"} |
| 33 | +
|
| 34 | + LocalSemverGroup -->|N| LocalAlreadyEquals{"already equals<br/>local?"} |
| 35 | + LocalAlreadyEquals -->|Y| V_IsIdenticalToLocal["✅ IsIdenticalToLocal"] |
| 36 | + LocalAlreadyEquals -->|N| F_DiffersToLocal2["🔧 DiffersToLocal"] |
| 37 | +
|
| 38 | + LocalSemverGroup -->|Y| LocalMatchesPreferred{"actual range =<br/>preferred range?"} |
| 39 | + LocalMatchesPreferred -->|Y| LocalPrefSatisfies{"preferred range<br/>satisfies local?"} |
| 40 | + LocalPrefSatisfies -->|Y| V_SatisfiesLocal_SG["✅ SatisfiesLocal"] |
| 41 | + LocalPrefSatisfies -->|N| C_MatchConflictsLocal["💥 MatchConflictsWithLocal"] |
| 42 | +
|
| 43 | + LocalMatchesPreferred -->|N| LocalPrefSatisfies2{"preferred range<br/>satisfies local?"} |
| 44 | + LocalPrefSatisfies2 -->|Y| F_SemverRangeMismatch_Local["🔧 SemverRangeMismatch"] |
| 45 | + LocalPrefSatisfies2 -->|N| C_MismatchConflictsLocal["💥 MismatchConflictsWithLocal"] |
| 46 | +
|
| 47 | + %% ── Catalog ── |
| 48 | + HasLocal -->|N| HasCatalog{"any instance uses<br/>catalog: protocol?"} |
| 49 | + HasCatalog -->|Y| ForEachCatalog["for each instance"] |
| 50 | + ForEachCatalog --> IsCatalog{"is catalog:?"} |
| 51 | + IsCatalog -->|Y| V_IsCatalog["✅ IsCatalog"] |
| 52 | + IsCatalog -->|N| F_DiffersToCatalog["🔧 DiffersToCatalog"] |
| 53 | +
|
| 54 | + %% ── Registry updates ── |
| 55 | + HasCatalog -->|N| HasUpdates{"eligible registry<br/>updates?"} |
| 56 | + HasUpdates -->|Y| ForEachUpdate["for each instance"] |
| 57 | + ForEachUpdate --> F_DiffersToNpmRegistry["🔧 DiffersToNpmRegistry"] |
| 58 | +
|
| 59 | + %% ── Highest/Lowest semver ── |
| 60 | + HasUpdates -->|N| HasHighest{"has highest/lowest<br/>semver specifier?"} |
| 61 | + HasHighest -->|Y| ForEachHighest["for each instance"] |
| 62 | + ForEachHighest --> SameVersion{"same version<br/>as highest?"} |
| 63 | +
|
| 64 | + SameVersion -->|N| F_DiffersToHighest["🔧 DiffersToHighestOrLowestSemver<br/>(with preferred range applied)"] |
| 65 | +
|
| 66 | + SameVersion -->|Y| HighestSemverGroup{"semver group prefers<br/>range ≠ highest range?"} |
| 67 | +
|
| 68 | + HighestSemverGroup -->|Y| HighestMatchesPref{"actual range =<br/>preferred range?"} |
| 69 | +
|
| 70 | + HighestMatchesPref -->|Y| HighestPrefSatisfies{"preferred range<br/>satisfies highest?"} |
| 71 | + HighestPrefSatisfies -->|Y| V_SatisfiesHighest["✅ SatisfiesHighestOrLowestSemver"] |
| 72 | + HighestPrefSatisfies -->|N| C_MatchConflictsHighest["💥 MatchConflictsWithHighestOrLowestSemver"] |
| 73 | +
|
| 74 | + HighestMatchesPref -->|N| HighestPrefSatisfies2{"preferred range<br/>satisfies highest?"} |
| 75 | + HighestPrefSatisfies2 -->|Y| F_SemverRangeMismatch_Highest["🔧 SemverRangeMismatch"] |
| 76 | + HighestPrefSatisfies2 -->|N| C_MismatchConflictsHighest["💥 MismatchConflictsWithHighestOrLowestSemver"] |
| 77 | +
|
| 78 | + HighestSemverGroup -->|N| HasPrefMismatch{"has preferred range<br/>AND actual ≠ preferred?"} |
| 79 | +
|
| 80 | + HasPrefMismatch -->|Y| PrefSatisfiesHighest3{"preferred range<br/>satisfies highest?"} |
| 81 | + PrefSatisfiesHighest3 -->|Y| F_SemverRangeMismatch_Adj["🔧 SemverRangeMismatch"] |
| 82 | + PrefSatisfiesHighest3 -->|N| C_MismatchConflictsHighest2["💥 MismatchConflictsWithHighestOrLowestSemver"] |
| 83 | +
|
| 84 | + HasPrefMismatch -->|N| HighestAlreadyEquals{"already equals<br/>highest?"} |
| 85 | + HighestAlreadyEquals -->|Y| V_IsHighest["✅ IsHighestOrLowestSemver"] |
| 86 | + HighestAlreadyEquals -->|N| F_DiffersToHighest2["🔧 DiffersToHighestOrLowestSemver"] |
| 87 | +
|
| 88 | + %% ── No semver ── |
| 89 | + HasHighest -->|N| AllIdentical{"every specifier<br/>identical?"} |
| 90 | + AllIdentical -->|Y| V_NonSemverIdentical["✅ IsNonSemverButIdentical"] |
| 91 | + AllIdentical -->|N| E_NonSemverMismatch["❌ NonSemverMismatch"] |
| 92 | +
|
| 93 | + %% ── Styling ── |
| 94 | + classDef valid fill:#d4edda,stroke:#28a745,color:#000 |
| 95 | + classDef fixable fill:#fff3cd,stroke:#ffc107,color:#000 |
| 96 | + classDef conflict fill:#f8d7da,stroke:#dc3545,color:#000 |
| 97 | + classDef suspect fill:#e2e3e5,stroke:#6c757d,color:#000 |
| 98 | + classDef unfixable fill:#f8d7da,stroke:#dc3545,color:#000 |
| 99 | +
|
| 100 | + class V_IsLocalAndValid,V_SatisfiesLocal_Link,V_SatisfiesLocal_WS,V_IsIdenticalToLocal,V_SatisfiesLocal_SG,V_IsCatalog,V_SatisfiesHighest,V_IsHighest,V_NonSemverIdentical valid |
| 101 | + class F_DiffersToLocal_Link,F_DiffersToLocal,F_DiffersToLocal2,F_SemverRangeMismatch_Local,F_DiffersToCatalog,F_DiffersToNpmRegistry,F_DiffersToHighest,F_SemverRangeMismatch_Highest,F_SemverRangeMismatch_Adj,F_DiffersToHighest2 fixable |
| 102 | + class C_MatchConflictsLocal,C_MismatchConflictsLocal,C_MatchConflictsHighest,C_MismatchConflictsHighest,C_MismatchConflictsHighest2 conflict |
| 103 | + class S_InvalidLocalVersion suspect |
| 104 | + class E_DependsOnInvalid,E_NonSemverMismatch unfixable |
| 105 | +``` |
| 106 | + |
| 107 | +## Legend |
| 108 | + |
| 109 | +| Icon | Category | Meaning | |
| 110 | +| ---- | --------- | ---------------------------------------------- | |
| 111 | +| ✅ | Valid | No action needed | |
| 112 | +| 🔧 | Fixable | Can be auto-fixed | |
| 113 | +| 💥 | Conflict | Semver group and version goal are incompatible | |
| 114 | +| ⚠️ | Suspect | Questionable but not fixable | |
| 115 | +| ❌ | Unfixable | Error that cannot be auto-fixed | |
| 116 | + |
| 117 | +## Branch Priority |
| 118 | + |
| 119 | +The top-level branches are evaluated in order — first match wins: |
| 120 | + |
| 121 | +1. **Invalid local** — local package has missing/invalid `.version` |
| 122 | +2. **Valid local** — dependency is developed in this monorepo |
| 123 | +3. **Catalog** — any instance uses `catalog:` protocol |
| 124 | +4. **Registry updates** — npm registry has eligible updates |
| 125 | +5. **Highest/lowest semver** — compare against highest (or lowest) version |
| 126 | +6. **No semver** — none of the above apply |
0 commit comments