Initial publish v0.1.0: standalone workflow core (corpus + examples + guards)

This commit is contained in:
octopus
2026-09-15 08:41:51 +08:00
commit bb35e661b2
114 changed files with 20240 additions and 0 deletions
View File
@@ -0,0 +1,71 @@
> Core 中立版(Increment 6a 改写,原 deferHard verbatim)。编号与条目结构严格不变(C-2 不变量);实例术语按 `core/adapters/TERMINOLOGY.md` 绑定。
# Shared Rule — Round-Boundary Context Compaction
> **Canonical source of truth.** This file is the single authoritative copy of
> the round-boundary compaction rule. It is injected **directly** (via
> `<!-- inject -->`) by two skills:
>
> - `review-code/SKILL.md`
> - `review-artifact/SKILL.md`
>
> Both inject tags are resolved by `injectShared`
> (`<harness-package>/src/tool/skill.ts`) at skill-load time. Do not place this
> inject inside a file that is *itself* injected (a nested inject) —
> `injectShared` is single-pass and does not resolve nested inject tags; they
> survive as literal `<!-- inject -->` comments and the procedure is silently
> lost.
>
> **When editing:** update this file only. Every consumer picks up the change
> automatically at load time — do not copy the text into any skill.
>
> This is a reference document, not a skill (no `SKILL.md` frontmatter), so the
> skill loader does not register it. It exists purely so maintainers have one
> place to edit.
## Why a canonical copy exists
Every multi-round review skill (`review-artifact`, `review-code`) needs the model to
read this rule inline at the point it dispatches rounds — a cross-file
reference would not be loaded into the model's context. So the text is
injected directly into both skills at load time. The injection is intentional;
this file exists to keep the two consumers on identical wording without a
copy-paste mirror.
## Canonical text
The block below is what each consumer receives at the inject site. The guard
referenced is the agent-initiated `compact` tool's minimum-turns guard,
configured at `compaction.agent.min_turns_since_compaction` (default **3**).
> **Context compaction (round ≥ 2)**: The Orchestrator is the only long-lived
> context across rounds; per-round dimension outputs, synthesis, and
> convergence tracking must not accumulate unbounded. Once Phase B has posted
> the commit status (all round state persisted in the synthesis comment + commit status)
> and before dispatching the next
> round in Phase A, the Orchestrator MUST run `compact` to summarize the
> conversation through the current round, then re-read
> the latest synthesis comment via `工单评论 API(见 TERMINOLOGY` to restore
> `current_round` and convergence tracking. The
> Synthesizer's return summary (verdict, per-dimension results, re-audit
> targets) is carried in the compacted conversation; if it was lost, re-read
> the synthesis comment's fenced JSON block as fallback before proceeding.
> Skip on round 1
> (single-pass reviews never loop). Each round then starts from a bounded
> context anchored on file state, not conversation memory.
>
> This round-boundary compaction is mandatory, but it is NOT exempt from the
> `compact` tool's `min_turns_since_compaction` guard
> (`compaction.agent.min_turns_since_compaction`, default 3): no such exemption
> exists in the tool implementation. If the tool rejects with
> `insufficient_turns`, accept the rejection and continue into the next round
> without compacting — do NOT retry or work around the guard. A round with too
> few turns since the last compaction simply keeps its context; the synthesis
> comment + commit status still bound what accumulates.
## Canonical note line
There is no `*(Shared rule …)*` italic note line in any consumer — the inject
directive resolves to this file's full content (this header included) at load
time. Older revisions of this document described a two-step "copy the
canonical text into each mirror" sync procedure; that procedure no longer
exists and must not be reintroduced.
+35
View File
@@ -0,0 +1,35 @@
# Shared Rule — Large Prompts via `prompt_file`
> **Canonical source of truth.** This file is the single authoritative copy of
> the large-prompts-via-prompt_file rule. The following skills reference this
> rule via an `<!-- inject -->` comment and MUST NOT maintain their own copies:
>
> - `review-code/SKILL.md`
> - `implement/SKILL.md`
> - `frontend/SKILL.md`
>
> (The `roadmap`, `requirements-elicitation`, `design`, and `plan-iterations`
> referencing skills were archived 2026-08-21, [org-internal #3072] phase 3 — see
> `.octopus/archive/skills/`; their copies of this directive moved with them.)
>
> **When editing:** update this file only. Each skill references this file via a
> one-line `<!-- inject -->` directive — there are no verbatim mirrors to sync.
>
> This is a reference document, not a skill (no `SKILL.md` frontmatter), so the
> skill loader does not register it.
## Why a canonical copy exists
The `task-tool-discipline` rule in `AGENTS.md` requires large prompts to be
written to a file and passed via `prompt_file`. This guidance applies to every
skill that dispatches sub-agents with potentially large prompts. A single
canonical source prevents drift and ensures the rule, file path convention
(`/tmp/octopus/<slug>.md`), and justification stay consistent.
## Canonical text
> **Large Prompts via `prompt_file`**
>
> Follow the `task-tool-discipline` rule in `AGENTS.md`: any prompt expected to
> exceed ~1 KB MUST be written to a file first and passed via the `task` tool's
> `prompt_file` parameter (e.g. `/tmp/octopus/<slug>.md`), never inlined.
@@ -0,0 +1,75 @@
# Shared Final Report Template
> Canonical final-report markdown template shared across review skills. Read
> on-demand by the Orchestrator when writing the final report in Phase E.
```markdown
# {REVIEW_TYPE} Final Report
- **Project**: {slug}
- **Rounds completed**: {N}
- **Final verdict**: {PASS | WARN | FAIL}
- **Converged**: {true | false — reason}
## Dimension Summary
| Dimension | Final Verdict | Pass Rate | R1→RN |
| --------- | ------------- | --------- | ----- |
| ... | | | |
## Outstanding Findings
### Accepted Risks (for tech-debt issue creation in verify Phase 5.5)
> verify Phase 5.5 会将下表每项 Accepted Risk 创建为 Gitea issue
> label `tech-debt`,标题含 `TD-NNN`)。issue 集即技术债登记表——
> 不再有 `tech-debt.md` 文件或 `{slug}/tech-debt` wiki 页。
| Finding ID | Severity | Description | Recommended Reactivation Trigger |
|-----------|----------|-------------|--------------------------------|
| {ID} | HIGH/MEDIUM/LOW | {描述} | {可客观判断的条件} |
> Note: the Severity column uses the **TD-registry severity scale**
> (HIGH/MEDIUM/LOW → Priority/High|Medium|Low labels, feeding verify Phase
> 5.5's `## TD 登记` rows and promotion labels), NOT the review-finding
> severity scale (BLOCKER/MAJOR/MINOR/INFO) — reviewers assign the TD
> severity directly when accepting a risk.
### Other Open Items
...
## Review History
| Round | Verdict | Blockers | Majors | Minors |
| ----- | ------- | -------- | ------ | ------ |
| ... | | | | |
## Round-1 Findings Summary
> Mandatory for `review-code` (Phase E step 2.55): one row per round-1
> finding, produced by the round-1 Synthesizer as
> `.octopus/runs/{slug}/reviews/{stage}/round1/findings-summary.md`
> (`_shared/review-pipeline-phases.md` Phase B step 3 "Round-1 findings
> digest") and copied verbatim by the Orchestrator — the Orchestrator does
> NOT read the findings JSONs — before they are deleted at archive-at-close.
> This table is the durable record retro M7 reads. Other targets MAY include
> it when round-1 findings data exists.
| Dim | Finding ID | Severity | Summary |
| --- | ---------- | -------- | ------- |
| ... | | | |
```
## Placeholders
- `{REVIEW_TYPE}` — the value the skill's Phase Bindings declares, used
verbatim in the title (`# {REVIEW_TYPE} Final Report`): "Code Review"
(review-code — human label; its commit-status context is separately fixed
to `pipeline/review-code`), or the machine ids `audit-process` /
`review-dag` (review-artifact targets — same id feeds the
`pipeline/{REVIEW_TYPE}` commit-status context; see review-artifact Phase
Bindings). (The design-space/plan targets were archived 2026-08-21,
[org-internal #3072] phase 3.)
- `{DIMENSION_COUNT}` — 10 (code review; 9 in standalone-bugfix mode), 3 (review-dag single gate), or 10 (process audit); used in
the skill's SKILL.md reference text, not inside this template body
@@ -0,0 +1,168 @@
> Core 中立版(Increment 6a 改写,原 deferHard verbatim)。编号与条目结构严格不变(C-2 不变量);实例术语按 `core/adapters/TERMINOLOGY.md` 绑定。
# Shared Orchestrator Rules (Mandatory)
> Canonical Orchestrator behavior rules shared across review skills. Read
> on-demand by the Orchestrator. These rules constrain the Orchestrator's behavior;
> violating any of them invalidates the review.
1. **No scope narrowing**: The Orchestrator MUST NOT add qualifiers to the
reviewer prompt that discourage thoroughness. Forbidden phrases include:
"Focus on the most important issues", "Prioritize critical findings",
"Limit to top N items", "Be concise", or any wording that suggests the
reviewer should self-censor.
2. **Standardized prompts only**: Every reviewer receives the prompt from the
template field-for-field. The Orchestrator MUST NOT modify the template
structure, add constraints, or remove required sections.
3. **Quantitative verdicts only**: A PASS verdict REQUIRES pass_rate = 1.0
with quantitative_summary populated. The Orchestrator MUST NOT promote a
WARN to PASS based on subjective judgment. If pass_rate < 1.0, the
dimension is at best WARN; if pass_rate < 0.7 or any BLOCKER finding exists,
the dimension MUST be FAIL. INFO findings do NOT reduce `pass_rate` and do
NOT affect the dimension verdict — a dimension whose only findings are INFO
is PASS. INFO is dispositioned (`TD-candidate` / `DISCARDED`) at synthesis
and never enters the revision loop (see
`_shared/review-pipeline-phases.md` Phase D, "Severity handling — INFO
disposition").
4. **Synthesis is aggregation, not reinterpretation**: The Orchestrator MUST NOT
soften, rephrase, or downgrade reviewer findings during synthesis.
If a reviewer writes "BLOCKER", it stays "BLOCKER" in synthesis.
5. **Anonymous findings preservation**: If two reviewers independently flag
the same issue from different dimensions, preserve both entries. Do not
deduplicate by discarding one — cross-dimension agreement is itself a
signal.
6. **File-based communication**: Do not inline large text blocks in prompts.
Use wiki page paths. Reviewer output goes to the skill's configured findings
sink (see `_shared/review-pipeline-phases.md` "Output model — two findings
sinks"): either a Tier 1 local file
`<runs-root>/{slug}/reviews/{stage}/round{N}/findings-{DIMENSION_CODE}.json`
(validated by `reviewer-output.schema.json`) for migrated skills like
`review-code`, or an issue comment via `工单评论 API(见 TERMINOLOGY` (Pattern
5) for unmigrated skills. Tier boundary: `core/rules/two-tier-artifacts.md`.
Either way, reviewer output does NOT travel as agent reply text.
7. **Auditor independence**: Reviewers are read-only Explorers. Only the
{REVISION_ROLE} (a separate Worker, run in a different phase) may edit files.
The Orchestrator MUST NOT let a reviewer both judge and fix.
8. **Subagent dispatch contract** (binding — omitting `subagent_type` is the
#1 review-skill dispatch failure): every `task` tool call MUST resolve an
agent type. The skill prose uses role names ("Orchestrator", "Architect",
"Reviewer", "Developer", "Analyst", "Planner", "Verifier", "Remediator", "Synthesizer") — those are
`role` values, NOT agent types. Map them to the `task` tool parameters as
follows:
| Skill role | `role` param | `subagent_type` param |
| ----------------- | -------------- | --------------------- |
| Reviewer | `Reviewer` | `explorer` |
| Orchestrator | `Orchestrator` | `worker` |
| Architect | `Architect` | `worker` |
| Developer / Tester| `Developer` | `worker` |
| Analyst | `Analyst` | `worker` |
| Planner | `Planner` | `worker` |
| Verifier | `Verifier` | `worker` |
| Remediator | `Remediator` | `worker` |
| Synthesizer | `Synthesizer` | `worker` |
The `agent` type is declared in `roles/*.yaml` for the five roles that
have a role YAML — `producer.yaml` (worker), `reviewer.yaml` (explorer),
`tool.yaml` (explorer), `verifier.yaml` (worker), and `coordinator.yaml`
(worker; the pool coordinator session — not a task-dispatch role, so it
does not appear in the table above). The task tool derives
`subagent_type` from `role` automatically when `subagent_type` is omitted
AND the role's `agent` field is defined. All other roles in the table above
(Orchestrator, Architect, Developer, Analyst, Planner, Remediator,
Synthesizer) have NO role YAML and therefore MUST pass `subagent_type`
explicitly — never pass only `role` + `description` and assume a default.
Concrete example (dispatching a reviewer):
```
task(
description: "Review CMP dimension",
role: "Reviewer",
subagent_type: "explorer", // optional here (derivable from role), shown for clarity
permissions: [{ permission: "read", pattern: "*", action: "allow" }, ...],
temperature: 0.1,
prompt: <filled reviewer template>
)
```
Forbidden: a `task` call that names the role in prose ("Spawn the
Architect") but omits BOTH `subagent_type` and a defined-`agent` `role` —
it fails with `TaskMissingSubagentType`.
## Default Explorer Permissions
All review and audit skills that spawn Explorer sub-agents use the same
read-only BASE permission set. The canonical base template is:
```
permissions: [
{ permission: "read", pattern: "*", action: "allow" },
{ permission: "glob", pattern: "*", action: "allow" },
{ permission: "grep", pattern: "*", action: "allow" }
]
```
Each skill references this base template via `core/skills/_shared/review-orchestrator-rules.md`
rather than inlining the JSON array. This centralization ensures the base
permission surface stays consistent across all review/audit skills.
**Tier-1 local exception** ([org-internal #1988]): migrated skills that write raw per-dimension
findings to `<runs-root>/**` (e.g. `review-code`) add ONE scoped `edit` allow
to the base set — `{ permission: "edit", pattern: "<runs-root>/**", action:
"allow" }` — so the reviewer can write its OWN finding file. This is a
documented exception to the read-only base, not a divergence: it does NOT grant
edit on the code under review, so rule #7 (auditor independence) holds. See
`_shared/review-pipeline-phases.md` Phase A step 4 and `review-code/SKILL.md`
Phase A step 3 for the inline extended array.
Residual risk — accepted trade-off (TD/STD-F004, [org-internal #2688]): the grant is scoped
to `<runs-root>/**`, wider than the reviewer's own findings file. Per-file
narrowing was considered and rejected: findings paths are round- and
dimension-dependent (`{slug}/reviews/{stage}/round{N}/findings-{DIMENSION_CODE}.json`),
which makes per-file permission patterns brittle at spawn time, and `runs/**`
holds only transient Tier 1 artifacts (digest-summarized at archive). The
residual exposure — a misbehaving reviewer could overwrite a sibling stage's
Tier 1 files — is recorded here as accepted; auditor independence is
unaffected (code under review lives outside `runs/`).
### No bash for Explorer sub-agents (deliberate boundary, [org-internal #2115] RC4 / [org-internal #2126])
Explorer sub-agents (Reviewer / auditor / Tool roles) have **no bash**. The
`explorer` agent is built deny-by-default at
`<harness-package>/src/agent/agent.ts` (`"*": "deny"` + explicit `read`/`glob`/
`grep` allows + `bash: "deny"`), so the `permissions` array above is read-only
by construction — a reviewer cannot run commands, mutate state, or escape its
read surface even if a prompt tried to grant bash. This is a deliberate
security boundary that upholds rule #7 (auditor independence): a reviewer must
never alter the artifact under review or its environment.
**Reviewer guidance**: do everything with `read`, `glob`, `grep`, and the
`gitea-rest` skill (curl against `/api/v1`; `gitea-mcp` is retired). If a
review dimension seems to require running a command
(executing a build, running a test, computing a metric), that is the
Synthesizer / Verifier's job (Worker sub-agents, which DO have bash via the
`worker` agent defaults) — not the Reviewer's. Report the need in findings
instead of attempting bash.
> **[org-internal #2126] Task 2 conclusion — keep + document.** The `deny **` pushed per-tool
> at `<harness-package>/src/session/prompt.ts` is the **session tool-enable /
> disable map** (it disables `todowrite` / `task` / `experimental.primary_tools`
> for subagents and allows forwarded MCP tools). It is NOT the source of the
> Explorer bash deny — that comes from the `explorer` agent config above, which
> is the correct place. Relaxing it would break auditor independence; worker-
> class sub-agents already have bash wherever the pipeline needs to run
> commands. Decision: **keep the deny, document it here** (this section).
## Placeholder
- `{REVISION_ROLE}` — "Developer" (code review), "Analyst" (review-dag single
gate), or "Remediator" (process audit). The Worker role that revises
artifacts between rounds. (The Architect/Planner revision roles belonged to
the archived design-space / iteration-plan targets, [org-internal #3072] phase 3.)
@@ -0,0 +1,493 @@
> 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) | `<runs-root>/{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 `<runs-root>/`
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 `<runs-root>/{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 —
`<instance-root>/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 `gitea_wiki__*` tools are registered (the
Orchestrator checks its own tool catalog; these tools are forwarded to
reviewer subagents when registered — [org-internal #2116] raised the tool cap 50→100 so
they are present in normal runs). If available → continue to step 2.
- **Fallback path** (when `gitea_wiki__*` is NOT registered, e.g. a
cross-repo scenario [org-internal #2115]): verify the **local wiki clone** at
`<wiki-clone-root>/{repo}-wiki/` is readable —
`glob(pattern="*.md", path="<wiki-clone-root>/{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 (register the wiki MCP server, or
populate `<wiki-clone-root>/{repo}-wiki/`).
This probe is belt-and-suspenders: RC1 (tool cap, [org-internal #2116]) already fixed the
primary cause of missing `gitea_wiki__*` in subagents. It exists so a future
environment regression 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
`<runs-root>/{slug}/reviews/{stage}/round{N}/findings-{DIMENSION_CODE}.json`
(validated by `core/schemas/reviewer-output.schema.json`). The reviewer
is granted scoped write to `<runs-root>/**` (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
`<runs-root>/{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
`<runs-root>/{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 `<runs-root>/{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
`<runs-root>/{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
(`<runs-root>/{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
`<runs-root>/{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
`<runs-root>/{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) —
`<instance-root>/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 — `<instance-root>/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 `<runs-root>/{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
`<harness-package>/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 `gitea_pull__add_reviewer`, 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}.
@@ -0,0 +1,135 @@
> Core 中立版(Increment 6a 改写,原 deferHard verbatim)。编号与条目结构严格不变(C-2 不变量);实例术语按 `core/adapters/TERMINOLOGY.md` 绑定。
# Shared Reviewer Prompt Template
> Canonical template for spawning parallel Explorer reviewers. Read on-demand
> by the Orchestrator when dispatching reviewers. The Orchestrator substitutes
> skill-level constants (fixed per skill, documented in the skill's SKILL.md)
> and per-dimension values (filled from the skill's dimension mapping table).
The prompt text below is sent to each reviewer. Substitute all `{PLACEHOLDER}`
values before dispatch.
```
You are a {REVIEWER_ROLE}. Your responsibility is exactly the dimension(s)
assigned to you — one dimension in the standard dispatch, or the named group
of dimensions in a risk-tier grouped dispatch (review-code S/M tiers). Do NOT
expand or narrow your scope beyond the assignment. Examine every file
assigned to you and flag every issue you find, regardless of perceived
importance.
{BASELINE_SECTION}
## Your Dimension(s)
Code: {DIMENSION_CODE}
Label: {DIMENSION_NAME}
## Checklist Items You Must Verify
{CHECKLIST_ITEMS}
{FILE_SECTIONS}
## Instructions
{READ_FIRST_STEPS}
4. For each checklist item, determine whether the {ARTIFACT_NOUN} satisfies it.
Every checklist item must receive a PASS or FAIL verdict.
Items that are genuinely not applicable must be marked NA with a reason.
5. For every FAIL, produce a finding with: a unique id (pattern {FINDING_ID_PREFIX}-F{NNN},
e.g. {FINDING_ID_EXAMPLE}), severity, checklist ref, {FINDING_DETAILS}, and a concrete
suggestion for how to fix it. {FINDING_ID_PREFIX} is the finding's OWN
dimension code — equal to {DIMENSION_CODE} in the standard single-dimension
dispatch; for a grouped dispatch each finding's id uses its own dimension
code (e.g. COR-F001, TST-F001), NOT the joined group code
(never `COR+TST+DBT-F001`). When your dispatch is multi-slot
(reviewer_count > 1), use the slot-prefixed form {DIM}R{k}-F{NNN} with your
1-based slot k (e.g. TOPOR1-F001) so parallel slot files never collide on id.
{CONTRADICTION_RULE}
7. Assign severity strictly:
{SEVERITY_DEFINITIONS}
8. Count every PASS and FAIL. Compute pass_rate = passed / total_checks.
Verdict rules:
- PASS: pass_rate = 1.0 (no failed checks)
- WARN: pass_rate >= 0.7 and no BLOCKER findings
- FAIL: any BLOCKER finding OR pass_rate < 0.7
9. Place the computed counts in a `quantitative_summary` object with fields
`total_checks`, `passed`, `failed`, `pass_rate`, `na_count` (if any items were marked NA),
and the four severity counts `blocker_count`, `major_count`, `minor_count`,
`info_count` (count the BLOCKER/MAJOR/MINOR/INFO findings you produced —
the Synthesizer consumes these for aggregation).
10. Write findings to `{OUTPUT_FILE}`
using the JSON Schema defined at `core/schemas/reviewer-output.schema.json`.
Populate `reviewed_files` with the list of file paths you actually read
during review.
## What You Must NOT Do
- Do NOT fabricate results. If a tool you need (e.g. `gitea_wiki__get_page`,
`read`, `codegraph_*`) is unavailable, errors, or returns empty, report the
failure honestly — emit a finding stating the dimension could not be
reviewed due to the missing tool. NEVER invent findings, severities, or
file references you did not derive from a successful tool call. Fabricated
review output is a critical defect (per `core/rules/claim-discipline.md`
§ "Tool-failure honesty").
- Do NOT narrow your scope because "this seems minor".
- Do NOT skip checklist items because "{LOOKS_FINE_PHRASE}".
- Do NOT produce a PASS verdict to save effort. Base verdicts on data.
- Do NOT add findings for dimensions you are not assigned to.
- Do NOT read files outside your assigned list.
- Do NOT edit any file EXCEPT your own findings file at `{OUTPUT_FILE}`. You are read-only for all other files.
```
## Placeholders
### Skill-level constants (fixed per skill — see the skill's SKILL.md)
- `{REVIEWER_ROLE}` — reviewer identity (e.g. "code reviewer", "DAG reviewer", "process auditor")
- `{BASELINE_SECTION}` — the full baseline section heading + body (differs structurally
per skill: single design baseline vs. dual requirements+design baselines)
- `{READ_FIRST_STEPS}` — instruction steps 13 (what to read first; includes the `1.` `2.`
`3.` prefixes)
- `{ARTIFACT_NOUN}` — "code" or "artifact" (used in step 4)
- `{FINDING_DETAILS}` — the location/evidence phrase in step 5 (e.g. "file path and line
number, a summary, detailed explanation, quoted code evidence")
- `{FINDING_ID_EXAMPLE}` — example finding id (e.g. COR-F001, CMP-F001)
- `{FINDING_ID_PREFIX}` — the id prefix for findings: the finding's own
dimension code (equal to `{DIMENSION_CODE}` in the standard dispatch; the
own-code of each finding inside a group in a grouped dispatch; see step 5's
per-finding rule and the multi-slot `{DIM}R{k}-F{NNN}` form)
- `{CONTRADICTION_RULE}` — step 6 in full (includes the `6.` prefix); the rule for when
an artifact contradicts its baseline
- `{SEVERITY_DEFINITIONS}` — the four severity bullets (BLOCKER/MAJOR/MINOR/INFO) with
leading indentation matching step 7's sub-items
- `{LOOKS_FINE_PHRASE}` — "the code looks fine" / "the artifacts look fine"
### Per-dimension values (filled by the Orchestrator from the dimension mapping table)
- `{DIMENSION_CODE}` — the dimension code (e.g. COR, ARCH). For a grouped
dispatch (review-code S/M risk tiers, where one reviewer covers several
dimensions), fill with the joined group, e.g. `COR+TST+DBT` — the reviewer
then emits one findings file for the group with per-finding ids prefixed by
each finding's own dimension code.
- `{DIMENSION_NAME}` — the dimension label (e.g. Correctness, Architecture);
for a grouped dispatch, the joined labels of the group.
- `{CHECKLIST_ITEMS}` — the inline checklist items text for this dimension (read from
the checklist file; inlined so the reviewer does not need to open the checklist)
- `{FILE_SECTIONS}` — the file sections block (skill-specific section headers + the
dimension's assigned file paths; include the "(none)" note where applicable)
- `{OUTPUT_FILE}` — the output target for this reviewer's findings. The skill
picks the findings sink (see `_shared/review-pipeline-phases.md` "Output model
— two findings sinks"):
- Tier 1 local (migrated skills, e.g. `review-code`): the absolute path inside
the worktree, e.g.
`<worktrees-root>/<repo>-<slug>/<runs-root>/{slug}/reviews/{stage}/round{N}/findings-{DIMENSION_CODE}.json`
— a JSON file validated by `core/schemas/reviewer-output.schema.json`.
The reviewer is granted scoped write to `<runs-root>/**` for this.
When the dispatch assigns MORE THAN ONE reviewer to this dimension
(dim_split > 1), each reviewer writes its OWN slot file
`findings-{DIMENSION_CODE}-r{k}.json` (k = its 1-based slot) with
`reviewer_slot` / `reviewer_count` set — parallel reviewers never share
one file.
- Tier 2 comment (unmigrated fallback): post an issue comment via
`工单评论 API(见 TERMINOLOGY` (Pattern 5) with a fenced ` ```json ` block,
labeled `{DIMENSION_CODE} review — round {N}` (multi-slot reviewers append
` · r{k}` to the label).
@@ -0,0 +1,57 @@
# Shared Revision Prompt Template
> Canonical prompt for spawning the {ROLE} (Worker) that revises artifacts
> between review rounds. Read on-demand by the Orchestrator in Phase C. The
> Orchestrator substitutes skill-level constants before dispatch.
```
Read `{TASK_FILE}` for your input files. {REVISION_SCOPE}
{RESOLUTION_RULE}
Your task file references ONLY dimensions with open BLOCKER, MAJOR, or MINOR
findings. INFO findings are NEVER in your scope — they are dispositioned
(`TD-candidate` or `DISCARDED`) at synthesis and excluded from revision (see
`.octopus/skills/_shared/review-pipeline-phases.md` Phase D, "Severity
handling — INFO disposition"). Do NOT act on or chase INFO observations.
From round 2 onward, every remaining purely cosmetic MINOR finding
({COSMETIC_MINOR_EXAMPLES} — per the severity definition above) MUST be explicitly
dispositioned: either fix it and mark it resolved, or waive it by recording
`WAIVED-{finding-id}` (e.g. `{WAIVED_EXAMPLE}`) with a one-line reason and setting
the finding's status to `WAIVED`. Cosmetic MINORs MUST NOT carry forward as open
items beyond round 2 — an unresolved cosmetic MINOR is itself a process violation.
This is the canonical definition of the `WAIVED-{id}` convention referenced in
`{CHECKLIST_REF}`.
After revising, report a summary of changes made:
- Files modified: {FILES_MODIFIED_LINE}
- Findings addressed: {counts by severity}
- Findings deferred with justification: {list}
- Findings waived as cosmetic MINOR (round 2+): {WAIVED-{id} → reason}
Persist that summary to `{RETURN_FILE}` as your LAST action, BEFORE
returning it — worker-report persistence ([org-internal #2847], see
`.octopus/skills/_shared/worker-report-persistence.md`). The persisted copy
is the report of record; if your completion notification is lost, the
Orchestrator recovers the revision outcome from this file.
```
## Placeholders
### Skill-level constants (fixed per skill — see the skill's SKILL.md)
- `{ROLE}` — "Developer" (code review), "Analyst" (review-dag single gate), or "Remediator" (process audit)
- `{TASK_FILE}``task-developer`, `task-architect`, `task-analyst`, `task-planner`, or `task-remediator` (wiki page name under `{slug}/reviews/{stage}/round{N}/`)
- `{REVISION_SCOPE}` — the opening paragraph: what to revise and the
minimal/surgical constraint
- `{RESOLUTION_RULE}` — the design-deviation / resolution-comment rule
- `{COSMETIC_MINOR_EXAMPLES}` — the parenthetical examples of cosmetic MINORs
- `{WAIVED_EXAMPLE}` — example waived id (e.g. `WAIVED-COR-R2-001`)
- `{CHECKLIST_REF}` — the checklist file(s) the WAIVED convention is defined in
- `{FILES_MODIFIED_LINE}``{list}` or `{list by artifact type}`
- `{RETURN_FILE}` — the Tier 1 return-persistence target for this dispatch,
following the skill's canonical stage path (e.g. review-code:
`.octopus/runs/{slug}/reviews/code/round{N}/revision-return.md`); default
per convention: `.octopus/runs/{slug}/workers/{chunk-id}-worker-{seq}.md`,
lightweight fallback `/tmp/octopus/{chunk-id}-worker-{seq}.md`
@@ -0,0 +1,86 @@
> Core 中立版(Increment 6a 改写,原 deferHard verbatim)。编号与条目结构严格不变(C-2 不变量);实例术语按 `core/adapters/TERMINOLOGY.md` 绑定。
name: Coordinator
purpose: >
Pool dispatch coordinator (billing-v2 [org-internal #4041] §7, REQ-F-012): a wake-driven,
STATELESS decision-maker session living in the pool pod alongside workers.
List eligible issues (Gitea API, scoped token) → produce DAG-aware dispatch
proposals → commit each dispatch through the N-16 assignment API
(POST /api/pools/:repo/assignments). The coordinator decides; the
control-plane dumbwaiters make the commit atomic. It never holds wallet
internals, never bypasses the central ledger, and never merges (E-4).
# Layer 5 — Executable
agent: worker
model: null
agents_md:
- workflow-routing
- claim-discipline
- code-graph
- compact
# Stateless iron rule (§7.1): the coordinator's ENTIRE dispatch state is the
# work_order_assignments table. After any wake (first start, re-wake after
# idle, or takeover after the previous coordinator died) the first action is
# ALWAYS: GET /api/pools/:repo/assignments?state=assigned — rebuild the
# in-flight view from the table, never from memory or notes. Dying costs
# nothing; the pool reconciler migrates the role to the earliest surviving
# member by convention (no election).
#
# Decision procedure (AC-1) — full text: docs/runbooks/pool-coordinator.md.
# 1. recover — read the assignment table (state=assigned) + org
# notifications (pools.assignment.expired = re-dispatch inbox)
# 2. list — eligible issues via Gitea API (scoped token): labeled
# Kind/* and unassigned/without active assignment row
# 3. propose — DAG-aware: respect blockedBy edges (epic DAG / node deps),
# sibling load (least-loaded member first), and the N>1
# self-assign constraints below; fast path (AC-1 简单场景):
# exactly one eligible issue and one available member →
# deterministic pick, no elaborate deliberation needed
# 4. commit — one POST per dispatch; proposal payload carries the audit
# trace (§audit contract below); 409/403 envelopes are
# terminal outcomes, not retried blindly
#
# Self-assign constraints (AC-2, §7.2 N>1) — the ledger is identity-blind, so
# the constraints live HERE and are enforced by convention + audited via the
# trace: least-loaded ordering puts self LAST (self-assign only when no other
# member is available); the coordinator holds at most 1 active assignment at
# a time, same cap as any worker; self-dispatch still goes through the same
# POST (铁律 — never "start working because I am the coordinator"). N=1:
# self-dispatch is the only form and is NOT a special branch.
#
# Audit contract (AC-4/AC-6): every POST carries proposal =
# { version: 1,
# decision: { coordinator, self_position: "last"|"n/a", held_active,
# cap: 1, fast_path: boolean, reason },
# eligible: [{ issue, blockedBy: [issue,...] }...],
# chosen: { issue, member } }
# The trace lands verbatim on the assignment row (jsonb proposal_payload),
# is queryable via GET /api/pools/:repo/assignments, and is exported in the
# assignmentAuditRecord shape (runs archive / pool-drill.sh). LLM decisions
# are not replayable — the recorded decision + the deterministic commit is
# the audit story.
#
# Security posture (AC-5, see docs/security/threat-model.md §自治批处理):
# the coordinator authenticates with a workspace-level API token
# (TD-687/[org-internal #3898] owner-minted, jti-revocable, role claim org_role). TODAY'S
# mint shape hardcodes org_role=viewer — read-capable only (assignment table
# + pool surfaces); the editor-capable mint needed for dispatch submission is
# an explicit deployment-time decision on the [org-internal #3898] channel (not landed with
# N-19). Either way the token can NOT mint tokens, touch wallet internals, or
# merge PRs — the merge gate stays human (E-4).
#
# Worker-report persistence ([org-internal #2847] — see
# core/skills/_shared/worker-report-persistence.md): a Coordinator
# sub-agent persists its FINAL RETURN REPORT to disk as its LAST action,
# before returning — <runs-root>/{slug}/workers/{chunk-id}-worker-{seq}.md
# (Tier 1 preferred; else /tmp/octopus/{chunk-id}-worker-{seq}.md), and
# appends its working-notes entry at stage exit per
# core/templates/runs-layout.md. The persisted copy is the report of
# record; re-read persisted artifacts after any compaction, never rely on
# conversation memory.
compact:
preserve:
- "docs/runbooks/pool-coordinator.md — the decision procedure is file-driven, never memory-driven; re-read after any compaction"
- "the recovered in-flight view (GET /api/pools/:repo/assignments?state=assigned) — the coordinator holds NO dispatch state in memory; re-read the table first after every wake/compaction"
- "the audit trace schema (decision.self_position / decision.held_active / decision.cap / eligible[].blockedBy) — drift here breaks AC-6 auditability"
- "self-assign constraints: self LAST in least-loaded order, active-assignment cap 1, self-dispatch goes through the same POST — never a special branch (§7.2 铁律)"
+41
View File
@@ -0,0 +1,41 @@
> Core 中立版(Increment 6a 改写,原 deferHard verbatim)。编号与条目结构严格不变(C-2 不变量);实例术语按 `core/adapters/TERMINOLOGY.md` 绑定。
name: Producer
purpose: >
Write production artifacts — requirements, design, plans, code, fixes,
refactors, releases, retrospectives. The specific task, its checklist, and
the current pipeline stage are injected by the calling skill.
# Layer 5 — Executable
agent: worker
model: null
agents_md:
- code-graph
- testing
- type-checking
- claim-discipline
- compact
# Worker-report persistence ([org-internal #2847], retro-2696 AI#2 — see
# core/skills/_shared/worker-report-persistence.md): every Producer
# sub-agent persists its FINAL RETURN REPORT to disk as its LAST action,
# before returning — <runs-root>/{slug}/workers/{chunk-id}-worker-{seq}.md
# (Tier 1 preferred; skill-canonical stage path when the skill defines one),
# else /tmp/octopus/{chunk-id}-worker-{seq}.md. The persisted copy is the
# report of record; the task notification is a convenience copy.
#
# Working-notes contract ([org-internal #2600] — see core/templates/runs-layout.md §
# working-notes.md): at stage exit the Producer appends ONE entry (≤30 lines:
# decisions and why, dead ends, reviewer feedback that changed the work, hints
# for the next stage) to <runs-root>/{slug}/working-notes.md, and registers
# its index.json row (type working-notes) once if absent.
#
# Role-level compaction contract (harness-enforced — see core/rules/compact.md
# § "Role-level re-read set"). After any compaction, a Producer sub-agent re-reads
# these persisted artifacts before continuing; it never relies on conversation
# memory for them.
compact:
preserve:
- "<runs-root>/{slug}/working-notes.md (re-read FIRST, before formal artifacts — carries prior stages' tacit context: decisions, dead ends, reviewer feedback)"
- "the work item / node spec being implemented (issue body or the frozen DAG copy {epic-slug}/dag + {epic-slug}/dag-nodes/{node-id} subpages)"
- "the node's design baseline: node spec + cross-session edge contracts in {epic-slug}/dag (design detail is folded into node AC + contracts)"
- "the acceptance criteria for the current node (node acceptance_criteria in {epic-slug}/dag, or the task ticket body)"
+30
View File
@@ -0,0 +1,30 @@
> Core 中立版(Increment 6a 改写,原 deferHard verbatim)。编号与条目结构严格不变(C-2 不变量);实例术语按 `core/adapters/TERMINOLOGY.md` 绑定。
name: Reviewer
purpose: >
Review one dimension of an artifact against its checklist, read-only.
Find issues that would block merge. One reviewer per dimension, all
running in parallel.
# Layer 5 — Executable
agent: explorer
model: null
agents_md:
- code-graph
- claim-discipline
- ticket-lifecycle
- compact
# Working-notes contract ([org-internal #2600] — see core/templates/runs-layout.md §
# working-notes.md): the Reviewer side appends ONE distilled entry via the
# Synthesizer at review convergence — never per dimension dispatch (9
# dimension dispatches = 1 entry) — to <runs-root>/{slug}/working-notes.md.
#
# Role-level compaction contract (harness-enforced — see core/rules/compact.md
# §"Role-level re-read set"). A Reviewer is read-only and short-lived (one
# dimension per dispatch); after any compaction it re-reads its checklist and
# the artifact under review, never conversation memory.
compact:
preserve:
- "<runs-root>/{slug}/working-notes.md (re-read FIRST — prior stages' tacit context; also the file the Synthesizer's convergence entry appends to)"
- "the dimension checklist being applied (core/checklists/*.md)"
- "the artifact under review ({slug}/ wiki page or PR diff)"
+26
View File
@@ -0,0 +1,26 @@
> Core 中立版(Increment 6a 改写,原 deferHard verbatim)。编号与条目结构严格不变(C-2 不变量);实例术语按 `core/adapters/TERMINOLOGY.md` 绑定。
name: Tool
purpose: >
Specialized tool agent for image interpretation and other vision/utility
tasks that require a specific model.
# Layer 5 — Executable
agent: explorer
model: opencode/claude-haiku-4-5
agents_md:
- code-graph
- claim-discipline
- compact
# Working-notes contract ([org-internal #2600] — see core/templates/runs-layout.md §
# working-notes.md): Tool agents append to <runs-root>/{slug}/working-notes.md
# ONLY when the task produced a load-bearing finding (e.g. image evidence that
# changed a decision) — not for routine single-shot output.
#
# Role-level compaction contract (harness-enforced — see core/rules/compact.md
# §"Role-level re-read set"). Tool agents are single-shot; after any compaction
# they re-read the task prompt's input artifact, never conversation memory.
compact:
preserve:
- "<runs-root>/{slug}/working-notes.md (re-read FIRST — prior stages' tacit context)"
- "the input artifact named in the task prompt (image path, file, or URL)"
+40
View File
@@ -0,0 +1,40 @@
> Core 中立版(Increment 6a 改写,原 deferHard verbatim)。编号与条目结构严格不变(C-2 不变量);实例术语按 `core/adapters/TERMINOLOGY.md` 绑定。
name: Verifier
purpose: >
Run the iteration's DoD matrix, integration tests, NFR validation, and
regression checks. The iteration is not Done until all DoD items pass.
# Layer 5 — Executable
agent: worker
model: null
agents_md:
- code-graph
- testing
- ticket-lifecycle
- type-checking
- claim-discipline
- compact
# Worker-report persistence ([org-internal #2847], retro-2696 AI#2 — see
# core/skills/_shared/worker-report-persistence.md): the Verifier
# persists its FINAL RETURN REPORT (DoD verdict + evidence pointers) to disk
# as its LAST action, before returning —
# <runs-root>/{slug}/workers/{chunk-id}-worker-{seq}.md (Tier 1 preferred),
# else /tmp/octopus/{chunk-id}-worker-{seq}.md. The persisted copy is the
# report of record; the task notification is a convenience copy.
#
# Working-notes contract ([org-internal #2600] — see core/templates/runs-layout.md §
# working-notes.md): the Verifier appends ONE entry at verify exit (what the
# DoD sweep caught, flaky/deferral context the next run should know). The
# Phase 5.5 archive step additionally inlines the final working-notes.md
# content into the archive bundle's working_notes field.
#
# Role-level compaction contract (harness-enforced — see core/rules/compact.md
# §"Role-level re-read set"). After any compaction, a Verifier re-reads these
# persisted artifacts before continuing; it never relies on conversation memory.
compact:
preserve:
- "<runs-root>/{slug}/working-notes.md (re-read FIRST, before formal artifacts — carries prior stages' tacit context: decisions, dead ends, reviewer feedback)"
- "the DoD matrix for the current iteration ({slug}/05-verify-*)"
- "the acceptance criteria (node acceptance_criteria in the frozen DAG copy {epic-slug}/dag, or the task ticket body)"
- "the final code-review report ({slug}/reviews/code/final/report)"
+236
View File
@@ -0,0 +1,236 @@
> Core 中立版(Increment 6a 改写,原 deferHard verbatim)。编号与条目结构严格不变(C-2 不变量);实例术语按 `core/adapters/TERMINOLOGY.md` 绑定。
# Synthesis Task — Round {ROUND_NUMBER}
> Shared template filled by the Orchestrator and dispatched to the Synthesizer
> (Worker) during Phase B of all review and audit skills. The Orchestrator
> writes this template to the skill's configured prompt location (Tier 1 local
> `<runs-root>/{slug}/reviews/{stage}/round{N}/task-synthesizer.md` for
> `tier1-local` skills like `review-code`; `/tmp/octopus/synthesis_task_{slug}_round{N}.md`
> + a Gitea wiki page for `tier2-comment` skills) for `prompt_file` dispatch.
>
> The Synthesizer reads all dimension findings from the skill's findings sink
> (local files or issue comments), posts the synthesis to the pipeline PR /
> source issue comment — for audit-process runs, the wiki page
> `audit/{date}/round{N}/synthesis` — with a fenced JSON block (Tier 2 human
> gate — always Gitea), updates the commit status, and returns a compact
> summary (≤ 2KB).
## Input
- **Target**: PR #{PR_NUMBER} (pipeline mode) or issue #{ISSUE_NUMBER} (standalone mode)
— for audit-process runs, the wiki page `audit/{date}/round{N}/synthesis`.
Post the synthesis to this target.
- **Findings sink**: `{FINDINGS_SINK}``tier1-local` or `tier2-comment`
(set by the skill; see `_shared/review-pipeline-phases.md`).
- **Dimension findings** — already produced by individual reviewers:
- `tier1-local`: read the finding files at
`<runs-root>/{slug}/reviews/{stage}/round{N}/findings-{DIMENSION_CODE}.json`,
each a JSON document matching `core/schemas/reviewer-output.schema.json`.
Read every `findings-*.json` in that round directory. A dimension with
multiple reviewer slots (dim_split > 1) contributes several files
(`findings-{DIM}-r1.json`, `-r2.json`, …) — aggregate them as ONE
dimension's findings (union of findings; worst verdict wins).
- `tier2-comment`: read the dimension issue comments via
`工单评论 API(见 TERMINOLOGY` (Pattern 3, `_shared/gitea-read-patterns.md`).
Each comment body contains a `**{DIMENSION_CODE} review — round {N}**` header
followed by a fenced ` ```json ` block matching
`core/schemas/reviewer-output.schema.json`.
- **Dimensions marked UNRESOLVED** (reviewer crashed/timed out, no finding exists):
`{UNRESOLVED_DIMENSIONS}`
Exclude these from aggregation.
- **Synthesis schema**: `core/schemas/synthesis.schema.json`
- **Commit SHA**: `{SHA}` (the HEAD commit being reviewed).
- **Review type**: `{REVIEW_TYPE}` — one of `review-code`, `review-dag`,
`audit-process` (the `review-design-space` / `review-iteration-plan` /
`review-roadmap` targets were archived 2026-08-21, [org-internal #3072] phase 3 — their
historical synthesis data remains readable).
## Your Job
1. Read all dimension findings from the configured sink. For each dimension,
extract:
- `verdict` (PASS / WARN / FAIL / UNRESOLVED)
- `pass_rate`
- `blocker_count`, `major_count`, `minor_count`, `info_count`
- `findings` array (each finding has: id, severity, checklist_ref, summary)
1b. **Finding-id uniqueness post-check (MANDATORY)**: after reading ALL
findings files of the round (including every `-r{k}` slot file), verify
that `id` values are unique across the whole round. The reviewer-output
schema's `uniqueItems` compares whole objects and does NOT enforce id
uniqueness — this step is the enforcement. On a duplicate id: do NOT
silently drop or renumber it; keep both findings in the raw files, note
the collision in the synthesis digest, and mark the affected dimension
WARN with the collision listed (the dispatch/prompt defect that produced
it — e.g. a missing slot prefix — is the fix target).
2. Compute `aggregated_stats`:
- `total_dimensions`: count of all dispatched dimensions (including UNRESOLVED)
- `passed_dimensions`, `warn_dimensions`, `failed_dimensions`
- `total_findings`, `blockers`, `majors`, `minors`, `infos`
- `unresolved_dimensions`: count of UNRESOLVED
3. Collect `top_findings` — top 10 ranked by severity:
- Order: BLOCKER > MAJOR > MINOR > INFO
- Within each severity, order by dimension code alphabetically
- Each entry: `{ id, dimension, severity, checklist_ref, summary }``id` is
the source reviewer finding id (Tier 1 findings file; slot-prefixed for
multi-slot dimensions), so each headline traces back to its raw finding
4. Compute `overall_verdict`:
- **PASS**: all available dimensions have verdict PASS.
- **WARN**: no FAIL dimensions AND no BLOCKER findings.
- **FAIL**: any FAIL dimension OR any BLOCKER finding.
5. Post the synthesis as a **PR comment** (pipeline mode) or **issue comment**
(standalone mode) via `工单评论 API(见 TERMINOLOGY`:
- **Exception — `audit-process` target**: process audits have no source
issue, so post the synthesis as a **wiki page** `audit/{date}/round{N}/synthesis`
via `wiki 读写 API(见 TERMINOLOGY` instead of a PR/issue comment (see
`_shared/review-pipeline-phases.md` Phase B step 3 and review-artifact/SKILL.md
process target Output Artifacts). The commit-status `target_url` then
carries that wiki page's `html_url`.
- The comment body MUST contain a **markdown summary** (dimension table,
top findings, verdict) followed by a **fenced ` ```json ` block** with
the structured synthesis data.
- Format:
```
## Synthesis — Round {N}
**Verdict**: {PASS|WARN|FAIL}
**Target**: PR #{N} | Issue #{N} (audit-process: wiki `audit/{date}/round{N}/synthesis`)
**Dimensions**: {passed} passed, {warned} warned, {failed} failed, {unresolved} unresolved
**Findings**: {B} BLOCKERs, {M} MAJORs, {m} MINORs, {I} INFOs
### Dimension Summary
| Dimension | Verdict | Pass Rate | B | M | m | I |
|-----------|---------|-----------|---|---|---|---|
| ... | ... | ... | | | | |
### Top Findings
1. [{DIM_CODE}] [{SEVERITY}] {checklist_ref}: {summary}
2. ...
```json
{
"round": N,
"overall_verdict": "PASS|WARN|FAIL",
"dimensions": {
"DIM_CODE": { "verdict": "PASS|WARN|FAIL|UNRESOLVED", "pass_rate": N, "blocker_count": N, "major_count": N, "minor_count": N, "info_count": N }
},
"aggregated_stats": {
"total_dimensions": N,
"passed_dimensions": N,
"warn_dimensions": N,
"failed_dimensions": N,
"total_findings": N,
"blockers": N,
"majors": N,
"minors": N,
"infos": N,
"unresolved_dimensions": N
},
"top_findings": [
{ "id": "CODE-F001", "dimension": "CODE", "severity": "BLOCKER|MAJOR|MINOR|INFO", "checklist_ref": "X.Y", "summary": "..." }
]
}
```
```
6. Post a **commit status** via REST:
```
POST /api/v1/repos/Octopus/octopus/statuses/{SHA}
Authorization: token {GITEA_TOKEN}
Content-Type: application/json
{
"state": "success"|"failure",
"context": "pipeline/{REVIEW_TYPE}",
"description": "Round {N}: {PASS|WARN|FAIL} — {passed}/{total} dims passed, {B}B {M}M",
"target_url": "{link to synthesis comment}"
}
```
- `state` is `"success"` for PASS or WARN, `"failure"` for FAIL (any BLOCKER).
- `context` uses the review type: `pipeline/review-code`,
`pipeline/review-dag`, `pipeline/audit-process` (slug `audit`).
(See `_shared/gitea-write-patterns.md` Pattern 8 for the full commit-status recipe.)
7. Append one `history[]` entry for this round to the run's review-status file
`<runs-root>/{slug}/reviews/{stage}/status.json` (validated by
`core/schemas/review-status.schema.json`): `round`,
`overall_verdict`, `blockers`, `majors`, `minors`, and update the file's
top-level `current_round` to this round number. This is the durable
per-round audit history (defined by `core/schemas/review-status.schema.json`
`history[]`; mirrors the audit-process checklist TRC 10.4). Initialization is the **Orchestrator**'s
job (Phase A step 0.5 of `_shared/review-pipeline-phases.md`): the file is
created before round 1 with the full schema-required field set `slug`,
`started_at`, `current_round`, `max_rounds`, `converged`, `history` — do NOT
create it here; the Synthesizer only appends `history[]` and updates
`current_round`.
8. **Round 1 only — findings digest** (rounds ≥ 2: skip this step): write the
rendered `## Round-1 Findings Summary` table (columns
`| Dim | Finding ID | Severity | Summary |`, one row per round-1 finding;
Summary = one-line 摘由/root cause) to
`<runs-root>/{slug}/reviews/{stage}/round1/findings-summary.md`,
distilled from the round-1 findings you just read in your own context, and
append the `index.json` row (type `review-synthesis`, stage
`{REVIEW_TYPE}`, produced_by `Synthesizer`). The Orchestrator copies this
file verbatim into the final report's Round-1 Findings Summary section at
Phase E (mandatory for `review-code` step 2.55; other targets MAY include
it — `_shared/review-final-report-template.md`) and does NOT read the
findings files itself (`_shared/review-pipeline-phases.md` Phase B step 3
"Round-1 findings digest").
9. Persist your return summary (the exact text from the Return Format
below) to `<runs-root>/{slug}/reviews/{stage}/round{N}/synthesis-return.md`
(Tier 1) as your LAST action, BEFORE returning it — worker-report
persistence ([org-internal #2847], `_shared/worker-report-persistence.md`). The persisted
copy is the report of record; if your completion notification is lost, the
Orchestrator recovers the round verdict from this file.
## Governance (Binding)
- **Rule 4 — Aggregation, not reinterpretation**: You MUST NOT soften, rephrase,
or downgrade reviewer findings. If a reviewer writes "BLOCKER", it stays
"BLOCKER" in synthesis.
- **Rule 5 — Anonymous findings preservation**: If two reviewers independently
flag the same issue from different dimensions, preserve both entries. Do not
deduplicate by discarding one — cross-dimension agreement is itself a signal.
- **Schema fidelity**: The fenced JSON block MUST validate against
`synthesis.schema.json`. Read the schema file first if unsure of field names.
## Return Format (Binding — ≤ 2KB)
Return ONLY this summary as your final message. Do NOT include the full
synthesis JSON — it is already posted to the pipeline PR / source issue
comment (for audit-process runs, the wiki page `audit/{date}/round{N}/synthesis`).
Persist this summary verbatim to
`<runs-root>/{slug}/reviews/{stage}/round{N}/synthesis-return.md` before
returning (step 9 above — worker-report persistence [org-internal #2847]).
```
## Synthesis Complete
**Round**: {N}
**Verdict**: {PASS|WARN|FAIL}
**Pass Rate**: {avg_pass_rate}
**Dimensions**: {passed} passed, {warned} warn, {failed} failed, {unresolved} unresolved
**Findings**: {B} BLOCKER, {M} MAJOR, {m} MINOR, {I} INFO
### Failed/Warn Dimensions (re-audit targets for next round):
- {DIM_CODE}: verdict={FAIL|WARN}, findings={count} ({finding_ids})
- ...
### Top Findings (for revision task):
1. [{DIM_CODE}] [{SEVERITY}] {checklist_ref}: {summary}
2. ...
### Artifacts Posted:
- Synthesis comment on {PR_OR_ISSUE} #{N} (audit-process: wiki `audit/{date}/round{N}/synthesis`)
- Commit status: pipeline/{REVIEW_TYPE} → {success|failure}
```
@@ -0,0 +1,98 @@
# Worker Report Persistence (mandatory)
> Canonical convention for every worker sub-agent dispatch (roles whose
> `agent:` is `worker` — Producer sub-roles, Verifier, Synthesizer, …),
> foreground or background (`task background=true`). Origin: retro WWW#5 /
> [org-internal #2696] action item #2 ([org-internal #2847]) — at least one confirmed case where a
> background worker's completion report reached the orchestrating session
> only via the task-tool notification; the notification was lost and the
> report evaporated, leaving nothing but reverse-engineering from artifacts.
## The problem
A worker's final return report (status, verdict, judgment, evidence
pointers, open items) travels to the orchestrating session through a single
channel: the task completion notification. If that notification is lost —
stream break, orchestrator interrupted before receipt, session restart —
the report is gone even though the worker finished its work. Conclusions
that exist only in a notification are not durable.
## The rule (Iron Law)
**Every worker MUST persist its final return report to disk as its LAST
action, BEFORE returning.** The persisted copy is the report of record; the
in-conversation return is a convenience copy. No worker return may exist
only in the task notification.
## Where to persist (path selection, in order)
1. **Tier 1 run workspace (preferred when it exists)** — if
`.octopus/runs/{slug}/` exists (see `.octopus/templates/runs-layout.md`),
write the report to:
- Default: `.octopus/runs/{slug}/workers/{chunk-id}-worker-{seq}.md`
- Skill-canonical override: when the dispatching skill already owns a
stage directory for this worker, the skill's canonical path replaces
the generic name (e.g. the review-code Synthesizer writes
`reviews/{stage}/round{N}/synthesis-return.md` next to its task file).
- Register one `index.json` row: `type: "other"`, `schema: null`
(markdown), `stage`: producing stage, `produced_by`: role name.
2. **Lightweight fallback (no run workspace)** — standalone / ad-hoc
sessions with no `.octopus/runs/{slug}/`: write to
`/tmp/octopus/{chunk-id}-worker-{seq}.md`. Survives session death and
compaction; not expected to survive machine restarts — acceptable for
the notification-loss window it covers.
### Naming
- `{chunk-id}` — the owning work identifier stated in the dispatch prompt
(ticket number, `WI-N`, chunk id, e.g. `2847`, `WI-3`).
- `{seq}` — 1-based dispatch sequence of THIS worker within that chunk-id
(a chunk dispatched a second time → `seq` 2). For review loops the round
number in the skill-canonical path plays this role.
- The dispatching Orchestrator MUST state both values (or the canonical
path) in the dispatch prompt — see "Dispatch duty" below.
## What to persist
The **verbatim final return report** — the exact text the worker returns
via the task result: status (`done` / `blocked`), verdict if any, key
results, evidence pointers (commit sha, test names, `file:line`), and open
items. Markdown, no schema. Keep it identical to the returned summary so
recovery reads exactly what the orchestrator would have received.
## Orchestrator recovery path (notification missing)
When a dispatched worker's completion notification never arrives —
background task result lost, or the orchestrating session was interrupted
and resumed — the orchestrating session recovers the worker's conclusion
from disk:
1. Identify the dispatch's `{chunk-id}` (from the dispatch record /
working-notes / task log).
2. Read, in order, the newest match by `{seq}` / mtime:
- `.octopus/runs/{slug}/workers/{chunk-id}-worker-*.md`
- the skill-canonical return file when the dispatch named one (e.g.
`reviews/{stage}/round{N}/synthesis-return.md`)
- `/tmp/octopus/{chunk-id}-worker-*.md`
3. The newest persisted report IS the worker's conclusion of record —
resume the pipeline from it. Do NOT re-dispatch a worker whose persisted
report shows `done`; re-dispatch (or resume via `task_id`) only on
`blocked` or a missing file.
## Dispatch duty (high-frequency dispatch points)
Every dispatch prompt for a worker MUST carry: (a) the persist instruction
("persist your final report to `{path}` before returning, per
`_shared/worker-report-persistence.md`"), and (b) the concrete target path
with `{chunk-id}`/`{seq}` or the skill-canonical location filled in. The
high-frequency templates carry it inline:
- `implement/SKILL.md` — Agent Role responsibilities + Phase 5 (Report)
persist step (applies to every mode's report phase).
- `review-code/SKILL.md` Phase B / Phase C dispatch steps, via the shared
templates `_shared/synthesis_task.md` and
`_shared/review-revision-prompt.md`.
Reviewer (Explorer) dispatches are covered structurally: their findings
JSON is already a Tier 1 persisted artifact, and the Synthesizer's
persisted return is the round's recoverable conclusion.