Conversation
Eleven modules and src/ipc/validation.ts each carried a byte-identical private copy of the object guard. They now share src/domain/records.ts, which validation.ts re-exports so every host caller and test keeps its import path. modPaths.ts drops its asRecord variant for the same guard.
Six modules restated the same three-line helper once per failure union,
create.ts twice. src/domain/refusal.ts carries the union as a type
parameter instead and returns the same { ok: false, reason } shape. The
richer refusals in backup.ts and restore.ts keep their own helpers.
CompressOutcome, ExtractOutcome and UnpackOutcome were byte-identical interfaces; DownloadOutcome was the same plus filePath. They now alias or extend HostOutcome, so the shape is written once. The four ports and every name they answer under are unchanged.
GameBuildVariant was spelled out twice, once in src/domain/versions/detect.ts and once as the ambient GameBuildVariantType, doc comment included. The ambient name now imports the domain type, so the Optimum token lives in one place and cannot drift. Resolves under all three tsconfigs.
This was referenced Sep 20, 2026
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.
Four shapes that
src/domainwrote out once per module now live in one place each. No behaviour changes, no test file is touched, and the hexagonal split is exactly as it was: the ports stay four ports, the modules stay pure, only the restated bodies go.What changes
isRecord, twelve copies into one (src/domain/records.ts, 56 lines removed). Ten domain modules,src/ipc/optimumOverlay.tsandsrc/ipc/validation.tseach held a byte-identical private copy of the guard. The audit counted eight; dev has moved since, andoptimum/manifest.ts,optimum/ndjson.tsandoptimumOverlay.tshad grown their own in the meantime.src/ipc/validation.tsre-exports the shared one, sosrc/config/configManager.ts, the five IPC handler modules, both e2e scripts andtests/ipc/fetchReleaseNotes.test.tskeep the import path they had.src/domain/redaction.tsset this precedent and its header explains it.asRecordinaccount/modPaths.tsgoes with them. Its two call sites now narrow withisRecorddirectly, which is one guard instead of two and the same predicate:value && typeof value === "object"andtypeof value === "object" && value !== nullaccept and reject the same values.refuse, seven copies into one generic (src/domain/refusal.ts, 32 lines removed). Six modules had the three-line helper andinstallations/create.tshad it twice, underrefuseFieldsandrefuseCreate. They differed only in the failure union, which is now the type parameter. The return type is still written out explicitly, soexplicit-function-return-typeis satisfied andokstays the literalfalsethe result unions discriminate on.installations/backup.tsandinstallations/restore.tskeep their own helpers.The four host outcomes get one shape (
src/domain/ports.ts, 2 interfaces removed).CompressOutcome,ExtractOutcomeandUnpackOutcomewere byte-identical; they now aliasHostOutcome, andDownloadOutcomeextends it with itsfilePath. The four port interfaces, and every name they answer under, are untouched.GameBuildVariantis declared once (src/global.d.ts, one declaration and one duplicated doc comment removed). The ambientGameBuildVariantTypenow imports the domain type instead of restating the"Optimum"literal and the rule behind it. Raised in the review of #457.What stays
Nothing was skipped: all four findings applied as written, plus the three
isRecordcopies the audit predates.The deliberate rules are untouched. The path policy, the IPC validation at the boundary, the mutation-tested guards and the accessibility wiring are not in this diff, and none of
tests/security-boundaries.test.ts,tests/log-provenance.test.ts,tests/text-contrast.test.tsortests/i18n/i18n-parity.test.tsis relaxed by anything here.toWireBuildVariantstill holds the wire value to the fixed token; the alias only moves where that token is spelled.Behaviour
No test file is added, changed or deleted. Every caller was checked by grep: the
isRecordcall sites all reach it through the two names they already used, the domain module insidesrc/domainand thesrc/ipc/validation.tsre-export everywhere else;refusehas 22 call sites, all inside the six modules that used to declare it; the outcome types are type-only and TypeScript is structural; the variant type has five referencing files and all three tsconfigs resolve the alias.Gate, against
origin/devat 5ddf93a:The percentages move by hundredths because the deleted lines were all covered: 17 statements, 39 branches and 18 functions leave the denominator along with the same count in the numerator. No uncovered code is added.
Worth knowing for whoever reruns this locally: three runs of the suite on
devitself, on this machine, each failed a different two or three timing-sensitive tests (activity centre focus, mod profiles, the compression and ModDB listing handlers), which is load and not the tree. The branch ran green twice, the coverage run included.npm run typecheckclean across the three configs,npm run lint:ci0 errors and the same 14 pre-existing warnings,npm run format:checkclean.git merge-treeagainst the six open PR branches reports no conflict.Closes #484. Part of #492.