### Phase 7: Report

> **TLDR**  -  Close the loop in two halves: **External delivery** delegated to `/multi-agent:channels` (multi-select kanal + content  -  always pauses in autopilot, 30-min timeout ends session). **Internal capture** stays inline: `agent-log.md` finalization, telemetry emission, knowledge base + memory update. Channels command is the single source of truth for Jira/Confluence/Wiki/PR-description delivery  -  full contract in `commands/multi-agent/channels/SKILL.md`.

<!-- progress-contract: applied -->
Progress emission per `$HOME/.claude/multi-agent-refs/progress-contract.md`  -  lines for channels dispatch entry, per-adapter result, agent-log finalization, telemetry emission, knowledge+memory write.

## Sub-step Tracking (required)

Phase 7 has **3 ordered sub-steps** (reduced from 5 in v5.7  -  external delivery consolidated under channels). Register all of them in the tracker at Phase 7 entry.

```bash
bash ~/.claude/scripts/phase-tracker.sh update 7 in_progress
for s in 1:Channels-Dispatch 2:Log+Telemetry 3:Knowledge+Memory; do
  bash ~/.claude/scripts/phase-tracker.sh sub 7 "${s%%:*}" "${s#*:}" pending
done
```

On entry: `phase-tracker.sh sub 7 <N> "<name>" in_progress`. On exit: `completed` / `failed` / `skipped` / `timeout`.

---

## Autopilot pause contract

Phase 7 is the single exception to the autopilot zero-interaction rule. Full contract in `$HOME/.claude/multi-agent-refs/phases/modes.md`:

- **Every mode, Full or Short, attended or autopilot**  -  ALL pause at the channels multi-select menu.
- **30-min timeout**  -  if user does not respond, session ends cleanly. External delivery is aborted (no silent apply of defaults  -  prevents accidental Jira comments / Confluence pages). Internal capture (Steps 2 + 3 below) STILL runs so `agent-log.md` + knowledge base are persisted.
- **Resumable**  -  state written as `{phase: 7, waitingFor: "user-channels-choice", channelsInput: <state-bundle>}`. User can `/multi-agent:resume <task-id>` any time later; channels menu re-opens with same inputs.
- **Timeout log line:** `Phase 7: channels menu timeout (30 min)  -  session ended, resume with /multi-agent:resume {taskId}`.

---

## Step 1  -  External Delivery (delegated to channels)

Phase 7 builds a state bundle and invokes `/multi-agent:channels` (Claude Code) or `multi-agent-channels` (Copilot CLI). This is the ONLY external-delivery step  -  no inline Jira/Confluence/Wiki/PR-description logic lives in Phase 7 any more.

> **Local-only short-circuit**: when `state.offlineOnly == true` (all repos
> are local  -  see Phase 0), Step 1 is **skipped entirely**. No channels menu,
> no Jira/Wiki/Confluence/PR adapters, no autopilot pause. The tracker
> records `step1.status = "skipped-offline"` and the run jumps straight to
> the internal capture sub-steps (agent-log finalization, telemetry,
> knowledge/memory write). The pipeline's only "report" for a local-only
> run is the agent-log on disk at `$HOME/.claude/logs/multi-agent/<repo>/<taskId>/agent-log.md`.

### State bundle passed to channels

```json
{
  "jiraId": "PROJ-12345",
  "prUrl": "https://.../pull/42",
  "prNumber": 42,
  "remoteType": "github" | "bitbucket",
  "taskType": "component" | "bugfix" | "feature" | "refactor",
  "figmaConfig": { /* from project figma-config.json, or null */ },
  "agentState": { /* agent-state.json contents */ },
  "pipelineLogPath": "$HOME/.claude/logs/multi-agent/<project>/<task-id>/agent-log.md",
  "invokedFrom": "phase-7"
}
```

Channels command reads this bundle, skips its own target-resolution step, goes straight to the multi-select menu.

### Figma evidence embedding (when `state.evidence.figma[]` is non-empty)

For every UI atom touched by the diff, the PR description, Jira comment, and Confluence page body MUST embed the Figma frame reference and screenshot URL. Channels adapters read `state.evidence.figma[]` and render a `## Figma reference` block per channel:

- **PR description**  -  one row per frame: `<canonicalComponentName>  <fileKey>:<nodeId>  [screenshot](<screenshotUrl>)`.
- **Jira comment**  -  same row shape; image macros may be added when the Jira project allows direct image embedding (see `$HOME/.claude/multi-agent-refs/channels/jira.md`).
- **Confluence page**: frame screenshots are uploaded as page attachments (cache locally first because Tier 2 signed URLs expire after 30 days) and referenced inline via `<ac:image><ri:attachment ri:filename="frame-<nodeId>.png"/></ac:image>`.

Tier 3 rows append `(tier 3: pending design review)` after the canonical component name so reviewers know the choice is provisional. The block is suppressed when `state.evidence.figma[]` is empty (non-UI tasks).

### Menu (identical to post-hoc `/channels` call  -  single source of truth)

```
Kanallar: [x] PR  [x] Jira  [ ] Confluence  [ ] Wiki
İçerik:   [x] Normal analiz  [x] Test senaryoları  [ ] Auto-diff  [ ] Manuel not
```

Pre-ticks from `prefs.global.reportChannels` + `prefs.global.reportContent`. Greyed-out rules (pipeline context, PR linked, taskType=component, wiki.enabled) per `channels.md` Step 3.

**User selects → channels dispatches → returns per-adapter results. Phase 7 records outcomes into tracker + summary block.**

### Per-adapter outcomes (from channels return)

```json
{
  "pr":         { "status": "posted|skipped|failed", "url": "...", "version": "3→4" },
  "jira":       { "status": "posted|skipped|failed", "url": "...", "commentId": "2484780" },
  "confluence": { "status": "posted|skipped|failed", "url": "..." },
  "wiki":       { "status": "posted|skipped|failed", "paths": [...], "adapter": "github-wiki" }
}
```

Tracker: `phase-tracker.sh sub 7 1 "Channels dispatch" completed` (or `failed` if ALL adapters failed; `skipped` if user declined all).

---

## Step 1.5  -  GitHub Issue channel (when tracker is a GitHub issue)

Runs only when `state.tracker.kind === "github-issue"`. The Issue adapter is mandatory whenever the run was triggered from a GitHub issue  -  no exceptions, no "I'll just update the flags" shortcut. Full template + rules: `$HOME/.claude/multi-agent-refs/channels/issue-comment.md`.

**Approval gate:** interactive runs show the composed comment body (and the Progress-flag diff intent) through the same preview + approval gate as channels Step 6 (`channels.md`) before posting  -  Approve & post / Edit / Cancel. Autopilot posts directly.

Two paired actions, in order:

```bash
# 1. Post the canonical comment (template per refs/channels/issue-comment.md).
bash ~/.claude/scripts/phase-tracker.sh sub 7 1.5 "Issue comment" in_progress
gh issue comment "$ISSUE_NUMBER" --repo "$ORG/$REPO" --body-file /tmp/channels-${TASK_ID}-issue.md
bash ~/.claude/scripts/phase-tracker.sh sub 7 1.5 "Issue comment" completed

# 2. Sync the Progress flag table from agent-state.json. Idempotent  -  the
#    script no-ops if the body already matches.
bash ~/.claude/scripts/phase-tracker.sh sub 7 1.6 "Progress flag sync" in_progress
bash ~/.claude/scripts/update-issue-progress.sh "$TASK_ID"
bash ~/.claude/scripts/phase-tracker.sh sub 7 1.6 "Progress flag sync" completed
```

Order matters: comment first (timestamp marks the run), flags second (one logical body diff). If the comment fails, do NOT update flags  -  the channel is incomplete and the user needs to retry.

The Progress flag values come from `state.flags`:

| state.flags key | Issue table label |
|---|---|
| `implementation` | Implementation |
| `testing`        | Testing        |
| `codeConnect`    | Code Connect   |
| `wiki`           | Wiki           |

Each value renders as `done` (`true`), `partial` (`"yellow"`), or `pending` (`false`/missing). Phase 3 / 5 / 6 are responsible for setting these honestly  -  Phase 7 only mirrors what's already in state.

**Hard rule (per `$HOME/.claude/multi-agent-refs/channels/issue-comment.md`):** never run only one of the two actions. The comment is the human-readable audit; flags are the machine-readable status. Either both or neither.

---

## Step 2  -  Finalize Report (internal)

Write `agent-log.md` with ALL sections:

```markdown
# {jiraId}  -  {Task Title}

**Branch**: {branch-name} | **Project**: {project}
**Started**: {datetime} | **Finished**: {datetime} | **Status**: Complete

## Plan Todo Rollup (when `prefs.global.planTodos.enabled`)

Rendered via `$HOME/.claude/lib/plan-todos.sh list "$TASK_ID"`. Inline the resulting markdown checklist verbatim  -  one line per todo with status marker ([x] / [~] / [/] / [!]), ID, task text, and notes when present. Status summary one-liner ("4/6 done, 1 skipped, 1 failed") goes at the top from `plan-todos.sh status`.

Skipped sections: when `planTodos.enabled` is false or no `plan.todos[]` was emitted, the Plan Todo block is omitted (no empty header). Phase 3 task-by-task progress still appears in the Timeline section either way.

## Timeline

| #   | Phase | Agent | Model | Duration | Tokens (in/out) | Status | Detail |
| --- | ----- | ----- | ----- | -------- | --------------- | ------ | ------ |

## Agent Activity Report

| Agent | Calls | Duration | Model |
| ----- | ----- | -------- | ----- |

## Review Iterations

| Iteration | Blocking | Important | Suggestion | Decision |
| --------- | -------- | --------- | ---------- | -------- |

## Files Changed

(git diff --stat output)

## Channels Summary

| Channel    | Status             | Link                                       |
| ---------- | ------------------ | ------------------------------------------ |
| PR         | posted \| skipped  | {prUrl} (reviewers preserved, v X→Y)       |
| Jira       | posted \| skipped  | {jiraUrl}?focusedCommentId={id}            |
| Confluence | created \| skipped | {confluenceUrl}                            |
| Wiki       | {N} files \| skip  | {wikiUrl} (adapter: {submodule\|...})      |

## Pipeline Metrics (last 30 days)

(embed `aggregate-metrics.mjs --since=<30 days ago> --json` output as collapsed JSON)
```

**Telemetry emission** (mandatory): forward the phase's own LLM spend (humanizer + report compose calls) to the tracker, then emit the final event:

```bash
LOG_METRIC_FORWARD_TO_TRACKER=1 $HOME/.claude/scripts/log-metric.sh "$TASK_ID" 7 report.compose \
  model=$REPORT_MODEL tokens_in=$R_IN tokens_out=$R_OUT duration_ms=$R_DUR
$HOME/.claude/scripts/log-metric.sh "$TASK_ID" 7 task.completed \
  phases=$PHASE_COUNT review_cycles=$CYCLES lang=$PROMPT_LANG \
  channels_pr=$PR_STATUS channels_jira=$JIRA_STATUS \
  channels_confluence=$CONF_STATUS channels_wiki=$WIKI_STATUS \
  duration_ms=$TOTAL_DURATION
```

**Operational reporting.** On by default since v16.8.0; `enabled: false` or `optOut: true` silences it. Sends command, duration, tokens, outcome - never a repo name or path. One POST per run, fire-and-forget, coarse run metadata only  -  no prompts, code, diffs, or absolute paths. The script no-ops when `usageLog.enabled` is not true or no token resolves, so the call is unconditional and never blocks the run.

```bash
node $HOME/.claude/scripts/usage-report.mjs --state "$STATE_FILE" >/dev/null 2>&1 || true
```

**Aggregate before reporting**: run `aggregate-metrics.mjs --since=$(date -u -v-30d +%Y-%m-%d)` and embed output in agent-log.md. Use `--json` for Jira. Aggregator handles missing files gracefully.

**Per-run outcome metrics (evidence corpus):** also emit `run-metrics.mjs --state <agent-state.json>` and append/persist its JSON. It records the numbers that actually answer "did this run go well" - review iterations (rework loops), first-pass-clean, reviewer signal-to-noise (accepted / raw findings), consensus verdict, build outcome. Accumulating these across real runs is the real-world validation that golden tasks + benchmarks only approximate; keep the corpus so the pipeline's quality can be measured, not asserted.

**Cost Breakdown emission (mandatory):** as part of agent-log compose, append the per-task cost block produced by `render-agent-log-cost.sh`:

```bash
COST_BLOCK=$(bash $HOME/.claude/scripts/render-agent-log-cost.sh "$TASK_ID" 2>/dev/null) && \
  printf '\n%s\n' "$COST_BLOCK" >> "$AGENT_LOG"
```

This is independent of the channels-side `reportContent.costSummary` (which gates the PR-body Cost Summary). agent-log always carries the breakdown when telemetry is present.

**Missing-telemetry disclosure (required).** Name untracked phase ids as cost-unavailable in the report and closing summary. Mechanic: `payload-contracts.md`.

**Triage memory ingest (mandatory):** after Phase 4 produces a final triage output, persist the accepted/deferred/rejected rows into the per-repo triage corpus so Phase 1 enrichment and Phase 4 prior-art lookup can recall them on future tasks. Idempotent  -  re-running on the same task writes 0 rows.

```bash
# Salvaged copy first: Phase 6 removes the worktree once the PR is open, and this
# reader is `[ -f ]`-guarded, so a wrong path degrades SILENTLY.
TRIAGE_PATH="$(jq -r '.artifactsPath // empty' "$STATE_FILE" 2>/dev/null)/triage-output.json"
[ -f "$TRIAGE_PATH" ] || TRIAGE_PATH="$WORKTREE/triage-output.json"
if [ -f "$TRIAGE_PATH" ]; then
  node $HOME/.claude/scripts/triage-memory.mjs ingest \
    --triage "$TRIAGE_PATH" \
    --task-id "$TASK_ID" \
    --task-title "$TASK_TITLE" \
    --stack "$DETECTED_STACK" >/dev/null 2>&1 || true
fi
```

Best-effort. The corpus is JSONL at `~/.claude/memory/multi-agent/<repo-slug>/triage-corpus.jsonl` (per-repo isolation  -  never cross-leaks between projects). Disabled when `prefs.global.priorArtEnrichment.ingestOnComplete = false`.

**Learnings ledger distill (on by default via `prefs.global.learningsLedger.enabled`):** distill this run's rejected findings into durable rejected-preference entries so future reviewers stop re-raising them, and record any durable architectural fact the analysis surfaced. Idempotent (dedup by kind + statement). Safety: `from-triage` skips blocking-severity rejections - a wrong rejection of a blocking issue must never permanently suppress that class; use `learnings-ledger.mjs forget` to clear a bad/stale entry.

```bash
if [ -f "$TRIAGE_PATH" ]; then
  node $HOME/.claude/scripts/learnings-ledger.mjs from-triage \
    --triage "$TRIAGE_PATH" --task "$TASK_ID" >/dev/null 2>&1 || true
fi
# Optionally capture a durable architectural fact the analysis established:
# node $HOME/.claude/scripts/learnings-ledger.mjs add --kind fact \
#   --statement "<one-line fact>" --scope "<path-glob>" --task "$TASK_ID" >/dev/null 2>&1 || true
```

The ledger is JSONL at `~/.claude/memory/multi-agent/<repo-slug>/learnings-ledger.jsonl`, next to the triage corpus, per-repo isolated. Its brief is replayed into Phase 1 analysis and Phase 4 triage on future runs.

Print compact summary to terminal:

```
{jiraId} complete
Total: Xm Ys | Agents: N calls | Files: N changed
Channels: PR ✓ · Jira ✓ · Confluence - · Wiki ✓ (3 files)
Commit: abc1234 | PR: #87
Full report: $HOME/.claude/logs/multi-agent/{project}/{task-id}/agent-log.md
```

Tracker: `phase-tracker.sh sub 7 2 "Log+Telemetry" completed`.

## Step 3  -  Knowledge Capture (incremental learning)

Extract reusable knowledge from this task and **append** to `$HOME/.claude/knowledge/{project-name}/`:

| File              | What to Add                                     | Source                            |
| ----------------- | ----------------------------------------------- | --------------------------------- |
| `architecture.md` | Newly discovered modules, relationships, deps   | Phase 1 Explore                   |
| `patterns.md`     | Conventions, naming, recurring structures       | Phase 1 + Phase 4                 |
| `gotchas.md`      | Build errors/solutions, edge cases, workarounds | Phase 3 retries, Phase 4 blocking |
| `decisions.md`    | Architectural decisions + rationale             | Phase 2 plan + Phase 4            |

**Rules:** Only save reusable knowledge (task-specific details stay in agent-log). Read existing files before appending  -  no duplicates. Add date: `<!-- captured: {date}, task: {jiraId} -->`. Conflicting info → update old entry, don't delete.

**Entry format:**

```markdown
<!-- captured: {date}, task: {jiraId} -->

### {Topic Title}

{Concise, actionable description}
```

**First task** (no knowledge dir): `mkdir -p`, create initial architecture.md + patterns.md from Phase 1. Log: "Knowledge base initialized for {project}"

**Save preferences**: Update prefs with Phase 7 selections (via channels return: `reportChannels`, `reportContent`, `confluenceUrls` per-project LRU).

**Per-repo memory synthesis (opt-in via `prefs.global.perRepoMemory`):**

After the knowledge-capture pass, dispatch a scoped synthesis subagent (sonnet, 10 turns max) that reads the current run's `agent-log.md` + the existing MEMORY.md index, and returns 0-3 new memory entries as a JSON array. Each entry: `{type: "user|feedback|project|reference", slug, title, description, body}`. The orchestrator then loops:

```bash
for entry in $(jq -c '.[]' memory-synthesis.json); do
  body=$(jq -r '.body' <<< "$entry")
  slug=$(jq -r '.slug' <<< "$entry")
  type=$(jq -r '.type' <<< "$entry")
  printf '%s\n' "$body" | bash $HOME/.claude/scripts/memory-save.sh "$PROJECT_ROOT" "$type" "$slug" -
done
```

**Synthesis prompt scope** (keep narrow):
- **feedback**: only if the user corrected a pipeline behavior during this run ("no, don't", "stop doing X", or explicit approval of an unusual choice)
- **project**: only if someone named a new deadline, constraint, or stakeholder that wasn't already in the task description
- **reference**: only if an external system URL / dashboard / channel was mentioned as the source of truth for some concern
- **user**: only if the user disclosed new context about their role, expertise, or team layout
- **Otherwise return `[]`**  -  blank is the correct default. Do not save derivable facts (code structure, git history, command outputs).

Zero new memories is normal and expected. A typical run produces one or none.

Tracker: `phase-tracker.sh sub 7 3 "Knowledge+Memory" completed`.

Log: "Phase 7: Knowledge captured ({N} entries to {files})"

---

## Timeout behavior (30-min channels menu wait)

If user does not respond at the channels multi-select menu within 30 minutes (wall clock from menu render):

1. Channels command aborts its interactive prompt, returns `{status: "timeout"}`.
2. Phase 7 records `phase-tracker.sh sub 7 1 "Channels dispatch" timeout`.
3. **Internal capture still runs**  -  Steps 2 + 3 write `agent-log.md` (with `channels: timeout` in summary), emit telemetry, update knowledge base.
4. Session exits cleanly. State persisted: `state.phase=7, state.waitingFor="user-channels-choice", state.channelsTimeout=true`.
5. Resume contract: `/multi-agent:resume <task-id>` re-opens the channels menu with the original state bundle (pipeline log, PR metadata, prefs pre-ticks).

Rationale: silently posting defaults to Jira / Confluence after a timeout would leak wrong-tone content to external systems. Hard-stop + resume is the safer policy.

---

#### Output Quality + Metrics Section

Append to every Phase 7 report (knowledge-capture pass) a **Quality & Metrics** block built from `metrics.jsonl` and the latest task's artifacts:

```
## Quality & Metrics

### Output Quality
- Commit message format: ✓ pass / ✗ fail
- PR body (real newlines, no HTML entities, sections present): ✓ / ✗
- Jira comment format: ✓ / ✗
- Reviewer JSON valid: N/N
- Triage JSON valid + no contradictions: ✓ / ✗
- Code review severity ratio (blocking : important : suggestion): X : Y : Z (target ≤ 1 : 2 : 4)

### Token & Cost
- Tokens in / out: <a> / <b>
- Premium calls (opus + gpt): <c>
- Estimated cost (USD): $<d.dd>
- Budget status: under | at | over

### Build / Push
- Build success rate this task: <pass>/<total>
- Push attempts (per repo): <n>
- Multi-repo: <repo count> repos, <skipped count> skipped
```

The block is generated by `output-quality-check.sh` + `token-budget-report.mjs --task $TASK_ID --json` and pasted verbatim. If either script is missing, that subsection is omitted (best-effort).

#### metrics.jsonl schema extension

Telemetry events may carry an `outputQuality` object inside `details`, plus a `channels` sub-object with per-adapter status:

```json
{
  "ts": "2026-04-21T...",
  "task_id": "PROJ-12345",
  "phase": "7",
  "event": "task.completed",
  "details": {
    "phases": 8,
    "review_cycles": 2,
    "channels": {
      "pr":         "posted",
      "jira":       "posted",
      "confluence": "skipped",
      "wiki":       "posted"
    },
    "outputQuality": {
      "commitFormat": "pass",
      "prBody": "pass",
      "jiraComment": "pass",
      "reviewerJsonValid": true,
      "triageJsonValid": true,
      "severityRatio": "1:2:4"
    }
  }
}
```

#### Final metrics report appended to PR body at release time

When the pipeline cuts a release, append the latest 30-day rollup to the release PR body:

```bash
node $HOME/.claude/scripts/token-budget-report.mjs --period monthly --append
gh pr edit "$RELEASE_PR" --body-file <(cat existing-body.md \
  && printf '\n\n## Release-window metrics (last 30 days)\n\n' \
  && tail -10 $HOME/.claude/logs/multi-agent/metrics-summary.md)
```
