# Progress Line Contract

> **TLDR**  -  Every non-trivial pipeline action emits an inline "I'm doing X right now" line so the user always knows what's happening. Immediate flush, no batching. One line per action, standard shape. Autopilot prefers verbose. Low overhead. Mirrored in telemetry as `progress.step` events.

This contract is consumed by every phase (0-7) and every dispatched sub-skill (including the marketplace component toolkits when enabled). It is enforced by `smoke-progress-contract.sh`  -  any phase doc that drops the contract marker or diverges from the line shape fails CI.

---

## Why

Today pipeline users see a phase banner (`→ Phase 3: Dev`) and then silence for 30 s-3 min while subagents churn. Users lose trust, hit ⌃C, or retry identical work. A single-line "current action" fixes it without adding noise: you see exactly what is happening, where, and for how long.

## Line shape

```
    → <verb> <object>[ <target>]
```

- **4-space indent**, same as subphase indent elsewhere.
- `→` arrow, single space.
- **verb**  -  lowercase present participle or imperative, one word: `fetching`, `reading`, `running`, `writing`, `posting`, `waiting`, `dispatching`, `retrying`, `building`, `committing`, `checking`.
- **object**  -  what is being acted on: file basename, API endpoint last segment, subagent name, command, smoke name.
- **target**  -  optional, human-readable scope: repo name, ticket ID, URL host.
- **Live mirror to the phase tracker**: each `normal`-tier emitted line is also mirrored to the active phase per `$HOME/.claude/multi-agent-refs/tracker-contract.md` "Active phase" (Claude Code `TaskUpdate activeForm`, other CLIs `phase-tracker.sh now`) - the authoritative throttling rules live there.

### Examples

```
    → fetching Jira issue ABC-12345
    → reading LoginView.swift
    → dispatching explore agent repo-map
    → running xcodebuild -scheme Components
    → posting review to Jira comment ABC-12345
    → retrying push origin (attempt 2/5)
    → committing squashed WIP to bugfix/ABC-12345
    → writing wiki FigmaComponents/Core/Button
    → checking smoke smoke-push-retry
```

### Forbidden

- **No** ANSI color inside the line (runtimes may not render it).
- **No** emoji in the verb slot (they collide with the phase banner glyphs).
- **No** payload ≥ 120 chars  -  truncate target with an ellipsis before shipping.
- **No** hostnames, tokens, PATs, stderr blobs  -  emit as `...` and attach detail via telemetry, not the line.
- **No** buffered flushes. `stdout` is flushed inside the same call that produces the line.

---

## When to emit

Emit a progress line **at least** at every one of these moments:

### Phase 0 (Init)
- parse input, classify, token lookup per service, repo scan, worktree create (per repo in multi-repo), identity bind, state file write.

### Phase 1 (Analysis)
- explore-agent start, explore-agent finish, analyst synthesis start, analysis.json write.

### Phase 2 (Planning)
- plan draft start, plan render, user-approval prompt.
- **v5.3.0 Plan Approval Gate (Full + interactive only  -  a Short run has no plan, autopilot may not ask):**
  - `clarification-ask` per round  -  orchestrator writes structured questions when Phase 1 flagged ambiguity (missing acceptance criteria, no Figma/endpoint link, vague language, parent-story scope drift)
  - `clarification-answer` per round  -  user reply captured into `state.phases["2"].clarificationAnswers`
  - `plan-edit-request` per free-text edit  -  user-supplied revision instruction captured into `state.phases["2"].planEditRequests`
  - `plan-approved`  -  terminal success, writes `state.phases["2"].planApprovedAt`
  - `plan-aborted`  -  terminal failure (user typed `iptal`/`no`), sets `state.status = "paused"`

### Phase 3 (Dev  -  non-component)
- every TDD cycle step: read-target, write-test, run-test RED, write-code, run-test GREEN, run-build, wip-commit.

### Phase 3 (Component  -  create-component plugin dispatch)
- subphase dispatch, Figma API call, token mapping, file write per artifact, build step, REVIEW.md gate.

### Phase 4 (Review)
- deterministic-gate per gate, reviewer dispatch per reviewer, reviewer finish per reviewer, triage start, triage verdict render, fix dispatch.

### Phase 5 (User Test)
- local-test prompt render; user-answer capture; if local-test selected: repo checkout, build instructions print.

### Phase 6 (Commit & PR)
- squash per repo, commit per repo, push per repo (each retry attempt), PR body generate (humanizer call), PR create per repo, Jira comment post.

### Phase 7 (Report)
- humanizer summary, wiki write (if enabled), Jira comment post (if enabled), Confluence publish (if enabled), metrics rollup, summary render.

### Sync / Setup / Utility commands
- file discovery, Keychain lookup, config read/write, prompt render.

---

## Verbosity

Controlled by `prefs.global.settings.progressVerbosity`. Enum:

| Value | Emits | Default for |
|---|---|---|
| `quiet` | Phase banners + terminal prompts only | CI runs, automated tests |
| `normal` | Banners + the "when to emit" list above | interactive users (default) |
| `verbose` | Banners + everything in `normal` + sub-agent internal actions, file-level reads, schema validations, retry internals | autopilot (forced), debugging |

**Autopilot rule:** When `mode: "autopilot"` is set in agent state, progressVerbosity is forced to `verbose` regardless of the preference  -  rationale: user isn't watching live, so the log must be audit-grade.

---

## Telemetry

Every emitted progress line also writes one `metrics.jsonl` event:

```json
{
  "ts": "2026-04-16T17:22:04Z",
  "event": "progress.step",
  "taskId": "T-0042",
  "phase": "phase-3-dev",
  "step": "run-test RED",
  "ms": 2318,
  "ok": true
}
```

`ms` captures the elapsed time since the previous `progress.step` in the same phase. `ok: false` flags the step that immediately preceded a phase fail (used by `token-budget-report.mjs` to localise slow or failing steps).

Overhead: ~40 bytes / line + one millisecond clock read. Negligible vs. any of the actions being measured.

### Token telemetry forwarding

Every phase that dispatches a billable LLM agent MUST forward the call's token totals to the tracker so the agent-log Cost Breakdown stays complete. The single canonical call shape is:

```bash
LOG_METRIC_FORWARD_TO_TRACKER=1 $HOME/.claude/scripts/log-metric.sh "$TASK_ID" <phase-id> <event> \
  model=<fable|opus|sonnet|haiku|gpt-5.4|gpt-5.6|gpt-5.6-terra> \
  tokens_in=$IN tokens_out=$OUT duration_ms=$DUR
```

`LOG_METRIC_FORWARD_TO_TRACKER=1` mirrors the same `tokens_in` / `tokens_out` / `model` into `phase-tracker.sh` so the JSONL metrics line and the tracker's per-phase accumulator stay in sync from one call site. The forward path is best-effort  -  if the tracker is missing or its file is unwritable, the JSONL write still succeeds. Without the flag the line is purely analytic and the cost block stays empty for that phase.

This contract is enforced by `smoke-agent-log-cost.sh` (forwarder unit test) and `smoke-tracker-contract.sh` (Phase 4 reviewer/triage emission).

### Cost budget gate (v9.2+)

When `prefs.global.costBudget.enabled` is true, run the budget check once at the END of every phase, right after that phase's token totals have been forwarded to the tracker:

```bash
$HOME/.claude/scripts/cost-budget-check.mjs --task-id "$TASK_ID" \
  --prefs "$HOME/.claude/multi-agent-preferences.json"
```

Act on the exit code:
- `0` (OK) - say nothing; the live per-phase tile already shows running cost.
- `10` (WARN) - surface the printed `WARN: ...` line to the user (and into `agent-log.md`). Continue.
- `11` (EXCEEDED) - surface the `EXCEEDED: ...` line. If `costBudget.onExceed = "halt"`, pause the run and require `multi-agent:resume` (autopilot included) so the user explicitly accepts the overage; if `"warn"`, continue.

The check is a silent no-op (exit 0) when the budget is disabled or no tracker data exists yet, so unconditional invocation is safe. It prices accumulated tokens conservatively (default `opus` rate) so the ceiling trips early. Enforced by `smoke-cost-budget.sh`.

---

## Phase adoption marker

Every phase doc under `$HOME/.claude/multi-agent-refs/phases/` must contain the marker line exactly once:

```
<!-- progress-contract: applied -->
```

The smoke scans all phase docs for this marker. A missing marker means the phase hasn't adopted the contract yet and fails CI. Adding the marker commits the phase to the contract  -  any PR that removes it must also remove the phase's progress emissions (no silent regression).

---

## Cross-CLI parity

The progress line shape and the `progress.step` event schema are part of the cross-CLI contract. Both Claude Code and Copilot CLI implementations emit identical bytes on the same action  -  the shape is **not** wrapped in CLI-specific prelude or color codes. `smoke-cross-cli-behavior.sh` includes a progress-line parity check.
