Cross Source Review
Drives same-family (same-family, fresh-context) + different-family (different-family, different-family) LLM cross multi-round review to substantive convergence on doc-shaped artifacts — a requirements input before blueprint-crafting, a design doc, a wiki page. It is the missing convergence layer upstream of blueprint-crafting (which is process-axis, same-family only) and reusable externally (e.g. fedaot wiki review).
Core Positioning
Substantive convergence, not zero-finding
A persistent adversarial reviewer always finds finer citation flaws; demanding zero findings loops forever. Substantive convergence = the core claims are coverage-verified AND no new BLOCKER-class finding appears for ≥2 rounds. Advisory findings never block (workspace rule 4). Caps bound the loop; cap-hit escalates to the human, never silent-pick (pd ADR #40 (e)).
Process-axis, not outcome-axis
The skill converges a doc's PROCESS-AXIS quality (well-formed, internally consistent, citation-accurate, coverage-complete). Whether the doc captures the RIGHT requirement is OUTCOME-AXIS — human only. The convergence-record carries substantive_converged (process) and never claims outcome correctness.
Same-family primary; different-family additive
The same-family leg (fresh-context, same family) is PRIMARY and always runs. The different-family leg (cross-family, e.g. DeepSeek) is an opt-in adversarial second opinion that catches same-family blind spots. Both legs are read-only and barred from outcome-axis judgment.
Scope
- In: drive same-family + different-family cross multi-round review of a doc-shaped artifact to substantive convergence; emit the converged artifact + a convergence-record.
- Out: code review (
parallel-development); spec/arch-design authoring (blueprint-crafting); research gathering (blueprint-crafting researcher); outcome-axis judgment (human).
Scope Guard (entry-time detection)
- Deliverable is source/test code, or a code diff under review → route to
parallel-development.
- Deliverable is authoring/rewriting a spec, arch-design, iteration-plan, or research → route to
blueprint-crafting.
- The request is "is this the right requirement / right conclusion?" → outcome-axis, human only.
The guard is soft (remind + route, not refuse). Phase A relies on explicit invocation (/cross-source-review); the 3-way trigger_check activation partition is DEFERRED to maturation (proposal §9 Q4) — explicit invocation minimizes, but does not zero, routing collision, which is why a Scope Guard still ships.
Quick Start
You are the orchestrator. The legs are read-only agents/subprocesses; the driver alternates them. Run in order; do not wait for the user between rounds.
Act from this Quick Start — don't reverse-engineer the legs. The protocol is this section; the legs are INVOKED, not read: spawn solidforge:doc-reviewer (its system prompt is its own — do NOT open agents/doc-reviewer.agent.md); run hetero_doc_review.py (the different-family leg — throughout this skill, the hetero_* code prefix names the different-family substrate) via the step-2 CLI (do NOT read its source — the CLI is the contract). Do not hand-validate the schemas — converge.py does that; you pass each leg's findings to it. Open references/install.md ONLY on a first run in a new project if the provider token/profile is not already clear from the env. The different-family leg's provider(s) are selected by the wrapper from HETERO_DOC_PROFILE (settable in <cwd>/.env.solidforge; comma-list = dual-different-family; default deepseek) — do NOT pass a hardcoded --profile. Each profile's token is its <NAME>_ANTHROPIC_AUTH_TOKEN env var (e.g. DEEPSEEK_ANTHROPIC_AUTH_TOKEN) — the SOLE source for that profile; do NOT look for or set <NAME>_API_KEY (namespace isolation — the _ANTHROPIC_AUTH_TOKEN suffix scopes the credential to this substrate; see install.md). The wrapper self-loads <cwd>/.env.solidforge then <cwd>/.env (shell wins) before reading the token, so a token set in .env.solidforge is absent from the shell env — do NOT preflight the token via echo $VAR / os.environ (a false negative); run the step-2 wrapper directly, which fail-fasts with a clear error if the token is genuinely missing.
Frame — identify the artifact under review + its authoritative reference (a source doc, or "self-contained / no external authority"). Enumerate the core claims (the load-bearing assertions each leg must verify — the coverage-verified prong checks these; omit them and that prong is vacuously true). Declare the size tier: short (cap=2) for a single-section / few-claim doc; long (cap=5–7) for a multi-section doc (heuristic: ≥5 sections or ≥300 lines — orchestrator judgment, stated in the record). Then open the run-progress sidecar (see the section below): append run-start (artifact / tier / cap) — this append also atomically points runs/LATEST at the new run dir (best-effort; the human-facing stable watch path).
Round loop (same-family ↔ different-family, orchestrator-driven) — each round:
- Run the same-family leg — spawn
solidforge:doc-reviewer (a fresh, independent context; it returns a doc-findings object). Append same-family-spawn before and same-family-complete (with the findings count) after (run-progress sidecar, below).
- Reconcile: revise the artifact per accepted findings, or reject a finding with rationale. RECORD each finding's disposition at reconcile time (the producing step for the record's per-round dispositions — fix A):
fixed = accepted, artifact revised; rejected = declined, with rationale (incl. coverage disclosures — not defects, carried in coverage notes); escalated = different-family-only findings, escalating to the human (the skill's only named escalation target). Append reconcile (fixed / rejected / escalated counts) to the sidecar at each reconcile.
- Run the different-family leg — from the project root (the wrapper reads
<cwd>/.env.solidforge), launch python3 "${CLAUDE_PLUGIN_ROOT}/skills/cross-source-review/infra/scripts/hetero_doc_review.py" --artifact <doc> --authority <ref-or-empty> --prior-findings <round-json> --round-index <k> --progress-file <run-dir>/progress.jsonl 2>>"<run-dir>/wrapper.stderr" as a BACKGROUND task (run_in_background; stderr — heartbeat lines, already tee'd to the progress file (ADR #61), plus pre-leg fail-fast diagnostics — is captured to <run-dir>/wrapper.stderr, keeping the task's output file a pure result JSON) on the revised artifact (fed the same-family findings as prior, so it hunts the gap, not restatements) → doc-findings. Do NOT pass --profile — the wrapper resolves the provider(s) from HETERO_DOC_PROFILE (loaded from <cwd>/.env.solidforge, shell wins; comma-list = dual-different-family; default deepseek); a hardcoded --profile silently drops every other configured provider. Pass --round-index <k> with the current round (if you forget, the wrapper derives it from the prior-findings JSON's round field — but pass it: the sidecar's round labels are only as good as this value). Use the absolute path; do NOT cd into the skill dir — cwd must stay at the project root or the token will not resolve. While it runs, do NOT block silently (ADR #62): poll the background task roughly every 2 minutes (e.g. a foreground sleep 120 between status renders); at each poll render ONE narration line to the conversation from the sidecar (csr_progress.py status <run-dir>, condensed — round k/cap · phase · findings · idle). The FIRST narration of each different-family leg must ALSO hand the human the on-demand watch pointer — the zero-interaction channel advertises the interactive one (observability-affordance, 2026-09-07): one line naming csr_progress.py status workspace/cross-source-review/runs/LATEST --watch 5 (or tail -f <run-dir>/progress.jsonl); subsequent condensed lines omit it. The CC task-manager view stays empty until the result JSON lands (stdout purity, ADR #62) — the sidecar watch is the ONLY live view on this substrate, so the pointer is not optional. Narration lines are ordinary assistant text — they stream to the user live, which IS the zero-interaction status report. When the task completes, parse the result JSON from the task's output file (stdout carries ONLY the result, with provider_runs[] stamped in it); if it exited non-zero with EMPTY stdout, read <run-dir>/wrapper.stderr for the cause (a pre-leg fail-fast: missing token / unknown profile). Where the harness lacks background execution, a synchronous call is the documented fallback — in that mode the stderr heartbeat lands in the tool result; treat those lines as progress, not errors (ADR #52) — then continue the round loop.
- Reconcile again. Apply the per-round reconciliation table (both-report→adopt; same-family-only→adopt; different-family-only→escalate; neither→pass; DEGRADED→adopt same-family).
Convergence judgment — substantive_converged when core claims are coverage-verified AND no new Blocker for ≥2 rounds. If the cap is hit without convergence → adversarial-stalemate, escalate to human (never silent-pick). Verify each leg's factual/citation claims against source independently — do not blind-trust either source. Append round-end (round / new_blockers) after each round, and run-end (outcome: converged or adversarial-stalemate or cap-hit or aborted) at the terminal judgment (run-progress sidecar, below).
Emit — the converged artifact + a convergence-record (infra/schemas/convergence-record.schema.json): rounds — each carrying the reconciled findings AND their per-finding dispositions, so a reader can list what was found and what was done about it (retention fix A; the counts-only record is obsolete) — substantive_converged, coverage notes, stalemate flag.
The pluggable seam: the driver takes findings-schema as a parameter (default doc-findings; a future code-shaped caller passes violation-log) and the same-family leg as a callback (default solidforge:doc-reviewer). This keeps a future code-domain caller a thin adaptation, not a rewrite (proposal §3).
Run-progress sidecar (ADR #61) — external live observability
A csr run is externally observable end to end: at Frame, create a run dir workspace/cross-source-review/runs/<stamp>-<slug>/ (gitignored, workspace rule 11) and append ONE JSONL event per state boundary via csr_progress.py append --file <run-dir>/progress.jsonl --type <t> [--field k=v ...] (strict registry — unknown type or field exits non-zero; bool/int/float coerced). An outside observer then watches live — tail -f <run-dir>/progress.jsonl, or csr_progress.py status workspace/cross-source-review/runs/LATEST [--watch 5] for a one-screen render (the run-start append atomically keeps runs/LATEST pointed at the current run — the stable path a human can type from memory) (round k of cap, phase + last-event age, leg + reconcile totals, terminal state). Torn last lines are counted, never fatal. The orchestrating session itself is the FIRST consumer: during each leg it renders one condensed status line to the conversation per ~2-minute poll (step 2) — zero-interaction in-session reporting (ADR #62).
Event vocabulary (single-sourced with csr_progress.py EVENT_REGISTRY — the self-gate blocks drift in either direction):
run-start — artifact / tier / cap (+authority) — writer: orchestrator, once at Frame
same-family-spawn — round
same-family-complete — round / findings
hetero-leg-start — round / provider — writer: the WRAPPER
hetero-heartbeat — provider / elapsed_s / model / idle_s / stream_bytes / events / assistant_events / killed — writer: the WRAPPER (stream mode, every 30s)
hetero-leg-end — round / provider / outcome: ok or degraded or malformed (+findings / model / elapsed_s / degraded) — writer: the WRAPPER
reconcile — round / fixed / rejected / escalated
round-end — round / new_blockers
run-end — outcome: converged or adversarial-stalemate or cap-hit or aborted (+rounds)
Best-effort contract: a failed progress append NEVER aborts the review — note it in the convergence-record coverage notes and continue. Pass --progress-file <run-dir>/progress.jsonl on every wrapper invocation; the wrapper appends its own leg + heartbeat events there (its stderr heartbeat, ADR #52, is unchanged).
Authority Chain
docs/proposal.md = authoritative design (master; SUBSTANTIVE-CONVERGED).
docs/iteration-plan.md = execution blueprint (Phase A work-items CSR-I0–CSR-I6).
docs/proposal.convergence.md = the proposal's own cross-review trail (the skill dogfooded on its own design).
Coordination with blueprint-crafting / parallel-development
primary-source-verification (psv) is the outcome-axis additive layer (per-claim fetched-source verification). GATE MODE: when rule-13 conditions hold, psv runs FIRST as a load-bearing-claims gate (GO/NO-GO batch signal; the gate record is NOT a coverage record); its load-bearing list becomes this skill's core-claims frame; the authoritative full-M psv record follows this skill's convergence. See the psv SKILL.md.
blueprint-crafting stays PURE (process-axis, same-family plan-reviewer, deterministic inner ring). It MAY call this skill for a different-family outer pass on its draft — calling a skill, not importing code.
parallel-development owns code review. The different-family substrate here is a copy-PATTERN of pd's infra/scripts/hetero_review.py (workspace rule 7 — self-contained deployability; proposal §5 — Phase-B copy-vs-share is evidence-gated, NOT pre-committed); the function-signature contract is preserved + divergences logged for Phase-B viability.
Phase-A coordination note: the bc/pd routing + call relationships above are CSR-side design intent. The reciprocal bc/pd Scope-Guard hints are DEFERRED per proposal §7 / Q4 (Phase A is explicit-invocation only); a reader following these claims into bc/pd will not yet find them mirrored there.
files_touched boundary: this skill owns cross-source-review/. It does not modify bc or pd. Phase B (pd adopting this substrate: B1 import / B2 copy / B3 shared-lib) is evidence-gated and out of Phase-A scope.
Self-Checks (Definition of Done)
A skill change is not done while any self-check fails (workspace rule 1). Run before commit:
python3 skills/cross-source-review/infra/test/disconnect_check.py # structure + loading-chain
python3 skills/cross-source-review/infra/test/plugin_layout.py # plugin.json + hooks.json + agents well-formed
python3 skills/cross-source-review/infra/test/findings_shape_check.py # every leg emit path produces a doc-findings-valid object (mirror adapter_shape_check)
python3 skills/cross-source-review/infra/test/hetero_doc_guards.py # different-family substrate guards: max-turns argv, streamed telemetry + heartbeat, byte-cap, wall-clock kill (ADR #52)
python3 skills/cross-source-review/infra/test/csr_progress_gates.py # run-progress sidecar + narration contract: registry sync, append shape, status render, wrapper tee, step-2 background narration (ADR #61/#62)
python3 skills/cross-source-review/infra/test/convergence_policy_check.py # offline caps + stalemate + reconcile + core-claims-coverage round-trip (mirror hetero_review_wiring)
python3 skills/cross-source-review/infra/test/lint_self.py # dogfood: lints this skill's own infra (ruff)
python3 skills/cross-source-review/infra/test/dogfood.py # runs the skill's own convergence loop on its own SKILL.md (skips gracefully when no API tokens; recorded log substitutes)
Reference Files
- proposal.md — authoritative design (the 3-skill inventory, non-goals, Phase-A ownership, Phase-B landing points, the §9 locked decisions).
- iteration-plan.md — Phase-A execution blueprint (CSR-I0–CSR-I6, DoD, DAG, risks).
- install.md — provisioning: the one token var,
.env resolution, adding a custom provider profile + the token-var naming rule. csr is env-armed (no arm command — its gates are self-gates).
1---2name: cross-source-review-23description: cross-source-review — a same-family (同源, fresh-context) + different-family (异源) cross multi-round review engine that drives a doc-shaped artifact to SUBSTANTIVE convergence. Use it when a high-quality document needs adversarial cross-source review to convergence — "cross-review this requirements doc", "converge this design doc", "异源 review this spec/wiki page", "drive 同源+异源 review on this artifact". Produces a converged artifact + an honest convergence-record. NOT for code review (route to parallel-development), spec/arch-design authoring (blueprint-crafting), or research gathering (blueprint-crafting researcher). Does NOT judge whether the doc is "right" (outcome-axis — human). Phase A activation is EXPLICIT invocation (`/cross-source-review`); auto-routing is deferred.4---56# Cross Source Review78Drives **same-family** (same-family, fresh-context) + **different-family** (different-family, different-family) LLM cross multi-round review to **substantive convergence** on doc-shaped artifacts — a requirements input before `blueprint-crafting`, a design doc, a wiki page. It is the missing convergence layer upstream of `blueprint-crafting` (which is process-axis, same-family only) and reusable externally (e.g. fedaot wiki review).910## Core Positioning1112### Substantive convergence, not zero-finding1314A persistent adversarial reviewer always finds finer citation flaws; demanding zero findings loops forever. **Substantive convergence** = the core claims are coverage-verified AND no new BLOCKER-class finding appears for ≥2 rounds. Advisory findings never block (workspace rule 4). Caps bound the loop; cap-hit escalates to the human, never silent-pick (pd ADR #40 (e)).1516### Process-axis, not outcome-axis1718The skill converges a doc's PROCESS-AXIS quality (well-formed, internally consistent, citation-accurate, coverage-complete). Whether the doc captures the RIGHT requirement is OUTCOME-AXIS — human only. The convergence-record carries `substantive_converged` (process) and never claims outcome correctness.1920### Same-family primary; different-family additive2122The same-family leg (fresh-context, same family) is PRIMARY and always runs. The different-family leg (cross-family, e.g. DeepSeek) is an opt-in adversarial second opinion that catches same-family blind spots. Both legs are read-only and barred from outcome-axis judgment.2324## Scope2526- **In**: drive same-family + different-family cross multi-round review of a doc-shaped artifact to substantive convergence; emit the converged artifact + a convergence-record.27- **Out**: code review (`parallel-development`); spec/arch-design authoring (`blueprint-crafting`); research gathering (`blueprint-crafting` researcher); outcome-axis judgment (human).2829### Scope Guard (entry-time detection)3031- Deliverable is source/test code, or a code diff under review → route to `parallel-development`.32- Deliverable is authoring/rewriting a spec, arch-design, iteration-plan, or research → route to `blueprint-crafting`.33- The request is "is this the right requirement / right conclusion?" → outcome-axis, human only.3435The guard is soft (remind + route, not refuse). Phase A relies on **explicit invocation** (`/cross-source-review`); the 3-way `trigger_check` activation partition is DEFERRED to maturation (proposal §9 Q4) — explicit invocation minimizes, but does not zero, routing collision, which is why a Scope Guard still ships.3637## Quick Start3839You are the orchestrator. The legs are read-only agents/subprocesses; the driver alternates them. Run in order; do not wait for the user between rounds.4041**Act from this Quick Start — don't reverse-engineer the legs.** The protocol is this section; the legs are INVOKED, not read: spawn `solidforge:doc-reviewer` (its system prompt is its own — do NOT open `agents/doc-reviewer.agent.md`); run `hetero_doc_review.py` (the different-family leg — throughout this skill, the `hetero_*` code prefix names the different-family substrate) via the step-2 CLI (do NOT read its source — the CLI is the contract). Do not hand-validate the schemas — `converge.py` does that; you pass each leg's findings to it. Open `references/install.md` ONLY on a first run in a new project if the provider token/profile is not already clear from the env. The different-family leg's provider(s) are selected by the wrapper from `HETERO_DOC_PROFILE` (settable in `<cwd>/.env.solidforge`; comma-list = dual-different-family; default `deepseek`) — do NOT pass a hardcoded `--profile`. Each profile's token is its `<NAME>_ANTHROPIC_AUTH_TOKEN` env var (e.g. `DEEPSEEK_ANTHROPIC_AUTH_TOKEN`) — the SOLE source for that profile; do NOT look for or set `<NAME>_API_KEY` (namespace isolation — the `_ANTHROPIC_AUTH_TOKEN` suffix scopes the credential to this substrate; see install.md). The wrapper self-loads `<cwd>/.env.solidforge` then `<cwd>/.env` (shell wins) before reading the token, so a token set in `.env.solidforge` is absent from the shell env — do NOT preflight the token via `echo $VAR` / `os.environ` (a false negative); run the step-2 wrapper directly, which fail-fasts with a clear error if the token is genuinely missing.42431. **Frame** — identify the artifact under review + its authoritative reference (a source doc, or "self-contained / no external authority"). **Enumerate the core claims** (the load-bearing assertions each leg must verify — the coverage-verified prong checks these; omit them and that prong is vacuously true). **Declare the size tier**: short (cap=2) for a single-section / few-claim doc; long (cap=5–7) for a multi-section doc (heuristic: ≥~5 sections or ≥~300 lines — orchestrator judgment, stated in the record). Then open the run-progress sidecar (see the section below): append `run-start` (artifact / tier / cap) — this append also atomically points `runs/LATEST` at the new run dir (best-effort; the human-facing stable watch path).44452. **Round loop** (same-family ↔ different-family, orchestrator-driven) — each round:46 - Run the **same-family leg** — spawn `solidforge:doc-reviewer` (a fresh, independent context; it returns a doc-findings object). Append `same-family-spawn` before and `same-family-complete` (with the findings count) after (run-progress sidecar, below).47 - Reconcile: revise the artifact per accepted findings, or reject a finding with rationale. RECORD each finding's disposition at reconcile time (the producing step for the record's per-round dispositions — fix A): `fixed` = accepted, artifact revised; `rejected` = declined, with rationale (incl. coverage disclosures — not defects, carried in coverage notes); `escalated` = different-family-only findings, escalating to the human (the skill's only named escalation target). Append `reconcile` (fixed / rejected / escalated counts) to the sidecar at each reconcile.48 - Run the **different-family leg** — from the **project root** (the wrapper reads `<cwd>/.env.solidforge`), launch `python3 "${CLAUDE_PLUGIN_ROOT}/skills/cross-source-review/infra/scripts/hetero_doc_review.py" --artifact <doc> --authority <ref-or-empty> --prior-findings <round-json> --round-index <k> --progress-file <run-dir>/progress.jsonl 2>>"<run-dir>/wrapper.stderr"` as a BACKGROUND task (`run_in_background`; stderr — heartbeat lines, already tee'd to the progress file (ADR #61), plus pre-leg fail-fast diagnostics — is captured to `<run-dir>/wrapper.stderr`, keeping the task's output file a pure result JSON) on the revised artifact (fed the same-family findings as prior, so it hunts the gap, not restatements) → doc-findings. **Do NOT pass `--profile`** — the wrapper resolves the provider(s) from `HETERO_DOC_PROFILE` (loaded from `<cwd>/.env.solidforge`, shell wins; comma-list = dual-different-family; default `deepseek`); a hardcoded `--profile` silently drops every other configured provider. Pass `--round-index <k>` with the current round (if you forget, the wrapper derives it from the prior-findings JSON's `round` field — but pass it: the sidecar's round labels are only as good as this value). Use the absolute path; do NOT `cd` into the skill dir — cwd must stay at the project root or the token will not resolve. While it runs, do NOT block silently (ADR #62): poll the background task roughly every 2 minutes (e.g. a foreground `sleep 120` between status renders); at each poll render ONE narration line to the conversation from the sidecar (`csr_progress.py status <run-dir>`, condensed — round k/cap · phase · findings · idle). The FIRST narration of each different-family leg must ALSO hand the human the on-demand watch pointer — the zero-interaction channel advertises the interactive one (observability-affordance, 2026-09-07): one line naming `csr_progress.py status workspace/cross-source-review/runs/LATEST --watch 5` (or `tail -f <run-dir>/progress.jsonl`); subsequent condensed lines omit it. The CC task-manager view stays empty until the result JSON lands (stdout purity, ADR #62) — the sidecar watch is the ONLY live view on this substrate, so the pointer is not optional. Narration lines are ordinary assistant text — they stream to the user live, which IS the zero-interaction status report. When the task completes, parse the result JSON from the task's output file (stdout carries ONLY the result, with `provider_runs[]` stamped in it); if it exited non-zero with EMPTY stdout, read `<run-dir>/wrapper.stderr` for the cause (a pre-leg fail-fast: missing token / unknown profile). Where the harness lacks background execution, a synchronous call is the documented fallback — in that mode the stderr heartbeat lands in the tool result; treat those lines as progress, not errors (ADR #52) — then continue the round loop.49 - Reconcile again. Apply the per-round reconciliation table (both-report→adopt; same-family-only→adopt; different-family-only→escalate; neither→pass; DEGRADED→adopt same-family).50513. **Convergence judgment** — substantive_converged when core claims are coverage-verified AND no new Blocker for ≥2 rounds. If the cap is hit without convergence → `adversarial-stalemate`, escalate to human (never silent-pick). Verify each leg's factual/citation claims against source independently — do not blind-trust either source. Append `round-end` (round / new_blockers) after each round, and `run-end` (outcome: converged or adversarial-stalemate or cap-hit or aborted) at the terminal judgment (run-progress sidecar, below).52534. **Emit** — the converged artifact + a `convergence-record` (`infra/schemas/convergence-record.schema.json`): rounds — each carrying the reconciled findings AND their per-finding dispositions, so a reader can list what was found and what was done about it (retention fix A; the counts-only record is obsolete) — `substantive_converged`, coverage notes, stalemate flag.5455The pluggable seam: the driver takes `findings-schema` as a parameter (default doc-findings; a future code-shaped caller passes `violation-log`) and the same-family leg as a callback (default `solidforge:doc-reviewer`). This keeps a future code-domain caller a thin adaptation, not a rewrite (proposal §3).5657### Run-progress sidecar (ADR #61) — external live observability5859A csr run is externally observable end to end: at Frame, create a run dir `workspace/cross-source-review/runs/<stamp>-<slug>/` (gitignored, workspace rule 11) and append ONE JSONL event per state boundary via `csr_progress.py append --file <run-dir>/progress.jsonl --type <t> [--field k=v ...]` (strict registry — unknown type or field exits non-zero; bool/int/float coerced). An outside observer then watches live — `tail -f <run-dir>/progress.jsonl`, or `csr_progress.py status workspace/cross-source-review/runs/LATEST [--watch 5]` for a one-screen render (the `run-start` append atomically keeps `runs/LATEST` pointed at the current run — the stable path a human can type from memory) (round k of cap, phase + last-event age, leg + reconcile totals, terminal state). Torn last lines are counted, never fatal. The orchestrating session itself is the FIRST consumer: during each leg it renders one condensed status line to the conversation per ~2-minute poll (step 2) — zero-interaction in-session reporting (ADR #62).6061Event vocabulary (single-sourced with `csr_progress.py` EVENT_REGISTRY — the self-gate blocks drift in either direction):6263- `run-start` — artifact / tier / cap (+authority) — writer: orchestrator, once at Frame64- `same-family-spawn` — round65- `same-family-complete` — round / findings66- `hetero-leg-start` — round / provider — writer: the WRAPPER67- `hetero-heartbeat` — provider / elapsed_s / model / idle_s / stream_bytes / events / assistant_events / killed — writer: the WRAPPER (stream mode, every 30s)68- `hetero-leg-end` — round / provider / outcome: ok or degraded or malformed (+findings / model / elapsed_s / degraded) — writer: the WRAPPER69- `reconcile` — round / fixed / rejected / escalated70- `round-end` — round / new_blockers71- `run-end` — outcome: converged or adversarial-stalemate or cap-hit or aborted (+rounds)7273Best-effort contract: a failed progress append NEVER aborts the review — note it in the convergence-record coverage notes and continue. Pass `--progress-file <run-dir>/progress.jsonl` on every wrapper invocation; the wrapper appends its own leg + heartbeat events there (its stderr heartbeat, ADR #52, is unchanged).7475## Authority Chain7677- `docs/proposal.md` = authoritative design (master; SUBSTANTIVE-CONVERGED).78- `docs/iteration-plan.md` = execution blueprint (Phase A work-items CSR-I0–CSR-I6).79- `docs/proposal.convergence.md` = the proposal's own cross-review trail (the skill dogfooded on its own design).8081## Coordination with blueprint-crafting / parallel-development8283- `primary-source-verification` (psv) is the outcome-axis additive layer (per-claim fetched-source verification). GATE MODE: when rule-13 conditions hold, psv runs FIRST as a load-bearing-claims gate (GO/NO-GO batch signal; the gate record is NOT a coverage record); its load-bearing list becomes this skill's core-claims frame; the authoritative full-M psv record follows this skill's convergence. See the psv SKILL.md.8485- `blueprint-crafting` stays PURE (process-axis, same-family `plan-reviewer`, deterministic inner ring). It MAY call this skill for a different-family outer pass on its draft — calling a skill, not importing code.86- `parallel-development` owns code review. The different-family substrate here is a copy-PATTERN of pd's `infra/scripts/hetero_review.py` (workspace rule 7 — self-contained deployability; proposal §5 — Phase-B copy-vs-share is evidence-gated, NOT pre-committed); the function-signature contract is preserved + divergences logged for Phase-B viability.87- **Phase-A coordination note**: the bc/pd routing + call relationships above are CSR-side design intent. The reciprocal bc/pd Scope-Guard hints are DEFERRED per proposal §7 / Q4 (Phase A is explicit-invocation only); a reader following these claims into bc/pd will not yet find them mirrored there.88- `files_touched` boundary: this skill owns `cross-source-review/`. It does not modify bc or pd. Phase B (pd adopting this substrate: B1 import / B2 copy / B3 shared-lib) is evidence-gated and out of Phase-A scope.8990## Self-Checks (Definition of Done)9192A skill change is not done while any self-check fails (workspace rule 1). Run before commit:9394```bash95python3 skills/cross-source-review/infra/test/disconnect_check.py # structure + loading-chain96python3 skills/cross-source-review/infra/test/plugin_layout.py # plugin.json + hooks.json + agents well-formed97python3 skills/cross-source-review/infra/test/findings_shape_check.py # every leg emit path produces a doc-findings-valid object (mirror adapter_shape_check)98python3 skills/cross-source-review/infra/test/hetero_doc_guards.py # different-family substrate guards: max-turns argv, streamed telemetry + heartbeat, byte-cap, wall-clock kill (ADR #52)99python3 skills/cross-source-review/infra/test/csr_progress_gates.py # run-progress sidecar + narration contract: registry sync, append shape, status render, wrapper tee, step-2 background narration (ADR #61/#62)100python3 skills/cross-source-review/infra/test/convergence_policy_check.py # offline caps + stalemate + reconcile + core-claims-coverage round-trip (mirror hetero_review_wiring)101python3 skills/cross-source-review/infra/test/lint_self.py # dogfood: lints this skill's own infra (ruff)102python3 skills/cross-source-review/infra/test/dogfood.py # runs the skill's own convergence loop on its own SKILL.md (skips gracefully when no API tokens; recorded log substitutes)103```104105## Reference Files106107- [proposal.md](docs/proposal.md) — authoritative design (the 3-skill inventory, non-goals, Phase-A ownership, Phase-B landing points, the §9 locked decisions).108- [iteration-plan.md](docs/iteration-plan.md) — Phase-A execution blueprint (CSR-I0–CSR-I6, DoD, DAG, risks).109- [install.md](references/install.md) — provisioning: the one token var, `.env` resolution, adding a custom provider profile + the token-var naming rule. csr is env-armed (no arm command — its gates are self-gates).