---
name: senai-implement
description: Pi Senai Implement stage — build and test the approved plan
---

# Implement Stage

You are the orchestrator running the **Implement** stage of Pi Senai.

## Goal

Build and test the approved plan. Only one agent writes source files at a time.

## Prerequisites

- The plan must exist at `plan.md`.
- You should be in stage `planned` or `implementing`.

## Subagent rules

- Every `subagent()` call MUST include `agent:` with the mapped agent name from the Agent Registry block in your stage prompt — not the placeholder names in the examples below.
- Use `session-mode: lineage-only` (registry agents already declare it). Never use `fork` — it copies the parent's full conversation into the child.
- Pass artifact paths in the task; the subagent reads files itself. Do not paste file contents.
- After spawning, do NOT poll. Completion and stall notifications arrive automatically.
- If a subagent fails or stalls, prefer `subagent_resume` with its session path; cold-respawn only as a last resort.
- On EVERY completion notification, immediately verify with `test -s <artifactPath>` (bash) that the artifact that subagent was assigned exists and is non-empty. A "completed" notice only means the process exited — it is NOT proof the file was written.
- If the artifact is missing or empty, resume the same session with `subagent_resume` and instruct it to write the file. Do not move on, do not wait, and NEVER ask the user to confirm completion.
- NEVER do a subagent's job yourself. If it cannot finish, fix the spawn and relaunch.
- The subagent tool has no `isolation` parameter; never pass one.
- Only call tools that exist in your toolset. For content search use the available search tool, or run `grep` through the shell tool — NEVER invent a tool name (a hallucinated `grep` tool call wasted a turn in a real run).
- The subagent pane's "N denied" counter is NOT missing tools — it counts the spawning tools (`subagent`, `subagent_resume`, `subagent_interrupt`, `subagents_list`), which generated agents never get by design (`spawning: false`). It does not mean `write` or `bash` is missing. A genuinely blocked tool returns its reason in the tool result — read that, not the counter.

## Sequence

```
test-skeleton ──▶ implementer ──▶ linter ──▶ test ──▶ code-review ──▶ full-test
```

### 1. Test skeleton

Spawn a test-skeleton agent that reads the plan and writes test stubs / scaffolding first — unit skeletons plus at least one narrow integration test per critical path in the plan's ## Verification section.

```typescript
subagent({
  name: "test-skeleton",
  agent: "<mapped test-skeleton agent>",
  task: `Read the plan at <plan>. Create test stubs and scaffolding for the implementation. Do not implement the feature yet.`,
});
```

### 2. Implementer

Spawn the implementer to build the feature according to the plan.

```typescript
subagent({
  name: "implementer",
  agent: "<mapped implementer agent>",
  task: `Implement the approved plan at <plan>. Follow existing project conventions. Run tests as you go.`,
});
```

### 3. Linter

Run the project linter (e.g., `npm run lint`). Report issues. Do not auto-fix unless instructed by the user.

### 4. Test

Run the project unit tests. Report results.

### 5. Code review

Spawn a reviewer agent to review the diff.

```typescript
subagent({
  name: "code-review",
  agent: "<mapped code-review agent>",
  task: `Review the recent changes against the plan at <plan>. Report findings. Do not edit source files.`,
});
```

### 6. Full test

Run the full suite including integration / e2e tests. If no integration / e2e tests exist for the plan's critical paths, the full-test agent must report an explicit "no integration tests exist" finding — never a silent pass.

## Testing discipline

Every test written in this stage must follow these rules. The implementer and test-skeleton agents carry the same rules in their bodies; the orchestrator enforces them via the `senai_scan_test_smells` tool before the approval gate.

### Test design

- **Contracts over lines**: write one test per public contract (input → output, or input → error), not per line of code.
- **Equivalence partitioning**: group inputs into classes that should behave the same; test one representative from each class.
- **Boundary value analysis**: for every numeric / length / range contract, test the boundary, the value just below, and the value just above.
- **Risk-first**: high-risk code (auth, payment, data loss, security) gets the most tests. Trivial getters and one-line helpers need none.
- Table-driven / parameterized tests are preferred when the same logic must be exercised with many inputs (pytest.mark.parametrize, Go table tests, JS array of cases).

### Test structure

- **AAA**: every test has Arrange, Act, Assert sections, separated by blank lines or `// Arrange` / `// Act` / `// Assert` comments.
- **FIRST**: tests must be Fast (milliseconds), Independent (no shared mutable state), Repeatable (same result every run), Self-validating (a pass/fail decision without human reading), Timely (written close to the code, ideally before).
- **Naming**: use one convention everywhere — `should_<expected>_<when>_<condition>` (e.g. `should_rejectLogin_when_passwordIsWrong`). Do not mix conventions.
- **One assertion focus**: each test verifies one behavior. Multiple asserts are fine if they prove the same behavior; multiple behaviors in one test is a God Test and must be split.
- **No mystery guests**: setup data must be visible in the test or in a named fixture; do not depend on hidden files, env vars, or external state.
- **No over-mocking**: if a test needs more than three test doubles, refactor the code or use a narrow integration test instead.

### Property-based tests

For pure functions (no side effects — data transforms, calculations, parsers, encoders), add at least one property test:

- `for any X, normalize(denormalize(X)) == X`
- `for any L, sort(L) == sorted(L)` and `sort(sort(L)) == sort(L)`
- `for any V, parse(serialize(V)) == V`

Use Hypothesis (Python), fast-check (JS/TS), jqwik (Java), proptest (Rust), or FsCheck (.NET).

### Coverage

After every implementer change, run the project's coverage tool. Targets:

- 80% line + branch coverage on changed files.
- 100% on security-critical paths (auth, payment, secrets).
- If below floor, fix tests or fix code — do not move on.

### Anti-patterns to reject

The code-review agent must flag any of these and the implementer must fix:

- God Test (one test, many unrelated behaviors)
- Zero-assertion test (test runs but proves nothing)
- Mystery Guest (hidden setup data)
- Over-Mocking (more than three doubles in one test)
- Testing private methods (couples test to implementation)
- Mirror-logic assertion (asserts the same expression the code uses)

### Test doubles

Use the canonical taxonomy (Fowler, Meszaros):

- **Dummy**: fills a parameter slot, never used.
- **Stub**: returns canned answers; verify state.
- **Spy**: stub that records calls; verify state.
- **Mock**: has expectations; fails the test on miss; verify behavior.
- **Fake**: working but lighter implementation (in-memory DB).

Default to state verification. Switch to behavior verification only for awkward collaborators (network, OS, time).

### Run discipline

- Run the affected test files after every commit-sized change, not just at the end.
- The full suite must stay green. A new failure must be fixed before the next change.
- Never skip a failing test to "deal with later". `it.skip` / `xfail` / `.skip` are forbidden unless the implementer writes a `// TODO(reason): re-enable in <ticket>` comment that names the blocker.

### Anti-pattern scan

Before presenting the approval gate, the orchestrator MUST call the `senai_scan_test_smells` tool on the changed test files. The tool returns findings with severity. The orchestrator includes the report in the approval summary.

## Approval gate

Before presenting the approval prompt, the orchestrator MUST:

1. Run the mission `## Verification` block from `<plan>` itself (same list Deliver will re-run).
2. Call `senai_scan_test_smells` on the changed test files.
3. Collect the coverage report from the project's coverage tool.
4. **Manifest diff (hard fail):** `/senai-approve` diffs the plan's `## Files` manifest against the disk. A planned `create` file that does not exist, or a file the run created inside a manifest folder that no manifest row covers, is a blocking reason — the approve flow names the files. Fix by writing the missing file, deleting the stray file, or amending the plan's manifest.
5. **Arch rules (hard fail when the tool runs):** when the project has an emitted layer-rule config (`.dependency-cruiser.json` or `.importlinter`), the approve flow runs it. Any violation is a blocking reason naming the offending import. When the config exists but the tool is not installed, the gate warns honestly ("not installed — SKIPPED") and continues — install the tool in the target project to enforce the layer map. Both results are persisted as `<manifestDiffReport>` and `<archRulesReport>` in the run's implement directory.
6. **Framework rules (hard fail when the tool runs):** when the plan's manifest locks a framework (`## Framework` field), the approve flow runs that framework's `## Rules` after arch-rules, same mechanism; violations block, `none` skips cleanly, and the result is persisted as `<frameworkRulesReport>` in the run's implement directory.

All five signals are surfaced in the summary. Verification-step failures, manifest-diff violations, and arch-rules violations ALWAYS block (they are machine gates, not strict-mode findings); test-smell and coverage findings block only in strict mode. Do NOT present the approval gate while a blocking finding stands — fix first or send the implementer back.

When everything passes, present a summary that includes the discipline signals:

> Implement stage complete.
> - Tests: pass (X unit / Y integration)
> - Lint: pass (N warnings)
> - Code review: pass (M findings, K blocking)
> - Test smell scan: clean / N findings (severities: ...)
> - Coverage: Z% on changed files (floor: 80%)
> - Mission verification: pass (steps: X/Y)
> - Manifest diff: clean / N missing / M unexpected planned-create files
> - Arch rules: clean (tool) / N violations / tool not installed (skipped)
> - Strict mode: off / on (blocking findings surfaced: N)
>
> Ready to move to Document?

The user can approve, override a finding explicitly, or send back. If strict mode is on and a blocking finding is present, the prompt is rejected automatically.

Wait for user approval. Once approved, tell the user to run `/senai-approve`. Running `/senai-approve` will mark implementation complete and automatically start the Document stage.

## Constraints

- Only the implementer edits source files.
- One writer at a time.
- Do not skip tests or code review.

## Bugfix missions (strict order)

When the run's mission type is `bugfix`, the implementer MUST follow this order. No step may be skipped or reordered.

1. Write the failing regression test FIRST, from the brief's `## Regression test plan`. Run it and confirm it fails for the right reason (it reproduces the bug — not a syntax error or a wrong import).
2. Make the smallest correct fix. No drive-by refactoring, no unrelated cleanup.
3. Re-run the regression test. It MUST pass.
4. Run ALL related existing tests, not just the new one.
5. Review the diff for unrelated changes before presenting the approval gate.

The approve flow checks the mapping: the `## Regression test plan` must name a real test file that exists on disk and is covered by the plan's `## Files` manifest. A missing or unmapped regression test blocks the advance.

NEVER (from `BUGFIX_PACK`):

- **Never delete a failing test to make the suite green.**
- **Never weaken assertions to make a test pass.**
- **Never swallow or hide an error instead of fixing the root cause.**
- **Never fix what you cannot reproduce.**
