@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.
- package/CHANGELOG.md +27 -0
- package/INSTALL.md +2 -4
- package/README.md +2 -2
- package/dist/mstar.js +2423 -1312
- package/harness-commands/amazing-pr-review.md +54 -0
- package/harness-skills/mstar-audit/SKILL.md +7 -6
- package/harness-skills/mstar-audit/references/audit-playbook.md +19 -1
- package/harness-skills/mstar-audit/references/codebase-audit.md +21 -3
- package/harness-skills/mstar-audit/references/finding-format.md +5 -1
- package/harness-skills/mstar-audit/references/pr-review-seat-evidence.md +28 -0
- package/harness-skills/mstar-audit/references/pr-review.md +169 -69
- package/harness-skills/mstar-audit/references/security-review.md +219 -0
- package/harness-skills/mstar-conventions/SKILL.md +4 -2
- package/harness-skills/mstar-conventions/references/harness-bootstrap-and-agents-layering.md +5 -1
- package/harness-skills/mstar-harness-core/SKILL.md +1 -1
- package/harness-skills/mstar-roles/SKILL.md +17 -13
- package/harness-skills/mstar-roles/references/_shared/leaf-executor-core.md +5 -7
- package/harness-skills/mstar-roles/references/architect.md +9 -11
- package/harness-skills/mstar-roles/references/code-reviewer.md +19 -22
- package/harness-skills/mstar-roles/references/frontend-dev.md +10 -11
- package/harness-skills/mstar-roles/references/fullstack-dev-shared.md +12 -13
- package/harness-skills/mstar-roles/references/ops-engineer.md +10 -12
- package/harness-skills/mstar-roles/references/product-manager.md +9 -11
- package/harness-skills/mstar-roles/references/project-manager/dispatch-and-assignment.md +2 -0
- package/harness-skills/mstar-roles/references/prompt-engineer.md +11 -14
- package/harness-skills/mstar-roles/references/qa-engineer.md +9 -13
- package/harness-skills/mstar-roles/references/qc-specialist-shared.md +10 -17
- package/harness-skills/mstar-roles/references/writing-specialist.md +8 -9
- package/package.json +1 -1
- 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
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
-
|
|
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
|
-
-
|
|
189
|
-
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
-
|
|
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
|
|
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
|
|
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
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
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
|
|
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
|
```
|