Skip to content

ci: add the mutation tier over the governance core - #724

Merged
soydachi merged 12 commits into
mainfrom
mutation-tier-evidence
Sep 16, 2026
Merged

soydachi merged 12 commits into
mainfrom
mutation-tier-evidence

Conversation

@soydachi

Copy link
Copy Markdown
Member

The mutation tier (§17.1), implemented against the approved contract in .ai-engineering/spec.html (whose sha256 is pinned in ai-eng.lock).

What it adds

  • stryker.conf.mjs — StrykerJS 9.6.1 + @hughescr/stryker-bun-runner 1.3.8, pinned exact; mutating src/guards, src/chain, src/floor; ignoreStatic, concurrency: 2, the incremental cache, thresholds { high: 90, low: 85, break: 85 }.
  • .github/workflows/mutation.yml — nightly on schedule + workflow_dispatch, plus a pull-request job routed by the governance paths. The campaign's exit code is the gate; the clear-text and JSON reports are uploaded as an artifact.
  • check.yml — the guards-adversarial path filter fetched no history, so git diff origin/<base>...HEAD failed and the job reported "skipped" for its whole life. Both jobs now fetch history and fail hard when the diff cannot be computed.

Why the gate is red on purpose. Measured locally, cold: 60.30% of 1,946 mutants, 11m 21s. break = 85 is a floor and a ratchet — red from the first run until the core climbs. The report names the survivors, so the red says what is missing.

This is an evidence PR. It exists to prove two things in CI that cannot be proven locally: that the oracle in guards-adversarial actually runs (not "oracle skipped"), and that the mutation job runs on a pull request that touches the core, with its real wall-clock number becoming timeout-minutes. It is not meant to be merged as-is: it also carries in-flight work that was uncommitted in the worktree, and the milestone's own slot files.

StrykerJS 9.6.1 and @hughescr/stryker-bun-runner 1.3.8, pinned exact, mutate
src/guards, src/chain and src/floor — nightly, on demand, and on the pull
request that touches them. thresholds.break = 85 is a floor and a ratchet: the
core kills 60.30% of 1,946 mutants, so the gate is red from its first run and
stays red until the core climbs.

The path filter guards-adversarial copied was a measured no-op (a shallow
checkout cannot diff origin/<base>, so the job reported "skipped" for its whole
life): both jobs now fetch history and fail hard when the diff cannot be
computed, because a gate that cannot run is red, never green.

This branch also carries the in-flight work that was uncommitted in the
worktree, and the milestone's own slot (.ai-engineering/spec.html + plan.html +
brainstorm.md, approved and pinned).

Receipt-Id: fcc53011
@hughescr/stryker-bun-runner@1.3.8 pins smol-toml to 1.7.0 exactly, and that
version is in the advisory range (<=1.7.0, denial of service via malformed TOML).
The exact pin is also why `bun audit fix` cannot move it: the range check blocks
the upgrade. An override installs the patched 1.7.2 for every consumer, which is
what keeps the security job's `bun audit --audit-level=high` gate green now that
the mutation tier has brought the dependency in.

Receipt-Id: 1c83ca7d
The pull-request job ran cold on ubuntu-latest: 59.72% of 1,946 mutants,
campaign 4m 29s, job wall-clock 4m 44s. timeout-minutes goes from the
provisional 30 to 15, which covers the slower of the two measurements (11m 21s
at concurrency: 2 on the laptop that built the tier) with room for the plugin's
known flake. The recap carries the CI number beside the local one, and plan.html
carries the run's evidence plus the contract findings the loop is not allowed to
fix itself.

Receipt-Id: 4da587a9
@trailhq-graft

trailhq-graft Bot commented Sep 15, 2026

Copy link
Copy Markdown

🌱 graft blast radius

5 areas changed → 7 areas can be affected. 55 dependent symbols, depth 2.
Tests: 4 areas updated their tests.

flowchart TB
  A0(("Command Management<br/>22 symbols"))
  A1(("CLI Interface<br/>14 symbols"))
  A2(("Floor Management<br/>6 symbols"))
  A3(("Guard Logic<br/>5 symbols"))
  A4(("AI Verification<br/>4 symbols"))
  AX(("2 smaller areas<br/>4 symbols"))
  classDef reached fill:#D9EDF3,stroke:#3AA7C9,stroke-width:1.5px,color:#0E313C;
  class A0,A1,A2,A3,A4 reached;
  classDef tail fill:#EEF2F3,stroke:#9AA4A9,stroke-width:1px,color:#3A4247;
  class AX tail;
Loading
Can be affected Symbols Nearest hop Reached from
Command Management 22 src/commands/config.ts:L193-L209 installSurfaceFiles — calls, depth 1 UI Assets, Surface Adapters +1
CLI Interface 14 src/cli.ts:L1-L129 cli.ts — calls, depth 1 UI Assets, src/spec/index.ts +1
Floor Management 6 src/floor/entry.ts:L10-L54 floor — calls, depth 2 UI Assets, tests/brand-generated.spec.ts
Guard Logic 5 src/guards/injection.ts:L98-L109 scanPath — calls, depth 1 tests/brand-generated.spec.ts
AI Verification 4 skills/ai-verify/evals/scripts/apply-pack.ts:L271-L353 main — calls, depth 1 tests/brand-generated.spec.ts
Chain Execution 2 src/chain/mod.ts:L195-L227 dispatchGuard — calls, depth 1 Self Protection, tests/brand-generated.spec.ts
1 smaller area 2 Feature Verification see below
All 55 dependent symbols, grouped by area

Command Management — 22 symbols in 6 files

  • src/commands/config.ts:L193-L209 — installSurfaceFiles (calls, depth 1)
  • src/commands/init.ts:L214-L244 — reinitHandoff (calls, depth 1)
  • src/commands/init.ts:L321-L351 — installCarriers (calls, depth 1)
  • src/commands/uninstall.ts:L81-L127 — sweepAsset (calls, depth 1)
  • src/commands/config.ts:L85-L124 — configMain (calls, depth 2)
  • src/commands/config.ts:L1-L218 — config.ts (imports, depth 2)
  • src/commands/doctor.ts:L1-L553 — doctor.ts (imports, depth 2)
  • src/commands/doctor.ts:L101-L117 — checkCanon (calls, depth 2)
  • src/commands/doctor.ts:L526-L552 — doctorMain (calls, depth 2)
  • src/commands/doctor.ts:L488-L505 — gc (calls, depth 2)
  • src/commands/doctor.ts:L360-L384 — runChecks (calls, depth 2)
  • src/commands/init-shared.ts:L1-L104 — init-shared.ts (imports, depth 2)
  • src/commands/init.ts:L1-L435 — init.ts (imports, depth 2)
  • src/commands/init.ts:L413-L434 — initMain (calls, depth 2)
  • src/commands/init.ts:L305-L318 — scaffoldAndCarriers (calls, depth 2)
  • src/commands/init.ts:L152-L185 — installCanonOrReport (calls, depth 2)
  • src/commands/init.ts:L120-L149 — resolveMachineSide (calls, depth 2)
  • src/commands/uninstall.ts:L1-L245 — uninstall.ts (imports, depth 2)
  • src/commands/uninstall.ts:L178-L244 — uninstallMain (calls, depth 2)
  • src/commands/uninstall.ts:L56-L70 — pickScope (calls, depth 2)
  • src/commands/uninstall.ts:L139-L176 — removeEverything (calls, depth 2)
  • src/commands/upgrade.ts:L1-L83 — upgrade.ts (imports, depth 2)

CLI Interface — 14 symbols in 4 files

  • src/cli.ts:L1-L129 — cli.ts (calls, depth 1)
  • src/cli.ts:L73-L121 — main (calls, depth 1)
  • src/embed.ts:L1-L195 — embed.ts (imports, depth 1)
  • src/env.ts:L13-L21 — home (calls, depth 1)
  • src/env.ts:L25-L33 — repoRoot (calls, depth 1)
  • src/env.ts:L79-L81 — machineBase (calls, depth 2)
  • src/env.ts:L113-L115 — versionFile (calls, depth 2)
  • src/env.ts:L49-L64 — declaration (calls, depth 2)
  • src/env.ts:L67-L70 — governanceGap (calls, depth 2)
  • src/env.ts:L72-L74 — isGoverned (calls, depth 2)
  • src/env.ts:L120-L130 — loadConfig (calls, depth 2)
  • src/env.ts:L90-L93 — receiptsDir (calls, depth 2)
  • src/notice.ts:L1-L61 — notice.ts (imports, depth 2)
  • src/notice.ts:L40-L60 — maybeNotice (calls, depth 2)

Floor Management — 6 symbols in 3 files

  • src/floor/entry.ts:L10-L54 — floor (calls, depth 2)
  • src/floor/index.ts:L19-L34 — preCommit (calls, depth 2)
  • src/floor/index.ts:L142-L155 — prePush (calls, depth 2)
  • src/floor/template.ts:L1-L177 — template.ts (imports, depth 2)
  • src/floor/template.ts:L36-L45 — gitEnv (calls, depth 2)
  • src/floor/template.ts:L114-L139 — installTemplateDir (calls, depth 2)

Guard Logic — 5 symbols in 3 files

  • src/guards/injection.ts:L98-L109 — scanPath (calls, depth 1)
  • src/guards/no-verify.ts:L49-L58 — hooksPathElsewhere (calls, depth 1)
  • src/guards/injection.ts:L111-L146 — runInjection (calls, depth 2)
  • src/guards/loop.ts:L21-L27 — stateFile (calls, depth 2)
  • src/guards/no-verify.ts:L63-L81 — checkBash (calls, depth 2)

AI Verification — 4 symbols in 2 files

  • skills/ai-verify/evals/scripts/apply-pack.ts:L271-L353 — main (calls, depth 1)
  • skills/ai-verify/evals/scripts/score.ts:L136-L150 — realResolve (calls, depth 1)
  • skills/ai-verify/evals/scripts/apply-pack.ts:L1-L356 — apply-pack.ts (calls, depth 2)
  • skills/ai-verify/evals/scripts/score.ts:L363-L518 — main (calls, depth 2)

Chain Execution — 2 symbols in 1 file

  • src/chain/mod.ts:L195-L227 — dispatchGuard (calls, depth 1)
  • src/chain/mod.ts:L119-L193 — runChain (calls, depth 2)

Feature Verification — 2 symbols in 1 file

  • skills/ai-verify/tiers/2-embedded/feature-verify/scripts/blast_radius.ts:L304-L412 — main (calls, depth 1)
  • skills/ai-verify/tiers/2-embedded/feature-verify/scripts/blast_radius.ts:L1-L415 — blast_radius.ts (calls, depth 2)
Test signal per changed area — 4 ✓ · 1 –

Reached = a node under a test path has a resolved edge into the changed symbol. It undercounts anything called indirectly — through a CLI, a spawned process or a dynamic import — so read a low ratio as “look here”, never as a coverage gate.

  • UI Assets — 6 of 13 reached · 2 test files changed here: src/spec/index.ts, tests/update-command.spec.ts
    • not reached: line, wrapMarked, wrapMessage, wrappedRow, fail, installMachineSide, machineSection
  • Surface Adapters — 1 of 1 reached · 2 test files changed here: tests/carriers-install.spec.ts, tests/uninstall-command.spec.ts
  • Self Protection — 2 of 3 reached · 2 test files changed here: tests/adversarial/governance-gate.test.ts, tests/self-protect.spec.ts
    • not reached: offendingPath
  • Branding Assets — 4 of 31 reached · 4 test files changed here: tests/artifact-brand.spec.ts, tests/brand-check.spec.ts, tests/brand-contrast.spec.ts, tests/brand-generated.spec.ts
    • not reached: artifactExpected, channel, cmdArtifact, cmdContrast, cmdGen, cmdLegacy, cmdParity, colourOf, …19 more
  • stryker.conf.mjs — no function, method or class changed here
9 test suites also reference this code

9 symbols, kept out of the diagram and the table so they cannot crowd out the areas a reviewer has to look at.

  • src/spec/triggers.ts
  • tests/cli-ux.test.ts
  • tests/config-command.spec.ts
  • tests/embed-canon.spec.ts
  • tests/embedded-path.spec.ts
  • tests/init-command.spec.ts
  • tests/notice.spec.ts
  • tests/spec-command.spec.ts
  • tests/spec-lifecycle.test.ts

⚠️ 10 deleted files (.ai-engineering/recap.html, .ai-engineering/research/001-cicd-gate-audit.html, .ai-engineering/research/002-surface-goal-modes.html, .ai-engineering/research/003-core-promotion-cursor-codex-copilot.html, .ai-engineering/research/004-hooks-global-vs-project.html) — their dependents cannot be computed from a graph built at this commit, since the files are gone from it.
⚠️ 55 changed files not in the graph (.ai-engineering/ai-eng.lock, .ai-engineering/config.toml, .changeset/security-findings-say-where-they-stand.md, .changeset/slots-are-the-sessions-to-write.md, .changeset/update-says-what-it-did.md) — no parser claims the extension, or the index predates the file.

graft blast · refs/graft/base...HEAD · depth 2 · 92 changed files

Open the interactive graph → — click an area to see the code that changed, and the line that reaches it.

The installer replaced arch.rules.json with its bootstrap template and
dropped .ai-engineering/security/run-*; restore the real layer map and
trim the sample-runs check it can no longer answer.

Receipt-Id: 12c0a179
Narrow terminals hard-wrapped long log rows at column 0; rows now wrap
ANSI-aware with the spine indent carried onto continuation lines. README
tightened to the stats table + cli-tour gif; smol-toml pin surfaced in
package.json; stryker-sandbox wrap tolerance in the uninstall spec.

Receipt-Id: eaf2155e
brand-generated.spec.ts reads the private ai-engineering-web checkout,
which CI has never had, so the check tier failed on every commit of this
branch. The tests now skip where the tree is missing and still assert
against the real files locally.

Receipt-Id: 1d305e41
@sonarqubecloud

Copy link
Copy Markdown

@soydachi
soydachi merged commit a045fa9 into main Sep 16, 2026
25 checks passed
@soydachi
soydachi deleted the mutation-tier-evidence branch September 16, 2026 02:07
@github-project-automation github-project-automation Bot moved this from Backlog to Done in ai-engineering Sep 16, 2026
@github-actions github-actions Bot mentioned this pull request Sep 16, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

1 participant