ccppccpp home
Browse the documentation

Plugins · CCPP Tester

Writing tests

How ccpp-tester finds what the user asked, writes tests with a source for every expectation, and attacks them with mutations.

ccpp-tester runs with model: inherit and effort: high, and the tools Bash, Read, Grep, Glob, Write and Edit. Its task message comes from the agent whose code it tests, which wants green. The tester treats that message as a claim to check, never as the specification.

Sources

Every expected value in a test must have a source the tester can cite:

SourceNot a source
The user's words in the parent transcript (message uuid)The code's behavior
A documentation page, a schema, a spec file (path or URL)A value the main agent printed from a run
An existing test, an earlier ccpp-tester's report"The user wants…" without a quote

No source, no expectation: the tester asks for it in its report instead of guessing.

The steps

  1. What the user asked. The tester finds the parent session's transcript and extracts its steps: the user's messages, the answers to questions, what the agent did and said. Thinking blocks are skipped: they are the parent arguing for itself.
  2. Earlier testers. It looks for the ccpp-testers that already wrote tests for the same files, and reads what each test was for.
  3. Conventions. The runner, its config, the nearest tests and helpers. It writes tests the way the repository already does, in its own framework: bun test, Vitest, Jest…
  4. Write. Exact assertions (toStrictEqual, the error's code), inputs the task message did not mention (boundaries, empty and invalid input, refusals, the user's own examples), and titles that state what the body asserts. A hard-coded implementation must fail.
  5. Run. Only its own files. A test failing on an assertion is a finding and stays red; a test that cannot run (syntax, import path, fixture) is the tester's own bug to fix.
  6. Attack, with the mutation skill below.

Dictation

The main agent may write "verbatim", paste assertions, give expected values, ask for toContain, or ask to raise a timeout. Pasted code is a wish: the tester keeps what a source backs, replaces or refuses the rest, and lists each refusal with its reason. Thresholds and timeouts come from the user or the docs; "it took 5.9 s" is not one.

Mutation testing

A green test proves nothing if a wrong implementation passes it too. The skill cpo:ccpp-tester, preloaded into the agent and refused to every other one, runs a mutation library on the code under test: StrykerJS for TypeScript and JavaScript. It injects small faults (< becomes <=, a branch is removed, a string is emptied), reruns the tests on each mutant, and every survivor is a wrong implementation the suite accepts.

  • Integrity first. With the exact campaign command, the tester shows that a false assertion fails it, that an empty selection fails it, and that the tests load the mutated code, not a stale build.
  • One campaign per task, on the task's files only, at most 2 workers; then one focused rerun on the mutants it targeted. More than about 150 mutants means the scope is too wide.
  • Survivors as diffs. The tester reads place, mutator and replacement through the skill's outcomes.jq filter: the raw report holds the code whole (enforcement).
  • Each survivor is killed by a new assertion with a source, or recorded as equivalent with the reasoning, or becomes a question for the user. A mutant shows where the contract decides; only a source says what.
  • Boundary probes the library does not generate: a dependency that rejects, duplicates, or answers for the wrong tenant, through the test doubles the tester controls.

Stryker and its config are installed outside the repository, in ~/.cache/ccpp-tester/ or /tmp/ccpp-mut-<name>/. For other languages the tester applies the same test design and reports mutation as not-run.

Moves that do not count

Lowering a threshold, excluding a mutator family, a disable comment, narrowing the scope to drop a survivor, raising a timeout until a mutant times out, counting compile or runtime errors as kills, asserting what the code returns instead of what the source says, or presenting a focused rerun as the full campaign.

Edit this page on GitHub