One v10 gate shipped, one attempted and reverted

Branch chore/v10-frontmatter-node · closes #3477 · #3461 attempted, reverted, premise disproved

One fix ships here, with the evidence this milestone's merge bar asks for: a test that failed before it and passes after. The other was attempted, broke a gate in CI, and was reverted; that failure is kept on this page because it disproves a premise written in #3461 itself.

The audit that created this milestone found 103 unit tests passing on a helper that was dead in production, because the fixtures mirrored the bug. So for #3477 the test is not the whole proof: the container was built and the server was actually run.

#3461 · attempted, REVERTED, and the issue's premise is wrong

The seven "free" deletions are load-bearing. CI proved it.

#3461's own comment says the 7 empty mcpServers: [] declarations are deletable today because "no test can depend on an empty declaration", and this PR originally acted on that. It is false. tests/performance/test-mcp-overhead.sh exists precisely to depend on it:

#   1. Every agent .md file has an explicit mcpServers field

const emptyField = /^mcpServers:\s*\[\s*\]/m.test(fm);
const hasField   = /^mcpServers:/m.test(fm);
...
echo "FAIL [${rest}]: missing mcpServers field"

The test distinguishes empty from absent and requires the field either way. An explicit mcpServers: [] is not leftover cruft, it is a deliberate marker meaning "this agent was considered for MCP and wants none". Deleting it makes an agent indistinguishable from one where somebody simply forgot.

FAIL [demo-producer]: missing mcpServers field
FAIL [emulate-engineer]: missing mcpServers field
FAIL [expect-agent]: missing mcpServers field
FAIL [genui-architect]: missing mcpServers field
FAIL [security-auditor]: missing mcpServers field
FAIL [security-layer-auditor]: missing mcpServers field
FAIL [system-design-reviewer]: missing mcpServers field
FAIL: MCP overhead regression detected.        Performance Tests, CI

Reverted in this branch. The consequence for #3461 is larger than one PR: there is no "free" subset. All 36 declarations, empty ones included, are gated by that test, so the whole issue needs the owner decision, not just the 29 with real content. Two local suites (agents, skills) were green while this was broken, because the gate lives in tests/performance/ which was not run locally.

Gate 2 · #3477 — the published image ran a Node nothing tested

Dockerfile pinned node:22 while .nvmrc said 24.19.0release artifact

release.yml builds and pushes this image with docker buildx build --platform linux/amd64,linux/arm64 --push ., so the divergence shipped. It was never an outage, which is why #3473 unified 26 of 31 Node call sites and missed this one.

source of truthsaidstatus
Dockerfile (2 FROM lines)node 22shipped
.nvmrc24.19.0tested by CI
root package.json engines>=24 <25demanded
src/mcp-server engines>=20.0.0satisfied either way, which is why it hid

The in-file comment claimed "Renovate/Dependabot bump it". Measured: zero docker entries in .github/dependabot.yml, and no renovate.json anywhere in the tree. The pin was frozen by construction, and the comment told the next reader not to worry about it. Bumping the digest alone would have recreated the same rot at 24, so the bumper lands in the same change.

$ bash tests/ci/test-dockerfile-node-version.sh

  .nvmrc major: 24
--- 1: every FROM node:<major> matches .nvmrc ---
  FAIL: FROM node:22 but .nvmrc says 24
  FAIL: FROM node:22 but .nvmrc says 24
--- 2: the major satisfies root package.json engines.node ---
  PASS: node 24 satisfies '>=24 <25'
--- 3: digest pin exists, and something is configured to bump it ---
  PASS: FROM lines are digest-pinned
  FAIL: nothing is configured to bump the digest.
RESULT: FAIL

exit 1

$ bash tests/ci/test-dockerfile-node-version.sh

  .nvmrc major: 24
--- 1: every FROM node:<major> matches .nvmrc ---
  PASS: FROM node:24
  PASS: FROM node:24
--- 2: the major satisfies root package.json engines.node ---
  PASS: node 24 satisfies '>=24 <25'
--- 3: digest pin exists, and something is configured to bump it ---
  PASS: FROM lines are digest-pinned
  PASS: dependabot.yml declares the docker ecosystem
RESULT: PASS

exit 0

The test passing is not the container working, so the artifact was exercised

$ docker buildx imagetools inspect node:24-alpine
  Digest:   sha256:d32cdf619f63fe0471182d08996dd516c6275bb5fd31ae06e55a570bd9e1ad43
  Platform: linux/amd64
  Platform: linux/arm64/v8          # both platforms release.yml pushes

$ docker build .                    # succeeded on the new digest
$ docker run --entrypoint node ... --version
  v24.19.0                          # exactly .nvmrc

$ MCP stdio: initialize + tools/list
  {"result":{"serverInfo":{"name":"orchestkit-docs","version":"1.0.0"}}}
  {"result":{"tools":[{"name":"orchestkit_docs_search", ...

The digest pinned is the multi-arch index, not a single-platform manifest, so one pin still covers amd64 and arm64.

Found while doing this, deliberately not fixed here

RETRACTED: the "hook count drift" reported earlier in this PR does not exist

An earlier revision of this page and the PR body claimed the build re-stamped 171 hooks active to 215, and called it a defect that silently undoes #3494. That was wrong, and the mistake was mine.

What actually happened: the build was running in the background and git diff was read while setup.mdx was mid-write. A partially-written file was reported as a finding. The counter is deterministic and correct:

$ bash bin/count-hooks.sh
  GLOBAL=150 AGENT=0 SKILL=21 TOTAL=171

$ bash scripts/stamp-counts.sh          # in isolation
  Stamped counts: ... 171 hooks (150g + 0a + 21s)
  files changed: 0

$ bash scripts/build-plugins.sh         # full build, clean tree
  [5/6] Found 171 hooks (150 global + 21 scoped)
  files changed: 0                       # idempotent

150 is simply the GLOBAL component, not a competing total. There is one source of truth, both callers agree, and re-running the build is a no-op. Recorded here rather than quietly deleted, because a milestone whose thesis is "tests that mirror the bug prove nothing" should not leave a retracted claim unmarked.

A timing assertion that fails on unmodified main

test-agent-skill-validation.sh Test 6 asserts a hard <200ms. Run three times on pristine origin/main with this branch stashed: 158ms PASS, 710ms FAIL, 312ms FAIL. A 4.5x spread means it fails for reasons unrelated to correctness, which trains reviewers to ignore red. Not caused by this branch. Filed as #3522. An earlier revision of this page said "filed separately" before any issue existed, which was a claim about work not done; the issue now exists and is linked.

Playground for chore/v10-frontmatter-node. All output above is captured from real runs, not reconstructed. Self-contained: no external scripts, styles, or render proxies.