@mstar-harness/opencode 3.2.6 → 3.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (30) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/INSTALL.md +2 -4
  3. package/README.md +2 -2
  4. package/dist/mstar.js +2423 -1312
  5. package/harness-commands/amazing-pr-review.md +54 -0
  6. package/harness-skills/mstar-audit/SKILL.md +7 -6
  7. package/harness-skills/mstar-audit/references/audit-playbook.md +19 -1
  8. package/harness-skills/mstar-audit/references/codebase-audit.md +21 -3
  9. package/harness-skills/mstar-audit/references/finding-format.md +5 -1
  10. package/harness-skills/mstar-audit/references/pr-review-seat-evidence.md +28 -0
  11. package/harness-skills/mstar-audit/references/pr-review.md +169 -69
  12. package/harness-skills/mstar-audit/references/security-review.md +219 -0
  13. package/harness-skills/mstar-conventions/SKILL.md +4 -2
  14. package/harness-skills/mstar-conventions/references/harness-bootstrap-and-agents-layering.md +5 -1
  15. package/harness-skills/mstar-harness-core/SKILL.md +1 -1
  16. package/harness-skills/mstar-roles/SKILL.md +17 -13
  17. package/harness-skills/mstar-roles/references/_shared/leaf-executor-core.md +5 -7
  18. package/harness-skills/mstar-roles/references/architect.md +9 -11
  19. package/harness-skills/mstar-roles/references/code-reviewer.md +19 -22
  20. package/harness-skills/mstar-roles/references/frontend-dev.md +10 -11
  21. package/harness-skills/mstar-roles/references/fullstack-dev-shared.md +12 -13
  22. package/harness-skills/mstar-roles/references/ops-engineer.md +10 -12
  23. package/harness-skills/mstar-roles/references/product-manager.md +9 -11
  24. package/harness-skills/mstar-roles/references/project-manager/dispatch-and-assignment.md +2 -0
  25. package/harness-skills/mstar-roles/references/prompt-engineer.md +11 -14
  26. package/harness-skills/mstar-roles/references/qa-engineer.md +9 -13
  27. package/harness-skills/mstar-roles/references/qc-specialist-shared.md +10 -17
  28. package/harness-skills/mstar-roles/references/writing-specialist.md +8 -9
  29. package/package.json +1 -1
  30. package/harness-commands/pr-deep-review.md +0 -39
@@ -2,53 +2,101 @@
2
2
 
3
3
  Read-only, evidence-first review of a pull request / branch / diff, producing exactly one verdict: `ship it` / `needs fixes` / `blocked`. Runs under `mstar-audit` § `pr` variant, reusing the Recon → Audit → Vet discipline (recon = PR scope + repo guidance; vet = three-way attack). The reviewer never edits the worktree, never merges, and never approves-as-merge.
4
4
 
5
+ ## Review pipeline (three-stage)
6
+
7
+ Deep PR review is a **three-stage pipeline**: collect → domain review → synthesis. One PR gets multi-seat coverage (code + security, split by domain) but exactly **one verdict and one GitHub Review**, synthesized and published by the main agent. Every seat is a read-only audit seat; only the main agent posts.
8
+
9
+ - **Stage 1 — Collect**: PM fans out lightweight read-only agents by **domain** — business domain / change surface / tech stack; use the host's lightest read-only agent (`scout` / `explorer` / `general` — whatever the host offers). Each collect seat reads the changed files in its domain plus related context and returns **evidence in its result payload** (any seat may be **write-blocked** — read-only sandbox / EPERM; the main agent extracts the payload and writes the evidence file — § Local report archive / `references/pr-review-seat-evidence.md`): `file:line` observations, potential issue surfaces, and security-surface observations (the seat carries a security lens per `security-review.md` §2/§3 **research** discipline — trace origin, never invent an attacker, never record secret values — and still records MEDIUM / unverified items as **leads** in its evidence payload; the HIGH-only filter applies to formal findings, not leads). Collect seats produce **no** findings table, compute **no** verdict, and publish **nothing**.
10
+ - **Stage 2 — Domain review**: mstar built-in roles (`code-reviewer` / `fullstack-dev` / `frontend-dev`) split along the same domain framing, each reviewing code + security in its domain (security via the `security-review.md` lens) and producing findings with **Merge class** (§ Merge class). Each domain seat returns its findings in the **result payload** — any seat may be **write-blocked**; the main agent writes the Stage 2 evidence file (§ Local report archive / `references/pr-review-seat-evidence.md`). A large PR (>~300 changed lines, or spanning multiple change surfaces/domains) or a security-sensitive surface (auth, LLM, supply chain, data — `security-review.md` §9 extended surfaces) adds an **independent cross-domain security seat**.
11
+ - **Stage 3 — Synthesis (main agent)**: the main agent (the command's orchestrator) collects all domain findings + evidence files → **dedupe** → **three-way vet** (open each cited file yourself; `file:line` must genuinely support the claim) → **tally** (§ Tally and derived score — formula unchanged) → **verdict** → report + **publish GitHub Review** (§ Comment posting — publishing authority belongs to the main agent). The main agent does not backfill uncollected / unreviewed domains — a missing domain is declared in the report under `- unverified:` / `- notes:`.
12
+ - A domain whose seat returned **no evidence** (crashed / Blocked / empty output) is an **uncollected domain**, declared the same way under `- unverified:` / `- notes:`.
13
+
14
+ **Scale-driven fan-out** (reuses the existing sizing bands — no new thresholds): Stage 1 collect seats scale with PR size; the extra security seat stays in Stage 2:
15
+
16
+ | Size | Stage 1 collect seats | Extra Stage 2 |
17
+ | --- | --- | --- |
18
+ | Small (~≤300 / single surface) | 2 (code + security) | independent cross-domain security seat only if security-sensitive (`security-review.md` §9) |
19
+ | Large (>~300 / multi-domain) | 2–3 by domain | cross-domain security seat as needed |
20
+
21
+ - The ~1000 band of § Sizing & change shape is unchanged (too large → advise split); the pipeline fan-out threshold **is** the ~300 band — there is no second set of numbers.
22
+
23
+ **Fan-out discipline**: every collect / domain seat is a **read-only audit seat** (shared contract → `mstar-roles` `references/_shared/leaf-executor-core.md` Audit Mode). PM creates the worktree and resolves the diff basis **first** (§ Worktree isolation), then fans out. Domain-seat Assignments may carry `Delegation: allowed (scout/explore only, read-only)` (reusing the full-audit pattern). **For three-stage seats, never-post is the permanent contract** — posting is Stage 3 only, by the main agent: the main agent (the command's orchestrator) posts the review; review seats never post. Audit Mode, Hard Rule 2, and Mode C are aligned; no seat-level POST carve-out exists.
24
+
25
+ **Seat prompts** — every seat loads the **seat evidence contract** at `references/pr-review-seat-evidence.md` (return evidence / findings in the result payload — any seat may be **write-blocked**; the main agent writes the evidence files — § Local report archive). Stage 1 collect seats get: the absolute path to `references/pr-review.md` and the sections to read, the review worktree absolute path, recon facts (language / framework / directories / what was skipped), decided tradeoffs, and **Hard Rules 4/5 verbatim** — no findings table, no verdict. Stage 2 domain / security seats additionally load the **findings contract**: `references/finding-format.md`, `references/security-review.md` (security seats), and the instruction to produce findings with **Merge class** (§ Merge class), return them in the result payload (writable seats may **best-effort** write the Stage 2 evidence file directly — the main agent merges — § Local report archive), and return only findings — no fixes; never post.
26
+
27
+ > **Engine check (when available):** run `mstar pr-review seat-prompt --stage 1|2 --domain <d> --seat <id> --worktree <path> [--security] [--recon <fact> ...]` (or `import { prReviewSeatPrompt } from "@mstar-harness/engine"` in a host hook) to generate the prompt skeleton — Hard Rules 4/5 verbatim, payload-return contract, no-verdict/no-post clauses, slug `<domain>-<seat>`, Merge-class instruction on stage 2. Judgment stays with the PM/agent: domain selection, which tradeoffs are decided, and whether the surface warrants the security lens. On `fail` -> do not proceed; fix and re-run. Skill text below remains authoritative when the runtime is absent.
28
+
29
+ ## Review depth (tiers)
30
+
31
+ PR review runs at one of three tiers — `quick` / `default` / `deep` — chosen by an explicit keyword or inferred from the change shape (§ Inference ladder). `deep` is the current three-stage pipeline verbatim; `default` is the no-flag landing tier for small code PRs; `quick` serves explicit intent and tiny-mechanical diffs. Every tier keeps the same verdict contract: one verdict derived from the tally, one GitHub Review, posted by the main agent (§ Verdict synthesis / § Comment posting).
32
+
33
+ | Tier | Seats | Domain split | Security coverage | Synthesis | Relative seat-time | Boundary |
34
+ | --- | --- | --- | --- | --- | --- | --- |
35
+ | `deep` | 4–7 (2–3 collect + 2–3 domain + 0–1 independent security) | 2–3 by domain | in-domain lens **+ independent cross-domain security seat** | main agent, all three stages | longest (= current) | = current three-stage pipeline, verbatim |
36
+ | `default` | 2 (two domain seats; collection folded in = seat reuse) | 2 seats by dominant surface (code/tests, backend/frontend); single-surface PR → second seat = dedicated same-domain security-lens seat | both seats carry the in-domain security lens; **no independent cross-domain seat** (cross-domain boundary issues still go to `- notes:`; the main agent may announce an upgrade to deep) | main agent (kept) | medium (≈ ½ of deep) | typical ≤~300 changed lines; PM may announce an upgrade to deep by risk shape and declare it in the report (`- notes:`); downgraded cuts are declared likewise |
37
+ | `quick` | 1 (single domain seat; collect + review in one pass) | none (one seat, one pass) | in-domain security lens in the same seat (§2/§3 discipline); **no independent seat**; sensitive surface + explicit quick → lens still runs, report declares reduced coverage under `- notes:` | main agent (kept, smallest input) | shortest (≈ ¼) | recommended ≤~300 / mechanical shape; report must declare tier |
38
+
39
+ **Inference ladder** (no flag given — first hit wins):
40
+ 1. **Explicit tier token** — matched **only** as a dedicated flag token (`--quick` / `--default` / `--deep`) or a trailing standalone tier word (the `[quick|default|deep]` argument position), **never** as a substring of the `[pr|branch|scope]` argument (a branch/PR title containing `quick` or `default` does not set a tier) → that tier; user intent beats every heuristic.
41
+ 2. **Too large** (>~1000 changed lines) → advise a split (existing rule); if the user insists on reviewing anyway → `deep`.
42
+ 3. **Sensitive surface** (`security-review.md` §9 extended surfaces — auth / LLM / supply chain / data — present in the diff) → `deep` at any size; security-sensitive surfaces are never thinned.
43
+ 4. **Large** (>~300 changed lines, or spanning multiple change surfaces/domains) → `deep`; large PRs never silently fall back to reduced coverage.
44
+ 5. **Small** (≤~300, single surface):
45
+ - tiny-mechanical shape (docs-only / rename / formatting / pure deletion — the existing "tiny mechanical → general only" shape test) → `quick`;
46
+ - anything else (real code change) → `default`.
47
+
48
+ The ladder reuses the existing ~100 / ~300 / ~1000 sizing bands — no second set of numbers: ~100 is quick's recommended domain, ~300 is default's upper bound (above = large → deep), ~1000 keeps its too-large meaning. Quick's typical lower band ~100 is operational guidance, not a separate trigger — small real-code PRs near that boundary still default to `default` to preserve evidence depth.
49
+
50
+ **Conflict rule**: at most **one** tier keyword may be given. Any two of `quick` / `default` / `deep` appearing together → **hard-stop conflict error** — report the conflict and ask the user to pick one; never silently take a priority. Explicit `quick` on a security-sensitive surface is respected (trust + transparency), but the seat must still run its in-domain security lens and the report must declare `- notes:` "quick tier — reduced coverage on a security-sensitive surface".
51
+
52
+ **Cuttable vs never-cut**:
53
+ - **Cuttable by tier**: seat count, stage-as-wave (Stage 1 as a separate wave), domain-split granularity, the independent security seat, the lens set, the seat-prompt ingredient set.
54
+ - **Never cut (any tier)**: the verdict-from-tally formula (one formula, all tiers), merge-class assignment + three-way vet, posting ownership (sole main agent), evidence discipline (`file:line` + self-check), Hard Rules 4/5, seats read-only, worktree isolation, the batch contract (first-only + backlog register), the local report archive contract incl. frontmatter, linked-issue AC counting.
55
+ - **Stage 3 is never skipped in any tier** — `quick` just feeds it the smallest input; one verdict / one Review / main-agent posting is the product contract.
56
+
57
+ **Report `tier` declaration**: report frontmatter gains an optional `tier: quick | default | deep` (absent = `default` semantics, valid — old reports stay valid). `quick` MUST declare its reduced coverage under `- notes:` (what did not run: independent security seat / Stage 1 wave / domain split); any announced upgrade or downgrade (e.g. PM announces deep-upgrade, or a downgraded cut happens) is declared the same way. Report template structure, tally counts, and the display contract are unchanged; tier never enters the report filename.
58
+
5
59
  ## Worktree isolation
60
+ - All git mechanics — real-base resolution (never assume `main`), collision-free branch naming (`pr-<n>` → `pr-<n>-<date>-<i>` loop before **any** fetch), explicit-refspec fetches (single-branch/narrowed fetch configs stay correct; do **not** substitute `gh pr checkout <n>` — it lands on the PR-head name instead of the recorded branch, bypassing the ownership protocol), worktree creation, changeset pre-flight (untracked-only working-tree changes count as non-empty), diff-basis computation, sidecar recording, removal + prune + exact-branch deletion — execute mechanically:
6
61
 
7
- - **Resolve the real base first**never assume `main`:
8
- - Reviewing a PR: `gh pr view N --json baseRefName --jq .baseRefName` → `<base>`.
9
- - Reviewing a bare branch/diff: resolve the remote default via `git symbolic-ref refs/remotes/origin/HEAD` (fall back to `origin/main` only when it genuinely is the default).
10
- - Choose the local branch name **before any fetch** — `pr-<n>` may already exist (a stale review, another reviewer's branch, the user's own branch); the fallback must also be collision-free, so loop until the recorded name is provably fresh:
11
- ```
12
- review_branch=pr-<n>
13
- i=1
14
- while git rev-parse --verify --quiet refs/heads/$review_branch; do
15
- review_branch=pr-<n>-$(date +%Y%m%d)-$((i++))
16
- done
17
- ```
18
- Record the final name as `<review-branch>` — it did not exist before this review created it.
19
- - Establish the refs **with explicit refspecs**, then create the dedicated worktree — never the primary repo cwd, never another harness worktree:
20
- ```
21
- git fetch origin +refs/heads/<base>:refs/remotes/origin/<base>
22
- git fetch origin pull/<n>/head:<review-branch>
23
- git worktree add <path> <review-branch>
24
- cd <path> # review from here — a new linked worktree, cannot touch the primary checkout
25
- ```
26
- The explicit `+refs/heads/<base>:refs/remotes/origin/<base>` refspec updates the remote-tracking ref even on single-branch/narrowed `fetch` configs, so `origin/<base>` is never stale or missing. Do **not** use `gh pr checkout <n>` as an alternative: it switches to the PR-head branch name (not the recorded `<review-branch>`) and bypasses the ownership protocol. If you only have a PR ref, fetch it into the recorded name and `git worktree add` exactly as above.
27
- - Compute the diff basis **inside the worktree, against the recorded refs — never the primary `HEAD`** (the primary checkout may sit on a different branch):
28
- ```
29
- cd <path>
30
- git diff origin/<base>...<review-branch> # three-dot: changes on the reviewed branch since the merge-base
31
- ```
32
- - **Bare branch input** (no PR number) — review the remote branch directly; no local ownership protocol needed:
33
- ```
34
- git fetch origin +refs/heads/<branch>:refs/remotes/origin/<branch>
35
- git worktree add --detach <path> origin/<branch> # detached worktree; creates no local branch
36
- cd <path>
37
- git diff origin/<base>...origin/<branch> # three-dot against the fetched remote-tracking ref
38
- ```
39
- Cleanup: `git worktree remove <path>` + `git worktree prune` only — there is no local branch to delete.
40
- - **Arbitrary diff input** (a changeset handed to the review, no ref attached) — review the provided diff as-is:
41
- - Verify its provenance first (stated base/head SHAs when present); do not invent a checkout or substitute a different ref.
42
- - Read the changed files in the current directory for context; the diff itself is the isolated changeset under review.
43
- - No worktree, no branch, no fetch — nothing to clean up.
62
+ > **Engine check (when available):** run `mstar pr-review worktree-setup --pr <n> | --branch <b> | --diff | --working-tree | --commit <sha> [--path <dir>]` (or `import { pickReviewBranchName, preflightChangeset } from "@mstar-harness/engine"` in a host hook) to create the isolated review worktree, compute the diff basis inside it, record a sidecar json, and print `{reviewBranch, worktreePath, base, mergeBase, diffCmd}`; clean up with `mstar pr-review worktree-cleanup --path <dir> --branch <name> --report-saved` removes the tree, prunes, deletes **exactly** the recorded branch (a foreign/unrecorded branch is refused) and refuses removal while the local report is unsaved. On `fail` -> do not proceed; fix and re-run. Skill text below remains authoritative when the runtime is absent.
63
+
64
+ Discipline that stays with the agent (behavior, not git mechanics):
65
+
66
+ - Review from a dedicated linked worktree — **never the primary repo cwd, never another harness worktree** — and compute the diff basis inside it against the recorded refs, three-dot style: **never the primary `HEAD`** (the primary checkout may sit on a different branch).
67
+ - Input-mode shape: bare branch reviews use a detached worktree against the fetched remote-tracking ref (no local ownership protocol, no local branch to delete); arbitrary-diff inputs verify stated provenance and read changed files locally — no worktree, no branch, nothing to clean up; working-tree inputs take tracked + staged + untracked changes read in full (the review stays read-only — no fixes, no stash; `comments: n/a-no-pr`); single-commit inputs verify provenance but read file context **at that commit** (`git show <sha>:<path>`), which diverges from the current checkout whenever HEAD ≠ `<sha>` or the file changed since.
68
+ - **Pre-flight before fanning out lenses** (every mode): named refs resolve (in modes that have refs) and the changeset is non-empty — an empty changeset reports "no changes to review" and stops; never spawn lenses on an empty changeset.
44
69
  - Record before computing: review cwd, `<review-branch>`, HEAD sha, merge-base.
45
- - Clean up after the review (and after the comment is posted): `git worktree remove <path>` + `git worktree prune`, then delete **exactly** the recorded `<review-branch>` — it was verified not to exist before the fetch created it, so it is provably this review's own branch; never delete a pre-existing branch. Never remove other harness worktrees.
70
+ - Clean up **once the local report is saved** — the save runs in all three posting branches (`posted: yes` / `n/a-no-pr` / `failed`; § Local report archive), so cleanup never waits on POST success — then delete **exactly** the recorded `<review-branch>` (provably this review's own branch); never delete a pre-existing branch. Never remove other harness worktrees.
46
71
 
47
72
  ## Scoping
48
73
 
49
74
  - Review the diff basis vs base: changed files plus what the change touches.
50
75
  - Read changed files **in full** — diffs hide context.
51
76
  - Inspect adjacent behavior when risk leaks past the named diff (importers, callers, dependent contracts).
77
+ - When the diff touches tests, read the tests before the implementation — they carry intent.
78
+ - Verification claims in the PR description must be reproducible from the diff/CI; a claim that cannot be checked is an `unverified` lead, not evidence.
79
+ - **Domains** — review is split by domain (**business domain / change surface / tech stack**; § Review pipeline). Each domain seat concludes **only on its own domain**.
80
+ - Cross-domain boundary issues (importers / callers reaching outside the seat's domain) → record to the evidence file `- notes:`; the main agent decides whether an additional cross-domain seat is warranted.
81
+
82
+ ## Sizing & change shape
83
+
84
+ - **Sizing bands:** ~100 changed lines → reviewable; ~300 → acceptable as one logical change; ~1000 → too large — advise a split (a `should-fix` finding with split advice, or a verdict note; never auto-`blocked`). Whole-file deletions and mechanical/automated refactors are exempt — verify intent, not every line.
85
+ - **File-size watch:** a small diff that materially grows a file past ~1000 *total* lines → advise extract/decompose first ("decompose, then add").
86
+ - **Split strategies:** stack · by file group · horizontal (shared code first) · vertical (full-stack slices); refactoring and feature work travel in separate changes.
87
+ - **Escalation by change shape:**
88
+
89
+ | Shape | Action |
90
+ | --- | --- |
91
+ | Database schema change | widen scrutiny |
92
+ | API contract change | widen scrutiny |
93
+ | New framework/library adoption | widen scrutiny |
94
+ | Performance-critical path | widen scrutiny — playbook §3 Performance depth |
95
+ | Security-sensitive surface | widen scrutiny — load `references/security-review.md` |
96
+
97
+ > **Engine check (when available):** run `mstar pr-review size --base <ref> --head <ref>` (or `import { prReviewSizing, resolvePrReviewTier } from "@mstar-harness/engine"` in a host hook) to classify the changeset into the bands above — it prints the band, the inferred tier, the Stage-1 seat plan, split advice and the file-size watch, so band and fan-out decisions are never hand-derived. On `fail` -> do not proceed; fix and re-run. Skill text below remains authoritative when the runtime is absent.
98
+
99
+ These shapes get deeper review, not automatic severity — name the escalation in the review body.
52
100
 
53
101
  ## Concern lenses
54
102
 
@@ -64,6 +112,23 @@ Conditional lenses:
64
112
  - `cleanup` — dead code, duplicate logic, indirection without value. Apply for refactors and added-then-removed surfaces.
65
113
  - `comments` — comment rot, docstring truthfulness. Apply when docs changed.
66
114
 
115
+ **Smell baseline** (under `general`): twelve labelled smells — one line each, what it is → remedy direction:
116
+
117
+ - Mysterious Name — unclear what it does/why → rename to intent.
118
+ - Duplicated Code — same shape in ≥2 places → extract the shared form.
119
+ - Feature Envy — method works mostly on another class's data → move it there.
120
+ - Data Clumps — same field groups travel together → promote to a single object.
121
+ - Primitive Obsession — domain concepts as bare primitives → introduce a type.
122
+ - Repeated Switches — same condition re-branched → replace with a dispatcher.
123
+ - Shotgun Surgery — one logical change touches many files → consolidate the coupling.
124
+ - Divergent Change — one class changes for many reasons → split by reason.
125
+ - Speculative Generality — flexibility nothing uses → delete it.
126
+ - Message Chains — callers wade through a.getB().getC() → hide the walk behind one method.
127
+ - Middle Man — class mostly delegates → fold or inline the pass-through.
128
+ - Refused Bequest — subclass inherits more than it wants → replace with composition.
129
+
130
+ Three binding rules: repo-documented standards override the baseline — a standards finding cites the standard's file + rule; anything tooling already enforces is skipped (existing `general` rule); smells are judgement calls — a smell alone is never `must-fix`, and a LOW-confidence smell without evidence is not a finding (existing disqualify rule): it goes on `- unverified:` per the § Merge class rule, never `nit`; an **evidenced** judgement-call smell may surface as `nit` with the smell label. No new lens row: repo-guidance conformance stays the `general` lens's job.
131
+
67
132
  **Selection by change shape** (UI / API / migration / refactor / doc / tiny mechanical). Default set = `general` + `technical-coverage` + `silent-failures`. Never spawn all lenses blindly; tiny mechanical diffs → `general` only.
68
133
 
69
134
  ## Evidence rules
@@ -72,6 +137,8 @@ Conditional lenses:
72
137
  - Run the **smallest runtime check that changes the verdict** (targeted command, not the full suite).
73
138
  - Mark unverified explicitly — a claim without verification is a lead, not a finding.
74
139
  - Mock-heavy tests around risky behavior = a finding (no real-surface proof), not proof of correctness.
140
+ - A "doesn't follow repo conventions / should use an existing abstraction" finding must cite the exemplar the diff should have followed (`file:line`); the simplest acceptable implementation is not a style finding (lint-covered cosmetics are already ignored by the `general` lens).
141
+ - **Scout / collector evidence = leads** — collect-seat evidence files and unchecked domain-seat notes are **leads, not findings**; a domain seat's Stage 2 output **after it opened the cited code itself** (`file:line`) is a formal finding, and the main agent (Stage 3) may still reject it during vet. A finding that cites a collector's relay without its own self-check is disqualified (same discipline as full-audit "excerpts come from your own reads").
75
142
  - What disqualifies a finding (no evidence, by-design, secret values, ungrounded suggestions) → **`references/finding-format.md`** § What disqualifies a finding.
76
143
 
77
144
  ## Attack and vet
@@ -82,7 +149,9 @@ Before writing a finding, run the three-way attack from `mstar-audit`:
82
149
  2. **Simpler explanation** — does a simpler explanation cover the same evidence?
83
150
  3. **Evidence verifiability** — open the cited lines and check they actually support the claim.
84
151
 
85
- Then open cited code yourself and dispose by-design / mis-attributed / duplicate. Subagents over-report; vet before presenting.
152
+ Each **domain seat** runs the three-way attack on its own findings, opens the cited code itself, and disposes by-design / mis-attributed / duplicate before presenting.
153
+
154
+ The **main agent** is the final vet layer: at synthesis it dedupes **all** findings across domains (cross-domain duplicates, mis-attribution), applies the same by-design / duplicate disposition with the `cited code yourself` discipline, and records every rejection in the report's **Considered & rejected** section. Subagents over-report; vet before presenting.
86
155
 
87
156
  ## Verdict synthesis
88
157
 
@@ -106,8 +175,12 @@ Classify each **accepted** finding (after three-way vet) as exactly one class. D
106
175
 
107
176
  Tie-break: unsafe to ship → `must-fix`; should be addressed before merge but ship-safe → `should-fix`; otherwise `nit`. A LOW-confidence smell that fails evidence rules is **not** a finding (existing disqualify rules) — put it on `- unverified:` if it must be mentioned.
108
177
 
178
+ Presumptive-structural classes: a refactor that relocates complexity instead of reducing it · a change pushing a file past the size boundary with no decomposition · feature logic added to a shared module · a near-duplicate of an existing canonical helper · a silent fallback hiding an unclear invariant → default `should-fix`; downgrade to `nit` only with a stated reason; never `must-fix` on shape alone without correctness/security evidence.
179
+
109
180
  Field placement: on each finding, `- **Merge class**: must-fix | should-fix | nit`, immediately after `Confidence` (before `Fix sketch`). The shared finding template (`references/finding-format.md`) is unchanged — this field is PR-review-only.
110
181
 
182
+ > **Engine check (when available):** run `mstar lint <file.md>` with `--type finding` (add `--pr-variant` for the PR Merge-class contract) — or `import { validateFindingDoc } from "@mstar-harness/engine"` in a host hook — to machine-check a findings document: `### [CATEGORY-NN]` numbering, category / effort / risk / confidence enums, evidence `path:line` shape, and the Merge-class presence/enum/placement rule. On `fail` -> do not proceed; fix and re-run. Skill text below remains authoritative when the runtime is absent.
183
+
111
184
  ## Tally and derived score
112
185
 
113
186
  Verbatim, applied after the three-way vet to **accepted** findings, then leftover unmet ACs:
@@ -149,6 +222,8 @@ ship it + score_pct < 100 → allowed (nits and/or unverified deducted)
149
222
  High score_pct never means APPROVE. Low score_pct never means REQUEST_CHANGES.
150
223
  ```
151
224
 
225
+ > **Engine check (when available):** the invariant is enforced structurally — `computePrTally` derives the verdict from the tally before the score is computed, and `mstar pr-review validate-report` flags a `verdict` that does not follow from the report's own tally (`prreview.report.verdict-mismatch`, severity high). On `fail` -> do not proceed; fix and re-run. Skill text below remains authoritative when the runtime is absent.
226
+
152
227
  ### Worked examples (check table)
153
228
 
154
229
  | must / should / nit / unverified | score_pct | verdict | Display line |
@@ -163,11 +238,22 @@ High score_pct never means APPROVE. Low score_pct never means REQUEST_CHANGES.
163
238
  | 1 / 2 / 1 / 1 | 17 | `blocked` | `blocked · 17%` |
164
239
  | 3 / 0 / 0 / 0 | 0 (floor) | `blocked` | `blocked · 0%` |
165
240
 
166
- `blocked · 60%` is still not shippable. `needs fixes · 85%` still means address findings.
241
+ `blocked · 60%` is still not shippable. `needs fixes · 85%` still means address findings. This table is mirrored row-for-row as the engine test fixture (`packages/engine/test/prreview.test.ts`) — the table text is kept here as the historical SSOT anchor.
242
+
243
+ > **Engine check (when available):** run `mstar pr-review tally --findings <file.json> [--unverified <n>] [--unmet-ac-unsafe <n>] [--unmet-ac-safe <n>]` (or `import { computePrTally } from "@mstar-harness/engine"` in a host hook) to compute this tally, verdict and score from the accepted findings JSON — the check table above is the SSOT the engine fixture mirrors (`packages/engine/test/prreview.test.ts`); never hand-compute when the CLI is available. On `fail` -> do not proceed; fix and re-run. Skill text below remains authoritative when the runtime is absent.
244
+
245
+ ## Originating spec discovery
246
+
247
+ Find the originating spec — the acceptance criteria live there, not in the diff:
248
+
249
+ - Issue references in the PR body / commit messages (`#123`, `Closes`, `Fixes`).
250
+ - A spec path the user named in the request.
251
+ - Repo candidates: `{SPECS_DIR}`, `docs/specs` / ADR directories, `STRATEGY.md` / `PRODUCT.md`, roadmap.
252
+ - None → ask the user once; still none → note "no spec available", score nothing, never invent requirements (existing rule).
167
253
 
168
254
  ## Linked-issue hygiene
169
255
 
170
- If the PR closes/fixes a tracked issue, score **every** acceptance criterion against the diff:
256
+ When an originating spec exists (§ Originating spec discovery — a tracked issue, spec file, or ADR), score **every** acceptance criterion against the diff:
171
257
 
172
258
  - Mark each: met / unmet / cut.
173
259
 
@@ -181,16 +267,30 @@ Check base-vs-branch before blaming the diff for CI failures. A red build that p
181
267
  ## Comment triage
182
268
 
183
269
  - Judge the validity of bot/peer review comments before acting on them.
184
- - The reviewer seat is read-only — never edit the reviewed worktree. For valid comments, fold the minimal fix suggestion into the finding/plan (plan output) for the Prepare → Execute flow. For invalid comments, disagree on the PR comment with clear reasoning. Never gold-plate.
270
+ - **Comment-triage replies are the main agent's job at Stage 3** seats never post or reply. For valid comments, the main agent folds the minimal fix suggestion into the finding/plan (plan output) for the Prepare → Execute flow. For invalid comments, the main agent disagrees on the PR comment with clear reasoning. Never gold-plate.
185
271
 
186
272
  ## Batch sibling PRs
187
273
 
188
- - One worktree + one reviewer per PR.
189
- - All worktrees created **first**; all reviewers dispatched in **one batch**.
190
- - PM 按 PR 业务信息(业务域 / 变更面 / 技术栈)将 batch **平均**分配到四个席位(`code-reviewer` general、`fullstack-dev`、`fullstack-dev-2`、`frontend-dev`),每席位约 N/4 个 PR —— 摊薄同模型并发,降低 rate-limit。
191
- - Implementer seats (`fullstack-dev` / `fullstack-dev-2` / `frontend-dev`) run under **Audit Mode** (shared contract → `mstar-roles` `references/_shared/leaf-executor-core.md`) — same read-only contract as `code-reviewer`.
192
- - Each reviewer owns review + comment for that PR only.
193
- - Sibling interactions are **noted, not fixed**, unless the ticket says so.
274
+ - **one session = one PR** (HARD): an `amazing-pr-review` session reviews exactly **one** PR. When multiple PRs are passed in, only the **first** — the first PR in the caller's argument / mention order, never sorted by PR number or recency — runs the review at its resolved tier (§ Review depth (tiers) for tier resolution → § Review pipeline for stage steps; `deep` = the full three-stage pipeline); the rest are **not** processed in this session.
275
+ - **Register the rest as audit todos** before the review starts, register every unprocessed PR in `{PROJECT_DIR}/<project-id>/residuals.json` (project-less reviews use `_default`) via the engine-backed CLI, one `--entry` per deferred PR:
276
+ ```
277
+ mstar status backlog-register --project <project-id> --key <plan-key> --entry '<entry json>' ...
278
+ ```
279
+ - `--project` defaults to `_default`; `--key` is the **base** batch key, `pr-deep-review-<YYYY-MM-DD>` for the first same-day session — the CLI selects the first free same-day key (`pr-deep-review-<YYYY-MM-DD>`, then `-2`, `-3`, …) inside the engine's status write lock and prints the key actually used; never compute the bumped key yourself.
280
+ - Each `--entry` is a JSON object with the nine residual fields for one deferred PR: `id` (unique within the batch, e.g. `pr-deep-review-<YYYY-MM-DD>-<n>`), `title: "pr-deep-review <owner>/<repo>#<n>"`, `severity: low`, `source: pr-deep-review batch input`, `scope: "deep review of <owner>/<repo>#<n> in a new amazing-pr-review session"`, `decision: defer`, `owner: project-manager`, `target: next session`, `tracking: pr-deep-review backlog`. The CLI fills the provenance fields (`source_plan` = the used key, `registered_at` = today).
281
+ - Entry-id uniqueness is **enforced in code** (B-9 ②): the engine rejects a duplicate `id` within the key — fail-loud, register unchanged — so ids only need to stay distinct within the batch.
282
+ - `<n>` is the GitHub PR number; `title`/`scope` carry the recoverable PR identity (`<owner>/<repo>#<n>` or the PR URL) so a later session can open the exact deferred PR from the register alone.
283
+ - **Concurrency/crash safety is engine-tested** (`packages/engine/test/backlog-register.test.ts`): registration runs inside `withStatusWriteLock` with atomic temp+rename writes — never hand-edit the register with python/jq.
284
+ - Lock-semantics delta vs. the old hand-rolled protocol: `withStatusWriteLock` has **no stale-lockdir auto-reclamation** — a crash-leaked lockdir ⇒ 30s timeout ⇒ `Blocked` with the `holder.pid` recovery hint (remove the lockdir only when no writer is alive).
285
+ - **Registration failure halts the session**: if the CLI exits non-zero (fail-loud validation, register unchanged), stop and report `Blocked` — never start the first-PR review while deferred PRs are unregistered (they would be neither reviewed nor tracked).
286
+ - **Backlog close**: when a session completes review of a PR that was previously deferred — at its resolved tier (`quick` / `default` / `deep` per § Review depth), it MUST look up the matching register entry (`tracking: pr-deep-review backlog`, identity `<owner>/<repo>#<n>` in `title`/`scope`) and close it in place at review completion:
287
+ ```
288
+ mstar status backlog-close --project <project-id> --key <used-key> --id <entry-id>
289
+ ```
290
+ (`--project` defaults to `_default`; `--key` is the key the entry was registered under — the one `backlog-register` printed; `--id` is the entry id; `--note` is optional, default `"closed by backlog close"`) — sets `lifecycle: resolved` + `closed_at: <YYYY-MM-DD>` + `closure_note` (no `closed` enum), per the register lifecycle contract in `mstar-project-governance`; never leave a stale open entry for a reviewed PR.
291
+ - **Suggest one session per PR**: the report's `- notes:` states that each remaining PR gets its own `amazing-pr-review` session and is tracked in the `_default` residuals backlog (`tracking: pr-deep-review backlog`).
292
+ - **Concurrency stays inside the single PR**: for the one PR under review, create the worktree first, then fan out per the resolved tier's seat plan (§ Review depth (tiers)) — `deep` fans out the Stage 1 collect seats in one batch (§ Review pipeline); `default` folds collection into the two domain seats; `quick` is a single pass. Deferred PRs get **no** review worktree and **no** review seats — backlog registration only. The old "all worktrees first, all reviewers in one batch" model no longer applies to N PRs.
293
+ - Sibling interactions are **noted, not fixed** — interactions with deferred sibling PRs go to the report's `- notes:`, unless the ticket says so.
194
294
 
195
295
  ## Plan output(handoff to execution)
196
296
 
@@ -201,32 +301,22 @@ Review findings that need fixing can become plans for the normal Prepare → Exe
201
301
 
202
302
  ## Comment posting
203
303
 
204
- Posting the GitHub Review is a **mandatory deliverable** of the `pr` variant — chat-only output is incomplete when a PR exists. The review seat that owns the PR posts it; PM only reports the URL.
304
+ Posting the GitHub Review is a **mandatory deliverable** of the `pr` variant — chat-only output is incomplete when a PR exists. The main agent (the command's orchestrator) posts the review; review seats never post — posting is Stage 3 only, by the main agent, and the seat-level carve-out is gone — Audit Mode, Hard Rule 2, and Mode C are aligned.
205
305
 
206
- - **Before anything else:** synthesize the verdict first, then post **before** worktree cleanup (see § Worktree isolation — cleanup happens after the comment is posted).
306
+ - **Before anything else:** synthesize the verdict first, then post **before** worktree cleanup (see § Worktree isolation — cleanup happens after the local report is saved).
207
307
  - **No PR number** (bare branch / arbitrary diff): set `comments: n/a-no-pr` and skip the API. Chat output still required; this is not a Blocked review.
208
308
  - **Auth / API failure:** deliver the chat verdict anyway; Completion Report status `Partial`/`Blocked` with the `gh` error. Do not claim `Done` — comments are mandatory when a PR exists. **The local report is still saved** (§ Local report archive — posting failure does not skip archival).
209
309
 
210
310
  ### Procedure
211
311
 
212
- 1. Resolve the targetthe **base** `owner/repo` (the repository that owns the PR number), PR number, head SHA:
213
- ```
214
- gh pr view <n> --json url,headRefOid
215
- ```
216
- `headRefOid` is the `commit_id`. Parse `owner/repo` from `url` (`https://github.com/{owner}/{repo}/pull/{n}`)that is the **base** repo. **Never** use `headRepository` (a fork's owner/name); Reviews API paths are scoped to the repo that owns the PR.
217
- 2. Build one review payload:
218
- - `event`: `COMMENT` — **never** `APPROVE`, **never** `REQUEST_CHANGES`, never a merge.
219
- - `commit_id`: the PR head SHA.
220
- - `body`: follow **§ Report template (below)** — three sections (Verdict → Review → Plan to fix). `event` stays `COMMENT` — **never** `APPROVE`, **never** `REQUEST_CHANGES`, never a merge.
221
- - `comments[]`: one entry per finding whose `path` + `line` is in the three-dot diff, `side: RIGHT`. Finding body = title + evidence + impact + fix sketch — not the whole plan.
222
- 3. Post it:
223
- ```
224
- gh api --method POST repos/{owner}/{repo}/pulls/<n>/reviews --input -
225
- ```
226
- (payload on stdin).
227
- 4. **Line fallback:** if GitHub rejects some inline comments (e.g. 422 — line not in the diff), retry the review **without** those entries and fold them into the summary body. Do not loop more than once.
228
- 5. Save the local report (§ Local report archive) — **mandatory in all three branches**: POST succeeded (record `html_url` / review id for `comments:` first), POST failed, or `n/a-no-pr` (archive the chat display content). Only then clean up the worktree; bare branch/diff reviews have no worktree, but the save still happens.
229
- 6. **Batch:** each reviewer posts on **their own PRs** only. No second PM summary comment unless the Assignment says so.
312
+ Executed by the main agent at Stage 3 review seats never run this procedure. The following are the binding contracts, independent of who executes:
313
+
314
+ 1. **Target resolution** — the **base** repo is `owner/repo` parsed from the PR `url` (`https://github.com/{owner}/{repo}/pull/{n}`); `commit_id` is `headRefOid`. **Never** derive the repo from `headRepository` (the fork view) — Reviews API paths are scoped to the repository that owns the PR number.
315
+ 2. **Payload** — `event` is the fixed literal `COMMENT` (**never** `APPROVE`, **never** `REQUEST_CHANGES`, never a merge — the engine's literal type admits no other value); `body` follows **§ Report template (below)**; `comments[]`: one entry per finding whose `path` + `line` sits in the three-dot diff, `side: RIGHT` — finding body = title + evidence + impact + fix sketch, not the whole plan.
316
+ 3. **Line fallback** if GitHub rejects some inline comments (e.g. 422 line outside the diff), retry the review **once**, without exactly the rejected entries and with them folded into the summary body; never loop a second time.
317
+ 4. **Save the local report (§ Local report archive) — mandatory in all three branches**: POST succeeded (record `html_url` / review id for `comments:` first), POST failed, or `n/a-no-pr` (archive the chat display content). Only then clean up the worktree (§ Worktree isolation); bare branch/diff reviews have no worktree, but the save still happens.
318
+
319
+ > **Engine check (when available):** run `mstar pr-review post --pr <n> --body-file <path> [--findings <file.json>]` (or `import { planReviewPost } from "@mstar-harness/engine"` in a host hook) to execute these contracts mechanically — url-based repo resolution, the `COMMENT` literal, payload POST via stdin, and the at-most-once 422 fallback printing `review_url`; auth/API failure exits 1 (`comments: failed`). On `fail` -> do not proceed; fix and re-run. Skill text below remains authoritative when the runtime is absent.
230
320
 
231
321
  ### Report template (GitHub Review `body`)
232
322
 
@@ -307,15 +397,17 @@ Never dump full plan files.
307
397
 
308
398
  ### Local report archive
309
399
 
310
- The posted PR comment is the deliverable; the local report is the durable reference copy — the PR thread may be buried, locked, or deleted, and bare-branch/diff reviews have no thread at all. The review seat saves **one markdown file per reviewed PR** (or branch/diff) as part of the mandatory deliverable, before worktree cleanup:
400
+ The posted PR comment is the deliverable; the local report is the durable reference copy — the PR thread may be buried, locked, or deleted, and bare-branch/diff reviews have no thread at all. The main agent saves **one markdown file per reviewed PR** (or branch/diff) at Stage 3 — the report it published — as part of the mandatory deliverable, before worktree cleanup:
311
401
 
312
402
  - **Path**: `{PROJECT_DIR}/<project-id>/reports/pr-review/` — `<project-id>` from the Assignment / project context, `_default` when the review runs outside any project flow (same id convention as `projects/<id>/residuals.json`). Gitignored local SSOT, same posture as residuals; a finding that must survive across clones gets promoted to tracked `{KNOWLEDGE_DIR}` / `{SPECS_DIR}`, not by tracking this directory.
313
403
  - **Write via the primary checkout, never the worktree**: harness discovery must not start from the review worktree — its root has no gitignored `.mstar/`, and anything written there is destroyed by `git worktree remove` (§ Worktree isolation). Record the primary repository's absolute path **before** creating the worktree and write the report under it. Never create a `.mstar/` inside the review worktree to "host" the report.
314
404
  - **Filename**: `<YYYY-MM-DD>-pr<N>.md`; bare branch → `<YYYY-MM-DD>-<branch-slug>.md`; arbitrary diff → `<YYYY-MM-DD>-diff-<short-head-sha>.md`, or `<YYYY-MM-DD>-diff.md` when no head SHA was provided with the changeset (never invent one). Same target twice in one day → append `-r2`, `-r3`, … (never overwrite a prior report).
405
+ - **Evidence files**: the **main agent** writes each seat's (Stage 1 / Stage 2) evidence file in the same `reports/pr-review/` directory — `<YYYY-MM-DD>-pr<N>-stage1-<slug>.md` (Stage 1) / `<YYYY-MM-DD>-pr<N>-stage2-<slug>.md` (Stage 2 findings draft). Seats return evidence / findings in their result payload (contract → `references/pr-review-seat-evidence.md` — any seat may be **write-blocked**; seats are **never required to write**). Writable seats may **best-effort** write their evidence file directly; the contract does not depend on it. The **main agent writes / consolidates all evidence files** from the seat payloads. `<slug>` is **domain-derived and unique per seat** — `<domain>-<seat>` — and is mandated in the seat Assignment, so two seats in the same pipeline can never collide. Bare branch / diff mirror the main-report forms: `<YYYY-MM-DD>-<branch-slug>-stage{1,2}-<slug>.md`, `<YYYY-MM-DD>-diff-<short-head-sha>-stage{1,2}-<slug>.md`, or `-diff-` alone when no head SHA was provided (never invent one). Same target re-reviewed twice in one day → append `-r2`, `-r3`, … (never overwrite a prior evidence file). Same gitignored directory and same **write via primary checkout, never the worktree** discipline applies (an evidence file written in the review worktree is destroyed by `git worktree remove`, § Worktree isolation).
315
406
  - **Frontmatter** (machine-readable metadata):
316
407
  ```yaml
317
408
  ---
318
409
  type: pr-review
410
+ tier: quick | default | deep # optional — absent = default semantics (§ Review depth)
319
411
  pr: <n> # omit for bare branch / diff
320
412
  url: <pr url> # omit for bare branch / diff
321
413
  head: <head sha>
@@ -323,14 +415,18 @@ The posted PR comment is the deliverable; the local report is the durable refere
323
415
  verdict: ship it | needs fixes | blocked
324
416
  score_pct: <n>
325
417
  tally: { must-fix: <n>, should-fix: <n>, nit: <n>, unverified: <n> }
418
+ comments: posted | n/a-no-pr | failed # posting tri-state — never collapse failed into n/a-no-pr ("yes" = posted alias)
326
419
  review_url: <posted review html_url> # n/a-no-pr when skipped; failed: <gh error summary> when POST failed
327
420
  generated_at: <YYYY-MM-DD>
421
+ pipeline: {stages: 3, seats: [<seat ids>]} # optional — omit when the review did not run the three-stage pipeline
328
422
  ---
329
423
  ```
330
424
 
331
425
  `head:` / `base:` are omitted when genuinely unknown (arbitrary diff without stated provenance) — never fabricate identifiers.
332
426
 
333
427
  **Posting failure does not skip archival.** The report is saved regardless of the POST outcome: on failure it archives the chat display content plus the `gh` error summary, so a failed POST still leaves the durable copy.
428
+
429
+ > **Engine check (when available):** run `mstar pr-review report-path --reports-dir <dir> --target pr:<n>|branch:<slug>|diff:<sha>|diff [--stage 1|2 --slug <domain-seat>] [--date <YYYY-MM-DD>]` (or `import { prReviewReportPath } from "@mstar-harness/engine"` in a host hook) to resolve the Filename / Evidence-file names above — including the same-day `-r2`/`-r3` escalation, which the resolver scans for instead of the agent eyeballing the directory. Pure resolution: it never writes; the main agent still writes the file content. On `fail` -> do not proceed; fix and re-run. Skill text below remains authoritative when the runtime is absent.
334
430
  - **Body**: the exact text posted as the GitHub Review body — verbatim, not a paraphrase. When `comments: n/a-no-pr` or posting failed, the body is the chat display content instead (§ Display contract two lines + ranked findings + leftover AC), so the local copy is still complete.
335
431
  - Fix plans referenced by the Plan-to-fix section keep living in `{PLAN_DIR}/audit-<date>/` when written — the report links them, never duplicates them.
336
432
 
@@ -356,8 +452,12 @@ The posted PR comment is the deliverable; the local report is the durable refere
356
452
 
357
453
  - `- report:` — local archive path (§ Local report archive), e.g. `{PROJECT_DIR}/<project-id>/reports/pr-review/2026-08-24-pr134.md` (`_default` when project-less); `n/a` only when the harness dir is undiscoverable.
358
454
 
455
+ > **Engine check (when available):** run `mstar pr-review validate-report <file.md>` (or `import { validatePrReviewReport } from "@mstar-harness/engine"` in a host hook) to machine-check a saved local report against the Frontmatter + Output-shape contract above — verdict-from-tally consistency, the locked-formula `score_pct` recompute, the comments tri-state (a failed POST is `failed`, never `n/a-no-pr`), the required-field set (`type`, `verdict`, `score_pct`, `tally`, `comments`, `review_url`, `generated_at`) and `generated_at` format. Exit 1 with violations; run it before worktree cleanup. On `fail` -> do not proceed; fix and re-run. Skill text below remains authoritative when the runtime is absent.
456
+
359
457
  ### Display contract (chat output)
360
458
 
459
+ Tone: matter-of-fact — no praise-padding, no flattery; state each severity together with the conditions that enable it.
460
+
361
461
  First two lines of the **chat** display — verbatim:
362
462
 
363
463
  ```