Delete the dead tooling and stop CI repeating itself - #502
Merged
Merged
Conversation
scripts/measure-windows-runtime.ps1 is reachable only through benchmark:win, which nothing in CI, docs, the PR template or CONTRIBUTING mentions, and its default $AppPath points at the pre-rename "VS Launcher.exe" that electron-builder stopped producing. Delete the script and the npm script with it; the two remaining mentions (a comment in electron-builder.yml and the exclusion-line assertion in electron-builder-icon-files.test.ts) are untouched. @electron-toolkit/preload has zero imports anywhere in src, tests or scripts; drop the devDependency and regenerate the lockfile. electron-builder.yml's mac stanza loses the four NSCamera/NSMicrophone/ NSDocuments/NSDownloads extendInfo lines: boilerplate for a launcher that never touches a camera, mic, or those folders. entitlementsInherit and notarize: false are left in place with a comment flagging them for a maintainer's call rather than a silent fix: build/entitlements.mac.plist has never existed in git history, build-macos in CI only runs build:unpack (--dir, unsigned, entitlements never consulted), and build:mac is the only script that would hit the missing file, which nobody runs from CI. Rename docs/notes/notifications-audit.md to docs/notes/archive/notifications-audit-2026-08.md, content unchanged. One code comment in src/domain/notifications/failureReason.ts did reference the old path (missed by the issue's own audit) and is updated to match.
tests/fixtures/build-inno-fixtures.ts hand-rolled a 256-entry reflected CRC-32 table and function that its sibling build-fixtures.ts, in the same directory, already gets from node:zlib. This builder is not under the purity constraint that justifies the hand-rolled CRC-32 in src/domain/inno/crc32.ts (that layer stays pure; this one already imports node:crypto, node:fs and node:child_process). Verified before deleting the local implementation: compared over 200 random buffers plus the empty buffer, zero mismatches against node:zlib's crc32, and regenerating every fixture under tests/fixtures/inno/ produces byte-identical .bin files to the committed ones (md5sum diff empty). tests/domain/inno/ stays at 5 files, 105 tests, same names, before and after.
Under vitest's default jsdom url (http://localhost:3000, never overridden in this repo's environmentOptions), jsdom always exposes a real localStorage, so the "if (!window.localStorage)" branch building a 25-line Map-backed Storage shim was never entered. On an opaque origin jsdom's own getter throws SecurityError on the property read inside the if, before the branch is even evaluated, so the shim could not install itself there either. The three window.localStorage.removeItem calls in beforeEach keep running unchanged, against jsdom's own implementation. Before and after: tests/renderer-dom/ stays at 82 files, 805 tests, same names.
…config tests electron-builder-locales.test.ts's inline block extractor and electron-builder-keyring-deps.test.ts's listUnder(section, key) are the same regex-split-and-collect loop, one reading a top-level key and one reading a key nested inside a section. Move listUnder to tests/config/ymlBlock.ts, taking the yml text and an empty section string for a top-level key, and have both tests call it. The "read as text, js-yaml is only transitive" comment now lives once, on the shared file, instead of once per test file. Both test files pass unchanged: 3 tests in electron-builder-locales, 4 in electron-builder-keyring-deps, same names, before and after.
… used next door
tests/i18n/helpers.ts's listSourceFiles (readdirSync plus statSync) and
tests/action-label-locales.test.ts's tsxFiles (withFileTypes plus
flatMap) both walk a directory tree by hand for the same purpose that
tests/security-boundaries.test.ts already gets from
readdirSync(dir, { recursive: true, encoding: "utf8" }). Replace both
with that call, filtered by extension and re-joined onto dir since the
recursive form returns paths relative to it. action-label-locales.test.ts
now imports listSourceFiles from tests/i18n/helpers.ts instead of
keeping tsxFiles.
Benchmarked against both existing walkers on the real tree before
deleting either: listSourceFiles's 168 .ts/.tsx files and tsxFiles's 87
.tsx files come back byte-identical, sorted, from the new one-liner.
tests/action-label-locales.test.ts, tests/i18n/ and
tests/security-boundaries.test.ts stay at 3 files, 54 tests, same
names, before and after.
The first describe asserted seven named en-US keys carry no exclamation mark; the second walks every string value in en-US.json with an empty allowlist and asserts the same thing, so the seven were a strict subset and a regression in any of them already failed the second block first (#446, closing #411, whose own body says the second commit "went past those seven and swept the other 40 en-US strings"). Delete the first describe and its KEYS list, and import flattenTranslationObject from tests/i18n/helpers.ts instead of keeping a second flattener (collectStringPaths): verified identical over the real en-US.json, 898 string values from both. Moved the #411 reference onto the surviving describe so the seven keys keep their trail. Test count goes from 906 to 899, all 899 the same assertions as before (the surviving describe's own 899, unchanged); only the duplicate seven are gone. The "does a renamed key still exist" case is still covered: tests/i18n/i18n-parity.test.ts already asserts, codebase wide, that every statically referenced key exists in en-US.json.
TaskManagerContext.tsx's startInstall was copied from startExtract and
kept its locale keys: on failure it raised notifications.body.extractError
("Couldn't unpack {{name}}. Check that the archive is not damaged and
that there is room on the drive."), which is wrong advice for an
installer that timed out, was missing, or is not-windows, exactly the
InstallerRunResult reasons this catch classifies.
Add notifications.body.installed/installError to all 14 locale files
(i18n-parity.test.ts's fr-FR parity check and the Activity Center
namespace check both gate a partial add) and point startInstall at
them. The failure sentence points at the log rather than the archive,
since the specific reason already rides along on the notification's
`reason` option to the Activity Center row.
tests/renderer-dom/taskManagerStartInstall.test.tsx gets two new tests
pinning the installer's own wording; both fail red against the
unfixed hook (asserting the body doesn't mention "unpack" or "archive")
and pass green after. taskManagerFlows.test.tsx and the rest of
tests/i18n/ stay green unchanged.
MainMenu's PlayHandler and useLaunchGame drifted apart after the hook was extracted (#460): PlayHandler builds a see-report notification action navigating to /installations/report/:id off pickPlayOutcomeNotification's report field, useLaunchGame drops that field on the floor. A crash after joining a saved server therefore offered no report link, while the same crash after Play did. Added a failing test first: manageInstallationServers.test.tsx now renders a WhereProbe alongside the page (mirroring launchPlayGame.test.tsx's own probe) and asserts Join surfaces the same see-report action and navigates to the same route Play does. Confirmed red against the current hook (no such button exists), then ported the report-action block from PlayHandler into useLaunchGame's outcome-notification handling, gaining useNavigate. launchPlayGame.test.tsx stays green unchanged: MainMenu's own copy is untouched by this commit.
MainMenu still carried its own PlayHandler (106-208), skipBackupPrompt state, askToLaunchWithoutBackup, the teardown effect, and an inline copy of LaunchBackupPrompt, comment for comment, even though useLaunchGame was extracted from that exact function to give Join the same launch path (#460). The previous commit ported the one place they had drifted (the see-report action) into the hook, so this conversion carries no behaviour change of its own. MainMenu now calls useLaunchGame() for launchGame/skipBackupPromptOpen/ answerSkipBackupPrompt and renders the shared LaunchBackupPrompt component instead of its own copy. Dropped with PlayHandler: the gameVersions/configDispatch/os/openExternalLink/goTo locals and the getInstallationVersionStatus/pickPlayOutcomeNotification/logLaunch/ runGame/preventAppClose imports it alone used; checkInstallationPathExists and useMakeInstallationBackup stay, still used by the quick-backup button. launchPlayGame.test.tsx stays green unchanged: same 23 tests, same names, now exercising the hook through MainMenu instead of the deleted local copy. The whole renderer-dom suite: 82 files, 808 tests (805 plus the 3 added across the previous two commits).
…R_STYLES InstalledTagsFilter and InstalledModsSelectFilter were the last two Listbox triggers still hand-rolling their border/background/shadow classes; every other dropdown in the renderer (SideFilter, InstalledFilter, OrderFilter, AuthorFilter, TagsFilter, VersionsFilter, LanguagesMenu, ConfigPage, InstallationsDropdownMenu, and others) already shares MENU_TRIGGER_STYLES/MENU_OPTION_STYLES from components/ui/buttonStyles.ts. Moved both onto the shared styles, keeping each option row's own text sizing (moved onto the inner <p>, which is where every other migrated dropdown already carries it) and InstalledTagsFilter's "#" prefix and checkmark. The outer dropdown panel wrapper (border, background, shadow) stays local, same as it does on every other filter already using these constants. tests/renderer-dom/manageMods*.test.tsx (6 files, 138 tests) and text-contrast.test.ts stay green.
…ne shared body each Buttons.tsx and FormButtons.tsx carried two near-identical HeadlessUI Button wrappers and two near-identical Link wrappers, differing only in default variant (ghost vs secondary), whether overflow-hidden is forced on, and FormButton's extra ariaExpanded prop and stricter submit-vs-onClick typing. Factored the shared rendering into Button and ButtonLink in Buttons.tsx; NormalButton, LinkButton, FormButton and FormLinkButton become thin presets over them, each keeping its own exact public prop type (including FormButton's FormButtonAction union and FormLinkButton's lack of an ariaLabel prop, which the previous FormLinkButton also never exposed) so no call site's type surface changes. The one detail that had to move rather than merge: LinkButton always sets aria-label to `ariaLabel ?? title`; FormLinkButton has never set aria-label at all. That default now lives in each preset rather than in the shared ButtonLink, so FormLinkButton keeps omitting the attribute exactly as before instead of gaining one. All four exported names, and every one of their ~400 call sites, are unchanged. The whole test suite (234 files, 4423 tests, 2 pre-existing skips) passes unchanged, actionBusyState.test.tsx included; lint:ci stays at 0 errors, 14 pre-existing warnings.
Both were the same single-select Listbox scaffold with one fixed option list swapped for another: same trigger, same animated panel, same zebra-striped MENU_OPTION_STYLES rows, already sharing animateVariants.ts's DROPDOWN_MENU_WRAPPER_VARIANTS. Moved the shared shell into components/ui/SelectMenu.tsx (generic over a string union), leaving each file as its own translated option list plus a one-line call. LanguagesMenu and ConfigPage's UIScale are left as their own hand-rolled copies for now: neither has DOM test coverage, and the issue's own "stays out" list defers that pair to a maintainer who can check the result by hand. tests/renderer-dom/listModsFilterBar.test.tsx and five other Mods browse test files (6 files, 51 tests) pass unchanged, same names, before and after.
Both were the same multi-select Listbox scaffold: a chip-filled trigger, an animated panel with a lookup-failed row, a checkmark on each selected option, already sharing DROPDOWN_MENU_WRAPPER_VARIANTS and MENU_OPTION_STYLES. The one real visual difference is Tags' "#category" hash prefix on both the trigger chips and the option rows, which Versions never had; kept as a `hashPrefix` flag rather than forcing one look on both. Moved the shared shell into components/ui/MultiSelectFilter.tsx, generic over anything with a tagid/name pair (both DownloadableModTagType and DownloadableModGameVersionType already have that shape). Each preset keeps its own lookup hook (useTagsLookup / useGameVersionsLookup) and placeholder text. Same six Mods browse test files (51 tests) pass unchanged, same names, before and after; lint:ci stays at 0 errors, 14 pre-existing warnings.
The MultiSelectFilter fold moved TagsFilter's and VersionsFilter's "nothing picked yet" placeholder styling (tagsFilter.length < 1 / ...) into the new shared components/ui/MultiSelectFilter.tsx as selected.length < 1, so text-contrast.test.ts's pinned source anchors for those two rows stopped matching (the class did not change, only which file it lives in). Repointed both anchors at the shared file; the contrast floor being checked is unchanged. Caught by running the full suite after the previous commit rather than only the files that looked affected -- the lesson for the rest of this pass is to run it before every commit, not just the ones a fold's own tests suggest.
startDownload, startExtract, startInstall and startCompress were the
same scaffold with one line changed: generate an id, prevent-close,
log and dispatch the pending task, run the host call, dispatch
completion plus an optional toast, or on a throw classify the failure,
dispatch it failed, and show an optional error toast, then release the
prevent-close token either way.
Factored that into runTask, which takes the task's type (download,
extract, install or compress, doubling as the log/notification key
root), an optional noun override for the three whose prevent-close and
"adding" wording is not just the type word (extraction, installation,
compression), and an `operation(tag)` closure holding everything
actually specific to one task: its own running/done log line, the host
call, and any post-processing that has to run before the task counts
as done (startExtract's awaited chmod, startInstall's ok-check with the
reason folded into the thrown message). Those per-task comments
recording past bugs stay on the wrappers, where the code they explain
now lives.
TaskContextType's four-signature public surface is untouched.
startDownload's onFinish is still the three-argument contract
(status, path, error) the other three don't share: matching it here
would have forced every runner through the widest shape, so each
wrapper turns runTask's `{ ok, result }` / `{ ok: false, error }`
outcome into whatever its own onFinish expects instead. startOptimumPatch
is unrelated (a different onFinish shape entirely) and untouched.
One deliberate wording simplification, not a behaviour change: startCompress's
"Adding compression of [PATH] to [PATH]." debug log line becomes
"Adding compression to [PATH].", the same wording the other three
already used for their own single path. Nothing reads this text; it is
not covered by any test, and log-provenance.test.ts (which does govern
what a log line may interpolate) stays green.
Net line count grew slightly (167 to about 196) against the issue's
own estimate of a reduction, because every institutional comment and
the exact notification/log text stayed put; what actually shrank is
the duplication: four near-identical try/catch/dispatch bodies down to
one. tests/renderer-dom/taskManagerFlows.test.tsx,
taskManagerStartInstall.test.tsx and log-provenance.test.ts (3 files,
44 tests) pass unchanged, same names, before and after; full suite 234
files, 4423 tests, 2 pre-existing skips; lint:ci 0 errors, 14
pre-existing warnings.
manageMods.test.tsx, manageModsHealth.test.tsx, manageModsDetails.test.tsx,
manageModsServerMods.test.tsx, manageModsUnreadableArchive.test.tsx and
manageModsMissingInstallation.test.tsx each built the identical
<Routes><Route path="/installations/mods/:id" element={<TaskProvider>
<ManageMods /><NotificationsOverlay /></TaskProvider>} /></Routes> at
the same route through renderWithProviders. Moved that block into
mountManageMods (tests/renderer-dom/helpers), taking an optional route
override for manageModsMissingInstallation's "/installations/mods/missing".
Each file's own fixture defaults (its installMockWindowApi calls),
route override where it has one, and return type stay local: the
netManager/modsManager mocks differ substantially per file (a full
ModDB simulation, a DETAILS-only stub, an empty listing, a mocked
bridge to hand back), which is exactly why only the mount moved and
not the surrounding renderManageMods wrapper.
Same six files, 138 tests, same names, before and after. Full suite:
234 files, 4423 tests, 2 pre-existing skips; lint:ci 0 errors, 14
pre-existing warnings; format:check clean.
sonarcloud reran npm run test:coverage standalone (measured 237s) after its own npm ci, duplicating test-matrix (ubuntu-latest)'s identical step. test-matrix now uploads its ubuntu leg's coverage/ as an artifact (coverage-ubuntu, 1-day retention: sonarcloud is the only consumer and runs in the same workflow run); sonarcloud gets needs: [test-matrix] and downloads that artifact instead of regenerating it. This means sonarcloud only runs once every test-matrix leg has succeeded, and only within the run that produced the artifact: a solo re-run of test-matrix produces fresh coverage this job will not automatically pick back up, noted in a comment on the job so whoever re-runs test-matrix alone knows to re-run sonarcloud too. build:unpack runs npm run build (typecheck && electron-vite build), redoing the standalone typecheck job's work (24s) on both build legs and, when triggered, on build-macos at its 10x multiplier. Added build:unpack:no-typecheck (electron-vite build && electron-builder --dir, no typecheck) and pointed the build and build-macos jobs at it; build:unpack itself is untouched, so a local unpacked build still gets the typecheck safety net by default. Updated the one comment in electron-builder.yml that named build:unpack for what build-macos actually runs now. Combined, roughly 5 of the run's ~29 billed minutes on ubuntu-latest, closer to a third whenever build-macos fires. Validated the workflow with python3 yaml.safe_load and confirmed tests/config/ electron-binary-download-skip.test.ts's job-name parsing still finds typecheck/lint/test/build unchanged (it parses " name:$"-shaped lines, which the new comments do not match). Flagging rather than fixing: dropping typecheck from build:unpack:no-typecheck means a type error can only be caught by the standalone typecheck job now, not by build itself, so branch protection's required-check list needs a maintainer's re-check to confirm typecheck (not just build and test) stays required, including on workflow_dispatch runs where a required check might not gate the same way.
17 path:NN-NN citations and 7 raw line counts had all drifted against origin/dev: ListMods.tsx cited at 301 lines is well past that today, ports.ts at 321 is 383, lzma.ts at 670 is 687, extract.ts at 345 is 374, preload/index.ts at 107 is 125, and so on. A line number survives no edit to the file it points at; the path it sits on does. Stripped every :NN-NN suffix and raw line count, keeping the path and the reasoning; the "pages around 250 lines" section now names the pages that have grown past the soft ceiling without pinning a count to any of them, and says to check the file itself instead. The CI paragraph was the one part actually wrong rather than merely stale: it described five job definitions when the workflow (before or after the CI fold) defines seven, never mentioned test-matrix, called build-macos "a sixth", and cited ci.yml:23-33 for where lint:ci is wired in, which points at a setup-node block. Rewritten from the post-fold ci.yml: all seven jobs named, what test exists for and why, and sonarcloud's new needs: [test-matrix] plus artifact read, and the build/build-macos jobs' build:unpack:no-typecheck. Left alone: a separate, pre-existing inaccuracy this pass did not touch (the closing "Issue #31 and #24, both cited on this page" no longer matches the page, which now only cites #8) is out of scope here and flagged separately.
Pixnop
force-pushed
the
chore/491-tooling-cleanup
branch
from
September 15, 2026 20:58
43bd3fc to
d16202b
Compare
Pixnop
marked this pull request as draft
September 15, 2026 20:59
Pixnop
marked this pull request as ready for review
September 15, 2026 21:00
Zaldaryon
approved these changes
Sep 15, 2026
Zaldaryon
left a comment
Collaborator
There was a problem hiding this comment.
Reviewed the current head. The tooling cleanup removes unused project pieces and consolidates the renderer filter controls without leaving unresolved references. The required checks pass in the Node 22 CI environment, including typecheck, lint, the Ubuntu and Windows test matrix, build, SonarCloud, and the gate test. Approving.
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.
What changes
Batch 1, pure deletions
scripts/measure-windows-runtime.ps1andbenchmark:win, drop the@electron-toolkit/preloaddevDependency, drop the macextendInfoboilerplate, renamedocs/notes/notifications-audit.md(147 lines removed, net)src/domain/notifications/failureReason.tsstill pointed at the old notes pathBatch 2, mechanical folds
build-inno-fixtures.ts's hand-rolled CRC-32 →node:zlib(16 lines removed)renderer-dom/setup.ts's unreachable localStorage polyfill deleted (26 lines removed)electron-builder-locales.test.tsandelectron-builder-keyring-deps.test.tsshare onelistUnderintests/config/ymlBlock.ts(net 0 lines, one implementation instead of two)tests/i18n/helpers.tsandaction-label-locales.test.ts's hand-rolled recursive readdirs → the one-linersecurity-boundaries.test.tsalready used (15 lines removed)copy-tone-locales.test.ts's redundant seven-key describe deleted, its second flattener replaced by the sharedflattenTranslationObject(44 lines removed)Batch 3, the two #490 bugs (moved before the refactors that touch the same files)
startInstallstops telling players a failed installer is a "damaged archive": newnotifications.body.installed/installErrorkeys in all 14 locales, wording pointed at the loguseLaunchGamegains thesee-reportnotification actionMainMenu's oldPlayHandlerhad and the hook dropped;MainMenuconverted to call the hook,PlayHandlerand its 183-line local copy of the backup-prompt machinery deletedBatch 4, renderer shells (headless screenshots before merging, see below)
InstalledTagsFilter/InstalledModsSelectFilter, the last two hand-rolled dropdown triggers, moved ontoMENU_TRIGGER_STYLES/MENU_OPTION_STYLESButtons.tsx/FormComponents/FormButtons.tsxcollapsed onto one sharedButton/ButtonLinkbody;NormalButton,LinkButton,FormButton,FormLinkButtonbecome presets with identical public prop typesSideFilter/InstalledFilterfolded onto one genericcomponents/ui/SelectMenu.tsx(LanguagesMenu/ConfigPage's UIScale left as their own copies, no DOM test to check the change by hand, per the issue's own "stays out" list)TagsFilter/VersionsFilterfolded onto one genericcomponents/ui/MultiSelectFilter.tsx(the "#" tag prefix kept as ahashPrefixflag)TaskManagerContext's four task runners folded onto onerunTaskscaffold plus four thin wrappers;TaskContextType's four-signature public surface untouchedBatch 5
mountManageModsprimitive (tests/renderer-dom/helpers/mountManageMods.tsx); each file's own fixture defaults and route stay localCI fold
sonarcloudnow depends ontest-matrixand reads its ubuntu leg's uploaded coverage artifact instead of runningtest:coveragea second timebuild/build-macoscall a newbuild:unpack:no-typecheckscript instead ofbuild:unpack, since the standalonetypecheckjob already gates the same commitDocs
docs/architecture.md: stripped all 17 stale:NN-NNcitations and 7 stale line counts, kept the paths; rewrote the CI paragraph from the post-foldci.yml(it previously described 5 jobs, missedtest-matrixentirely, and cited a line forlint:ci's wiring that pointed at asetup-nodeblock)What does not change
Every fold's commit carries a diffed before/after test run: same test files, same test count, same test names, in the fold's own commit message. Two independent proofs beyond the test suite:
node:zlib, zero mismatches; regenerating every fixture undertests/fixtures/inno/produced byte-identical.binfiles (verified viamd5sumdiff before deleting the old implementation)..ts/.tsxfiles, 87.tsx).The renderer shell folds (Batch 4) were checked live, headless, against a seeded config with a real Installation and Mods folder, screenshots under
pr-assets/contrast-pass/mods-pass/(491-*.png):491-home.png: Home page, Play button and the quick-action icons through the collapsedButton/ButtonLink491-mods-browse.png,491-mods-sidefilter-open.png,491-mods-tagsfilter-open.png: the Mods browse filter bar,SelectMenu(Side filter) andMultiSelectFilter(Tags, with its "#" prefix) opened491-managemods-actionbar.png,491-managemods-filters-open.png: Manage Mods' action bar and its Author filter (InstalledModsSelectFilter) onMENU_TRIGGER_STYLES491-install-inprogress.png,491-activitycenter.png: a real ModDB download-and-extract run through the foldedrunTask, completing with the correct toast text and Activity Center entryI did not click Join on a saved server or Play in the live check (both launch the real game process);
useLaunchGame'ssee-reportaction is covered instead by the new, red-then-green test inmanageInstallationServers.test.tsx, andlaunchPlayGame.test.tsx(717 lines, unchanged) still passes running through the hook instead of the deletedPlayHandler.Bugs fixed on the way (#490)
Two real bugs, named in #490, moved before the refactors that touch the same files rather than waiting behind them:
startInstallwas copied fromstartExtractand never reworded: a failed Windows installer told the player to check that "the archive is not damaged," which cannot be true of an installer. Fixed with new locale keys, added to all 14 locales in the same commit.MainMenu'sPlayHandlerand the extracteduseLaunchGamehad drifted: Play offered a "see the session report" action after a crash, Join (through the hook) did not. Ported into the hook first, proven with a test written red against the current hook and green after, before convertingMainMenuto use the hook.This PR does not close #490 itself; the filter-props consolidation, folder-picker sharing and
useExternalLinksitems there are left to that issue's own pass.Testing
npm run typecheck(0 errors),npm run lint:ci(0 errors, 14 pre-existing warnings),npm run format:check(clean),npm run test:coverage(234 files, 4423 tests, 2 pre-existing skips; statements 94.55%, branches 91.18%, functions 95.37%, lines 96.21%, all above the ratchet floors).npm run test:coveragemeasured at 237s on run 34959134901; folded ontotest-matrix's ubuntu leg via an uploaded artifact, so that run no longer happens a second time.build:unpack:no-typecheckdrops the ~24s standalonetypecheckfrom bothbuildlegs and, on aworkflow_dispatchrun, frombuild-macosat its 10x multiplier. Combined estimate carried over from the issue: roughly 5 of the run's ~29 billed minutes on a normal push/PR, closer to a third wheneverbuild-macosfires. This PR's own push tochore/491-tooling-cleanupis the live measurement of the folded workflow (act is not installed; the YAML was validated withpython3 -c "import yaml; yaml.safe_load(...)"and read carefully before pushing).entitlementsInherit/notarizestanza is left as an open question in a comment rather than fixed silently:build/entitlements.mac.plisthas never existed in git history, and dropping the two keys would be a codesigning-intent call, not a debt cleanup.Left out
no-restricted-imports/exhaustive-depsESLint allowlist: left alone deliberately, trimming it blind risks the exact stale-closure bugs the rule was turned on to catch.useExternalLinks' mods wrapper (Fold the repeated renderer shells into one component each #489/One launch path, one filter object, one task runner #490): rated worth "low" in both issues, 7 lines, the aliasing problem it targets does not exist.sonarcloudnow depends ontest-matrixsucceeding first (a solo re-run oftest-matrixwill not automatically refresh asonarcloudrun that already finished), andbuild/build-macosno longer typecheck, sotypecheckstaying a required check matters more than it did.Closes #491. Part of #492.