# Verification phase contract

Homeboy's verification surface is deliberately split into isolated primitive
commands that can be composed by higher-level workflows.

## Primitive phases

The shared phase vocabulary is:

1. `syntax` — parser-level validation such as `php -l`, `rustc --emit=metadata`, or equivalent
2. `lint` — static lint findings from PHPCS, ESLint, clippy, rustfmt, etc.
3. `typecheck` — type/system checks such as PHPStan, `tsc --noEmit`, or `cargo check`
4. `audit` — Homeboy structural analysis such as duplicates, orphaned tests, and code smells
5. `test` — behavioral test harness execution such as PHPUnit, cargo test, or npm test

These phases are a contract, not a requirement that one command runs all of
them. `homeboy test` runs only the `test` phase. `homeboy lint` runs only the
`lint` phase. `homeboy audit` runs only the `audit` phase. A composed command
or CI wrapper can run the phases in canonical order when it needs a full check.

## Exit codes

Every primitive command uses the same exit code convention:

- `0`: clean
- `1`: findings, lint violations, audit findings, or test failures
- `2` or higher: infrastructure failure such as missing dependencies, harness bootstrap failure, or runtime crash

This distinction matters because code findings should fail CI differently from
broken tooling. Composed workflows can stop early on infrastructure failures and
still report normal findings as actionable code work.

## Structured output

Primitive command output should include a phase report:

```json
{
  "phase": {
    "phase": "test",
    "status": "failed",
    "exit_code": 1,
    "summary": "test phase reported 2 failure(s) out of 120 test(s)"
  },
  "failure": {
    "phase": "test",
    "category": "findings",
    "summary": "2 test failure(s) detected"
  }
}
```

`status` is one of:

- `passed`
- `failed`
- `error`
- `skipped`
- `not-run`

`failure.category` is one of:

- `findings`
- `infrastructure`

The core Rust contract lives in `src/core/extension/runner_contract.rs`:

- `VerificationPhase`
- `PhaseStatus`
- `PhaseFailureCategory`
- `PhaseReport`
- `PhaseFailure`

## Composition

Composed workflows should treat primitive commands as independent inputs:

```text
syntax     -> optional primitive / extension runner
lint       -> homeboy lint
typecheck  -> optional primitive / extension runner
audit      -> homeboy audit
test       -> homeboy test
```

This keeps each command debuggable on its own while giving CI, future `check`
commands, and rig workflows one stable shape to aggregate.

## Extension phase timings

Extensions and providers may emit a generic phase timing sidecar through
`HOMEBOY_PHASE_TIMINGS_FILE`. Homeboy core treats phase names, messages, and
metadata as provider-owned labels; it only preserves and renders them.

```json
{
  "phase_timings": [
    {
      "name": "provider-shared-resource",
      "duration_ms": 90000,
      "status": "waiting",
      "message": "waiting for exclusive provider resource",
      "metadata": {
        "provider": "fixture"
      }
    }
  ]
}
```

`status` is optional and free-form. Review/fleet summaries highlight generic
waiting states when providers declare `waiting`, `blocked`, or `queued`. Tool
specific detection, such as recognizing a package manager lock message, belongs
in the provider or extension that understands that tool; Homeboy core only
surfaces the declared generic phase state.
