> Core 中立版(Increment 6a 改写,原 deferHard verbatim)。编号与条目结构严格不变(C-2 不变量);实例术语按 `core/adapters/TERMINOLOGY.md` 绑定。 # Review Pipeline Phases (Canonical) This document is the **single source of truth** for the shared Phase A→E review/audit pipeline flow. Individual review and audit skills bind their own parameters and reference this doc rather than duplicating the shared mechanics. **Terminology**: "reviewer" and "auditor" are interchangeable — `audit-process` legitimately uses "auditor"/"Re-audit" terminology. All generic descriptions use `{REVIEWER_OR_AUDITOR}`. **Output model — two findings sinks** ([org-internal #1988] Tier 1 migration): raw per-dimension reviewer findings have ONE of two sinks, chosen per skill. The **synthesis** (human gate) and **commit status** are ALWAYS Tier 2 (Gitea) regardless of the findings sink. | Sink | Skills | Reviewer writes findings to | Synthesizer reads findings from | |---|---|---|---| | `tier1-local` (migrated) | `review-code`, `review-artifact` (live targets: review-dag, audit-process; the roadmap/design-space/plan targets were archived 2026-08-21, [org-internal #3072] phase 3) | `/{slug}/reviews/{stage}/round{N}/findings-{DIMENSION_CODE}.json` (validated by `core/schemas/reviewer-output.schema.json`) | the local finding files (no Gitea call) | | `tier2-comment` (fallback) | _(none — all targets migrated)_ | Gitea PR/issue comment via `工单评论 API(见 TERMINOLOGY)` (Pattern 5) | `工单评论 API(见 TERMINOLOGY)` (Pattern 3) | Migration complete — every review/audit skill now outputs `tier1-local`; the `tier2-comment` row remains as the documented fallback (boundary rule: `core/rules/two-tier-artifacts.md`; a skill without `/` write access falls back to comment-based output unchanged). A `tier1-local` reviewer additionally appends an `index.json` row per finding file (Tier 1 manifest, see `core/templates/runs-layout.md`). **Tier 2 outputs (always)**: synthesis is posted as a **Gitea PR/issue comment** with a fenced ` ```json ` block — except for the `audit-process` target, which has no source issue and posts the synthesis as a **wiki page** `audit/{date}/round{N}/synthesis` instead (see `review-artifact/SKILL.md` process target Output Artifacts); status tracking uses **REST commit status** (`pipeline/{REVIEW_TYPE}` context). The full Gitea output pattern is documented in `_shared/gitea-write-patterns.md`. --- ## Phase A — Parallel {REVIEWER_OR_AUDITOR_NAME} **Orchestrator actions**: 0. **Move the source issue to the `review` column** on the Pipeline Stages board per `_shared/gitea-write-patterns.md` Pattern 7.5. Skip if no source issue exists. The review stage is a single board column covering all review targets (DAG single gate / code review / audit-process). 0.5. **Initialize the review-status file** (round 1 only). The Orchestrator creates `/{slug}/reviews/{stage}/status.json` before dispatching round 1, with the full schema-required field set (`core/schemas/review-status.schema.json`): `slug`, `started_at` (RFC 3339 timestamp), `current_round: 1`, `max_rounds` (per-skill binding — `review-artifact` = 3, `review-code` = 2 default / 3 high-risk — `/workflow-routing.yaml` `review.max_rounds`), `converged: false`, `history: []`. This step is skipped on rounds ≥ 2 — the file already exists and the Synthesizer appends to it in Phase B. Initialization is the Orchestrator's sole responsibility; the Synthesizer never creates the file. The Orchestrator also appends an `index.json` row for the file (`type` = `review-status`, `stage` = `{REVIEW_TYPE}`, `produced_by` = `Orchestrator`, `schema` = the review-status `$id`). 1. **Tool-availability probe** (defense-in-depth, [org-internal #2115] RC1 / [org-internal #2126]). Before dispatching any {REVIEWER_OR_AUDITOR}, verify reviewers will be able to read wiki artifacts (requirements / design / plan / prior-review reports). A reviewer that cannot read its inputs aborts or hallucinates — catch this ONCE in the Orchestrator instead of letting {DIMENSION_COUNT} parallel subagents each rediscover the gap: - **Primary path**: confirm the Gitea REST API is reachable — probe `GET /api/v1/repos/{owner}/{repo}/wiki/pages` with `-H "Authorization: token "` (a 200 or 403 response means reachable; anything else — timeout, connection refused, 5xx — is unreachable). If reachable → continue to step 2. - **Fallback path** (when REST is unreachable, e.g. a cross-repo scenario [org-internal #2115]): verify the **local wiki clone** at `/{repo}-wiki/` is readable — `glob(pattern="*.md", path="/{repo}-wiki/")`. If the clone is readable → continue to step 2; reviewers use the local clone (see `_shared/gitea-read-patterns.md` Pattern 1 fallback note and the "Tool Availability & Path Resolution" section of this skill). - **Both unavailable → FAIL FAST**: post a `failure` commit status (state `"failure"`, context `pipeline/{REVIEW_TYPE}`, description naming the missing capability) and abort — do NOT enter parallel dispatch. Report the gap so the environment can be fixed (restore Gitea REST reachability — token validity, network route — or populate `/{repo}-wiki/`). This probe is belt-and-suspenders: it exists so a future environment regression (REST unreachable, stale token) fails gracefully with one clear message instead of N silent subagent aborts. 2. Post an initial pending commit status via REST: ``` POST /api/v1/repos/Octopus/octopus/statuses/{SHA} state: "pending" context: "pipeline/{REVIEW_TYPE}" description: "Round {N}: {DIMENSION_COUNT} reviewers dispatched" ``` (See `_shared/gitea-write-patterns.md` Pattern 8.) 3. Spawn **all {DIMENSION_COUNT} {REVIEWER_OR_AUDITOR}s in parallel**. Each receives: - The standardized prompt with dimension-specific values filled in. - Agent type: Explorer. - Permission: Explorer read-only (see `core/skills/_shared/review-orchestrator-rules.md` — Default Explorer Permissions). - Temperature: 0.1 (deterministic output). 4. Wait for all {REVIEWER_OR_AUDITOR}s to complete. Each reviewer writes its dimension result to the skill's configured findings sink: - `tier1-local` (e.g. `review-code`, `review-artifact`, `audit-process`): write the JSON to `/{slug}/reviews/{stage}/round{N}/findings-{DIMENSION_CODE}.json` (validated by `core/schemas/reviewer-output.schema.json`). The reviewer is granted scoped write to `/**` (read-only everywhere else — this emits the reviewer's OWN output, it does not touch the artifact under review, so it does not violate rule #7 auditor independence). - `tier2-comment` (fallback): post an issue comment via `工单评论 API(见 TERMINOLOGY)` (Pattern 5, `_shared/gitea-write-patterns.md`) with a fenced ` ```json ` block on the target PR (pipeline mode) or issue (standalone mode). The comment is labeled `**{DIMENSION_CODE} review — round {N}**`. After all reviewers complete, for `tier1-local` the Orchestrator appends one `index.json` row per finding file (type `review-findings`, stage `{REVIEW_TYPE}`, produced_by `Reviewer`, schema = the reviewer-output `$id`). 5. If a {REVIEWER_OR_AUDITOR} fails (crash / timeout), retry once. If still failed, mark that dimension as `UNRESOLVED` in the synthesis and continue. Record it for deferred retry at the start of the next round. --- ## Phase B — Synthesis (Delegated) > **Context optimization**: Phase B is delegated to a Synthesizer (Worker) to > prevent the Orchestrator's context from accumulating N dimension finding > Read results (up to N × 50KB per round). The Synthesizer reads all dimension > findings in its own context, posts the synthesis comment + commit status, > and returns a compact summary (≤ 2KB). The Orchestrator never reads > dimension findings directly. **Orchestrator actions**: 1. Fill the synthesis task template (`core/skills/_shared/synthesis_task.md`) with: - Round number. A round-1 dispatch carries the template's step-8 "Round-1 only — findings digest" duty (the Synthesizer writes `round1/findings-summary.md`, consumed verbatim at Phase E — see the digest bullet in step 3 below); verify it is present in the filled template. - Target PR number (pipeline mode) or issue number (standalone mode). - List of UNRESOLVED dimensions (reviewers that crashed/timed out). - Commit SHA and review type. - For `tier1-local` skills (all review/audit): write the filled template to `/{slug}/reviews/{stage}/round{N}/task-synthesizer.md` (Tier 1 synthesis prompt; append an `index.json` row, type `synthesis-prompt`). The `/tmp/octopus/` embryo is NOT used — Tier 1 owns the prompt. - For `tier2-comment` skills: write the filled template to `/tmp/octopus/synthesis_task_{slug}_round{N}.md` for `prompt_file` use; the authoritative copy is published as a Gitea wiki page at `{slug}/reviews/{stage}/round{N}/task-synthesizer`. 2. Dispatch the **Synthesizer** (Worker) via the `task` tool: - `role`: `Synthesizer` - `subagent_type`: `worker` - `prompt_file`: the synthesis-prompt path from step 1 (Tier 1 local path for `tier1-local` skills; `/tmp/octopus/...` for `tier2-comment` skills) - `permissions`: read + write + edit + bash + glob + grep (all allow) - `temperature`: 0.1 - Foreground (the Orchestrator waits for completion). 3. The Synthesizer: - Reads all dimension findings from the skill's configured sink: - `tier1-local`: read the finding files at `/{slug}/reviews/{stage}/round{N}/findings-*.json` (in its OWN context, not the Orchestrator's — no Gitea call). - `tier2-comment`: read all dimension comments via `工单评论 API(见 TERMINOLOGY)` (Pattern 3, `_shared/gitea-read-patterns.md`) on the target PR/issue. - **Post-check finding-id uniqueness (MANDATORY)**: verifies that `id` values are unique across ALL findings files of the round (including every `-r{k}` slot file) — the schema's `uniqueItems` does NOT enforce this. On a duplicate id, keeps both findings, notes the collision in the digest and marks the affected dimension WARN (per step 1b of `_shared/synthesis_task.md`). - Computes synthesis following `core/schemas/synthesis.schema.json`. - Posts synthesis as a PR/issue comment via `工单评论 API(见 TERMINOLOGY)` (Pattern 5, `_shared/gitea-write-patterns.md`) — markdown summary + fenced ` ```json ` block matching `core/schemas/synthesis.schema.json`. (This is the Tier 2 human-gate output — it stays on Gitea for both sinks.) For the `audit-process` target (no source issue), post the synthesis as a wiki page `audit/{date}/round{N}/synthesis` instead of a PR/issue comment. - Posts a commit status via REST with context `pipeline/{REVIEW_TYPE}` (Pattern 8, `_shared/gitea-write-patterns.md`): `state: "success"` for PASS/WARN, `"failure"` for FAIL. - Appends one `history[]` entry for this round to the run's review-status file `/{slug}/reviews/{stage}/status.json` (validated by `core/schemas/review-status.schema.json`) — `round`, `overall_verdict`, `blockers`, `majors`, `minors` — the durable per-round audit history (defined by `core/schemas/review-status.schema.json` `history[]`; mirrors the audit-process checklist TRC 10.4), and updates the file's `current_round` to this round number. (The Orchestrator initializes `status.json` before round 1 — see Phase A step 0.5 — with the full required field set `slug` / `started_at` / `current_round` / `max_rounds` / `converged` / `history`. The Synthesizer only appends `history[]` and updates `current_round`; it never re-creates the file.) - **Round-1 findings digest (round 1 only)**: for skills whose final report carries a Round-1 Findings Summary (`review-code` Phase E step 2.55 and any target adopting the template section), the round-1 Synthesizer is the PRODUCER of that table: it writes `/{slug}/reviews/{stage}/round1/findings-summary.md` — the rendered `## Round-1 Findings Summary` table (columns `| Dim | Finding ID | Severity | Summary |`, one row per round-1 finding; Summary = one-line 摘由/root cause) — distilled from the round-1 findings it just read in its own context (append an `index.json` row, type `review-synthesis` — Synthesizer-produced synthesis artifacts; the round-verdict return summary `synthesis-return.md` keeps the generic worker-report type `other` per `_shared/worker-report-persistence.md`). This duty is wired into the Synthesizer's operative prompt as step 8 of `_shared/synthesis_task.md` ("Round 1 only — findings digest"). The Orchestrator at Phase E copies this file verbatim into the final report; it does NOT read the findings JSONs (step 4 below) — the digest file is the wired producer→consumer path. - Returns a compact summary (≤ 2KB) with verdict, per-dimension results, and re-audit targets. 4. The Orchestrator receives the summary and proceeds to Phase C or Phase E based on `overall_verdict`. The Orchestrator does NOT read dimension comments or the synthesis comment directly — all decision-relevant data is in the Synthesizer's return summary. 5. Present the Synthesizer's return summary to the user in chat. --- ## Phase C — {REVISION_ROLE} Revision If `overall_verdict` is PASS, skip to Phase E. Otherwise: **Orchestrator actions**: 1. Prepare the {REVISION_ROLE}'s input as a structured task, published as a Gitea wiki page at `{slug}/reviews/{stage}/round{N}/task-{ROLE}` (Pattern 1, `_shared/gitea-write-patterns.md`). For the `audit-process` target, the process target's Phase Bindings override the wiki namespace to `audit/{date}/` — publish to `audit/{date}/round{N}/task-{ROLE}` instead (see `review-artifact/SKILL.md` process target). This must contain: - Paths to all files that need revision. - Reference to the synthesis comment on the PR/issue (posted in Phase B). - References to the dimensions that have open BLOCKER, MAJOR, or MINOR findings, via the skill's findings sink: - `tier1-local`: paths to the Tier 1 finding files (`/{slug}/reviews/{stage}/round{N}/findings-{DIMENSION_CODE}.json`). - `tier2-comment`: references to the individual dimension issue comments. Dimensions whose only findings are INFO are excluded — INFO is dispositioned at synthesis and never revised (see the "Severity handling — INFO disposition" rule in Phase D). - The {REVISION_ROLE} MUST NOT receive the full text of all findings inline — use finding-file path / comment references only to avoid context explosion. - Convergence status: blockers/majors remaining vs. previous round. - **Revision survival ([org-internal #2601]) — primary path**: the task tool's completion output includes a `task_id`; passing it back resumes the SAME {REVISION_ROLE} subagent session instead of creating a fresh one. Round 1: persist the returned `task_id` with the round's working-notes entry (it must survive round-boundary compaction). Round ≥ 2: dispatch with `task_id` set to the previous revision round's — the reviser continues in-session with its prior context. {REVIEWER_OR_AUDITOR}s are NEVER resumed — per-round freshness is their independence. - **Revision survival — fallback** (task_id lost to compaction before it was persisted): for round ≥ 2, include the current `/{slug}/working-notes.md` content (run narrative memory: prior decisions, dead ends, earlier reviewer feedback) and a reference to the previous round's revision summary. State explicitly that this revises work a prior round already produced — re-derive from upstream artifacts ONLY when a finding requires it. 2. Spawn the {REVISION_ROLE} (Worker) with revision instructions from `core/skills/_shared/review-revision-prompt.md`. For round ≥ 2, pass the previous reviser `task_id` to resume the same session (see Revision survival above); reviewer/auditor dispatches are always fresh. --- ## Phase D — {REVIEWER_OR_AUDIT_LOOP} Loop After the {REVISION_ROLE} completes revision: 1. Increment the round counter, and update `current_round` in `/{slug}/reviews/{stage}/status.json` to match. Post the round transition as a progress update to the commit status (Pattern 8): ``` state: "pending" context: "pipeline/{REVIEW_TYPE}" description: "Round {N+1}: re-reviewing after {REVISION_ROLE} revision" ``` 2. Go back to Phase A (spawn a new round against the revised files). **Convergence criteria** (stop the loop when ANY of these is true): | Condition | Action | | -------------------------------------- | ---------------------------------------------- | | `overall_verdict` = PASS | Stop. Proceed to Phase E. | | `current_round` > `{MAX_ROUNDS}` | Stop. Proceed to Phase E with final status. | | No improvement in 2 consecutive rounds | Stop. (Same blocker count AND same major count | | (compared to 2 rounds ago) | without reduction). Proceed to Phase E. | | User explicitly approves current state | Stop. Proceed to Phase E. | **`{MAX_ROUNDS}` binding** (per skill): - `review-artifact` skill (process target): **3**. - `review-artifact` skill (`review-dag` single-gate target): **per depth** from `dag_metrics.review_depth` — D1 ≤2, D2 ≤3, D3 ≤3, D4 ≤4 (D4 reaches round 4, exceeding the shared `review-artifact` default; the per-depth cap takes precedence — see `review-artifact/SKILL.md` review-dag Phase D and `core/schemas/review-status.schema.json`). - `review-code` skill: **2** default / **3** high-risk (a BLOCKER survived a round, or the diff touches a frozen cross-session contract) — `/workflow-routing.yaml` `review.max_rounds` ([org-internal #3567], process-diet P0b). At budget without convergence: STOP, register the remaining BLOCKER/MAJOR findings as TD registry rows, verdict FAIL if any BLOCKER remains else PASS — the debt is carried by those TD registry rows (see `review-code/SKILL.md` Phase D). This supersedes the 2026-08-25 [org-internal #3380] calibration (p50=2, p95=5, n=66): the runs past round 3 are the marathon tail this budget exists to cut; their residue flows to the TD registry instead of reviewer rounds. Ledger: `docs/workflow-refactor/thresholds-ledger.md`. Rationale for `review-artifact` = 3 (evaluated in [org-internal #1969], replacing the prior blanket default of 5): (1) the Round-3 Escalation Guardrail below already detects non-convergence at the start of round 3; (2) the INFO-disposition rule below removes spurious WARN rounds previously caused by non-blocking INFO findings; (3) the "no improvement in 2 consecutive rounds" stop condition naturally fires by round 3; (4) the marginal value of rounds 4-5 is low once the guardrail has fired and they burn reviewer budget. (`review-code` no longer reaches round 4 — its round budget is 2/3, see the binding above.) **Provisional, not empirically validated**: the four safeguards make `review-artifact`=3 defensible, but it has no fresh run-data yet. Treat it as a hypothesis to confirm via the retrospective signal ([org-internal #1834]; gate-trim's landing machinery was retired, [org-internal #3072] phase 3 — the check is observational, not a trim proposal): if live `review-artifact` runs frequently hit round 3 still improving yet are forced to stop at WARN/FAIL, bump it back to 4. **Severity handling — INFO disposition** (non-blocking, non-revising): INFO findings are observations/recommendations beyond the checklist. They NEVER block convergence and NEVER drive a reviser round. - A dimension whose ONLY open findings are INFO has effective verdict **PASS** — INFO does not reduce `pass_rate` and does not cause WARN/FAIL (see orchestrator rule #3). Such a dimension does NOT count against `overall_verdict`. - At synthesis, the Synthesizer dispositions EVERY INFO finding as one of: - `TD-candidate` — actionable observation worth a tech-debt item; record a suggested REGISTRY ROW (one-line summary + reactivation trigger, `core/rules/ticket-lifecycle.md`). Actual `TD-NNN` registration is deferred to the `verify` skill's Phase 5.5 registry filing — a TD-candidate does NOT open an independent ticket at review time (consistent with how `review-code` Accepted Risks flow to verify). - `DISCARDED` — not worth tracking; record a one-line reason. Dispositions are listed in the synthesis comment (markdown). INFO counts carry in `info_count` / `infos` for reporting only. - Phase C reviser task (step 1 above) references ONLY dimensions with open BLOCKER/MAJOR/MINOR findings. INFO findings are NEVER passed to the {REVISION_ROLE}. **Round-3 Escalation Guardrail** (applies to the `review-artifact` skill's live targets — the process audit target AND the review-dag single-gate target; the roadmap / design / plan targets were archived 2026-08-21, [org-internal #3072] phase 3). `review-code` does NOT use this guardrail; it uses its round budget ([org-internal #3567], see the `{MAX_ROUNDS}` binding above). At the start of round 3, if the combined blocker+major count is still >50% of round 1's count, STOP the loop and surface to the user: > Review not converging: round 1 had {N1} blockers+majors, round 2 still has > {N2} ({pct}% of round 1). This suggests the artifact under review needs a > substantive rewrite, not incremental patching. Options: > (a) Return to upstream — for the `process` audit target, escalate to the > octopus maintainer for a process-level redesign (the legacy producer skills > `roadmap` / `requirements-elicitation` / `design` / `plan-iterations` were > archived 2026-08-21, [org-internal #3072] phase 3 — `/archive/`), then restart this > review at round 1. > (b) Continue iterating — accept that more rounds may be needed (capped by > `{MAX_ROUNDS}`). Choosing (a) returns control to the upstream skill (or maintainer); the review's commit status is marked with description `"escalated"` and `converged: false`. **Round-4 user-choice escalation point (shared)**: a multi-round review skill MUST offer the user an explicit choice before entering round 4 — (a) stop the review and escalate to a human, or (b) continue into round 4 — via the `question` tool. The guard means "escalate with a choice", NOT "force-stop". The choice is UNCONDITIONAL at the round-3→4 transition — instance skills may narrow when an escalation *variant* (e.g. a design-level decision) fires, but never narrow the choice gate itself. The call MUST be optionized ([org-internal #3378]): two options — `Stop & escalate to a human` (first, when recommended) / `Continue into round 4` — with `stage` set to the running skill's stage so auto-approval applies in autonomous mode; free-text asking is a spec violation. Instance: review-dag D4 option-b (`review-artifact/SKILL.md` §"Round-3 Escalation Guardrail interaction") — the sole live instance, its D4 round cap (≤4) being the only budget that reaches round 4 (`review-code` is capped 2/3, [org-internal #3567]). Rationale (retro 2026-08-21 / [org-internal #3050]): unattended late-round loops drift into review marathons — the choice forces the design-level stall to surface to a human at round 4 instead of round 6. **Efficiency optimization**: From round 2 onward ([org-internal #2598]), only spawn {REVIEWER_OR_AUDITOR}s for dimensions that had FAIL, WARN, or UNRESOLVED in the previous round. Every 2 rounds, run a full {DIMENSION_COUNT}-reviewer sweep to catch regressions. From round 2 onward reviewer input is also delta-scoped: the file list handed to each reviewer covers only the files changed since the previous round plus their convention-comparison neighbors, not the round-1 full set. --- ## Phase E — Final Report **Orchestrator actions**: 1. Write the final report as a Gitea wiki page at `{slug}/reviews/{stage}/final/report` (Pattern 1, `_shared/gitea-write-patterns.md`), using the template at `core/skills/_shared/review-final-report-template.md`. For the `audit-process` target, write to `audit/{date}/final/report` instead (wiki namespace override `audit/{date}/`). Also set the lifecycle `converged` flag in `/{slug}/reviews/{stage}/status.json`: `true` when the final verdict is PASS (or WARN with no unresolved BLOCKERs), else `false`. This is the ONLY place `converged` is written — the Orchestrator at Phase E; the Synthesizer never sets it. 2. Update the `## 工件索引` comment on the source issue per `_shared/gitea-write-patterns.md` Pattern 10: add/update the `REVIEW-{stage}` row (位置 = the final report page, 重读 = CORE — review conclusions are part of the compaction re-read set). Skip if no source issue exists (Pattern 10 step 1). 2.5. **Inline-ization (skills that bind it)** — when the review target is a PR and the skill's Phase E binds the inline-comment step (currently `review-code` only, as its Phase E step 2.56), execute it now: post the final round's findings as PR inline comments via `/script/post-inline-comments.ts` (consumer of frozen contract `shared/review-inline-v1`): noise findings (INFO / DISCARDED / `WAIVED-{id}`) are never inlined (contract behavior 2), multi-round posts delete the identity's previous inline comments first (behavior 5), stale lines degrade to the file's first diff hunk (behavior 4; caveat: this server build returns HTTP 500 rather than the contract's 422 for stale lines — TD-611 — so stale items currently surface under `failed` with `fallback` empty), and partial failures are accounted — never blocking the verdict (behavior 6) — in the synthesis comment and final report's `inline: X posted / Y file-level / Z fallback / W failed` line. Skills whose target has no PR (e.g. `audit-process`) skip this step. The skill-specific binding (runner invocation, findings paths, identity, cleanup) is normative in the binding skill's own Phase E section. 3. Post final commit status via REST: ``` POST /api/v1/repos/Octopus/octopus/statuses/{SHA} state: "success"|"failure" context: "pipeline/{REVIEW_TYPE}" description: "Final: {PASS|WARN|FAIL} after {N} rounds — converged: {true|false}" target_url: "{wiki page link}" ``` 3.5. **Gitea approval timing (mandatory when the PR needs a human approval gate)**: if the flow requests approval from a human reviewer on Gitea, register the reviewer FIRST via `POST /api/v1/repos/{owner}/{repo}/pulls/{index}/requested_reviewers`, wait for the `REQUEST_REVIEW` registration to be recorded, and only THEN ask the human to approve. Approving before the reviewer is registered produces `official: false` on the approval — the gate treats the PR as unapproved and the approval must be re-done. ([org-internal #1931]: audit R2→R3 re-loop + re-approve, ~30min wasted on exactly this ordering.) 4. Present the final report to the user and obtain approval via the `question` tool, passing `stage: "{STAGE}"`. The `question` tool auto-approves per `core/rules/auto-approve.md` (stage `"{STAGE}"`). Do NOT advance without approval (auto or explicit). --- ## Orchestrator Rules (Mandatory) See `core/skills/_shared/review-orchestrator-rules.md` for the mandatory Orchestrator behavior rules. Substitute `{REVISION_ROLE}`: {REVISION_ROLE}.