integrate

Workflow Phase 9 — Integration and Verification. Runs the full test suite, stamps the binding verdict at .claude/state/last_test_result, optionally runs a cross-engine smoke check, and writes harness_

SKILL.md

GitHub
<!-- character:begin -->

Character

  • Soul. The one who reads the verdict out loud, unchanged, whoever it disappoints.
  • Motivation. Every gate downstream trusts this stamp. A verdict bent once is a verdict nobody can rely on again.
  • Mantra. I never relax the criteria to make a run pass. A red suite is reported red, in its own words.
  • Temperament. Impartial to the point of coldness and comfortable carrying bad news. Unmoved by how close the run came, with no stake in the outcome and every stake in the reading.
  • Voice. Reports the result in the suite's own words, failure output included. No softening adverb, and no "only" in front of a failure count.
  • Resolve. Every gate after me trusts this stamp. Bend it once and it is worth nothing again.
<!-- character:end -->

Checker config (tier-dial:read-path): this checker's floor/ceiling come from the tier dial at .claude/hooks/lib/tier-dial.mjs via resolveCheckerThreshold('ac-conformance'). Advisory only this slice (v1 piece 2); blocking is piece 5.

Prereq

simplify in completed AND (security in completed OR security in exceptions).

Steps

  1. Verify prereq. Read .claude/state/workflow.json and confirm.

  2. Run the binding test command and stamp the verdict (inlined verify). This used to be a verify skill invocation; the four mechanical operations are inlined here per .claude/skills/verify/SKILL.md (the contract doc). In order:

    • Read .claude/project.json → test.cmd. If absent or empty, the verdict is FAIL with reason "project.json not configured" and step 2 stops with that verdict.
    • Run the command via Bash from the project root. Capture stdout, stderr, and exit code. Do not retry. Do not pass {file} placeholders — integrate always runs the full suite.
    • Apply verdict rules: PASS iff exit code 0 AND at least one test executed AND no failed/errored test; otherwise FAIL. A timeout is FAIL. A killed process is FAIL.
    • Atomically write .claude/state/last_test_result with exactly four lines plus a single trailing newline:
      <PASS|FAIL>
      <ISO-8601 UTC timestamp>
      <exact command>
      <exit code>
      
      The verify_pass_guard hook reads line 1 as the binding verdict.
    • Emit the human-readable inline report (Verdict, Command, Exit code, Output tail) alongside the statefile write.
  3. Read line 1 of .claude/state/last_test_result.

    • PASS → continue to step 4.
    • FAIL → stop. Do not proceed to documentation or commit. Surface the verdict's reason and the output tail to the user. Write harness_state per step 5 with state: "yielded" and reason: "integrate failed: <one-line summary>; needs user decision". Together with the user, decide whether to loop back to /tdd (mechanical bug) or escalate to a spec change.

3.5. Code-review fan-out (D8a — enforcement oracle framework). After the binding PASS and before the smoke check, run the code-review checkers over the branch diff — this is the point where all three inputs exist (the docs/security/<slug>-*.md report from Phase 8, the /simplify verdict table from Phase 7, and code-structure) and the diff is final. Assemble ctx = {slug, rootDir, diffContent, securityReport, simplifyTable} and call runCheckerFanout({slug, rootDir, enabled, phase: 'code-review', ctx}) from .claude/skills/harness/checker-fanout.mjs. changedFiles is built by the helper, not by you — assembleContext in .claude/skills/harness/assemble-context.mjs supplies it when ctx does not, and the merged verdict carries inputState (measured when files were examined, no-input when none were). Its element type is {path, content, prior} — path is repo-relative, content is the working-tree bytes, and prior is the same file at HEAD or null when this change created it. State it rather than assume it: the paragraph named changedFiles without naming its element type, main context guessed per run, and code-structure and backlog-deferral both read file.content off a bare string and went vacuous. assertChangedFilesShape throws on a non-conforming element before any code-review checker runs. This paragraph used to ask main context for changedFiles and no helper produced it, so every archived checker-fanout-code/*.json read {"findings": [], "verdict": "CLEAN"} — the checkers had never run against real input, and a CLEAN from no input was spelled exactly like a CLEAN from a real one. Read inputState before trusting a clean verdict. It writes the parallel projection at .claude/state/checker-fanout-code/<slug>.json — never the gate-A .claude/state/checker-fanout/<slug>.json. Read the merged verdict: on BLOCKED, surface the BLOCKER findings to the user and do not proceed to commit (a code-review BLOCKER blocks the landing, per D8); on CLEAN, continue. Gated by velocity.code_review.enabled (fail-open: disabled/absent → skip this step). Checkers are mechanical .mjs run via Promise.all — no subagent (§II.A clause 6). Two non-UI oracle adapters (C5) also ride this phase, both gated off by default: mutation-score (velocity.mutation_oracle.enabled) grades test-suite strength from a score supplied by a runner injected via ctx.oracleRunner (the mutation engine is a dev-only tool, never shipped; on the baseline's own tree main context wires the runner in when the flag is on), and ac-conformance (velocity.ac_conformance.enabled) flags any approved-spec AC not referenced in the diff. Both are fail-open (disabled / no target-or-runner / no spec → no findings), so with the flags off the merged verdict is byte-unchanged. Verdict severity is binary on the tier-dial floor — BLOCKER when the threshold is mandatory, else ADVISORY (ceiling is a rounds count, not a score band).

  1. Cross-engine smoke (optional, fires only if applicable). Run this step only if all of the following hold:

    • playwright is declared in .mcp.json (check before invoking — never hallucinate the tool).
    • The project has a frontend surface (a package.json with a dev/start script, or docs/site/, or a similar live-rendered target).
    • The diff under review touched at least one file that affects the rendered UI (anything under src/, app/, pages/, components/, docs/site/, or styles/templates referenced from those).

    When all three hold:

    • Start the dev server in a background Bash with a known port.
    • For each engine in chromium, webkit, firefox (skip engines outside the project's declared browser-support floor):
      • mcp__playwright__browser_navigate to the project's primary surfaces (home, plus any route the diff touched).
      • mcp__playwright__browser_snapshot for the accessibility tree; check for unexpected [disabled] / [hidden] regressions vs the prior snapshot if one exists at docs/site/_visual/<slug>/<engine>.snapshot.json.
      • mcp__playwright__browser_screenshot per primary surface; save to docs/site/_visual/<slug>/<engine>-<route>.png.
    • Surface any console errors, 4xx/5xx network responses, or accessibility-tree diffs as integrate findings — these don't block the verify verdict but the user must triage before /commit.
    • Stop the dev server.

    Skip silently if any of the three conditions don't hold. Don't surface a "no playwright" warning on every run — backend-only repos shouldn't see noise.

  2. Marker op FIRST, then write harness_state. Append "integrate" to workflow.json → completed. Then:

    • On state: "continue" (more phases remain): echo "<slug>" > .claude/state/.harness_active to refresh the active marker, then write .claude/state/harness_state with {state: "continue", slug, reason}. The next pending non-excepted phase fires on the same turn via the Stop hook.
    • On state: "yielded" (line 1 of last_test_result was FAIL — handled in step 3): rm -f .claude/state/.harness_active, then write harness_state with {state: "yielded", slug, reason}. The state file carries exactly three keys; no written_at, no tick_count. Tell the user: "Tests green<.|, with cross-engine smoke clean / N findings to triage>. Next: /document."

Constraints

  • Always run the full suite at this phase — not affected-tests-only. The inlined verify operations run the full project test.cmd.
  • Always preserve the four-line last_test_result byte format. The verify_pass_guard hook depends on it.
  • Do not modify code here. If integrate fails, the fix happens in /tdd (bug) or via a spec change (design).
  • Cross-engine smoke is opt-in via .mcp.json presence. Do not install playwright as a side effect of running this skill; the user (or /init-project) controls whether playwright is declared.
  • Cross-engine findings are advisory. They surface to the user; they do not flip the verify verdict from PASS to FAIL.
  • Always write harness_state before the terminal message. The Stop hook reads it to drive auto-continuation; integrate's terminal message ends the turn.
  • State-write discipline (binding — see .claude/CONSTITUTION.md §2 "State-write discipline"). last_test_result, harness_state, and .harness_active are Tier 2 workflow state — not consent paths. Prefer the Write tool for the last_test_result and harness_state writes; the marker refresh uses a builtin redirect and the marker delete uses rm -f (the sole external-binary exception). Never use tee or sed -i, and never resolve paths with dirname/basename/[ -f ] (PATH-fragile) — use Read/Glob.