Repository navigation
chore(release): version packages - #267
Merged
Merged
Conversation
github-actions
Bot
force-pushed
the
changeset-release/main
branch
4 times, most recently
from
October 10, 2026 06:09
d2fa959 to
e625586
Compare
github-actions
Bot
force-pushed
the
changeset-release/main
branch
from
October 10, 2026 07:51
e625586 to
ea2ab2b
Compare
github-actions
Bot
force-pushed
the
changeset-release/main
branch
from
October 10, 2026 10:35
ea2ab2b to
220a61c
Compare
github-actions
Bot
force-pushed
the
changeset-release/main
branch
2 times, most recently
from
October 10, 2026 17:12
a95ef15 to
bacf827
Compare
ryanleecode
pushed a commit
that referenced
this pull request
Oct 10, 2026
…clares (#282) * fix(stryker-js-cli-contract): judge a version PR by the version it declares The contract version law decided the pin was stale whenever the committed version differed from the released one, so every version PR (changesets consumed, manifest at the next version) was refused as a stale baseline (#267). The pin is now compared with the version main declares at merge-base(origin/main, HEAD): a version PR is judged with its bump as the declared version, while a pin lagging main's version is still refused. * fix(stryker-js-cli-contract): name a missing main baseline and never cache the law's verdict Per the R-LAW review rulings: without origin/main the law reports code main-baseline-unavailable with next: git fetch origin main instead of dying; the stale-baseline failure carries its code and next action; the cli-contract test task is uncached (turbo.json) because the law reads git state, .changeset and .sfs-deps. Real-git tests pin the merge-base read and the missing-baseline path. * test(stryker-js-cli-contract): name the code of every contract-law failure
github-actions
Bot
force-pushed
the
changeset-release/main
branch
2 times, most recently
from
October 10, 2026 19:51
b499f04 to
b9f68bf
Compare
github-actions
Bot
force-pushed
the
changeset-release/main
branch
from
October 10, 2026 20:39
b9f68bf to
c78f679
Compare
systemfsoftware-maker
added a commit
that referenced
this pull request
Oct 11, 2026
…ine from the version commit The contract version law read its released documents from the stryker-published tarballs, so release #267 (cli-contract 0.5.0) turned main red with stale-baseline until the pin moved by hand (run 38091732019). The law now reads the released documents from git, at the version commit: the newest first-parent commit of the merge base that changes the line "version": "<main version>" in the package manifest, accepted only when its parent holds no manifest or a lower version. Released and main versions come from one commit, so stale-baseline can no longer fire and is removed (R-BASE ruling OQ1). Every case where that commit cannot be established refuses with released-baseline-unavailable, a cause (no-version-commit, history-truncated, version-unchanged, version-rollback) and the next action. Plan: docs/plans/2026-10-11-0045-fix-law-baseline-from-version-commit-plan.md (U1).
ryanleecode
pushed a commit
that referenced
this pull request
Oct 11, 2026
Main's check failed the contract-version law with stale-baseline: the 0.5.0 release (#267, 9e81669) declares cli-contract 0.5.0 while the released documents still came from 0.4.0 (stryker-published at 27b1075, v18.1.0). The stryker-published flake input moves to 9e81669, the commit every release tag of that release points to (stryker-js v19.0.0, cli-contract v0.5.0, plugin-interface v16.0.0, ...). flake.lock moves only that input and its own subtree. pnpm-lock.yaml takes the new tarballs' integrity. The CLI's npm-alias override key follows the 19.0.0 manifest, which asks for the runner as npm:@systemfsoftware/stryker-js-vitest-runner@^9.0.2; with the old ^9.0.1 key pnpm went to the registry, where 9.x does not exist. Every released package still resolves from .sfs-deps. The law and its tests are unchanged.
ryanleecode
pushed a commit
that referenced
this pull request
Oct 11, 2026
…ne from the version commit (#302) * docs(plans): read the contract law's released baseline from the version commit Step B of R-BASE (it_bdfe56a0): a release left main red on the contract-version law (run 38091732019, stale-baseline 0.4.0 vs 0.5.0) because the released side came from the stryker-published pin. The plan reads it from git at the commit that set main's version, refuses when that commit cannot be established (shallow history, rollback), and removes the released-* aliases. Stops at the plan for the supervisor's ruling. * test(stryker-js-cli-contract): read the contract law's released baseline from the version commit The contract version law read its released documents from the stryker-published tarballs, so release #267 (cli-contract 0.5.0) turned main red with stale-baseline until the pin moved by hand (run 38091732019). The law now reads the released documents from git, at the version commit: the newest first-parent commit of the merge base that changes the line "version": "<main version>" in the package manifest, accepted only when its parent holds no manifest or a lower version. Released and main versions come from one commit, so stale-baseline can no longer fire and is removed (R-BASE ruling OQ1). Every case where that commit cannot be established refuses with released-baseline-unavailable, a cause (no-version-commit, history-truncated, version-unchanged, version-rollback) and the next action. Plan: docs/plans/2026-10-11-0045-fix-law-baseline-from-version-commit-plan.md (U1). * build(stryker-js-cli-contract): drop the released-* development aliases The contract version law reads its released baseline from the version commit (previous commit), so nothing installs the stryker-published tarballs into cli-contract any more. The two aliases leave package.json and pnpm-lock.yaml. The packed manifest changes, so a patch changeset records it (R-BASE ruling on U2). Mutation keeps installing the pinned tarballs from .sfs-deps (START-6 unchanged). Plan: docs/plans/2026-10-11-0045-fix-law-baseline-from-version-commit-plan.md (U2). * docs(solutions): the contract law reads its baseline from the version commit Rename released-baseline-law-refuses-a-lagging-pin.md to contract-law-reads-its-baseline-from-the-version-commit.md and rewrite it for the version-commit baseline: the lookup and its refusal causes, why -S on the exact version line and not -G (measured cold in CI's fetch shape), the shallow-history refusal, and the OQ3 assumption that the version commit is released before its tag exists (R-BASE ruling). The doc no longer tells a reader to move the stryker-published pin to fix a stale baseline. Plan: docs/plans/2026-10-11-0045-fix-law-baseline-from-version-commit-plan.md (U3). * refactor(stryker-js-cli-contract): share the version decode and type the refusal cause in the law test ce-simplify-code pass on the version-commit law (plan U1): one versionFrom decode-or-die for the working-tree and git reads, readVersionCommitParent takes the one parent it reads, and the refusal outline's rows are typed by ReleasedBaselineCause so a misspelled cause fails tsc. * docs(solutions): name the old refusal without the removed failure kind Definition of Done for the version-commit plan: no stale-baseline reference outside CHANGELOGs and dated plans. * test(stryker-js-cli-contract): split the baseline refusal into tagged variants and check the candidate's manifest Review rulings on fix/release-moves-baseline: - #2 (CONST-D2): released-baseline-unavailable carried four outcomes in a string cause. Each is now its own Schema.TaggedError (NoVersionCommit, HistoryTruncated, VersionUnchanged, VersionRollback) with its stable code and next action; the refusal outline asserts the _tag. - #3: a candidate is the baseline only if its own manifest declares main's version. The bare-key search ("version": alone) finds the commit that added each manifest (6f8a052 at 0.1.0, 54e1272 at 4.0.0), whose parent holds no manifest, so it used to be adopted. On a full clone that probe now turns the live scenario red with no-version-commit for both packages. - #4: live scenarios clone a temporary repository at depth 1 and 2 and assert HistoryTruncated; a full history whose root commit set the version adopts that root commit. The solution doc states the variants, the candidate check, and that a truncated history refuses, never passes; CI check unshallows, so CI never reaches that refusal.
ryanleecode
added a commit
that referenced
this pull request
Oct 12, 2026
…gate wrong NotAssignable in the parity lane (#277) * docs(brainstorms): requirements for the TypeScript 7 native checker unit Gap table against origin/main, measured updateSnapshot cost on the TS 7.0.2 unstable/async API, the edit-location soundness rule for the importer re-check shortcut, the CI verdict-parity design, the api-extractor TS5 ruling, and six open questions for the supervisor. * docs(plans): implementation plan for the TypeScript 7 native checker unit Twelve stacked units: per-produce diagnostic rendering, snapshot/emit counters, the edit-location Soundness Rule shortcut, file-bounded rounds, one emit build per call, a sharded checker-parity CI lane against the merge-base checker, and the B10 type-query port for Stream I. Report-only ce-doc-review findings with proposed dispositions appended. * docs(plans): apply review dispositions and the four-layer stack Applies every document-review disposition the supervisor ruled in .omp-brief/rulings-plan.md (F1-F11, F13-F18; F13 and F17 narrowed; F12 rejected) and records OQ-P1..OQ-P10 as ruled. Replaces the nine-layer sequencing with four linear layers: docs+U1-U3, lane U8-U10, type query U11-U12, shortcut U4-U7. * docs(stryker-js-typescript-checker): state the TypeScript 7 floor and correct the 8.1.0 importer note U1 of docs/plans/2026-10-09-2106-perf-typescript-checker-tsgo-native-plan.md (R11). README prerequisites match minimumTypeScriptVersion; the changeset corrects the released importer re-check claim without rewriting CHANGELOG.md. * docs(plans): record the root rulings on OQ-P5, OQ-P8 and OQ-P9 * docs(plans): record the agentic-quality standing rule in R9, U9, U10 and U12 * fix(stryker-js-typescript-checker): render each mutant's diagnostics against its own text U2 of the TS7 checker plan (R14, KTD3). checkOne renders its mutant's diagnostics while that mutant's snapshot is current; MutantCheck carries rendered lines and Checker.cell no longer renders after the call. AE10 (per-mutant-position fixture) fails on the old code (line 16 instead of 20). The join property targets dogfood mutant 47892392410b616f. * feat(stryker-js-typescript-checker): report snapshot updates, re-splices and emit builds per check U3 of the TS7 checker plan (R3, KTD6). A per-call tally on the compiler runtime counts snapshot updates, parenthesized re-splices and TCE emit builds and sums emit time; check annotates them on typescript-checker.compiler.check with typescript.counts.schema_version 1. No new span names. No package test (Test Admission refused); the parity lane's zero-updates refusal catches a tally that is never set. * test(checker-parity): add an isolatedDeclarations corpus fixture U8 of the TS7 checker plan (R6, B1). A four-file project with isolatedDeclarations: true whose exported functions carry explicit return types and call each other across files, so the edit-location shortcut has a non-empty proof set on the parity lane. tsc -p (TS 7.0.2) passes; an unannotated export fails with TS9013. * feat(checker-parity): add the parity driver that runs one checker side and compares sides U9 of the TS7 checker plan (R3, R6-R10, KTD5, KTD6, KTD8, KTD9). Private package @systemfsoftware/stryker-checker-parity: 'run' discovers the R6 corpus, instruments a shard of it with the branch instrumenter, drives main's and the branch's checker worker over Plugin.CheckerRpcs, collects counts from the check spans through a local OTLP/HTTP receiver and writes versioned NDJSON; 'compare' merges every shard and fails on a verdict difference, a missing shard, or (when on) the shortcut-count and speed gates. Every record and failure carries schemaVersion 1, a closed reason code and a next action. The package's lint debt is cleared in the next commit; switch flags (--allow-local, --shortcut-gate, --speed-gate) no longer swallow the next positional. * ci: add the checker-parity lane U10 of the TS7 checker plan (R7, R9, KTD5, KTD8). checker-parity-changes decides whether a pull request reaches the corpus (every push to main runs); six checker-parity legs build main's checker at the merge-base and this commit's checker, then run both over one shard; checker-parity-report merges the shards and fails on any verdict difference or a missing shard. Every failure writes a ::error annotation and a step summary naming the reason code and the artifact to read. The shortcut-count and speed gates stay off until layer 4. * test(stryker-js-typescript-checker): clear the two lint errors layer 1 added The per-mutant-position fixture's padding variable is now _b (oxlint no-unused-vars); lines and the shift mutant's body span are unchanged. The reason-format law is deleted: the OwnRenderedDiagnostics law already pins every mutant's reason over the full CheckMutantsInput schema, and the duplicate declared a module-scope schema in a test file (schema-declaration-location). * build(checker-parity): satisfy the repo's source-resolution gritlint pack The isolatedDeclarations fixture moves to __fixtures__/, which gritlint and the package's oxlint config ignore: it is corpus input with its own standalone tsconfig, not package source. The driver's export map is now generated by tsdown through @systemfsoftware/tsdown-config's sourceExports, like every other tsdown package, so publishConfig.exports exists and carries no source condition. * refactor(checker-parity): clear the pure core's lint debt compare-sides.workflow.ts combines conditions with effect/Boolean every/some (complexity 1) and indexes verdicts and skipped projects through HashMap, keeping last-wins values and first-seen order. Parity.schema.ts exports schemas only; the shell applies S.fromJsonString(ParityLine) and Shard at its boundary. shard.ts hashes with @noble/hashes and offers dual signatures. The property test draws the production VerdictStatus schema. shard.property.test.ts is deleted: inShard is defined through shardOf, so its two partition laws restated the implementation, and its decode law copied the Shard refinement as its oracle. * refactor(checker-parity): run the driver shell on Effect platform services The imperative shell (run-side.ts, main.ts, otlp-receiver.ts, corpus.ts) now reads env through Config, files through FileSystem/Path, children through ChildProcessSpawner, serves OTLP on effect/http, and parses argv with effect/cli. Exit codes are mapped once at the runMain edge: 0 for parity or --help, 1 for a parity break, 2 for a driver failure. OTLP and shell-failure schemas move to Otlp.schema.ts and Shell.schema.ts; span counting is the pure span-counts.ts. compare-sides: normalising a reason now replaces the own-file position with a replacer function, so a file name holding a String.replace pattern ($&, $1) stays literal. The OwnFilePositionOnly law found it with the file name "\0\0$&". The three src/__tests__ files tested the hand-rolled parser, receiver and corpus helpers this replaces, and the test-layout rules forbid them there; their behaviour is internal to a private CLI whose only surface is the bin, so they are deleted, not moved. * ci(checker-parity): fail the lane report when change detection fails checker-parity-report ran only after checker-parity-changes succeeded, so a failed change detection skipped the report and the required context read as passing. The report now always runs, fails with code shard-incomplete when change detection failed or was cancelled, and still passes when the bot-push guard skipped it. * fix(checker-parity): key each side's verdict cache on its worker bundle A cached verdict is reused only when the stored identity (hash of the worker bundle directory that actually runs, the checker's program digest, and the mutant wires) equals the current one. Reuse is decided by the pure reuse-cached-verdicts workflow; the identity is a sidecar written after the verdicts, so a torn write re-checks. Replaces the hash of the checker's src tree, which missed bundled dependencies (plugin-interface, cli-contract, effect-cell-types). * fix(checker-parity): trigger the lane from the pnpm dependency closure The trigger subcommand runs the pure trigger-parity-lane workflow over the workspace closure pnpm reports for the checker and the driver, plus shared inputs (lockfile, workspace manifest, flake.lock, ci.yml, e2e testResources). Replaces the hand-maintained path regex in ci.yml. * fix(checker-parity): decide exit code, annotations and summary in the pure core report-parity-outcome maps a compare result or driver failure to exit code (0 holds, 1 broken, 2 driver failure), one ::error per violation kind, and the step summary; property laws pin each. ci.yml calls the trigger subcommand and drops the removed --main-source/--branch-source flags. * fix(checker-parity): read checker answers as CheckAnswer after the ignore-rule change Main (#262) widened the check RPC to CheckAnswer, whose ignored reason is unknown. The driver records it as the reason text so ignored verdicts still compare exactly. * fix(checker-parity): fail when no mutant was compared; resolve layer 2 review Review of #266 (ce-code-review mode:agent, 7 reviewers + validator): compare refuses a run that compared zero mutants (nothing-compared, exit 1); per-shard skips no longer list a project measured in another shard; the trigger step fails on a driver failure (pipefail); shard NDJSON is appended per project so a killed leg leaves its rows; no throw in the workflow or schema files; Shell.schema.ts renamed DriverFailure.schema.ts; PhaseLine declared once; the pure-core import ban covers every pure module. * feat(stryker-js-plugin-interface): add the provisional type-query port New ./type-query entry: TypeQuery service tag and the schema-version-1 request, response, answer and refusal shapes (plan U11, OQ-P8: port only, no Layer). Refusals carry a version literal and a next action per the root standing rule. * fix(checker-parity): mutate each source only in the project that owns it A workspace package's sources appear in the program of every project that imports it, so the first lane run mutated the instrumenter's files once per importing project (html-reporter spent 790 of its 835 s per side on them) and four shards hit the 2400 s timeout. The listing now keeps only files under the project's own directory: 581 -> 306 files over four sampled projects. * feat(stryker-js-typescript-checker): type-query implementation and lane gate, work in progress Work in progress, paused by root ruling to fix the layer 2 parity lane budget. Checker and parity packages typecheck, lint and pass their unit suites locally; TypeQueryLive not yet smoke-run against tsgo; no PR. * feat(checker-parity): pull request scope, per-file verdict cache, concurrent sides Work in progress on the root parity-budget ruling: PR legs check changed-file mutants (capped per file) plus a seeded drift sample from pr-scope.json; full corpus runs on push and workflow_dispatch; verdict cache is per file and saved with if: always() under run id + attempt + shard; legs write scope-<k>.json with the wall-time split. * refactor(checker-parity): assign drift legs in a workflow and seed order in the schema file Per ADR-0001's closed taxonomy and ADR-0002: the unsuffixed select-scope.ts is gone. The drift-leg assignment is a decision, so it is the assign-drift-legs workflow (mutation-graded, laws in its own suite); the seeded order is a pure operation over ScopeSettings, so it lives in Parity.schema.ts with its law in-source, and both the scope workflow and the run-side shell call it instead of private copies. * chore(checker-parity): drop the property seed file the in-source law run left behind * fix(checker-parity): match the mutant's own file in absolute diagnostic paths The checker writes absolute paths in diagnostics while each Verdict carries the repo-relative fileName, so the OQ-P1 own-file position normalisation anchored at the start of the line never fired in CI: runs 38036865136 and 38037569511 failed on 28 and 31 position-only differences. The own file now matches as a path suffix at a path boundary; the position laws draw absolute roots, and a sibling sharing the name's suffix is still refused. * fix(checker-parity): write the leg scope file when the leg starts Per the sub-conductor's l2 budget ruling (P3): scope-<k>.json was written only after the leg's last project, so a timed-out leg's shard-timeout annotation named a file that did not exist. The leg now writes a tagged LegStarted record (shard, scope, settings, the leg's projects, discovery time) before the first project and replaces it with the tagged LegScope when it finishes. compare decodes either and refuses a started-only leg with shard-incomplete naming the unfinished project count. * fix(stryker-js-typescript-checker): refuse NotAssignable under any generic callee, pin the type query against tsgo The generic-call guard read getResolvedSignature, which tsgo 7.0.2 returns already instantiated, so id('x') under declare function id<T>(x: T): T reported no type parameters and answered "" NotAssignable to "x" although id('') compiles. The CallArgument fact is now declaredGeneric: any declared signature of the callee (getSignaturesOfType) has type parameters. This corrects plan KTD11's condition, which named the resolved signature. tests/type-query-pins.integration.test.ts runs the real TypeQueryLive against tsgo: AE12-AE14 answers, contextual types unchanged by the appended probe statements, a file ending in a line comment, AE15 (the #4804 panic file refused server-crashed while the next file is answered and a concurrent check keeps its verdicts), and unsupported-version and not-in-project refusals. tests/type-query.differential.test.ts applies every answered candidate through CheckerRuntime and fails on a NotAssignable whose verdict is not CompileError; it went red on the reintroduced generic-call bug. * test(stryker-js-plugin-interface): state the type-query request's refusal boundary The generated laws prove TypeQuery's schemas round-trip but never present a value they must refuse. The request refuses a fractional version and an empty file list; the in-source law draws the unrefined base with boundary seeds and fails when either refinement is widened (checked: S.Array for files, S.Finite for version). * fix(stryker-js-typescript-checker): answer NotAssignable only where a declared type enforces the context CI run 38040840996 failed wrong-not-assignable twice: a 'true' inside '[[..., true] as const]' returned from an arrow passed to Option.match, and a literal inside S.Literals([...]). Both sites had a contextual type that was inference, not a constraint, so the mutant compiled. The KTD11 condition 'not an argument of an overloaded or generic call' only looked at the immediate parent. The handle now walks from the site to where its contextual type comes from (ContextOrigin): an annotated declaration, assignment, satisfies or annotated return is DeclaredContext; a call argument carries its callee's signature facts; anything else is UnenforcedContext, answered Unknown 'context-not-enforced' (new UnknownReason, last in KTD11 order). The walk climbs parentheses, array/object literals, spreads, conditional branches, ??/||/&&, 'as const' and unannotated arrow/function bodies. Plain 'as T' / <T> assertions stop as unenforced. Pins and the differential spec gain the two CI shapes plus a plain assertion, an annotated array element and a const-asserted tuple under an annotation; both were red against the previous handle. The lane's unknown-reason counts are now keyed by the port's UnknownReason. Probed with the built TypeQueryLive on the two CI sites: both now Unknown overloaded-or-generic-call. * fix(stryker-js-typescript-checker): resolve layer-3 review findings ce-code-review (report-only, base stryker/tsgo-checker-parity-lane) on ddda557; validator confirmed 13 of 14. - project-open-failed: a tsconfig tsgo cannot open used to refuse every file not-in-project (wrong next action); openServer now refuses the request project-open-failed when the snapshot holds no project for the tsconfig. Pinned (red before). - Handler reasons: a separate type-query-reasons fixture drives no-contextual-type, site-not-expression, error-type, instantiable-target and site-not-found through the real handler, plus a second answered file on the same server. - Differential oracle now fails when the query refuses the fixture instead of passing vacuously. - Probe slots are taken from the text being appended, not re-derived from the join layout. - answerTypeQuery decides its own AnswerDecision classes (shared type id, as check-mutants does); the handle maps them to the port's TypeAnswer. The constructor shim answer-type-query.schema.ts is gone: it exported functions from a *.schema.ts. - Harness schemas moved to tests/__fixtures__/*-law.fixture.ts (schema-declaration-location). - classify-candidate law renders the Parenthesized case as '(x)', not 'x)'. - checker-parity: the type-query driver moved out of run-side.ts into type-query-side.ts (run-side 1412 -> 1210 lines); TypeQueryRefusalReason is the union of the port's two refusal schemas. * feat(checker-parity): report each project's type-query answers, reasons and refusals Sub-conductor ruling 1 on #277: R18 stays (a project with no answers passes), and the step summary and summary JSON must show a dark project. - TypeQueryProjectShare gains unknownReasons (keyed by the port's UnknownReason), refusedFiles and refusedMutants. - The step summary gains a type-query line and one line per project: answered, NotAssignable, Unknown with its non-zero reasons, refused files and mutants, and 'no answers' when nothing was answered. - The type-query count schemas are NonNegativeInt (exported from Parity.schema.ts): the new summary law drew a negative Unknown count, a state no run can produce. - Plan U11: TypeQueryLive is a fixed Layer (ruling 2), amended in place as the ruling directs. * fix(checker-parity): make every full-corpus attempt advance Sub-conductor ruling on dispatch 38039482257 (legs 3 and 4 made no progress over two attempts). Evidence: both stuck legs stopped after packages/stryker-js-html-reporter in corpus order, so they sat in packages/stryker-js-instrumenter, which leg 1 measured at 1066 s for 309 mutants on main's side; the per-file verdict cache never saved a file that never finished. scope-3/4.json were missing because the dispatched head 819d513 predates LegStarted (d5007c5). - Cache slot is one check batch (<= 16 mutants of one file), stored when the batch finishes. - run --deadline: no batch starts after it, a running batch is interrupted at it; deferred work is written as Deferred lines and the leg exits 2 with code leg-deadline, so the rerun resumes at the first unchecked batch. - A batch interrupted on two attempts in a row is cached as UnitOverBudget and compare fails with unit-over-budget naming side, file and mutant ids. - Full-corpus legs are balanced by measured file cost (greedy LPT over test/checker-parity/file-costs.json, seeded from run 38039482257: 165 files, 6158 s); compare --costs-out writes the merged table. - Every batch logs a heartbeat line (elapsed, side, project, file, batch); the step summary names the leg's planned load. - CI passes --deadline (2160 s full, 210 s PR) inside the unchanged step budgets; the failure-pointer step defers to the driver's own annotation on exit 2. * feat(stryker-js-typescript-checker): typeQuery RPC, capability and function-body sites (WIP) Parked per sub-conductor ruling 2026-10-10 09:58 (#266 convergence round 2 first). Not yet reviewed; package gates pass locally: checker 39 files/255 tests, parity 17/245, plugin-interface laws 48. * fix(checker-parity): plan full-corpus legs from measured per-file cost Per sub-conductor ruling 2026-10-10 09:58 (#266 convergence round 2). The plan step packs contiguous 64-mutant blocks of each file into legs by first-fit-decreasing at 70% of the leg deadline, using a cost table merged from every finished run's leg artifacts (cache, bootstrapped from the newest earlier run on a miss). Matrix width comes from total measured cost. Verdict caches merge across legs. Replaces the hand-edited file-costs.json and the hash balancer. * fix(stryker-js-typescript-checker): name the served type-query versions from the schema The unsupported-version refusal referenced a deleted constant and threw instead of refusing; a version 3 request now gets the refusal, pinned in type-query-pins. typeQueryServingOf moves out of TypeQuery.schema.ts into type-query-serving.ts with a pipeable signature; TYPE_QUERY_VERSIONS is gone (TypeQueryVersion.literals). Plugin-interface oxlint and lint:tsgo now exit 0. * refactor(checker-parity): hold blocks.ts and file-costs.ts to the pure core Both are effect-only pure modules, so they join pureCore in oxlint.config.ts (the package's convention for unsuffixed pure modules, beside shard.ts, span-counts.ts, corpus.ts); plan-legs.defaults.ts carried a suffix outside any convention and its one constant moves to the --fill flag default. * fix(checker-parity): plan from every run's measurements, conservatively Sub-conductor ruling 2026-10-10 10:58 (#266): the proof dispatch 38061043178 bootstrapped its costs from one PR-scope run (31 files) and planned 5 legs. Every FileRate and ProjectOverhead now carries the run that measured it, and mergeCosts keeps the newest measurement per file, so fold order is irrelevant and a narrower run never drops files it did not touch (cost table v3, cache prefix checker-parity-costs-v3-). The plan job folds every recent completed ci.yml run with leg artifacts (workflow_dispatch, push to main, this branch; every attempt) and saves the folded table for the report job. An unmeasured file is estimated at its project's p90 per-mutant rate, else the corpus p90. Legs are a greedy contiguous segmentation of the (project, file, block) sequence, so adding a file never lowers the width; full-corpus plans use 16-mutant blocks. The step summary shows width, each leg's estimate against the 70% budget, and measured vs estimated files. * fix(stryker-js-cli-contract): name the rpc.typeQuery span The checker RPC group gained typeQuery, and the span-taxonomy law (every protocol method has an rpc.<method> span) refuted it. Review finding (learnings, api-contract, adversarial; P1). * refactor(stryker-js-plugin-interface)!: serve the type-query port as the root TypeQuery namespace The ./type-query subpath is gone; every caller imports the TypeQuery namespace from the package root, the way Checker and Plugin are reached. The checker RPC group carries the type-query classes, and a consumer's declaration emit can name them only through a root export. * fix(stryker-js): build against the checker RPC group with typeQuery The engine's checker handle now names only the RPCs it calls (check, group, digest), so its declaration no longer needs the type-query answer classes (TS2883). The in-repo fake checkers answer capabilities with no type-query versions and refuse every typeQuery with unsupported-version. The RPC changeset is major: every checker worker must answer the two new RPCs (BREAK-1). Review findings: learnings/api-contract P1, changeset bump P2. * fix(stryker-js-typescript-checker): serialize type queries per tsgo server Two queries on one tsconfig interleaved the probe update and the reads: query 1 read a program holding query 2's probe text and answered candidate-not-found. Each server now has a Semaphore(1) held for the whole per-file probe; a fiber that waited on a server another fiber discarded takes the current one; a crash discards only its own server; close is bounded by a 1 s grace like the checker's. Regression: metamorphic relation in type-query.differential.test.ts (concurrent per-site queries answer as the same queries one at a time) failed before (seed 767247522: Unknown candidate-not-found vs overloaded-or-generic-call) and passes after. Review #277 P2 rows 4 and 5. * test(checker-parity): pin the type-query recall counters compileErrorTotal counts branch-side compileError verdicts only, and compileErrorAnsweredNotAssignable counts those whose own project answered NotAssignable. The law fails when the side filter is dropped or the project join is reduced to the mutant id. Review #277 P2 row 6. * test(checker-parity): draw the recall law's answer split in the generator 745bce9 failed oxlint (no-nested-quantification). The law now draws mutants answered NotAssignable and mutants answered Assignable, Unknown or nothing as two arrays, builds lines in a module helper, and counts branch compileErrors per group. It still fails when the side filter is dropped or the project join is reduced to the mutant id. * refactor(checker-parity): type the function-body mutator name against the stock catalog A renamed stock mutator now fails to compile (TS2820) instead of silently turning every function-body site into an expression site. Review #277 P3 row 10. * test(stryker-js-typescript-checker): assert the served type-query version The worker scenario now carries the version out of each serving answer and checks versions 1 and 2, served and refused. It fails when typeQueryServingOf answers the wrong version. Review #277 P3 row 11. * refactor(stryker-js-plugin-interface)!: require TypeQuerySite.kind A site's kind was an optional key whose absence meant expression, resolved at a distance in the checker (site.kind ?? 'expression') and written by presence in the lane. Every site now names its kind; the checker reads it directly and the lane writes it for every site. Review #277 P3 row 9 (schema-laws: tagged unions over state by presence). * chore(changeset): record the stryker-js RPC-group touch as a none intent #267's release consumed the intents that covered stryker-js's Checker.handle.ts typing change (a436092), so Changeset Check on 6f99d56 (run 38092173764) found stryker-js moved with no intent. The change is type-only: the engine's client is narrowed to check, group and digest. --------- Co-authored-by: Ryan Lee <drdgvhbh@gmail.com>
ryanleecode
added a commit
that referenced
this pull request
Oct 13, 2026
…s inside function bodies (#286) * docs(brainstorms): requirements for the TypeScript 7 native checker unit Gap table against origin/main, measured updateSnapshot cost on the TS 7.0.2 unstable/async API, the edit-location soundness rule for the importer re-check shortcut, the CI verdict-parity design, the api-extractor TS5 ruling, and six open questions for the supervisor. * docs(plans): implementation plan for the TypeScript 7 native checker unit Twelve stacked units: per-produce diagnostic rendering, snapshot/emit counters, the edit-location Soundness Rule shortcut, file-bounded rounds, one emit build per call, a sharded checker-parity CI lane against the merge-base checker, and the B10 type-query port for Stream I. Report-only ce-doc-review findings with proposed dispositions appended. * docs(plans): apply review dispositions and the four-layer stack Applies every document-review disposition the supervisor ruled in .omp-brief/rulings-plan.md (F1-F11, F13-F18; F13 and F17 narrowed; F12 rejected) and records OQ-P1..OQ-P10 as ruled. Replaces the nine-layer sequencing with four linear layers: docs+U1-U3, lane U8-U10, type query U11-U12, shortcut U4-U7. * docs(stryker-js-typescript-checker): state the TypeScript 7 floor and correct the 8.1.0 importer note U1 of docs/plans/2026-10-09-2106-perf-typescript-checker-tsgo-native-plan.md (R11). README prerequisites match minimumTypeScriptVersion; the changeset corrects the released importer re-check claim without rewriting CHANGELOG.md. * docs(plans): record the root rulings on OQ-P5, OQ-P8 and OQ-P9 * docs(plans): record the agentic-quality standing rule in R9, U9, U10 and U12 * fix(stryker-js-typescript-checker): render each mutant's diagnostics against its own text U2 of the TS7 checker plan (R14, KTD3). checkOne renders its mutant's diagnostics while that mutant's snapshot is current; MutantCheck carries rendered lines and Checker.cell no longer renders after the call. AE10 (per-mutant-position fixture) fails on the old code (line 16 instead of 20). The join property targets dogfood mutant 47892392410b616f. * feat(stryker-js-typescript-checker): report snapshot updates, re-splices and emit builds per check U3 of the TS7 checker plan (R3, KTD6). A per-call tally on the compiler runtime counts snapshot updates, parenthesized re-splices and TCE emit builds and sums emit time; check annotates them on typescript-checker.compiler.check with typescript.counts.schema_version 1. No new span names. No package test (Test Admission refused); the parity lane's zero-updates refusal catches a tally that is never set. * test(checker-parity): add an isolatedDeclarations corpus fixture U8 of the TS7 checker plan (R6, B1). A four-file project with isolatedDeclarations: true whose exported functions carry explicit return types and call each other across files, so the edit-location shortcut has a non-empty proof set on the parity lane. tsc -p (TS 7.0.2) passes; an unannotated export fails with TS9013. * feat(checker-parity): add the parity driver that runs one checker side and compares sides U9 of the TS7 checker plan (R3, R6-R10, KTD5, KTD6, KTD8, KTD9). Private package @systemfsoftware/stryker-checker-parity: 'run' discovers the R6 corpus, instruments a shard of it with the branch instrumenter, drives main's and the branch's checker worker over Plugin.CheckerRpcs, collects counts from the check spans through a local OTLP/HTTP receiver and writes versioned NDJSON; 'compare' merges every shard and fails on a verdict difference, a missing shard, or (when on) the shortcut-count and speed gates. Every record and failure carries schemaVersion 1, a closed reason code and a next action. The package's lint debt is cleared in the next commit; switch flags (--allow-local, --shortcut-gate, --speed-gate) no longer swallow the next positional. * ci: add the checker-parity lane U10 of the TS7 checker plan (R7, R9, KTD5, KTD8). checker-parity-changes decides whether a pull request reaches the corpus (every push to main runs); six checker-parity legs build main's checker at the merge-base and this commit's checker, then run both over one shard; checker-parity-report merges the shards and fails on any verdict difference or a missing shard. Every failure writes a ::error annotation and a step summary naming the reason code and the artifact to read. The shortcut-count and speed gates stay off until layer 4. * test(stryker-js-typescript-checker): clear the two lint errors layer 1 added The per-mutant-position fixture's padding variable is now _b (oxlint no-unused-vars); lines and the shift mutant's body span are unchanged. The reason-format law is deleted: the OwnRenderedDiagnostics law already pins every mutant's reason over the full CheckMutantsInput schema, and the duplicate declared a module-scope schema in a test file (schema-declaration-location). * build(checker-parity): satisfy the repo's source-resolution gritlint pack The isolatedDeclarations fixture moves to __fixtures__/, which gritlint and the package's oxlint config ignore: it is corpus input with its own standalone tsconfig, not package source. The driver's export map is now generated by tsdown through @systemfsoftware/tsdown-config's sourceExports, like every other tsdown package, so publishConfig.exports exists and carries no source condition. * refactor(checker-parity): clear the pure core's lint debt compare-sides.workflow.ts combines conditions with effect/Boolean every/some (complexity 1) and indexes verdicts and skipped projects through HashMap, keeping last-wins values and first-seen order. Parity.schema.ts exports schemas only; the shell applies S.fromJsonString(ParityLine) and Shard at its boundary. shard.ts hashes with @noble/hashes and offers dual signatures. The property test draws the production VerdictStatus schema. shard.property.test.ts is deleted: inShard is defined through shardOf, so its two partition laws restated the implementation, and its decode law copied the Shard refinement as its oracle. * refactor(checker-parity): run the driver shell on Effect platform services The imperative shell (run-side.ts, main.ts, otlp-receiver.ts, corpus.ts) now reads env through Config, files through FileSystem/Path, children through ChildProcessSpawner, serves OTLP on effect/http, and parses argv with effect/cli. Exit codes are mapped once at the runMain edge: 0 for parity or --help, 1 for a parity break, 2 for a driver failure. OTLP and shell-failure schemas move to Otlp.schema.ts and Shell.schema.ts; span counting is the pure span-counts.ts. compare-sides: normalising a reason now replaces the own-file position with a replacer function, so a file name holding a String.replace pattern ($&, $1) stays literal. The OwnFilePositionOnly law found it with the file name "\0\0$&". The three src/__tests__ files tested the hand-rolled parser, receiver and corpus helpers this replaces, and the test-layout rules forbid them there; their behaviour is internal to a private CLI whose only surface is the bin, so they are deleted, not moved. * ci(checker-parity): fail the lane report when change detection fails checker-parity-report ran only after checker-parity-changes succeeded, so a failed change detection skipped the report and the required context read as passing. The report now always runs, fails with code shard-incomplete when change detection failed or was cancelled, and still passes when the bot-push guard skipped it. * fix(checker-parity): key each side's verdict cache on its worker bundle A cached verdict is reused only when the stored identity (hash of the worker bundle directory that actually runs, the checker's program digest, and the mutant wires) equals the current one. Reuse is decided by the pure reuse-cached-verdicts workflow; the identity is a sidecar written after the verdicts, so a torn write re-checks. Replaces the hash of the checker's src tree, which missed bundled dependencies (plugin-interface, cli-contract, effect-cell-types). * fix(checker-parity): trigger the lane from the pnpm dependency closure The trigger subcommand runs the pure trigger-parity-lane workflow over the workspace closure pnpm reports for the checker and the driver, plus shared inputs (lockfile, workspace manifest, flake.lock, ci.yml, e2e testResources). Replaces the hand-maintained path regex in ci.yml. * fix(checker-parity): decide exit code, annotations and summary in the pure core report-parity-outcome maps a compare result or driver failure to exit code (0 holds, 1 broken, 2 driver failure), one ::error per violation kind, and the step summary; property laws pin each. ci.yml calls the trigger subcommand and drops the removed --main-source/--branch-source flags. * fix(checker-parity): read checker answers as CheckAnswer after the ignore-rule change Main (#262) widened the check RPC to CheckAnswer, whose ignored reason is unknown. The driver records it as the reason text so ignored verdicts still compare exactly. * fix(checker-parity): fail when no mutant was compared; resolve layer 2 review Review of #266 (ce-code-review mode:agent, 7 reviewers + validator): compare refuses a run that compared zero mutants (nothing-compared, exit 1); per-shard skips no longer list a project measured in another shard; the trigger step fails on a driver failure (pipefail); shard NDJSON is appended per project so a killed leg leaves its rows; no throw in the workflow or schema files; Shell.schema.ts renamed DriverFailure.schema.ts; PhaseLine declared once; the pure-core import ban covers every pure module. * feat(stryker-js-plugin-interface): add the provisional type-query port New ./type-query entry: TypeQuery service tag and the schema-version-1 request, response, answer and refusal shapes (plan U11, OQ-P8: port only, no Layer). Refusals carry a version literal and a next action per the root standing rule. * fix(checker-parity): mutate each source only in the project that owns it A workspace package's sources appear in the program of every project that imports it, so the first lane run mutated the instrumenter's files once per importing project (html-reporter spent 790 of its 835 s per side on them) and four shards hit the 2400 s timeout. The listing now keeps only files under the project's own directory: 581 -> 306 files over four sampled projects. * feat(stryker-js-typescript-checker): type-query implementation and lane gate, work in progress Work in progress, paused by root ruling to fix the layer 2 parity lane budget. Checker and parity packages typecheck, lint and pass their unit suites locally; TypeQueryLive not yet smoke-run against tsgo; no PR. * feat(checker-parity): pull request scope, per-file verdict cache, concurrent sides Work in progress on the root parity-budget ruling: PR legs check changed-file mutants (capped per file) plus a seeded drift sample from pr-scope.json; full corpus runs on push and workflow_dispatch; verdict cache is per file and saved with if: always() under run id + attempt + shard; legs write scope-<k>.json with the wall-time split. * refactor(checker-parity): assign drift legs in a workflow and seed order in the schema file Per ADR-0001's closed taxonomy and ADR-0002: the unsuffixed select-scope.ts is gone. The drift-leg assignment is a decision, so it is the assign-drift-legs workflow (mutation-graded, laws in its own suite); the seeded order is a pure operation over ScopeSettings, so it lives in Parity.schema.ts with its law in-source, and both the scope workflow and the run-side shell call it instead of private copies. * chore(checker-parity): drop the property seed file the in-source law run left behind * fix(checker-parity): match the mutant's own file in absolute diagnostic paths The checker writes absolute paths in diagnostics while each Verdict carries the repo-relative fileName, so the OQ-P1 own-file position normalisation anchored at the start of the line never fired in CI: runs 38036865136 and 38037569511 failed on 28 and 31 position-only differences. The own file now matches as a path suffix at a path boundary; the position laws draw absolute roots, and a sibling sharing the name's suffix is still refused. * fix(checker-parity): write the leg scope file when the leg starts Per the sub-conductor's l2 budget ruling (P3): scope-<k>.json was written only after the leg's last project, so a timed-out leg's shard-timeout annotation named a file that did not exist. The leg now writes a tagged LegStarted record (shard, scope, settings, the leg's projects, discovery time) before the first project and replaces it with the tagged LegScope when it finishes. compare decodes either and refuses a started-only leg with shard-incomplete naming the unfinished project count. * fix(stryker-js-typescript-checker): refuse NotAssignable under any generic callee, pin the type query against tsgo The generic-call guard read getResolvedSignature, which tsgo 7.0.2 returns already instantiated, so id('x') under declare function id<T>(x: T): T reported no type parameters and answered "" NotAssignable to "x" although id('') compiles. The CallArgument fact is now declaredGeneric: any declared signature of the callee (getSignaturesOfType) has type parameters. This corrects plan KTD11's condition, which named the resolved signature. tests/type-query-pins.integration.test.ts runs the real TypeQueryLive against tsgo: AE12-AE14 answers, contextual types unchanged by the appended probe statements, a file ending in a line comment, AE15 (the #4804 panic file refused server-crashed while the next file is answered and a concurrent check keeps its verdicts), and unsupported-version and not-in-project refusals. tests/type-query.differential.test.ts applies every answered candidate through CheckerRuntime and fails on a NotAssignable whose verdict is not CompileError; it went red on the reintroduced generic-call bug. * test(stryker-js-plugin-interface): state the type-query request's refusal boundary The generated laws prove TypeQuery's schemas round-trip but never present a value they must refuse. The request refuses a fractional version and an empty file list; the in-source law draws the unrefined base with boundary seeds and fails when either refinement is widened (checked: S.Array for files, S.Finite for version). * fix(stryker-js-typescript-checker): answer NotAssignable only where a declared type enforces the context CI run 38040840996 failed wrong-not-assignable twice: a 'true' inside '[[..., true] as const]' returned from an arrow passed to Option.match, and a literal inside S.Literals([...]). Both sites had a contextual type that was inference, not a constraint, so the mutant compiled. The KTD11 condition 'not an argument of an overloaded or generic call' only looked at the immediate parent. The handle now walks from the site to where its contextual type comes from (ContextOrigin): an annotated declaration, assignment, satisfies or annotated return is DeclaredContext; a call argument carries its callee's signature facts; anything else is UnenforcedContext, answered Unknown 'context-not-enforced' (new UnknownReason, last in KTD11 order). The walk climbs parentheses, array/object literals, spreads, conditional branches, ??/||/&&, 'as const' and unannotated arrow/function bodies. Plain 'as T' / <T> assertions stop as unenforced. Pins and the differential spec gain the two CI shapes plus a plain assertion, an annotated array element and a const-asserted tuple under an annotation; both were red against the previous handle. The lane's unknown-reason counts are now keyed by the port's UnknownReason. Probed with the built TypeQueryLive on the two CI sites: both now Unknown overloaded-or-generic-call. * fix(stryker-js-typescript-checker): resolve layer-3 review findings ce-code-review (report-only, base stryker/tsgo-checker-parity-lane) on ddda557; validator confirmed 13 of 14. - project-open-failed: a tsconfig tsgo cannot open used to refuse every file not-in-project (wrong next action); openServer now refuses the request project-open-failed when the snapshot holds no project for the tsconfig. Pinned (red before). - Handler reasons: a separate type-query-reasons fixture drives no-contextual-type, site-not-expression, error-type, instantiable-target and site-not-found through the real handler, plus a second answered file on the same server. - Differential oracle now fails when the query refuses the fixture instead of passing vacuously. - Probe slots are taken from the text being appended, not re-derived from the join layout. - answerTypeQuery decides its own AnswerDecision classes (shared type id, as check-mutants does); the handle maps them to the port's TypeAnswer. The constructor shim answer-type-query.schema.ts is gone: it exported functions from a *.schema.ts. - Harness schemas moved to tests/__fixtures__/*-law.fixture.ts (schema-declaration-location). - classify-candidate law renders the Parenthesized case as '(x)', not 'x)'. - checker-parity: the type-query driver moved out of run-side.ts into type-query-side.ts (run-side 1412 -> 1210 lines); TypeQueryRefusalReason is the union of the port's two refusal schemas. * feat(checker-parity): report each project's type-query answers, reasons and refusals Sub-conductor ruling 1 on #277: R18 stays (a project with no answers passes), and the step summary and summary JSON must show a dark project. - TypeQueryProjectShare gains unknownReasons (keyed by the port's UnknownReason), refusedFiles and refusedMutants. - The step summary gains a type-query line and one line per project: answered, NotAssignable, Unknown with its non-zero reasons, refused files and mutants, and 'no answers' when nothing was answered. - The type-query count schemas are NonNegativeInt (exported from Parity.schema.ts): the new summary law drew a negative Unknown count, a state no run can produce. - Plan U11: TypeQueryLive is a fixed Layer (ruling 2), amended in place as the ruling directs. * feat(stryker-js-typescript-checker): importer shortcut, check rounds and option (in progress) Edit-site facts, admit-importer-shortcut and plan-check-rounds workflows, the check-rounds runner over CheckSteps, the importerCheck option and a shortcut integration test that has not run yet. Paused per sub-conductor ruling to fix the layer-2 full-corpus lane first. * fix(checker-parity): make every full-corpus attempt advance Sub-conductor ruling on dispatch 38039482257 (legs 3 and 4 made no progress over two attempts). Evidence: both stuck legs stopped after packages/stryker-js-html-reporter in corpus order, so they sat in packages/stryker-js-instrumenter, which leg 1 measured at 1066 s for 309 mutants on main's side; the per-file verdict cache never saved a file that never finished. scope-3/4.json were missing because the dispatched head 819d513 predates LegStarted (d5007c5). - Cache slot is one check batch (<= 16 mutants of one file), stored when the batch finishes. - run --deadline: no batch starts after it, a running batch is interrupted at it; deferred work is written as Deferred lines and the leg exits 2 with code leg-deadline, so the rerun resumes at the first unchecked batch. - A batch interrupted on two attempts in a row is cached as UnitOverBudget and compare fails with unit-over-budget naming side, file and mutant ids. - Full-corpus legs are balanced by measured file cost (greedy LPT over test/checker-parity/file-costs.json, seeded from run 38039482257: 165 files, 6158 s); compare --costs-out writes the merged table. - Every batch logs a heartbeat line (elapsed, side, project, file, batch); the step summary names the leg's planned load. - CI passes --deadline (2160 s full, 210 s PR) inside the unchanged step budgets; the failure-pointer step defers to the driver's own annotation on exit 2. * feat(stryker-js-typescript-checker): finish the importer shortcut layer The integration test read spans after the OpenTelemetry layer shut down and reset the in-memory exporter, so every count came back empty. It now reads them inside the scope. AE7 edited the parameter, which is outside the function body, so it now edits the return statement. Checked against a planted defect: when admitImporterShortcut always takes the shortcut, AE2, AE3 and AE9 change from compileError to passed and the scenario fails. Adds the importerCheck changeset (minor). * feat(stryker-js-typescript-checker): typeQuery RPC, capability and function-body sites (WIP) Parked per sub-conductor ruling 2026-10-10 09:58 (#266 convergence round 2 first). Not yet reviewed; package gates pass locally: checker 39 files/255 tests, parity 17/245, plugin-interface laws 48. * fix(checker-parity): plan full-corpus legs from measured per-file cost Per sub-conductor ruling 2026-10-10 09:58 (#266 convergence round 2). The plan step packs contiguous 64-mutant blocks of each file into legs by first-fit-decreasing at 70% of the leg deadline, using a cost table merged from every finished run's leg artifacts (cache, bootstrapped from the newest earlier run on a miss). Matrix width comes from total measured cost. Verdict caches merge across legs. Replaces the hand-edited file-costs.json and the hash balancer. * fix(stryker-js-typescript-checker): name the served type-query versions from the schema The unsupported-version refusal referenced a deleted constant and threw instead of refusing; a version 3 request now gets the refusal, pinned in type-query-pins. typeQueryServingOf moves out of TypeQuery.schema.ts into type-query-serving.ts with a pipeable signature; TYPE_QUERY_VERSIONS is gone (TypeQueryVersion.literals). Plugin-interface oxlint and lint:tsgo now exit 0. * refactor(checker-parity): hold blocks.ts and file-costs.ts to the pure core Both are effect-only pure modules, so they join pureCore in oxlint.config.ts (the package's convention for unsuffixed pure modules, beside shard.ts, span-counts.ts, corpus.ts); plan-legs.defaults.ts carried a suffix outside any convention and its one constant moves to the --fill flag default. * fix(checker-parity): plan from every run's measurements, conservatively Sub-conductor ruling 2026-10-10 10:58 (#266): the proof dispatch 38061043178 bootstrapped its costs from one PR-scope run (31 files) and planned 5 legs. Every FileRate and ProjectOverhead now carries the run that measured it, and mergeCosts keeps the newest measurement per file, so fold order is irrelevant and a narrower run never drops files it did not touch (cost table v3, cache prefix checker-parity-costs-v3-). The plan job folds every recent completed ci.yml run with leg artifacts (workflow_dispatch, push to main, this branch; every attempt) and saves the folded table for the report job. An unmeasured file is estimated at its project's p90 per-mutant rate, else the corpus p90. Legs are a greedy contiguous segmentation of the (project, file, block) sequence, so adding a file never lowers the width; full-corpus plans use 16-mutant blocks. The step summary shows width, each leg's estimate against the 70% budget, and measured vs estimated files. * fix(stryker-js-cli-contract): name the rpc.typeQuery span The checker RPC group gained typeQuery, and the span-taxonomy law (every protocol method has an rpc.<method> span) refuted it. Review finding (learnings, api-contract, adversarial; P1). * refactor(stryker-js-plugin-interface)!: serve the type-query port as the root TypeQuery namespace The ./type-query subpath is gone; every caller imports the TypeQuery namespace from the package root, the way Checker and Plugin are reached. The checker RPC group carries the type-query classes, and a consumer's declaration emit can name them only through a root export. * fix(stryker-js): build against the checker RPC group with typeQuery The engine's checker handle now names only the RPCs it calls (check, group, digest), so its declaration no longer needs the type-query answer classes (TS2883). The in-repo fake checkers answer capabilities with no type-query versions and refuse every typeQuery with unsupported-version. The RPC changeset is major: every checker worker must answer the two new RPCs (BREAK-1). Review findings: learnings/api-contract P1, changeset bump P2. * fix(stryker-js-typescript-checker): check an edited file in the project that owns it Under project references every referencing program holds the referenced project's sources but reports no semantic errors for them. projectOfFile returned the first program holding the file, so on the enterprise fixture the importer shortcut read gates.ts diagnostics from analytics, saw none, and passed real compile errors; the TCE emit for those false passes then wrote core/src/*.js into the fixture, which turned check's oxlint red. The project whose rootFiles list the file now answers first. Regression: importer-shortcut scenario over a two-project fixture (app references lib) fails before, passes after. e2e-core placement suite 7/7 with no stray files. * fix(stryker-js-typescript-checker): serialize type queries per tsgo server Two queries on one tsconfig interleaved the probe update and the reads: query 1 read a program holding query 2's probe text and answered candidate-not-found. Each server now has a Semaphore(1) held for the whole per-file probe; a fiber that waited on a server another fiber discarded takes the current one; a crash discards only its own server; close is bounded by a 1 s grace like the checker's. Regression: metamorphic relation in type-query.differential.test.ts (concurrent per-site queries answer as the same queries one at a time) failed before (seed 767247522: Unknown candidate-not-found vs overloaded-or-generic-call) and passes after. Review #277 P2 rows 4 and 5. * test(checker-parity): pin the type-query recall counters compileErrorTotal counts branch-side compileError verdicts only, and compileErrorAnsweredNotAssignable counts those whose own project answered NotAssignable. The law fails when the side filter is dropped or the project join is reduced to the mutant id. Review #277 P2 row 6. * test(checker-parity): draw the recall law's answer split in the generator 745bce9 failed oxlint (no-nested-quantification). The law now draws mutants answered NotAssignable and mutants answered Assignable, Unknown or nothing as two arrays, builds lines in a module helper, and counts branch compileErrors per group. It still fails when the side filter is dropped or the project join is reduced to the mutant id. * refactor(checker-parity): type the function-body mutator name against the stock catalog A renamed stock mutator now fails to compile (TS2820) instead of silently turning every function-body site into an expression site. Review #277 P3 row 10. * test(stryker-js-typescript-checker): assert the served type-query version The worker scenario now carries the version out of each serving answer and checks versions 1 and 2, served and refused. It fails when typeQueryServingOf answers the wrong version. Review #277 P3 row 11. * refactor(stryker-js-plugin-interface)!: require TypeQuerySite.kind A site's kind was an optional key whose absence meant expression, resolved at a distance in the checker (site.kind ?? 'expression') and written by presence in the lane. Every site now names its kind; the checker reads it directly and the lane writes it for every site. Review #277 P3 row 9 (schema-laws: tagged unions over state by presence). * test(stryker-js-typescript-checker): refuse an unknown importerCheck value A scenario in importer-shortcut.integration.test.ts checks the annotated body edit under importerCheck 'sometimes', 1, 'always', 'location-rule' and no option, on real tsgo. An unknown value must fail the check with the invalid-checker-options refusal naming the mutant; 'always' takes no shortcut; the rule, set or defaulted, takes one. It closes the planted option-schema gap on #286: emptying the importerCheck struct lets an unknown value decode, so the check no longer refuses it. * fix(stryker-js-typescript-checker): keep the importer shortcut off a body's closing brace and off files with syntax errors Edit-site facts move into edit-site.schema.ts beside their schemas (ADR-0002); edit-site.ts is gone. The scan stops descending into nodes that end before the span, check() keeps only the last round as lastMutants, and projectsOf is no longer exported. One fixture and one scenario row per Soundness Rule guard (declare global, require, typeof import, non-module file, removed terminator, syntax error) plus shared-round solo fallback and unheld-member scenarios. Review #286 rows 2, 5, 7, 10, 11, R1, R3 and the shared-round gaps. * test(stryker-js-typescript-checker): pin one-update two-file snapshots and the importerCheck digest The U6 pin drives typescript/unstable/async over a virtual file system: one updateSnapshot serves both changed texts and re-checks a rewritten file. The digest scenario shows importerCheck moves the program digest. Review #286 gaps. * fix(checker-parity): judge speed only on projects checked fresh on both sides speedViolationsFor compares projects with a non-cached check on main and branch; with none, parity holds and warns speed-gate-unmeasured with a next action in the step summary and an Actions annotation. Review #286 row 3 and R4. * chore(changeset): record the stryker-js RPC-group touch as a none intent #267's release consumed the intents that covered stryker-js's Checker.handle.ts typing change (a436092), so Changeset Check on 6f99d56 (run 38092173764) found stryker-js moved with no intent. The change is type-only: the engine's client is narrowed to check, group and digest. * fix(checker-parity): check the isolatedDeclarations fixture on every pull-request run checker-parity-report on fef35ce (run 38092982281) failed zero-shortcuts isolatedDeclarations because no leg checked the fixture: PR scope checks changed files plus 6 seeded drift projects, the fixture ranks 22nd in the seeded order, and once #266 landed on main its files were no longer changed against the merge base. The shortcut gate judges that fixture on every run, so its files now count as changed on every pull-request leg (checkedOnEveryPullRequest in corpus.ts). No guard refused its sites. * test(checker-parity): the shortcut gate fixture takes the importer shortcut Real tsgo checks every stock mutant of the isolatedDeclarations fixture with importerCheck location-rule and always: the shortcut decides at least one mutant and every verdict matches. Planting admitImporterShortcut to refuse every site fails it (shortcutTaken false). * fix(checker-parity): keep the isolatedDeclarations fixture on every pull-request leg checker-parity-report on ad5769a (run 38097541662) still failed zero-shortcuts for isolatedDeclarations: 8e4c82b made changedOnThisLeg select every fixture file, but processProject first asks inScopeOf, which only admits a project that samples on the leg or has a changed file in its directory. The fixture did neither once main held it, so it never reached the file filter (no leg's scope-k.json lists it). One pure rule now decides both questions: checkedOnLeg and projectOnLeg in corpus.ts over a ProjectLeg schema (Parity.schema.ts), and run-side builds a ProjectLeg once per project. Laws in corpus.workflow.property.test.ts draw ProjectLeg: a fixture file checked on its shard puts the fixture project on the leg, fixture files are checked on their shard whatever the pull request changed, and an unsampled project with no change is off the leg. Removing the fixture clause from projectOnLeg (the shipped bug) or from checkedOnLeg fails them. * fix(checker-parity): count in the summary exactly the projects the speed gate compares The summary's measuredProjectCount and the main/branch timing totals used measuredOnBoth (any fresh phase line on both sides), while the speed gate compares checkedOnBoth (a fresh check call on both sides). A run could report projects as measured that the gate never compared. summaryOf now uses checkedOnBoth, and measuredOnBoth is deleted. The law SummaryMeasuredCount pins the count and totals to the compared set; it fails when the old set is restored. * refactor(stryker-js-typescript-checker): move round and shortcut decisions into their schema files Ruling 1 (ADR-0002): CheckRound.schema.ts holds SoloRound, SharedRound and roundOfLayer, which takes a non-empty layer, so an empty round cannot be built (ruling 9). ImporterShortcut.schema.ts holds the decision union with takesShortcut and fallbackKeyOf. The effect-schema lint (schema-file-exports-schemas-only) refuses the counting helper whose parameter is an array of optional decisions, so the span counts stay in check-rounds.ts. RoundCandidate ids are branded MutantId. A check call that names one mutant twice is refused by the PlanCheckRoundsCommand schema and fails with reason duplicate-mutant-ids and a next action (BREAK-1, major changeset), not a defect. The importer-shortcut feature pins the refusal. * fix(stryker-js-typescript-checker): read file facts without a fake span, and stop the edit-site scan at the edit Ruling 7: FileFacts (typescriptModule, declaresGlobal) is its own schema read by fileFactsOf, and the handle's declare-global check uses it in place of an end-of-file span. Ruling 6: the scan returns at the first node that starts after the edit, and a counter of inspected nodes is checked against an unpruned model. Ruling 2: in-file laws state that an admitted span stops before the closing brace and that widening an edit never turns a refusal into an admit. Planting the unpruned scan or a brace-inclusive editable end fails these laws. * fix(stryker-js-typescript-checker): never take the importer shortcut for a constructor body An unannotated class property takes its type from the constructor's assignments, so a constructor body edit can change what an importer sees while the edited file stays clean. Real tsgo 7.0.2: 'this.start = 1' to "'one'" leaves constructed.ts clean and fails consumer.ts with TS2322. Constructors leave SIGNATURE_FREE_KINDS; the rule scenario gains a constructor row that fails if constructors are admitted again (review cycle 3, finding 2). * refactor(stryker-js-typescript-checker): give decided one exported home check-rounds.ts and type-query.handle.ts each carried a private copy of decided; both now import the one ts-compiler.handle.ts exports (ADR-0002: each pure operation has one home; review cycle 3, finding 6). * test(stryker-js-typescript-checker): laws for the shortcut schema operations and refusals takesShortcut, fallbackKeyOf and fileFactsOf each get an in-source law (finding 3); fileFactsOf runs real tsgo over drawn extensions, export and declare-global choices. SharedRound ids, MutantBound, distinct round candidates and EditSpan state what they refuse against named domain predicates (finding 7). Each law fails on a planted change to its subject. * fix(stryker-js-typescript-checker): refuse the importer shortcut for a file another project compiles Review cycle 3, finding 5. The shortcut read the mutated file's errors from its owning project only. A second open project that includes or imports the same file checks it under its own compiler options and can reject an edit the owner accepts, so the shortcut passed a mutant that 'always' rejects. Probed on tsgo 7.0.2: a project that reaches the owner through references, directly or through another project, holds the file but reports none of its diagnostics; a project that includes or imports it without a reference path reports them under its own options. So the refusal counts only holders with no reference path to the owner. A literal "more than one holder" refusal would turn the shortcut off for every project-references monorepo. - The tsconfig walk records each config's references; otherCompilersOf (Tsconfig.schema.ts) is the pure count, with a transitive-closure law. - DecideImporterShortcutCommand carries compiledElsewhere; the new shared-file clause (tree original) reports under its own fallback key. - importer-shortcut-overlap: two referenced projects include one file, one with noImplicitReturns and one with noUncheckedIndexedAccess; each edit is rejected by one of them only, so the scenario fails whichever owns the file. Before the fix the rule run passed one edit that 'always' rejected. * test(stryker-js-typescript-checker): pin the unread fallback count for a file no project includes Review cycle 3 gap: nothing asserted typescript.importer_shortcut.fallback.unread.count. A mutant in a file the tsconfig excludes is read from disk but held by no project, so its edit-site facts are never read; both modes pass it with nothing blamed and only the rule run counts it as unread. Dropping the unread key fails the scenario. * test(stryker-js-typescript-checker): check two calls on one checker so each starts from the original files Review cycle 3 gap: no scenario sent a second check call to the same checker. The harness now runs a list of calls on one checker. The first call checks an edit that retypes a function the consumer reads; the second checks an edit in the consumer, which must pass. Skipping the reset of the previous call's last round, or leaving its files out of the snapshot refresh, fails the scenario. * fix(stryker-js-typescript-checker): give invalid options and duplicate ids their own tagged errors InvalidCheckerOptions and DuplicateMutantIds replace the two CompilerFailed reasons this layer added (CONST-D2, review round 3 P1). Each carries its code and next action in the message; the scenarios assert both. Raising the duplicate refusal as InvalidCheckerOptions fails the duplicate scenario. * refactor(stryker-js-typescript-checker): move check-rounds decisions into a workflow and schema operations Round-3 ruling Q1 step 1 and Q2. Each decision check-rounds.ts made inline now lives where the mutation set and a law reach it: - concludeSharedRound (new workflow): a shared round is kept only when every member took the shortcut, else redone one mutant at a time. - importerCheckOf / readsEditSites (Checker.schema.ts): the location-rule default and which mode reads edit sites. - shortcutCountsOf (ImporterShortcut.schema.ts): per-outcome shortcut and fallback counts, zero under 'always'. - ShortcutClause / ShortcutTree move beside the decision they tag. Planted bugs: default flipped to 'always', counts under 'always', unread folded into none each fail an in-source law; every->some in concludeSharedRound fails the shared-round integration scenario (the vacuity gate refused a redo law, so it was deleted rather than reshaped). * refactor(stryker-js-typescript-checker): run the check rounds on the compiler handle and delete check-rounds.ts Round-3 ruling Q1 step 2. With every decision moved into a workflow or schema operation (previous commit), what is left of check-rounds.ts is sequencing over the handle's lastMutants and snapshot state, so it moves back into ts-compiler.handle.ts and the unsuffixed module is deleted (no shim). check keeps its SpanTaxonomy span (typescriptCheckerCompilerCheck), as every Effect.fn in the repo names a taxonomy span. The helpers only the round loop used (CheckSteps, checkStepsOf, classifyBatchTce, annotateDiagnosticSample, CompilerState, EditTree, emptyTally, runtimeOf, TSCompilerRuntime) are no longer exported. * test(stryker-js-typescript-checker): pin the importer shortcut for a set accessor body Round-3 finding P2 (testing). stored.ts pairs an annotated getter with a set accessor; the rule scenario gains a row whose edit sits in the setter body. It takes the shortcut and gets the same verdict under both importerCheck values. Dropping SET_ACCESSOR from SIGNATURE_FREE_KINDS fails the scenario. * refactor(stryker-js-typescript-checker): group mutant ids by file in one schema operation fileIdsOf in FileIds.schema.ts groups ids by file, files in first-seen order and ids in input order. groupMutants and planCheckRounds both use it instead of two copies of the same accumulator. Law: ∀m_FileIdsOf_≡FilesInFirstSeenOrderEachHoldingItsIdsInInputOrder; planting prepend for append fails it. * refactor(stryker-js-typescript-checker): pick a mutant's diagnostics from its decision in one place decidedOutcomeOf reads own-file errors when the decision takes the importer shortcut and every program's errors otherwise; the solo path and kept shared rounds both use it. Swapping its branches fails three importer-shortcut scenarios. * fix(stryker-js-instrumenter): render an error as errno only when it names its syscall An error with a string code but no syscall (Node ERR_* errors, the checker's InvalidCheckerOptions and DuplicateMutantIds) rendered as '<name>: <code> (undefined) <stack>'. It now renders as '<name>: <message>' and its stack. Law ∀e_ErrorTextOf_≡ErrnoFormatOnlyWhenTheErrorNamesItsSyscall; the checker refusal scenarios assert the rendered cause starts with '<Tag>: typescript checker (<code>): ' and holds the next action. Planting the old predicate fails the law and both refusal scenarios. * fix(stryker-js-typescript-checker): refuse unknown typescriptChecker keys and split the speed gate out of compare-sides Review fixes for the full-path ce-code-review of 8457ce7 (run 20261013-173047-a6b753e3), validator-confirmed findings 1, 3 and 6: - 1 (P1): compare-sides.workflow.property.test.ts crossed 1000 lines (849 -> 1033) in this diff. The speed gate moves into its own speed-gate.workflow.ts with its six laws in speed-gate.workflow.property.test.ts; compare-sides calls it and reads the decision. Same violations, warnings and summary values; 35 laws before and after. - 3 (P2): the typescriptChecker block was decoded with the default excess-property policy, so a misspelled key (importercheck) was silently ignored while the published JSON schema refuses it. The block is now decoded on its own with onExcessProperty: 'error' and fails with InvalidCheckerOptions; the option scenario gains a misspelled-key and an extra-key row, red under the old decode. - 6 (P3): the isolatedDeclarations zero-shortcuts nextAction told the operator to exercise an importer re-check path, the opposite of what the gate checks. --------- Co-authored-by: Ryan Lee <drdgvhbh@gmail.com>
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.
Consumes the pending .changeset intents via pnpm version -r
Need help on this PR? Tag
@codesmith-botwith what you need. Autofix is disabled.