Skip to content

feat(stryker-js)!: key verdict reuse on a digest of the installed engine - #249

Merged
ryanleecode merged 13 commits into
mainfrom
chore/engine-keyed-reuse
Oct 9, 2026
Merged

ryanleecode merged 13 commits into
mainfrom
chore/engine-keyed-reuse

Conversation

@systemfsoftware-maker

@systemfsoftware-maker systemfsoftware-maker commented Oct 9, 2026 •

Copy link
Copy Markdown
Collaborator

Layer 2 of the guard retirement (#247). The deleted check-verdict-semantics.ts guard made a human bump VERDICT_SEMANTICS_VERSION whenever a regex-listed source path changed. Now the engine enforces the invariant itself: the incremental cache keys on engineDigest, a SHA-256 over the manifest and every file under the files field of the installed @systemfsoftware/stryker-js and its runner. A verdict written by any other build of the engine is refused as semanticsChanged. A declared path that is missing on disk (an unbuilt dist) stops the run instead of hashing less.

Breaking: the incremental report replaces verdictSemanticsVersion (integer) with engineDigest (string), and incrementalVersion moves to '4', so reports written by earlier releases are discarded with cacheLayoutChanged. The changeset bumps @systemfsoftware/stryker-js major.

Enforcer evidence

  • tests/incremental-reuse.integration.test.ts: a report rewritten as if another engine wrote it (asWrittenByAnotherEngine) is refused with semanticsChanged. This runs in CI check (pnpm test).
  • In-source laws in src/verdict-semantics.ts: the shipped bytes decide the digest, an unshipped file never moves it, and an unbuilt shipped directory refuses it. Sabotage check: with packageDigestOf changed to ignore file bytes, ∀ss_String_≡ShippedBytesDecideThePackageDigest is refuted (shrunk ["","\u0000"]).

Review dispositions on this layer

  • Rejected (perf, memoize): one digest measured 75 ms, about 0.3 s per run across four call sites, against runs that take minutes. runInputsDigestOf already uses the same per-call pattern at the same sites.
  • Rejected (plugin coverage narrowed): a checker or ignorer installed from a registry or tarball moves the lockfile integrity that runInputsDigest hashes. A linked plugin's edits were never keyed by the old integer either.
  • Rejected (digest moves every release): intended. A release is a different engine, and the lockfile moves anyway.
  • Applied: the semanticsChanged discard message names the engine digest instead of a "verdict semantics version".
  • Rejected (orDie on unreadable identity): a missing file the engine's own manifest declares is a broken install, not a user input; the defect carries the EngineIdentityUnreadable message.

…te the local guards

The repository-local deno guards under scripts/guards/ and their helpers
under scripts/lib/ are gone, with the guard:projects and test:scripts
scripts that ran them.

- single-plan (REPO-D2) now runs as `repo-checks single-plan` from the
  systemfsoftware flake, exposed as this flake's `repo-checks` output.
  `pnpm gate:repo` runs it inside check:ci and .husky/pre-push runs it per
  pushed ref. CI `check` fetches main's commit graph first, and a step plants
  two plans in a plumbing commit and requires exit 1.
- The tarball-contents job is removed: the flake packs the committed tree
  with `pnpm pack` itself, so the comparison was one command against itself.
- AGENTS.md, test/e2e/AGENTS.md, .changeset/README.md and the ledger
  solution doc now name the enforcer that holds each rule.

The plan maps every guard to its enforcer or its drop reason.
The verdict cache refused reuse across engine releases only when someone
bumped VERDICT_SEMANTICS_VERSION and a repository guard parsed commit
trailers to make them. The engine now derives that identity itself: it
hashes the manifest and every file under the paths the `files` field
declares, for @systemfsoftware/stryker-js and its runner, as resolved at
run time. A report or record written under another digest is refused with
the existing `semanticsChanged` reason, and a declared path missing on disk
stops the run instead of being hashed as absent.

The incremental report replaces `verdictSemanticsVersion` with
`engineDigest` and moves `incrementalVersion` to '4'.

In-source laws over packageDigestOf hold that shipped bytes decide the
digest, an unshipped file never moves it, and an unbuilt dist refuses it.
The contract-version law the deleted guard read from git history now runs as
a test of the product. It reads every contract document of
stryker-js-cli-contract and stryker-js-plugin-interface from the released
tarballs the workspace dogfoods (installed from .sfs-deps under aliases) and
from the committed packages, and weighs them:

- a committed document narrower than its release needs a declared next
  version (the package version raised by its pending intents) at the next
  major, or the next minor while the release is 0.x;
- a narrower stream schema needs a higher stream version major;
- a document's law is chosen by the members it declares, never by its file
  name, and a document that declares none fails by name;
- a package whose committed version differs from its released tarball while
  an intent for it is pending fails as a stale baseline, so a lagging
  stryker-published pin cannot hide a member added in an unpinned release.
…invariants

Reverted by the next commit. A contract member removed under a patch intent
must turn the contract version law red, and a registry journey pointing at a
missing file must turn the status closure red.
Pruning scripts/deno.json to what mutation-backstop.ts imports dropped
@std/fs/ensure-dir and @std/encoding, which test/e2e/scripts/export-traces.ts
and import-traces.ts resolve through the same config, so every e2e lane failed
at Export telemetry.
# Conflicts:
#	packages/stryker-js/src/run/incremental-reuse.cell.ts
The semanticsChanged discard still said the report had a verdict semantics
version; the value it prints is now the digest of the engine that wrote it.
- The contract version law no longer lets a version an unpinned release
  already raised clear a narrowing: while the released tarball lags the
  workspace version, any pending intent or any narrowed document refuses
  the comparison as a stale baseline.
- The law's package list must equal the workspace packages whose manifest
  files field declares a contract directory, so a new contract package
  cannot go unweighed.
- The status closure requires each registry journey's file to exist and no
  longer scans its source with a skip/only/todo regex, matching the plan's
  drop of that clause.
- The CI plant runs through `pnpm gate:repo --head <planted>`, the script
  check:ci runs, instead of the repo-checks binary directly.
@systemfsoftware-maker systemfsoftware-maker changed the title chore/engine keyed reuse feat(stryker-js)!: key verdict reuse on a digest of the installed engine Oct 9, 2026
@ryanleecode
ryanleecode removed this pull request from stack #250 October 9, 2026 11:52
Base automatically changed from chore/distributable-guards to main October 9, 2026 11:52

@kiro-systemf kiro-systemf Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Conductor verdict: 11/11 green on 8ab77f5; engine refusal of foreign verdicts tested; dispositions audited.

@ryanleecode
ryanleecode merged commit ee7d614 into main Oct 9, 2026
11 checks passed
@ryanleecode
ryanleecode deleted the chore/engine-keyed-reuse branch October 9, 2026 11:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants