Picker refresh: one survival authority, settled attribute semantics, 9 new distros - #1
Merged
Merged
Conversation
…pse the duplicate Artix entry npm run typecheck was failing with 126 errors on master. They were the visible symptom of three schema fields that no code ever read: - QuestionOption.isHardConstraint / .preferenceWeight (types.ts): set nowhere and read nowhere. Their only effect was to make Zod's inferred output type require them on every option literal. - Question.constraintMode (types.ts, set once at questions.ts:358): required by the schema, read by nothing. Hardness lives in src/engine/eliminate.ts and nowhere else; these fields were a half-migrated second authority for it, so they are deleted rather than populated. Also fixed, all reported by the same typecheck run: - ConstraintKeys was missing constraint_init_other and constraint_pkg_other, which state.ts:1057/1077 already emit and state.ts:1147/1165 already switch on. Added to the enum (the i18n keys already existed in en). - ResultsVM requires hardConstraintConflict/hardConstraintConflictFields; the pre-completion branch of resultsVM omitted them (TS2769 at state.ts:469). Artix: distros.json carried two entries for one distro (artix, artix_linux) with contradictory attributes. Collapsed into artix, verified against https://artixlinux.org/download.php (read 2026-09-28): - installerExperience MANUAL -> GUI: graphical ISOs ship Calamares and the page tells non-experts to use them ("Unless you really know your way around Linux, use a graphical or community edition"). - supportedDesktops [OTHER, TILING] -> the six documented graphical editions (KDE, XFCE, MATE, CINNAMON, LXQT) plus OTHER (LXDE). - description, distroSeaUrl, lastVerified (2026-09-28) from the removed entry. tests/recommendation.regression.spec.ts passes WITHOUT re-blessing its snapshot: the failing diff was `+ "artix_linux"`, `- "arch"`, i.e. the duplicate was crowding arch out of the OPENRC persona's top five. The snapshot was correct and the data was wrong. Gates: typecheck exit 0 (0 errors, was 126) | test exit 0 (74/74, was 73/74) | validate exit 0 | lint exit 0.
…uthority The results page ran its own, weaker filter: applyHardConstraints in scoring.ts enforced only architecture, avoid-proprietary and secure boot, while /compare ran eliminateDistros with eight rules. The same answers therefore produced different verdicts on the two pages: measured across eight profiles, 87 entries were presented as matches on the results page that the other engine excludes — an old-hardware, no-terminal, XFCE user saw 54 matches and zero exclusions. Survival - buildResultsPresentation now runs buildCompatibility (eliminate.ts) only. - scoring.ts is deleted, along with the hardcoded weights (gaming LIMITED 0.5, NVIDIA GOOD/OK 2/1, the beginner docs bonus) and its hard-constraint filter. - The low-coverage escape hatch is gone: eliminateDistros no longer softens a stated constraint when fewer than three distros match it. Removing the threshold machinery also removes EnginePolicyOptions, which compatibility.ts and recommend.ts no longer take. - Architecture became a real elimination rule (exclude_architecture_unsupported): it was enforced only by the deleted filter, so /compare previously kept x86-only distros for an ARM user (31 of 33 in one profile). - The dead scoring types (ScoredDistro, MatchDetail) are deleted, as is the never-discriminating sort tier that consumed them. Ordering - Counts only, as agreed: stated constraints matched, then stated preferences matched (weight 1 each, no half matches), then name. The persona harness already asserted this order; it now actually receives populated reason lists, so its second tier discriminates for the first time. Explainability - Every compatible card carries its inclusion reasons and every excluded entry the rule that fired, instead of one generic "does not match" string. - hardConstraintConflictFields is derived from the exclusion axes, covering all eight rules rather than three. - The UI no longer prints a match score: DistroCard and ResultsView show the matched-constraint count and the reason chips. Tests - tests/scoring.* are replaced by tests/hardConstraints.spec.ts (the elimination contract, including "never relaxes a stated constraint when few distros match it"), tests/ranking.spec.ts (ordering, reason completeness, stability under dataset reordering, hard-conflict reporting) and tests/pickerIntegration.spec.ts. - compatibility.spec.ts's low-coverage test asserted the old behaviour; it now asserts that a stated requirement is enforced. - Two persona snapshots were re-blessed after review; the whole diff is "- alpine" and "- almalinux"/"+ bazzite". Alpine leaves the privacy persona because GUI + no-terminal now exclude its manual installer and hands-on maintenance; almalinux leaves the server persona for hands-on maintenance and bazzite inherits the alphabetical slot among two-reason distros. i18n - 7 locales are now key-identical to en (380 keys each): added reasons.exclude_architecture_unsupported, constraints.constraint_init_other, constraints.constraint_pkg_other, reasons.exclude_hard_constraint_mismatch, wizard.narrowChoice and results.distroCard.links.tryOnline, translated constraint_pkg_portage (was English in all six) and removed the four stale q_experience_depth keys left behind by a deleted question. Gates: typecheck exit 0 | test exit 0 (81/81, was 73/74 before slice 0) | validate exit 0 | lint exit 0 | build exit 0 (33 routes prerendered).
…mily Definitions recorded in docs/distro-data-evidence.md: - proprietarySupport = how far a DEFAULT install of the main desktop edition gets you to proprietary drivers/codecs through first-party means, with no repo added or edited. FULL = on/offered by default or installable from default-enabled repos; OPTIONAL = documented first-party opt-in only; NONE = no first-party path. Firmware blobs are excluded from the field, which is what made Debian-class entries unclassifiable. - secureBootOutOfBox = a default install boots with UEFI Secure Boot enabled (signed shim + kernel, no hand-enrolled MOK). Corrections, each cited in docs/distro-data-evidence.md: - ubuntu: secureBootOutOfBox false -> true. help.ubuntu.com/community/UEFI: "All current Ubuntu 64bit (not 32bit) versions now support this feature [Secure Boot]". The value contradicted the five *buntu flavours that already said true while sharing Ubuntu's archive and signed shim. - kubuntu, xubuntu, lubuntu, ubuntu_budgie, ubuntu_studio, ubuntu_mate: proprietarySupport OPTIONAL -> FULL. They are built from the same archive with the same default components (main restricted universe multiverse; restricted is the proprietary-driver component) and the same ubuntu-drivers tooling, so Ubuntu=FULL with flavour=OPTIONAL was indefensible. - The six flavour entries and ubuntu move to lastVerified 2026-09-28 / verificationMethod MANUAL, which is the date these values were actually checked. The remaining INFERRED entries are untouched and still flagged as inferred. The Debian claim from the review is recorded as refuted: official Debian media ship main + non-free-firmware only, contrib/non-free stay disabled and nvidia-driver is [non-free], so OPTIONAL is the correct value there — the defect was the undefined field, not the value. Gates: typecheck 0 | test 98/98 | validate PASSED (53 entries) | lint 0.
PART 1 — corrections (53 entries before, evidence in docs/evidence/ and docs/distro-data-evidence.md) Verification was done against primary sources for each changed field; the corrections are the ones the sources forced, not preferences: - linux_mint: proprietarySupport OPTIONAL->FULL, secureBootOutOfBox false->true - lmde: same two changes (medium confidence: LMDE configures main contrib non-free non-free-firmware; its shim was later hit by an SBAT revocation) - zorin_os, debian: secureBootOutOfBox false->true (Zorin docs; Debian ships shim since Buster) - arch, endeavouros, manjaro, mx_linux, parrot: proprietarySupport OPTIONAL->FULL (nvidia in extra; ISO NVIDIA boot entry + nvidia-inst; free vs proprietary driver choice at install; MX Tools driver GUI; parrot.list ships contrib non-free non-free-firmware) - qubes_os: proprietarySupport OPTIONAL->NONE (no first-party non-free path; NVIDIA "may require significant troubleshooting"; only third-party repos) - whonix: proprietarySupport OPTIONAL->FULL (maintainer states non-free and contrib ship enabled; upstream wiki unreachable, recorded as medium) Unchanged after checking, against my own review's expectation: pop_os and mx_linux keep Secure Boot false; Arch/EndeavourOS/Manjaro keep it false; debian keeps proprietarySupport OPTIONAL. All 13 changed entries now carry lastVerified 2026-09-28 and verificationMethod MANUAL. PART 2 — seven new entries (53 -> 59) omarchy, steamos, rhel, raspios (Raspberry Pi OS), antix, devuan. Each is schema-valid, has verified-resolving links (field-level quotes in docs/evidence/new-distros-tier1.md) and carries the values the sources support. Four calls were mine rather than an upstream statement, and are recorded as such: - omarchy.installerExperience = MANUAL: the ISO ships the Omarchy Configurator, a guided text-mode wizard over archinstall. The enum has no "guided CLI" value; GUI would hand Omarchy to a no-terminal beginner. - steamos.supportedArchitectures = ["x86_64"]: Valve ships SteamOS on x86_64 handhelds/machines; no ARM build exists. - raspios.nvidiaExperience = HARD: no NVIDIA path on Raspberry Pi hardware, and the dataset invariant bans UNKNOWN. - devuan.initSystem = OTHER: sysvinit has no enum value. Deliberately not added, with reasons in docs/evidence/coverage-*.md: HoloISO (end of life), ArcoLinux (superseded), BlackArch (dormant), XeroLinux (paid ISO), RebornOS (installer failures), Batocera (not a desktop), Amazon Linux and Ubuntu Core (cloud/IoT), postmarketOS/Nura (mobile), Slax and Porteus (dormant), Oracle Linux (fourth RHEL rebuild when RHEL itself is now listed). Test consequences, all reviewed before re-blessing: - compatibility.spec.ts used linux_mint as its "no Secure Boot" example in three places; that value is now (correctly) true, so the negative example moved to pop_os, whose upstream docs require Secure Boot to be off. - The duplicate "keeps hard filtering when coverage is at least 3" test encoded the removed coverage heuristic and is deleted. - hardConstraints.spec.ts asserted "fewer than 5 NONE distros", a dataset snapshot that the Qubes change broke; it now asserts a relative bound. - Two persona snapshots changed: "- arch / + antix" (equal counts, name tie-break) and "+ raspios / - bazzite" for the server persona. Both were explained from a decision trace before being re-blessed. Gates: typecheck 0 | test 97/97 (15 files) | validate PASSED | validate:invariants PASSED over 59 entries | lint 0.
Three entries added (59 -> 62): vanilla_os (Vanilla OS 3), guix (Guix System), archcraft. Sources in docs/evidence/new-distros-tier2.md. Two draft values were replaced before merging because they were unverified and would have overpromised: - vanilla_os.secureBootOutOfBox true -> false (no upstream statement that its kernel chain is signed) - vanilla_os.nvidiaExperience OK -> HARD (NVIDIA on an immutable ABRoot base is not a documented easy path) Architecture spellings: the draft used aarch64/armv7/i686, while the engine only consults x86_64, arm64 and x86 (src/engine/eliminate.ts). "aarch64" would have made an ARM answer silently miss Guix and Archcraft, so the entries were normalised to the engine vocabulary and a new invariant now fails the build on any other spelling, with a negative fixture in tests/distroInvariants.spec.ts. 32-bit ARM and i686 remain inexpressible, the same limitation Raspberry Pi OS's armhf runs into. Tiny Core Linux was dropped rather than merged: it has no logo on Wikimedia Commons and its own site was unreachable from the verification environment, so it could not satisfy the missing_core_data invariant. Carving an exception into the invariant for one optional entry was the worse trade; the entry is easy to re-add once a hotlinkable logo exists. One persona snapshot changed and was explained from a decision trace before being re-blessed: advanced_rolling_system_preferences now reads [arch, archcraft, cachyos, endeavouros, garuda]. Archcraft legitimately answers that persona's stated rolling + systemd + pacman answers, and all eight Arch-family entries tie at 3/3, so the order among them is the name tie-break. Gates: typecheck 0 | test 98/98 (15 files) | validate PASSED | validate:invariants PASSED over 62 entries | lint 0 | build 0 (33 routes).
Every CI run in this repository so far failed at actions/setup-node, not at a gate: the workflow configured cache:"npm" and ran `npm ci` while the project is pnpm (pnpm-lock.yaml, lockfileVersion 9.0, no package-lock.json). GitHub Actions reported "Dependencies lock file is not found in .../pickyourlinux" and stopped before typecheck, test, validate or lint executed once. The six most recent runs on master are all failures for that reason, which is why the 126 typecheck errors and the failing persona snapshot could sit there unseen. - pnpm/action-setup@v4 (version 11, matching the local toolchain) - setup-node cache: "pnpm", then `pnpm install --frozen-lockfile` - gates run as pnpm scripts: typecheck, test, validate, lint - `pnpm build` added: typecheck does not cover prerendering, and the site is a statically prerendered Nuxt app (33 routes), so a broken prerender should fail the pipeline rather than the deploy
There was a problem hiding this comment.
Copilot review overview
🟡 Changes recommended
Some of the newly added/updated tests contain logic bugs (incorrect filtering/counting and a broken Ubuntu presence assertion), and there is an API inconsistency where buildResultsPresentation ignores its distros argument for compatibility computation.
Review effort: Lite
Findings: 4
Open (4)
What changed in this PR
Unifies the picker around a single elimination-based compatibility engine, removes the legacy scoring system, expands/normalizes distro data, and adds stronger invariants + contract tests to prevent future drift between results and compare views.
Changes:
- Replaces score-based ranking with deterministic ordering based on matched stated constraints, then matched stated preferences, then name; improves explainability via inclusion/exclusion reasons.
- Removes the old
scoring.tspipeline and associated tests; adds broader contract tests (result set, explanations, determinism, invariants). - Updates distro dataset semantics (notably
proprietarySupport,secureBootOutOfBox), adds new distros, and introduces invariant validation scripts/docs.
| File | Description |
|---|---|
| tests/ui/smoke.spec.ts | Updates UI smoke expectations to reflect removal of match score and addition of “reasons” text. |
| tests/scoring.softPreferences.spec.ts | Removes tests for deleted soft scoring pipeline. |
| tests/scoring.integration.spec.ts | Removes integration tests for deleted scoring/hard-constraint pipeline. |
| tests/scoring.hardConstraints.spec.ts | Removes tests for deleted applyHardConstraints. |
| tests/resultContract.spec.ts | Adds oracle-based contract test for “never violate answers”, exact kept set, explainability, determinism. |
| tests/ranking.spec.ts | Adds ordering/score-removal contract tests for results ranking. |
| tests/pickerIntegration.spec.ts | Adds end-to-end profile tests for the new engine behavior and explainability. |
| tests/hardConstraints.spec.ts | Adds direct tests for eliminateDistros as single survival authority. |
| tests/engine/state.spec.ts | Updates state tests to reflect new ordering model (no score). |
| tests/engine/compatibility.spec.ts | Updates compatibility tests to reflect strict enforcement (no low-coverage escape hatch). |
| tests/distros.ferenos.spec.ts | Updates distro-specific test to use compatibility reasons rather than scoring fields. |
| tests/distros.artix.spec.ts | Updates Artix test for deduped ID and reason-based inclusion/exclusion. |
| tests/distroInvariants.spec.ts | Adds tests for new dataset invariants and compare-view reconciliation. |
| tests/__snapshots__/recommendation.regression.spec.ts.snap | Updates regression snapshot outputs after ranking + dataset changes. |
| src/pages/compare.vue | Removes minRam from compare view (aligns with schema/invariants). |
| src/engine/state.ts | Reworks results presentation to use compatibility reasons + count-based ordering; removes score fields. |
| src/engine/scoring.ts | Deletes legacy scoring engine (hard constraints + weighted soft scoring). |
| src/engine/recommend.ts | Updates recommendations to call compatibility without policy options. |
| src/engine/eliminate.ts | Removes low-coverage softening; adds architecture rule + excludedAxes for conflict reporting. |
| src/engine/compatibility.ts | Removes policy options; builds inclusion reasons from elimination survivors. |
| src/data/types.ts | Removes unused question-level constraint/scoring metadata and scoring-related types. |
| src/data/reason-templates.ts | Adds new exclusion key and new constraint keys for OTHER init/pkg variants. |
| src/data/questions.ts | Removes unused constraintMode / isHardConstraint fields from questions/options. |
| src/data/distros.json | Updates many distro attributes, dedupes Artix, and adds new distro entries. |
| src/components/ResultsView.vue | Removes score UI and switches to “why it matches” reason chips. |
| src/components/DistroCard.vue | Removes score UI and updates card/detail views to show reasons + confidence label. |
| scripts/validate-distro-invariants.ts | Replaces ad-hoc checks with findInvariantViolations and compare-view key validation. |
| scripts/distro-invariants.ts | Introduces semantic dataset invariants (identity, freshness, satisfiable answers, compare-view keys, etc.). |
| i18n/locales/pt.json | Adds/adjusts keys for new UI text, reasons, and constraints. |
| i18n/locales/nl.json | Adds/adjusts keys for new UI text, reasons, and constraints. |
| i18n/locales/it.json | Adds/adjusts keys for new UI text, reasons, and constraints. |
| i18n/locales/fr.json | Adds/adjusts keys for new UI text, reasons, and constraints. |
| i18n/locales/es.json | Adds/adjusts keys for new UI text, reasons, and constraints. |
| i18n/locales/en.json | Adds translation for new architecture exclusion reason. |
| i18n/locales/de.json | Adds/adjusts keys for new UI text, reasons, and constraints. |
| docs/evidence/new-distros-tier2.md | Adds evidence table for tier-2 distro additions (method + sources). |
| docs/evidence/distro-attributes-ubuntu-family.md | Adds verification notes/sources for Ubuntu-family-related attribute semantics. |
| docs/evidence/distro-attributes-arch-strict.md | Adds verification notes/sources for Arch-family-related attribute semantics. |
| docs/evidence/coverage-non-arch.md | Adds coverage analysis + rationale for included/excluded non-Arch distros. |
| docs/evidence/coverage-arch-gaming-desktop.md | Adds coverage analysis + rationale for Arch/gaming/desktop distro inclusion decisions. |
| docs/distro-data-evidence.md | Documents operational definitions + verified values and deltas in the dataset. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Comment on lines
+1185
to
1188
| // Single survival authority: eliminate.ts. The results page applies exactly the | ||
| // same rules as /compare and the persona suite — no second, weaker filter. | ||
| const compatibility = buildCompatibility(intent); | ||
| const distrosById = new Map(distros.map((distro) => [distro.id, distro] as const)); |
Comment on lines
+123
to
+129
| // Within a group of equal fit, the order is alphabetical by name — never by a | ||
| // hidden score. Groups are keyed by (strict matches, stated-preference matches). | ||
| const keys = presentation.compatible.map((item) => ({ | ||
| strict: item.matchedConstraints.length, | ||
| preferences: item.includedBecause.length, | ||
| name: item.name, | ||
| })); |
Comment on lines
+51
to
+55
| const keys = presentation.compatible.map((item) => ({ | ||
| strict: item.matchedConstraints.length, | ||
| preferences: item.includedBecause.filter((reason) => reason !== "include_meets_requirements").length, | ||
| name: item.name, | ||
| })); |
Comment on lines
+71
to
+72
| const ubuntu = keys.find((key) => new Set(presentation.compatible.map((item) => item.name)).has("Ubuntu")); | ||
| expect(ubuntu).toBeDefined(); |
…ault pnpm 11 fails the install with ERR_PNPM_IGNORED_BUILDS for @parcel/watcher and esbuild unless they are explicitly approved, which is what stopped the first CI run that actually reached an install step. Approving exactly these two package names (esbuild covers both pinned versions) keeps the stricter default for everything else rather than disabling it wholesale.
…1 allowBuilds) pnpm 11 ignores the package.json "pnpm" field entirely and fails the install with ERR_PNPM_IGNORED_BUILDS unless each dependency needing a lifecycle script is named in pnpm-workspace.yaml. The key is allowBuilds (a name -> true map), which is what pnpm approve-builds writes itself; onlyBuiltDependencies in the same file did not take effect. @parcel/watcher and esbuild are the only two, and everything else keeps the stricter default. The ignored package.json#pnpm field left by the previous attempt is removed. Verified: pnpm install --frozen-lockfile exits 0 and leaves pnpm-lock.yaml unchanged.
'nuxt typecheck' needs both, and neither was declared: typescript was only present transitively, so a clean CI install made Nuxt fetch it through npx and that fetched TypeScript 7, whose exports map no longer provides './lib/tsc' (ERR_PACKAGE_PATH_NOT_EXPORTED). Pinning typescript 5.9.3 — the version the gates were verified against locally — and vue-tsc 3.3.11 keeps the gate deterministic. Verified: pnpm typecheck exits 0 with the declared toolchain.
Removals, both from the coverage evidence in docs/evidence/: - feren_os: no release since 2025.03 (2025-04-10) after a ~4.5-year gap, and the project's own post concedes it; DistroWatch's 6-month rank slid to 253. - bodhi_linux: DistroWatch status "Dormant", last release 7.0.0 (2023-08-21). A picker's worst failure mode is confidently recommending something unmaintained, so an entry that cannot be verified as alive does not stay in the dataset. Both were proprietarySupport OPTIONAL and neither appeared in a persona's top five, so no snapshot moved. centos_stream stays but now says what it is: "RHEL's upstream development branch: a rolling preview of the next RHEL release, without the long lifecycle of a stable server distribution." It is maintained, so removing it would lose information; the confusion came from the description. Tests: tests/distros.ferenos.spec.ts is deleted with the entry. Two tests used the removed entries as their examples and now use live ones — pickerIntegration.spec.ts takes Debian for the APT-match-but-not-runit case, and compatibility.spec.ts's docs-ecosystem test uses antiX (compatible under the same answers, docs only OK) instead of Bodhi. Dataset: 62 -> 60 entries. Gates: typecheck 0 | test 93/93 (14 files) | validate PASSED | validate:invariants PASSED over 60 entries | lint 0.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

Picker refresh: one survival authority, settled attribute semantics, 9 new distros
Five commits on
refresh/picker-0.7. Every claim below is checkable in the diff, indocs/distro-data-evidence.md, or indocs/evidence/(the raw verification records).Why: the two defects the review found
scoring.ts(
applyHardConstraints, three rules: architecture, avoid-proprietary, secure boot) while/compareusedeliminateDistros(eight rules). Measured across eight answer profiles,87 entries were presented as matches on the results page that the other engine excludes.
An old-hardware, no-terminal, XFCE user got 54 matches and 0 exclusions; a no-terminal
beginner was offered Alpine as a 2/5 "match"; an ARM user's
/comparekept 31 of 33 entriesthat cannot run on ARM.
npm run typecheckfailed with 126 errors andnpm run testwith one snapshot, and every CI run since 2026-07-17 wasfailure. Thesnapshot failure was the duplicate Artix entry crowding
archout of a persona's top five.What changed
Engine (
e951f76). Survival now has exactly one authority:eliminate.ts, called throughbuildCompatibility.scoring.tsis deleted, with its hardcoded weights (gamingLIMITED0.5,NVIDIA
GOOD/OK2/1, a beginner docs bonus) and the low-coverage escape hatch that silentlyturned a stated constraint into a soft one when fewer than three distros matched it. Architecture
became a real elimination rule. Ordering is counts only — stated constraints matched, then stated
preferences matched (weight 1 each), then name — which is the contract the persona suite already
asserted but could not actually exercise, because inclusion reason lists were always empty.
Explainability is now real: every card carries its inclusion reasons, every excluded entry the rule
that fired, and the conflict report covers all eight rules instead of three. The UI no longer
prints a match score.
Data (
39563f3,b90ab46,ccb1731). Three dead question-level fields(
isHardConstraint,preferenceWeight,constraintMode) were deleted — no code read them, andtheir only effect was the 126 typecheck errors.
proprietarySupportgot an operationaldefinition (default-path scale, firmware deliberately excluded) and 13 entries were corrected
from primary sources. The Ubuntu family is consistent again. Artix is one entry, verified against
the project's download page.
Coverage (
ccb1731,5048ffd,40da0fe). Nine entries added: Omarchy, SteamOS, RHEL,Raspberry Pi OS, antiX, Devuan, Vanilla OS 3, Guix System, Archcraft. Two entries removed as
unmaintained —
feren_os(no release since 2025.03 after a ~4.5-year gap) andbodhi_linux(DistroWatch "Dormant", last release 2023) — and
centos_streamnow says it is RHEL's upstreamdevelopment branch rather than reading like a stable server choice. Dataset: 53 → 60. Reasons for everything
deliberately not added are in
docs/evidence/coverage-*.md(HoloISO EOL, ArcoLinux superseded,BlackArch dormant, XeroLinux paid ISO, RebornOS installer failures, Batocera not a desktop,
Amazon Linux / Ubuntu Core cloud-only, Numa mobile, Slax / Porteus dormant, Oracle Linux a fourth
RHEL rebuild).
Gates (
b90ab46,ccb1731,5048ffd).validate-distro-invariantsis now real: identity(duplicate name, not just id), freshness ratchet, core-data completeness, Secure Boot requires
a UEFI architecture, Ubuntu-family consistency, architecture vocabulary, "no stated answer may be
unsatisfiable", and a compare-view reconciliation that fails if a rendered row is not in the
distro schema. Each check has a negative fixture.
tests/resultContract.spec.tssweeps 120answer combinations plus all personas against an independent oracle: no result may violate an
answer the user gave, kept set == oracle set, every entry explained, conflict flag exact,
output stable under dataset reordering.
Gates, verbatim
Local and in CI: run
36393018936was the first CI run in this repository to execute a gate —its ten steps (install, typecheck, test, validate, lint, build) all report
success, and thesame is true for the run on the current HEAD (
40da0fe).npm run typecheck→ exit 0 (was 126 errors)npm run test→ exit 0, 93/93 across 14 files (was 73/74; the feren_os spec file left with its entry)npm run validate→ PASSED,validate:invariantsPASSED over 60 entriesnpm run lint→ exit 0npm run build→ exit 0, 33 routes prerenderedWhere my own review was wrong
main+non-free-firmwareonly;contrib/non-freestay off andnvidia-driveris[non-free], soOPTIONALis correct.The defect was the undefined field, not the value. Recorded as refuted.
non-freeandcontribship enabled →FULLunder the definition (medium confidence: upstream wiki unreachable, forum statement used).
Qubes, which I expected to stay
OPTIONAL, becameNONE.proprietarySupport, notsecureBootOutOfBox: Ubuntu andits flavours shared the signed shim already; they disagreed about proprietary support.
Residuals — honest, not fixed here
verificationMethod: INFERREDwith onebulk-stamped date. The freshness invariant now fails on staleness, so this cleans up as entries
are touched rather than in one bulk pass (
backfill-distro-metadatais what stamped themMANUAL-across-the-board; it should not be re-run).
rocky_linux/almalinux/centos_streamlistsupportedDesktops: ["OTHER"]while thenew RHEL entry uses
["GNOME"](upstream default). Same family, different answer — the sameclass of drift as the Ubuntu family. Not changed here because it needs a decision, not a
source: either RHEL drops to
OTHERfor consistency or the three clones move toGNOME.rolling + systemd + pacman, eight Arch-family entries tie at 3/3 and the order among them is the name; the serverpersona now has Raspberry Pi OS at rank 4 and Ubuntu at 5 for the same reason. The fix is a
question that discriminates (desktop image vs server image), not a hidden weight.
minRamremains collected and consumed by nothing, and the i18n demo keydistros.new_distrois still present. Neither is user-visible any more, both are dead weight.Merge
Branch only, deliberately: merging to
mastertriggers the production deploy, so that is yourcall.