@mstar-harness/dsh 3.10.2 → 3.11.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 (52) hide show
  1. package/dist/index.js +66 -5
  2. package/harness-agents/code-reviewer.md +25 -0
  3. package/harness-agents/qc-specialist-2.md +25 -0
  4. package/harness-agents/qc-specialist-3.md +25 -0
  5. package/harness-agents/qc-specialist.md +25 -0
  6. package/harness-commands/iteration-start.md +1 -1
  7. package/harness-skills/mstar-artifacts/SKILL.md +1 -1
  8. package/harness-skills/mstar-artifacts/references/plan-files-and-reports.md +7 -0
  9. package/harness-skills/mstar-artifacts/references/plan-quality-bar.md +13 -0
  10. package/harness-skills/mstar-artifacts/references/plan-workflow-lifecycle-contract.md +7 -0
  11. package/harness-skills/mstar-artifacts/references/status-and-residuals.md +9 -5
  12. package/harness-skills/mstar-audit/SKILL.md +2 -2
  13. package/harness-skills/mstar-audit/references/codebase-audit.md +53 -4
  14. package/harness-skills/mstar-audit/references/finding-format.md +44 -0
  15. package/harness-skills/mstar-audit/references/security-review.md +194 -32
  16. package/harness-skills/mstar-branch-worktree/SKILL.md +3 -2
  17. package/harness-skills/mstar-dispatch-gates/SKILL.md +7 -0
  18. package/harness-skills/mstar-harness-core/SKILL.md +3 -2
  19. package/harness-skills/mstar-host/references/cursor.md +1 -13
  20. package/harness-skills/mstar-host/references/omp.md +30 -5
  21. package/harness-skills/mstar-iteration/references/iteration-artifact-boundaries.md +7 -5
  22. package/harness-skills/mstar-iteration/references/iteration-compass-template.md +21 -3
  23. package/harness-skills/mstar-iteration/references/phase-1-prepare.md +38 -4
  24. package/harness-skills/mstar-iteration/references/phase-2-worktree-lease.md +3 -3
  25. package/harness-skills/mstar-iteration/references/phase-3-iteration-close.md +1 -0
  26. package/harness-skills/mstar-iteration/references/phase-6-post-merge-close.md +1 -1
  27. package/harness-skills/mstar-iteration/references/plan-scoped-pm.md +3 -1
  28. package/harness-skills/mstar-phase-gates/SKILL.md +1 -1
  29. package/harness-skills/mstar-roles/SKILL.md +9 -8
  30. package/harness-skills/mstar-roles/references/architect.md +1 -1
  31. package/harness-skills/mstar-roles/references/code-reviewer.md +9 -3
  32. package/harness-skills/mstar-roles/references/frontend-dev.md +1 -1
  33. package/harness-skills/mstar-roles/references/fullstack-dev-shared.md +1 -1
  34. package/harness-skills/mstar-roles/references/ops-engineer.md +1 -1
  35. package/harness-skills/mstar-roles/references/project-manager/dispatch-and-assignment.md +1 -2
  36. package/harness-skills/mstar-roles/references/project-manager/plan-management.md +5 -0
  37. package/harness-skills/mstar-roles/references/prompt-engineer.md +1 -1
  38. package/harness-skills/mstar-roles/references/qa-engineer/acceptance-gate.md +25 -4
  39. package/harness-skills/mstar-roles/references/qa-engineer.md +29 -7
  40. package/harness-skills/mstar-roles/references/qc-specialist-shared.md +1 -1
  41. package/harness-skills/mstar-sdd/SKILL.md +6 -16
  42. package/harness-skills/mstar-sdd/references/file-handoffs.md +11 -6
  43. package/harness-skills/mstar-sdd/references/implementer-continuation-prompt.md +0 -1
  44. package/harness-skills/mstar-sdd/references/implementer-prompt.md +0 -1
  45. package/harness-skills/mstar-sdd/references/sticky-implementer-session.md +0 -1
  46. package/harness-skills/mstar-sdd/references/task-reviewer-prompt.md +18 -6
  47. package/harness-skills/mstar-use-cli/SKILL.md +166 -0
  48. package/harness-skills/mstar-use-cli/references/checks-and-lints.md +70 -0
  49. package/harness-skills/mstar-use-cli/references/plan-and-workflow.md +165 -0
  50. package/harness-skills/mstar-use-cli/references/preconditions.md +85 -0
  51. package/harness-skills/mstar-use-cli/references/status-and-registers.md +75 -0
  52. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1477,7 +1477,7 @@ function validatePlanProgress(value, what = "coordination.progress") {
1477
1477
  }
1478
1478
  return violations;
1479
1479
  }
1480
- function validatePlanHandoff(value, what = "coordination.handoff") {
1480
+ function validatePlanHandoff(value, what = "coordination.handoff", route = "integration") {
1481
1481
  if (!isPlainObject2(value))
1482
1482
  return [invalid("coordination.row.handoff-shape", `${what} must be an object`)];
1483
1483
  const allowed = [
@@ -1617,7 +1617,25 @@ function validatePlanHandoff(value, what = "coordination.handoff") {
1617
1617
  }
1618
1618
  }
1619
1619
  if ((value.state === "integrating" || value.state === "merged" || value.state === "completed") && value.integration === undefined) {
1620
- violations.push(invalid("coordination.row.handoff-field", `${what}.state ${String(value.state)} requires integration`));
1620
+ if (route === "standalone-development" && value.state === "completed") {
1621
+ if (value.completed_at === undefined) {
1622
+ violations.push(invalid("coordination.row.handoff-field", `${what}.state completed requires completed_at for a standalone handoff`));
1623
+ }
1624
+ if (!isNonEmptyString(value.accepted_at)) {
1625
+ violations.push(invalid("coordination.row.handoff-field", `${what}.accepted_at is required for a standalone completed handoff`));
1626
+ }
1627
+ if (!isNonEmptyString(value.accepted_by)) {
1628
+ violations.push(invalid("coordination.row.handoff-field", `${what}.accepted_by is required for a standalone completed handoff`));
1629
+ }
1630
+ if (value.qc === undefined) {
1631
+ violations.push(invalid("coordination.row.handoff-field", `${what}.qc is required for a standalone completed handoff`));
1632
+ }
1633
+ if (value.qa === undefined) {
1634
+ violations.push(invalid("coordination.row.handoff-field", `${what}.qa is required for a standalone completed handoff`));
1635
+ }
1636
+ } else {
1637
+ violations.push(invalid("coordination.row.handoff-field", `${what}.state ${String(value.state)} requires integration`));
1638
+ }
1621
1639
  }
1622
1640
  return violations;
1623
1641
  }
@@ -1653,7 +1671,7 @@ function validatePreparedCoordination(value, what = "coordination.prepared") {
1653
1671
  }
1654
1672
  return violations;
1655
1673
  }
1656
- function validateRowCoordination(value, what = "coordination") {
1674
+ function validateRowCoordination(value, what = "coordination", route = "integration") {
1657
1675
  if (!isPlainObject2(value))
1658
1676
  return [invalid("coordination.row.shape", `${what} must be an object`)];
1659
1677
  const allowed = ["revision", "prepared", "session", "progress", "handoff"];
@@ -1672,7 +1690,7 @@ function validateRowCoordination(value, what = "coordination") {
1672
1690
  if (value.progress !== undefined)
1673
1691
  violations.push(...validatePlanProgress(value.progress, `${what}.progress`));
1674
1692
  if (value.handoff !== undefined)
1675
- violations.push(...validatePlanHandoff(value.handoff, `${what}.handoff`));
1693
+ violations.push(...validatePlanHandoff(value.handoff, `${what}.handoff`, route));
1676
1694
  if (value.handoff !== undefined && value.session === undefined) {
1677
1695
  violations.push(invalid("coordination.row.handoff-field", `${what}.handoff requires a bound plan session`));
1678
1696
  }
@@ -1974,6 +1992,45 @@ var WORKFLOW_TERMINAL_STATUSES = ["completed", "failed", "stopped"];
1974
1992
  var WORKFLOW_LIFECYCLE_TYPES = ["plan", "iteration"];
1975
1993
  var WORKFLOW_DELIVERY_KINDS = ["development", "verification/report-only"];
1976
1994
  var WORKFLOW_COMPOUND_OUTCOMES = ["created", "updated", "skipped"];
1995
+ function isStandaloneDevelopmentWorkflow(snapshot) {
1996
+ return snapshot.type === "plan" && snapshot.delivery_kind === "development" && Array.isArray(snapshot.plans) && snapshot.plans.length === 1;
1997
+ }
1998
+ function rowValidationRoute(snapshot, row) {
1999
+ if (isStandaloneDevelopmentWorkflow(snapshot) && snapshot.plans[0]?.id === row.id) {
2000
+ return "standalone-development";
2001
+ }
2002
+ return "integration";
2003
+ }
2004
+ function validateStandaloneCompletedCoherence(snapshot, row) {
2005
+ const violations = [];
2006
+ if (!isStandaloneDevelopmentWorkflow(snapshot) || row.id !== snapshot.plans[0]?.id)
2007
+ return violations;
2008
+ const coordination = row.coordination;
2009
+ if (!isPlainObject2(coordination) || !isPlainObject2(coordination.handoff))
2010
+ return violations;
2011
+ const handoff = coordination.handoff;
2012
+ if (handoff.state !== "completed" || handoff.integration !== undefined)
2013
+ return violations;
2014
+ if (row.status !== "Done") {
2015
+ violations.push(violation3("high", "coordination.row.handoff-field", `standalone completed handoff requires row ${String(row.id)} to be Done`));
2016
+ }
2017
+ if (row.execution_lease !== undefined) {
2018
+ violations.push(violation3("high", "coordination.row.handoff-field", `standalone completed handoff requires no execution lease on row ${String(row.id)}`));
2019
+ }
2020
+ if (snapshot.integration_merge_lease !== undefined) {
2021
+ violations.push(violation3("high", "coordination.row.handoff-field", "standalone completed handoff requires no integration_merge_lease on the snapshot"));
2022
+ }
2023
+ const source = snapshot.branch?.source;
2024
+ const target = snapshot.branch?.target;
2025
+ if (!isNonEmptyString(source) || !isNonEmptyString(target)) {
2026
+ violations.push(violation3("high", "coordination.row.handoff-field", "standalone completed handoff requires nonblank branch.source and branch.target"));
2027
+ } else {
2028
+ if (handoff.source_branch !== source) {
2029
+ violations.push(violation3("high", "coordination.row.handoff-field", `standalone completed handoff source_branch ${String(handoff.source_branch)} must equal branch.source ${source}`));
2030
+ }
2031
+ }
2032
+ return violations;
2033
+ }
1977
2034
  function violation3(severity, code, message, fix) {
1978
2035
  return { ok: false, severity, code, message, fix };
1979
2036
  }
@@ -2084,13 +2141,17 @@ function validateWorkflowSnapshot(doc) {
2084
2141
  } else if (!Array.isArray(doc.plans)) {
2085
2142
  violations.push(violation3("high", "workflow.snapshot.invalid-plans", "plans must be an array of legacy plan rows"));
2086
2143
  } else {
2144
+ const snapshotDoc = doc;
2087
2145
  for (const row of doc.plans) {
2088
2146
  violations.push(...validatePlanRow(row).violations);
2089
2147
  if (isPlainObject2(row) && row.execution_lease !== undefined) {
2090
2148
  violations.push(...validateExecutionLease(row.execution_lease).violations);
2091
2149
  }
2092
2150
  if (isPlainObject2(row) && row.coordination !== undefined) {
2093
- violations.push(...validateRowCoordination(row.coordination, `plans[${String(row.id)}].coordination`));
2151
+ const planRow = row;
2152
+ const route = rowValidationRoute(snapshotDoc, planRow);
2153
+ violations.push(...validateRowCoordination(row.coordination, `plans[${String(row.id)}].coordination`, route));
2154
+ violations.push(...validateStandaloneCompletedCoherence(snapshotDoc, planRow));
2094
2155
  }
2095
2156
  }
2096
2157
  }
@@ -48,6 +48,31 @@ permission:
48
48
  "cloc*": allow
49
49
  "scc*": allow
50
50
  "tokei*": allow
51
+ # Morning Star read-only validators (skills require machine-checked gates)
52
+ "mstar qc validate-report": allow
53
+ "mstar qc validate-report *": allow
54
+ "mstar-harness qc validate-report": allow
55
+ "mstar-harness qc validate-report *": allow
56
+ "mstar lint": allow
57
+ "mstar lint *": allow
58
+ "mstar-harness lint": allow
59
+ "mstar-harness lint *": allow
60
+ "mstar dispatch validate": allow
61
+ "mstar dispatch validate *": allow
62
+ "mstar-harness dispatch validate": allow
63
+ "mstar-harness dispatch validate *": allow
64
+ "mstar worktree qc-alignment": allow
65
+ "mstar worktree qc-alignment *": allow
66
+ "mstar-harness worktree qc-alignment": allow
67
+ "mstar-harness worktree qc-alignment *": allow
68
+ "mstar status validate": allow
69
+ "mstar status validate *": allow
70
+ "mstar-harness status validate": allow
71
+ "mstar-harness status validate *": allow
72
+ "mstar lease verify": allow
73
+ "mstar lease verify *": allow
74
+ "mstar-harness lease verify": allow
75
+ "mstar-harness lease verify *": allow
51
76
  # Audit read-only checks (matches mstar-audit Hard Rule 2)
52
77
  # Deny mutating variants before the exact read-only allows
53
78
  "npm audit fix*": deny
@@ -36,6 +36,31 @@ permission:
36
36
  "cloc*": allow
37
37
  "scc*": allow
38
38
  "tokei*": allow
39
+ # Morning Star read-only validators (skills require machine-checked gates)
40
+ "mstar qc validate-report": allow
41
+ "mstar qc validate-report *": allow
42
+ "mstar-harness qc validate-report": allow
43
+ "mstar-harness qc validate-report *": allow
44
+ "mstar lint": allow
45
+ "mstar lint *": allow
46
+ "mstar-harness lint": allow
47
+ "mstar-harness lint *": allow
48
+ "mstar dispatch validate": allow
49
+ "mstar dispatch validate *": allow
50
+ "mstar-harness dispatch validate": allow
51
+ "mstar-harness dispatch validate *": allow
52
+ "mstar worktree qc-alignment": allow
53
+ "mstar worktree qc-alignment *": allow
54
+ "mstar-harness worktree qc-alignment": allow
55
+ "mstar-harness worktree qc-alignment *": allow
56
+ "mstar status validate": allow
57
+ "mstar status validate *": allow
58
+ "mstar-harness status validate": allow
59
+ "mstar-harness status validate *": allow
60
+ "mstar lease verify": allow
61
+ "mstar lease verify *": allow
62
+ "mstar-harness lease verify": allow
63
+ "mstar-harness lease verify *": allow
39
64
  task:
40
65
  "*": deny
41
66
  explore: allow
@@ -36,6 +36,31 @@ permission:
36
36
  "cloc*": allow
37
37
  "scc*": allow
38
38
  "tokei*": allow
39
+ # Morning Star read-only validators (skills require machine-checked gates)
40
+ "mstar qc validate-report": allow
41
+ "mstar qc validate-report *": allow
42
+ "mstar-harness qc validate-report": allow
43
+ "mstar-harness qc validate-report *": allow
44
+ "mstar lint": allow
45
+ "mstar lint *": allow
46
+ "mstar-harness lint": allow
47
+ "mstar-harness lint *": allow
48
+ "mstar dispatch validate": allow
49
+ "mstar dispatch validate *": allow
50
+ "mstar-harness dispatch validate": allow
51
+ "mstar-harness dispatch validate *": allow
52
+ "mstar worktree qc-alignment": allow
53
+ "mstar worktree qc-alignment *": allow
54
+ "mstar-harness worktree qc-alignment": allow
55
+ "mstar-harness worktree qc-alignment *": allow
56
+ "mstar status validate": allow
57
+ "mstar status validate *": allow
58
+ "mstar-harness status validate": allow
59
+ "mstar-harness status validate *": allow
60
+ "mstar lease verify": allow
61
+ "mstar lease verify *": allow
62
+ "mstar-harness lease verify": allow
63
+ "mstar-harness lease verify *": allow
39
64
  task:
40
65
  "*": deny
41
66
  explore: allow
@@ -36,6 +36,31 @@ permission:
36
36
  "cloc*": allow
37
37
  "scc*": allow
38
38
  "tokei*": allow
39
+ # Morning Star read-only validators (skills require machine-checked gates)
40
+ "mstar qc validate-report": allow
41
+ "mstar qc validate-report *": allow
42
+ "mstar-harness qc validate-report": allow
43
+ "mstar-harness qc validate-report *": allow
44
+ "mstar lint": allow
45
+ "mstar lint *": allow
46
+ "mstar-harness lint": allow
47
+ "mstar-harness lint *": allow
48
+ "mstar dispatch validate": allow
49
+ "mstar dispatch validate *": allow
50
+ "mstar-harness dispatch validate": allow
51
+ "mstar-harness dispatch validate *": allow
52
+ "mstar worktree qc-alignment": allow
53
+ "mstar worktree qc-alignment *": allow
54
+ "mstar-harness worktree qc-alignment": allow
55
+ "mstar-harness worktree qc-alignment *": allow
56
+ "mstar status validate": allow
57
+ "mstar status validate *": allow
58
+ "mstar-harness status validate": allow
59
+ "mstar-harness status validate *": allow
60
+ "mstar lease verify": allow
61
+ "mstar lease verify *": allow
62
+ "mstar-harness lease verify": allow
63
+ "mstar-harness lease verify *": allow
39
64
  task:
40
65
  "*": deny
41
66
  explore: allow
@@ -59,7 +59,7 @@ Execute **`mstar-host`** → active host 的 **plan-mode bridge**(其 "mstar-i
59
59
 
60
60
  Command-unique 补充(bridge 未枚举):
61
61
 
62
- - **空白脚手架字段**:Direction / Scope / Acceptance Criteria / Non-Goals / Delivery Branch Policy(`iteration_base_branch` / `spec_integration_branch` / `target_branch`)/ Plans / Feedback log / Deferred grill log
62
+ - **空白脚手架字段**:Direction / Scope / Decisions / Open Questions / Acceptance Criteria / Non-Goals / Delivery Branch Policy(`iteration_base_branch` / `spec_integration_branch` / `target_branch`)/ Plans / Feedback log / Deferred grill log
63
63
  - **Build 才勾的 todos**(顺序):`harness-init` → `finalize-compass-plans`(同一 session plan 落成 compass + plans + `status.json` 登记 + 索引)→ review-edit-product-manager → review-edit-architect → review-edit-writing-specialist → `pm-lock` → `integration-branch`
64
64
 
65
65
  ## 非 Plan 路径从这里继续 ↓
@@ -39,7 +39,7 @@ description: "Morning Star plan harness artifacts — `{PLAN_DIR}` main plans an
39
39
 
40
40
  - **`{WORKFLOW_DIR}/<id>/notes.jsonl`**: per-workflow append-only notes ledger (runtime); snapshot plan-row `notes` is the legacy verbatim copy. **Tech-debt rollup**: `mstar status tech-debt <project-dir>` over the project registers — **`references/status-and-residuals.md`**.
41
41
  - **Iteration Phase 2 leases** (snapshot: `integration_worktree_path`, `plans[].execution_lease`, top-level `integration_merge_lease`): field semantics → **`references/status-and-residuals.md`** (“Iteration execution leases”); Phase 2 execution checklist → **`mstar-iteration`** `references/phase-2-worktree-lease.md`; full protocol prose (single copy) → **`mstar-engine-legacy`** `references/lease-protocol.md`.
42
- - **Plan-scoped coordination is a domain-call surface**: plan-row `coordination` block (`prepared` / `revision` / `duplicate-holder`), session JSON, handoff record, and `--expect <revision>` semantics have their **single runtime home** in **`references/status-and-residuals.md`**; flag shapes and exit codes → `docs/cli.md`; route semantics → **`mstar-iteration`** `references/plan-scoped-pm.md`. Every plan-row mutation goes through the verbs (`mstar plan bind | show | prepare | progress | residual-add | residual-close | handoff | accept | return | integration-start | integration-accept | complete | reconcile`) — hand-editing snapshot rows or the register outside those verbs is **not** an authorized path.
42
+ - **Plan-scoped coordination is a domain-call surface**: plan-row `coordination` block (`prepared` / `revision` / `duplicate-holder`), session JSON, handoff record, and `--expect <revision>` semantics have their **single runtime home** in **`references/status-and-residuals.md`**; flag shapes and exit codes → **`mstar-use-cli`**; route semantics → **`mstar-iteration`** `references/plan-scoped-pm.md`. Every plan-row mutation goes through the verbs (`mstar plan bind | show | prepare | progress | residual-add | residual-close | handoff | accept | return | integration-start | integration-accept | complete | reconcile`) — hand-editing snapshot rows or the register outside those verbs is **not** an authorized path.
43
43
 
44
44
  > **Engine check (when available):** run `mstar lease verify --workflow <id> [--plan <plan-id>]` or `mstar lease verify-integration --workflow <id>` (or import `validateExecutionLease` / `validateIntegrationMergeLease` from `@mstar-harness/engine` in a host hook) to validate the iteration leases above on the workflow snapshot (execution_lease / integration_merge_lease). On `fail` -> do not proceed; fix and re-run. Skill text below remains authoritative when the runtime is absent.
45
45
 
@@ -20,7 +20,12 @@ QC/QA 原始过程报告默认是 **ephemeral review bundle**,置于 `{SDD_DIR
20
20
  | QC 单席报告(**`inline` / hotfix 例外**) | `qc.md` |
21
21
  | QC 汇总结论(tri 模式) | `qc-consolidated.md` |
22
22
  | QA 验收报告(`QA gate: mandatory`) | `qa.md`(或 Assignment 指定的同目录 basename) |
23
+ | L2 task review 报告(**`Execution mode: sdd`,每个已完成 task**) | `../task-N-review.md` |
24
+ | Implementer 报告(**`Execution mode: sdd`**) | `../task-N-report.md` |
23
25
 
26
+ 后两行的 `../` 是**必需**前缀:这两份 artifact 落在 `{SDD_DIR}` 根,**不在** review bundle 内。按上表表头解析,`../task-N-review.md` = **`{SDD_DIR}/task-N-review.md`**,`../task-N-report.md` = **`{SDD_DIR}/task-N-report.md`**;漏掉该前缀(即让它们落在 `{SDD_DIR}/review/` 下)就是错误解析。两者由不同角色写、互不覆盖:implementer 始终写 `task-N-report.md` —— 它是 L2 的**输入**,永不被 L2 覆盖,也不能充当独立审查;fresh L2 task reviewer 始终写 `task-N-review.md`(`Execution mode: sdd` 下每个已完成 task 都必有,不是可选、也不是只留在对话里)。**不存在**接受任一 basename 的 fallback。
27
+
28
+ Mandatory QA always writes `${SDD_DIR}/review/qa.md` (or the Assignment's explicit same-directory basename) with its AC → evidence → result mapping; report-only is a mode, not a condition for mandatory report landing.
24
29
 
25
30
  ## SDD 运行时(不入 reports)
26
31
 
@@ -41,10 +46,12 @@ Raw bundle files may disappear after the working context is gone. Before Done, P
41
46
  - `Review range / Diff basis`
42
47
  - `Review bundle`: `{SDD_DIR}/review/`
43
48
  - `QC inputs`: `qc1.md` / `qc2.md` / `qc3.md` or `qc.md`
49
+ - `Task reviews`: compact per completed task — task number + review range + earned `Task quality` + report pointer (`task-N-review.md` → `{SDD_DIR}/task-N-review.md`); required for handoff once `{SDD_DIR}` is unavailable. The L2 report file lives at the SDD root (outside `{SDD_DIR}/review/`); bundle cleanup removes only the ephemeral `review/` artifacts. The pointer names the L2 report, never the implementer's `task-N-report.md`
44
50
  - `Blocking result`: fixed / none / deferred with reason
45
51
  - `Residual findings`: each open R# — id + short title + severity + tracking location (register `entries[<plan-id>]`) + owner/target + blocker-defer flag (`N/A — none open` when none)
46
52
  - Main plan `## QA Gate Summary` when QA applies:
47
53
  - `QA gate` / `QA mode`
54
+ - acceptance trace: compact AC → evidence → result mapping + coverage/gap disclosure + exact report pointer (`qa.md` → `{SDD_DIR}/review/qa.md`)
48
55
  - evidence reused vs newly run checks
49
56
  - related R# closure recommendations
50
57
  - `{PROJECT_DIR}/<id>/residuals.json` (default `{HARNESS_DIR}/projects/<id>/`): open R# machine SSOT — `entries[<plan-id>]`.
@@ -8,6 +8,19 @@ SDD implementers start with a fresh session — they have not seen the Prepare c
8
8
 
9
9
  The plan is the spec. Everything the executor needs must be in the file or reachable from a file path it names.
10
10
 
11
+ ## Prepare-writing bar (Phase 1 editing roles)
12
+
13
+ The Phase 1 editing roles (`product-manager` → `architect` → `writing-specialist`) carry the mirror-image obligation of the principle above: **they are the ones who write the context carrier.** The PM draft and the compass are the artifact a fresh session reads, and a dispatched Phase 1 role works from disk — never from the PM's conversation. A decision that lives only in that conversation is invisible to the role, which will re-derive it wrong.
14
+
15
+ So the draft is held to the same self-containment standard as a plan:
16
+
17
+ - **Context first, depth second.** Locked direction, settled decisions, open questions with owners, non-goal rationale, constraint sources, acceptance seed, branch policy. Coarse detail is legitimate; an *unmarked* hole is not — it has no owner and no place to be discharged.
18
+ - **Every unfinished part carries its owner.** The marker grammar is defined once, in `mstar-iteration/references/phase-1-prepare.md` §1.3 (`TODO(owner: …)`); this file cites it and does not restate it.
19
+ - **An editing role discharges the markers naming it** in its own edit pass, re-owning to `PM` whatever it cannot close, and reports the count.
20
+ - **No role-owned marker survives the compass lock.** Cleared (or explicitly re-owned to `PM` and raised to the user) before compass `status: locked` — never silently dropped.
21
+
22
+ The unowned-`TBD` ban is unchanged at every stage.
23
+
11
24
  ## Quality checklist
12
25
 
13
26
  Before a plan is locked, verify every item:
@@ -113,3 +113,10 @@ The direction and reason columns record the reasoning behind each answer; the an
113
113
  - No silent completion anywhere. Every stage transition records its evidence; failure renders the workflow blocked/active, never implicitly done.
114
114
  - No cleanup authorization is implied by lifecycle completion: worktree/branch deletion stays explicit and ownership/merge-guarded, exactly as the existing post-merge-close contract requires.
115
115
  - Close never releases leases, and terminal `failed`/`stopped` states are never rewritten as `completed` (§5).
116
+
117
+ > Amendment 2026-09-17 (recorded at PM lock): **legacy delivery-source correction.**
118
+ >
119
+ > Legacy source correction is a distinct coordinator-owned domain operation for a running, single-row standalone development workflow whose registered source erroneously equals its target. It derives the replacement source only from that row's already-accepted, evidence-pinned handoff and verifies the source Git ref/commit. Under the existing snapshot lock and expected row revision it may replace only `branch.source`, advance that row's coordination revision and update snapshot `updated_at`. All PR/merge evidence, statuses, leases, timestamps other than `updated_at`, target and handoff pins remain unchanged. It cannot record Done, delivery success or remote merge. Missing proof, foreign authority, terminal state, a nonmatching legacy shape, or a repeat after correction refuses without writes. Future registrations must name the true delivery source; this is not their normal lifecycle step.
120
+ >
121
+ > This extends §4a (registration is an authorized domain operation) with a bounded repair for pre-existing snapshots only, and leaves §5 (failure and abandonment) and the Binding negatives above untouched.
122
+
@@ -375,7 +375,7 @@ These are **full-protocol prose** — the single canonical copy lives in **`msta
375
375
 
376
376
  ## Plan-scoped coordination (bind / revision / session / handoff) — sole runtime field home
377
377
 
378
- The scoped route(`/iteration-drive --assignment | --workflow <id> --plan <id> | --resume <session.json>` → `mstar plan …`)keeps **one process authority**: the same workflow snapshot (`workflows/<id>/snapshot.json`) and the same root `status.json` — no per-plan snapshot clone, database, daemon or second status copy. This section is the **single runtime home** for the coordination / session / handoff / revision fields; command flags and exit codes → `docs/cli.md`; route semantics → **`mstar-iteration`** `references/plan-scoped-pm.md`; engine API shapes → `packages/engine/src/coordination.ts`.
378
+ The scoped route(`/iteration-drive --assignment | --workflow <id> --plan <id> | --resume <session.json>` → `mstar plan …`)keeps **one process authority**: the same workflow snapshot (`workflows/<id>/snapshot.json`) and the same root `status.json` — no per-plan snapshot clone, database, daemon or second status copy. This section is the **single runtime home** for the coordination / session / handoff / revision fields; command flags and exit codes → **`mstar-use-cli`**; route semantics → **`mstar-iteration`** `references/plan-scoped-pm.md`; engine API shapes → `packages/engine/src/coordination.ts`.
379
379
 
380
380
  ### Snapshot fields
381
381
 
@@ -407,12 +407,16 @@ The scoped route(`/iteration-drive --assignment | --workflow <id> --plan <id>
407
407
 
408
408
  | Actor | May write |
409
409
  | --- | --- |
410
- | coordinator — `mstar plan prepare · accept · return · integration-start · integration-accept · complete · reconcile` | selected row `coordination.prepared` and handoff transitions, `status`, both coordination leases, `Done` |
410
+ | coordinator — `mstar plan prepare · accept · return · integration-start · integration-accept · complete · repair-delivery-source · reconcile` | selected row `coordination.prepared` and handoff transitions, `status`, coordination leases, `Done` (route-specific) |
411
411
  | plan session — `mstar plan progress · residual-add · residual-close · handoff` | its own row `status` + `coordination.progress`, `metadata.track_branches`, its `entries[<planId>]` register bucket, and the handoff record |
412
412
  | anyone else | nothing scoped — sibling rows, lifecycle anchors, root register, `execution_policy`, `compass_ref`, shared indexes, the iteration PR and Phase 3–6 stay on the coordinator / global route |
413
413
 
414
- - **State machine:** `Todo → InProgress` (bind) → `InReview` (handoff; lease kept) → `accepted` → `integrating` → `merged` → `completed` ⇒ `Done`. `progress` allows only `InProgress → InProgress | InReview | Blocked`, `Blocked → Blocked | InProgress`, and `InReview → InReview | InProgress | Blocked` **before** handoff — never `Todo` / `Done` / lease removal. After handoff, all scoped progress/residual mutations are rejected until `return`.
415
- - **`complete` is the one atomic write** that sets `Done` (with verified Git proof and the findings gate), retains row `metadata.working_branch` / `metadata.worktree_path` and existing `track_branches`, and deletes the row `execution_lease` plus the coordinator's `integration_merge_lease`. `accept` is ownership transfer only — no merge, no `Done`; `integration-accept` keeps both leases and `InReview` until `complete`.
414
+ - **State machine:** `Todo → InProgress` (bind) → `InReview` (handoff; lease kept) → `accepted` → (`integrating` → `merged` on the **iteration route only`) → `completed` ⇒ `Done`. `progress` allows only `InProgress → InProgress | InReview | Blocked`, `Blocked → Blocked | InProgress`, and `InReview → InReview | InProgress | Blocked` **before** handoff — never `Todo` / `Done` / lease removal. After handoff, all scoped progress/residual mutations are rejected until `return`.
415
+ - **Two completion routes (engine-selected):**
416
+ - **Iteration** (`type: iteration`, or any workflow that is not standalone development): after `accept`, `integration-start` → coordinator merge → `integration-accept` → `complete`. `complete` is the one atomic write that sets `Done`, completes the handoff, and deletes the row `execution_lease` plus the coordinator's `integration_merge_lease`. `accept` is ownership transfer only — no merge, no `Done`; `integration-accept` keeps both leases and `InReview` until `complete`.
417
+ - **Standalone development** (`type: plan`, `delivery_kind: development`, exactly one owned row): after `accept`, `complete` directly from the accepted handoff with no integration record or merge lease. `complete` sets `Done`, completes the handoff, retains cleanup metadata, deletes only the row `execution_lease`, and leaves the workflow `running` until ordinary delivery evidence and terminal close. Integration verbs refuse this route; missing integration anchors never select it.
418
+ - **Legacy delivery-source repair:** `repair-delivery-source` is coordinator-only, not a normal lifecycle step, and applies only to the pre-fix legacy shape `branch.source === branch.target` with an accepted handoff naming a different source. Under the row revision lock it may replace only `branch.source` (derived from the sealed handoff), advance `coordination.revision`, and update snapshot `updated_at`. It never records `Done`, changes delivery/merge evidence, statuses, leases, or handoff pins, and refuses a second application once aligned. Future registrations must record the true delivery source; `workflow evidence --declare-kind --branch-source` cannot amend an already-registered anchor.
419
+ - **Reconcile:** iteration route observes Git in the recorded integration checkout (`completed` / `retry-ready` / `already-completed`); standalone route only replays an already-completed row as byte-identical `already-completed` — it never manufactures `Done` from Git truth alone.
416
420
  - **Legacy helpers refuse coordinated keys:** `appendProjectRegisterEntries` / `closeProjectRegisterEntry` / backlog next-free-key and `persist` replacements reject an existing coordinated plan bucket with `coordination.scoped-writer-required` (directing the caller to `residual-add` / `residual-close`), and hand writes to protected snapshot / register / root targets are refused with `coordination.direct-write-refused`. Root and global lifecycle operations stay on the existing coordinator route and are never `mutatePlanCoordination` targets.
417
421
  - Read-only validators (`mstar lease verify`, `mstar lease verify-integration`, `mstar worktree check`, `mstar status validate`) remain **checks** — never mutation substitutes.
418
422
 
@@ -432,7 +436,7 @@ Reconciliation observes **Git ancestry / HEAD facts** in the recorded repository
432
436
 
433
437
  **Both byte tokens, always.** The amendment carries the current raw-byte SHA-256 of the **snapshot bytes** and of the workflow's reviewed **compass Markdown bytes** (`sha256:<64 lowercase hex>`, read from the read-only `show-prepare`); both are required even on the first amendment, and neither is a per-plan `coordination.revision`. The compass token binds the reviewed declaration: the resulting plan-id **set** must equal the compass `plans:` list exactly, and its `spec_integration_branch` / `integration_worktree_path` declarations must agree with what the call would leave recorded. The compass is re-read immediately before the single atomic commit.
434
438
 
435
- **Refusals are mutation-free.** `coordination.prepare-amendment.{stale, invalid-patch, not-prepare, execution-started, duplicate-plan, invalid-plan, compass-mismatch, invalid-worktree}` (`coordination-write.ts`), plus the existing auth/scope errors; when each fires, the exact exit code and payload → `docs/cli.md` § `mstar-harness workflow`. The protected snapshot, root register, other workflows and the compass stay byte-identical.
439
+ **Refusals are mutation-free.** `coordination.prepare-amendment.{stale, invalid-patch, not-prepare, execution-started, duplicate-plan, invalid-plan, compass-mismatch, invalid-worktree}` (`coordination-write.ts`), plus the existing auth/scope errors; when each fires, the exact exit code and payload → **`mstar-use-cli`** `references/plan-and-workflow.md`. The protected snapshot, root register, other workflows and the compass stay byte-identical.
436
440
 
437
441
  **Prepare-only, no force.** Recovery from a `stale` token is re-read `show-prepare` → review again → retry with the fresh tokens. There is no force, no replacement snapshot, no reset and no hand-editing workaround for a workflow that has left Prepare or already owns execution.
438
442
 
@@ -61,7 +61,7 @@ Full codebase audit: nine-category fan-out across **`references/audit-playbook.m
61
61
  2. **Simpler explanation** — does a simpler explanation cover the same evidence?
62
62
  3. **Evidence verifiability** — open the cited `file:line` and check it actually supports the claim.
63
63
 
64
- Dispose per the five-state rule (single-pass version — the four dispositions below implement the survey's five-state semantics: uncovered-keep == 未提及保留; never-drop == 全空/null 回流):
64
+ Dispose each attacked finding into exactly one of the four dispositions below:
65
65
 
66
66
  - **Survived** — passes to vet unchanged.
67
67
  - **Refuted** — drop, and record in the index's "considered and rejected" section: `- <finding>: not worth doing because <one line>`.
@@ -90,7 +90,7 @@ Do not write 30 plans nobody asked for. If running non-interactively (no user av
90
90
 
91
91
  The output contract is common; per-variant output shapes live in the variant reference.
92
92
 
93
- - **Full codebase audit**: audit index `README.md` template (findings table, direction, execution order & status, considered-and-rejected, red-team dispositions) and the `mstar audit scaffold` Engine-check callout → **`references/codebase-audit.md`** § Output format. Plan writing → **`## Plan output (all variants)`** below.
93
+ - **Full codebase audit**: audit index `README.md` template (findings table, direction, Coverage table, execution order & status, considered-and-rejected, red-team dispositions, plus the Needs verification and Hardening & checked notes disposition sections) and the `mstar audit scaffold` Engine-check callout → **`references/codebase-audit.md`** § Output format. Plan writing → **`## Plan output (all variants)`** below.
94
94
  - **PR review**: `findings` / `verdict` / `score_pct` / `tally` / `evidence` / `unverified` / `next` / `notes` / `comments` → **`references/pr-review.md`** § Output shape.
95
95
  - Every finding follows **`references/finding-format.md`** — read it before the first finding.
96
96
 
@@ -22,12 +22,12 @@ Audit depth follows the **effort level** (default `standard`; set with `quick` /
22
22
 
23
23
  | | `quick` | `standard` (default) | `deep` |
24
24
  |---|---|---|---|
25
- | Coverage | Recon hotspots only — highest-churn, highest-criticality code | Hotspot-weighted, key packages | Whole repo, every package |
25
+ | Breadth | Recon hotspots only — highest-churn, highest-criticality code | Hotspot-weighted, key packages | Whole repo, every package |
26
26
  | Subagents | 0–1 (sweep directly when feasible) | ≤4 concurrent | ≤8 concurrent, one per category |
27
27
  | Categories | correctness, security, tests | all nine | all nine |
28
28
  | Findings | top ~6, HIGH-confidence only | full table | full table incl. LOW-confidence "investigate" items |
29
29
 
30
- Whatever the level, state in the final report what was *not* audited.
30
+ Whatever the level, record what was examined and what was not in the final report's **Coverage** section (see § Output format) — one row per material review question, never a bare "not everything was audited" disclaimer.
31
31
 
32
32
  Every finding follows **`references/finding-format.md`** — read it before the first finding.
33
33
 
@@ -48,7 +48,15 @@ Plan-file layout, Status block, commit stamp, and handoff follow the shared cont
48
48
 
49
49
  **Excerpts come from your own reads, never from a subagent's report.** Before writing each plan, open every cited file yourself — subagent line numbers and attributions are leads, not facts.
50
50
 
51
- If an audit directory from a previous run exists, **reconcile, don't duplicate**: read its `README.md`, keep numbering monotonic, skip findings already planned or listed as rejected, mark superseded plans stale.
51
+ If an audit directory from a previous run exists, read its `README.md` before planning this run and carry the prior record over under five rules:
52
+
53
+ 1. **Prior `covered` is priority input, not fresh evidence.** Unchanged prior evidence may shape where this run looks, but a unit not re-examined this run is `deferred` with an explicit reference to the prior evidence and a reason — never marked `covered` this run. Do not relabel earlier checks as current work.
54
+ 2. **Prior `deferred`, `blocked` and `out_of_scope` units become current work when now in scope.** They are priority input for this run, never suppression keys.
55
+ 3. **A prior rejected claim suppresses only the exact claim with unchanged relevant evidence and control assumptions.** It never exempts the whole unit; changed evidence reopens the question.
56
+ 4. **Prior quick/scoped/truncated runs contribute only their recorded evidence and gaps; partial or missing prior coverage implies no clean remainder** — missing coverage means unknown, not clean.
57
+ 5. **Match finding fingerprints where present.** A re-considered claim keeps its fingerprint identity even if its disposition changes; legacy findings without fingerprints are compared by evidence and root cause, and never assigned a fabricated identity. The scaffold keeps numbering monotonic and never upserts or re-sorts prior rows; new plans get the next numbers in caller order. Findings already planned or listed as rejected in the prior run are skipped, not re-planned; plans the new run supersedes are marked stale in the index rather than deleted.
58
+
59
+ When finalizing the index, write the **Coverage** section per § Output format — and mind the scaffold ordering documented there, since `mstar audit scaffold` rebuilds the README and does not preserve Coverage.
52
60
 
53
61
  Plans generated from `simplify` / removal findings must carry **behavior-preservation verification gates**: existing tests pass *unmodified*, and characterization tests come first where coverage is thin (playbook §4). When the simplification would touch more than ~500 lines, recommend a codemod/automation pass rather than manual edits.
54
62
 
@@ -64,16 +72,33 @@ Plans generated from `simplify` / removal findings must carry **behavior-preserv
64
72
  | # | Finding | Category | Impact | Effort | Risk | Confidence | Evidence |
65
73
  |---|---------|----------|--------|--------|------|------------|----------|
66
74
 
75
+ The base table has the seven columns above; `Impact` is always prose — what goes wrong and who pays — never a numeric or category label. When at least one displayed finding carries a fingerprint, an optional `Fingerprint` column is appended after `Evidence`; when at least one carries a structured severity, the three columns `Likelihood | Severity impact | Severity` are appended after it (matching `finding-format.md` § Rendering consequences: `# | Finding | Category | Impact | Effort | Risk | Confidence | Evidence [| Fingerprint][| Likelihood | Severity impact | Severity]`). These columns are table-wide: a row without the metadata shows `—` in the new cells, and a table where no row carries it keeps the base header exactly. A fingerprint is optional author-chosen identity per `finding-format.md`; the scaffold never invents one and never re-sorts rows by it.
76
+
67
77
  ## Direction (separate)
68
78
 
69
79
  [2-4 grounded suggestions with evidence and trade-offs]
70
80
 
81
+ ## Coverage
82
+
83
+ | # | Unit (surface × boundary/invariant × subsystem × category) | Status | Evidence / check | Reason / gap |
84
+ |---|---|---|---|---|
85
+ | C1 | order route × owner binding × API × security | covered | `src/orders.ts:42` → verified lookup binds order and actor; unauthorized branch rejects | — |
86
+ | C2 | webhook × signature verification × worker × security | blocked | `src/hooks.ts:18` → found verifier call; deployment key source unavailable | Missing deployed key configuration; see Needs verification lead "webhook provenance" |
87
+ | C3 | export job × tenant scope × worker × security | deferred | — | Not examined: this run's budget was spent on the auth and webhook surfaces; export tenant scope needs its own pass |
88
+
89
+ Coverage is partial. Not examined: export tenant scope. Unresolved: webhook deployment key source. No previous coverage record was available.
90
+
91
+ (When no row is non-covered, close with the gap-free form instead — same rules, no numeric tallies:)
92
+ Coverage is complete for the declared scope. Every material review question has a row and all are covered; no unresolved gaps. No previous coverage record was available.
93
+
71
94
  ## Needs verification
72
95
 
73
96
  [MEDIUM-confidence or runtime-dependent leads — mainly from the Security pass (`references/security-review.md`). One line each; these are not findings and get no plan until verified:]
74
97
 
75
98
  - <lead>: what to verify, how (the exact check), evidence so far (`file:line`).
76
99
 
100
+ **Finding / lead exclusivity.** The same causal claim is either a reportable finding or a Needs-verification lead in the current report — never both. This is a semantic judgment by the author, over the causal claim, not over titles: similar wording is not proof of identity, and different wording does not prove two claims. Nothing enforces this mechanically; the lead carrier has no fingerprint field (a lead's prose may cite one, which the scaffold does not parse). A blocked Coverage row may link a lead, but that does not promote the lead to a finding; a runtime-dependent security claim stays here as requires runtime verification until resolved.
101
+
77
102
  ## Hardening & checked notes
78
103
 
79
104
  [Security-pass leftovers, one line each, no plan unless the user asks. Not findings and not rejected findings — they stay visible so the next run doesn't redo them:]
@@ -96,7 +121,31 @@ Plans generated from `simplify` / removal findings must carry **behavior-preserv
96
121
  - <finding>: <survived / refuted / hallucination-dropped / uncovered-kept>, <one-line reason>
97
122
  ```
98
123
 
99
- > **Engine check (when available):** run `mstar audit scaffold <findings-file> [--dir <out-dir>]` (or `import { scaffoldAuditPlan, validateAuditStatusBlocks } from "@mstar-harness/engine"` in a host hook) to scaffold the `audit-<date>/` plan directory (numbered plan files + README index) from findings, validate the audit Status blocks per **`mstar-audit` SKILL.md** `## Plan output (all variants)`, and redact credentials from audit excerpts. The findings file may be a bare array or `{findings, needsVerification?, hardeningChecked?}`. Disposition policy: a supplied `needsVerification` / `hardeningChecked` set is authoritative and replaces its index section on rebuild (resolved leads are removed by dropping them); an omitted field carries the previous section's entries over, so hand-added security dispositions survive an index rebuild. On `fail` -> do not proceed; fix and re-run. Skill text below remains authoritative when the runtime is absent.
124
+ ### Coverage contract
125
+
126
+ The Coverage table records which material review questions this run examined, could not examine, or did not attempt. The rows above are a **synthetic example**, not evidence from any particular repository.
127
+
128
+ **Unit.** A row is one *material review question*, described as surface × boundary/invariant × subsystem × category. It is not the Cartesian product of those lists — write the questions a competent reviewer would actually ask, not every combination. For security work the boundary is the control or invariant checked (entry surface × owner binding × API × security). For non-security categories the boundary is the affected operation and property (list rendering × bounded query count × orders UI × perf); do not invent fictitious attackers for `next`, `docs`, `simplify`, or performance work. The `C1`/`C2` labels are author-maintained and report-local — they need not survive another run and imply no generated identity. The descriptive Unit cell is what human reviewers reconcile against.
129
+
130
+ **Statuses.** Exactly five final statuses:
131
+
132
+ | Status | Meaning |
133
+ |---|---|
134
+ | `covered` | Actually examined this run. Requires a reviewed repository `file:line`, the invariant checked, and the observed result — in the cell or in evidence it links. The cited location must be one the author opened this run, and the observed result must be what that read showed — never a location known only from a subagent report or memory. `covered` means examined, **not clean**: the row may carry a confirmed finding. A runtime-dependent unresolved claim makes the row `blocked`, not `covered` with an optimistic footnote. |
135
+ | `blocked` | Attempted but stopped. Record whatever was examined, if anything; when access failed before the first read, the Evidence cell may be `—`. The Reason cell names the concrete missing fact/access/dependency, linking a Needs-verification lead when one exists. |
136
+ | `deferred` | In scope but not examined this run. Evidence cell is `—` or a link to explicitly identified prior evidence — never passed off as this run's work. Reason states why it was not examined: it must name a cause (scope decision, budget, dependency, or access) and must not restate the status. A surface outside the declared scope is `out_of_scope`, not `deferred`. |
137
+ | `out_of_scope` | Material surface deliberately outside the declared scope; Evidence cell is `—` (nothing was examined, so there is nothing to cite); Reason says why. |
138
+ | `not_applicable` | The surface does not exist or the question does not apply; a short recon reference when available, never a fabricated check. |
139
+
140
+ A check is a compact sentence: cited location → question/invariant → observed static result. If an already-authorized, side-effect-free command contributed, identify that command and its result; this adds no new execution permission. Link fuller prose (Needs verification, Hardening & checked notes, a finding) when a cell would become unwieldy — keep each fact in one place rather than duplicating it across cells.
141
+
142
+ **Completeness.** `covered` requires a reviewed path and an actual check/result; every non-covered row requires a concrete reason. Split a materially unexamined sub-question into its own row rather than hiding it under a `covered` parent. A quick/scoped/truncated run states partial explicitly; a broader run never implies unlisted surfaces are clean. Close with an uncounted partial/gap summary that names the gaps already in the table — no numeric tallies, no aggregate path unions, no extra bookkeeping columns. A run with no non-covered rows closes with the gap-free form of the same summary (every material question rowed and covered; no unresolved gaps), subject to the same no-tally rule. Scope completeness and evidence sufficiency remain reviewer judgments, not guarantees delivered by this table.
143
+
144
+ **Scaffold boundary.** `mstar audit scaffold` rebuilds the README index from scratch and carries over only the two security-disposition sections (Needs verification, Hardening & checked notes) — it has **no** coverage input or preservation API and does not validate Coverage. Therefore: read and retain the prior coverage from the existing index **before** invoking the scaffold; after the final scaffold, restore/reconcile the Coverage section into the rebuilt README and write this run's rows. If the scaffold must run again, repeat that ordering. Missing prior coverage is reported as unavailable in the closing summary — never reconstructed from memory.
145
+
146
+ **Deferred enforcement.** The Coverage table is a reviewer-checked authoring structure, not a machine guarantee. Status membership, required cell presence, and reference resolution are deterministic checks, but no existing engine or CLI command performs them: `mstar audit scaffold` neither preserves nor validates Coverage, and nothing else in the harness reads the table. Until a validator ships, a completed Coverage section attests only that the author followed this contract — it is never an "audit complete" certificate, and shape enforcement must not be claimed in prose or added ad hoc.
147
+
148
+ > **Engine check (when available):** run `mstar audit scaffold <findings-file> [--dir <out-dir>]` (or `import { scaffoldAuditPlan, validateAuditFindingGates, validateAuditStatusBlocks } from "@mstar-harness/engine"` in a host hook) to scaffold the `audit-<date>/` plan directory (numbered plan files + README index) from findings, redact credentials from audit excerpts, and run the deterministic finding gates (`validateAuditFindingGates`) before anything is written. The scaffold emits Status blocks that conform to the contract, but it does not re-validate existing plan files: validating audit Status blocks per **`mstar-audit` SKILL.md** `## Plan output (all variants)` is done by a host hook explicitly calling `validateAuditStatusBlocks` (also from `@mstar-harness/engine`) — the CLI command itself never invokes it. The findings file may be a bare array or `{findings, needsVerification?, hardeningChecked?}`; the finding-object field contract — JSON-to-engine mapping, absent-field defaults, string vs structured evidence, fingerprint/trace/severity — is owned by **`mstar-audit` references/finding-format.md § Machine-readable findings file**. Carrier acceptance is engine work; reportability, finding/lead exclusion, and coverage stay reviewer judgement — the engine enforces none of them. Disposition policy: a supplied `needsVerification` / `hardeningChecked` set is authoritative and replaces its index section on rebuild (resolved leads are removed by dropping them); an omitted field carries the previous section's entries over, so hand-added security dispositions survive an index rebuild. On `fail` -> do not proceed; fix and re-run. Skill text below remains authoritative when the runtime is absent.
100
149
 
101
150
  ## Handoff to execution
102
151
 
@@ -42,6 +42,50 @@ When the finding is structural, the Fix sketch names the restructuring move —
42
42
  | `DOCS` | Documentation |
43
43
  | `DIR` | Direction (features & roadmap) |
44
44
 
45
+ ## Machine-readable findings file (`mstar audit scaffold`)
46
+
47
+ The scaffold command `mstar audit scaffold <findings-file> [--dir <out-dir>]` turns a findings file into the numbered plan directory. The file is either a bare JSON array of finding objects or an object `{findings, needsVerification?, hardeningChecked?}`. This section is the contract for the finding objects; what the engine enforces is **carrier acceptance only** — deciding whether a finding is reportable, choosing its severity wording, grouping one root cause, and keeping a claim out of Needs-verification stay reviewer judgement.
48
+
49
+ ### Field mapping and defaults
50
+
51
+ | JSON field | Engine field | Present-value contract | Absent-field behavior |
52
+ |---|---|---|---|
53
+ | `title` | `title` | String, trimmed; visible nonempty content | Usage error, exit 2 |
54
+ | `description` | `impact` | String, trimmed; visible nonempty content. No `impact` JSON alias | Usage error, exit 2 |
55
+ | `priority` | `priority` | `P1` \| `P2` \| `P3` | Usage error, exit 2 |
56
+ | `effort` | `effort` | `XS` \| `S` \| `M` \| `L` \| `XL` | Usage error, exit 2 |
57
+ | `risk` | `risk` | `LOW` \| `MED` \| `HIGH` | Usage error, exit 2 |
58
+ | `category` | `category` | `bug` \| `security` \| `perf` \| `tests` \| `tech-debt` \| `migration` \| `dx` \| `docs` \| `direction` | Usage error, exit 2 |
59
+ | `confidence` | `confidence` | `HIGH` \| `MED` \| `LOW`; explicit value is preserved (previously discarded) | Defaults to `MED` |
60
+ | `evidence` | `evidence` | Array of non-empty strings (legacy free text, rendered as-is) or `{file, line?, description}` location objects; item order preserved; `[]` valid | Defaults to `[]` |
61
+ | `evidence[i].file` | `AuditEvidence.file` | Required safe repository-relative POSIX path | Usage error, exit 2 |
62
+ | `evidence[i].line` | `AuditEvidence.line` | Positive integer when present | Omitted; rendered without a colon or invented line |
63
+ | `evidence[i].description` | `AuditEvidence.description` | Required non-empty string | Usage error, exit 2 |
64
+ | `fingerprint` | `fingerprint` | Non-empty string; passed through untrimmed/unnormalized | Omitted; the engine never invents one |
65
+ | `trace` | `trace` | Array of `{kind, file, line, scope, description}`; all five members required | Omitted |
66
+ | `severity` | `severity` | Object with all of `likelihood`, `impact`, `overall`, each `informational` \| `low` \| `medium` \| `high` \| `critical` | Omitted; never inferred from priority, risk or confidence |
67
+ | `dependsOn` | `dependsOn` | `none`, `plans/NNN-*.md`, or a plan number `NNN` (normalized to `plans/NNN-*.md`); case-insensitive | Omitted; plan renders `none` |
68
+ | `fixSketch` | `fixSketch` | Optional non-empty string | Plan block omitted |
69
+ | `verification` | `verification` | Optional non-empty string | Plan block omitted |
70
+
71
+ A finding is a finding only with non-empty `title` and `description` and valid enums; in `title`, `description`, `priority`, `effort`, `risk`, `category`, `confidence`, `evidence`, `fingerprint`, `trace`, and `severity`, a supplied `null`, wrong type, malformed object, or invalid enum value is a usage error (exit 2, diagnostics name the field path without echoing submitted values), never silent omission or default. `dependsOn` is the one exception: a supplied `null`, non-string, or empty value is treated as absent (the plan renders `Depends on: none`); only a non-empty invalid string is a usage error. Omitting `confidence`, `evidence`, or any optional field is a valid choice, not an error. Authoring guidance such as "2–5 strongest locations" is advice, not an array gate — an accepted carrier is not automatically a reportable finding.
72
+
73
+ ### Deterministic gates the engine runs
74
+
75
+ > **Engine check (when available):** if the harness engine runtime is present, `validateAuditFindingGates(findings)` runs inside `scaffoldAuditPlan` before any file is written, so invalid findings exit 2 with no partial output. Skill text below remains authoritative when the runtime is absent. The gate checks, deterministically and only these:
76
+
77
+ - **Fingerprint** (when supplied): grammar `^[A-Za-z0-9][A-Za-z0-9._:/@+-]*$`, credential rejection (a value redaction would alter is rejected, never rewritten into a different identity), exact case-sensitive uniqueness in the batch, and strict ASCII ordering of the supplied subsequence. Absent fingerprints are skipped; out-of-order input is rejected, never sorted — positions control plan numbers and `dependsOn`. Mixed legacy/enriched batches are accepted. Choosing a stable root-cause identity, grouping one cause, and cross-run matching remain reviewer duties.
78
+ - **Severity**: every rank must be a valid enum value, and `severity.overall` must not exceed `severity.impact`. Nothing else is computed — the gate never infers severity and never proves the claimed impact.
79
+ - **Trace**: non-empty; a single step must be `entrypoint` or `sink`, longer traces must run `entrypoint` → `propagation…` → `sink`. Each step needs a positive-integer `line`, a safe typed path, and visible `scope`/`description`. Topology validation proves format, never reachability — the trace still means what the author claims: where data enters, how it travels, where it lands.
80
+ - **Evidence locations**: object evidence `file` and trace `file` must be repository-relative POSIX paths (no absolute/drive/UNC paths, backslashes, control characters, lone surrogates, empty/`.`/`..` segments, segments ending in a dot or space), and object evidence `line` must be a positive safe integer when present (`audit.finding.evidence.line`). Unsafe paths are never normalized into acceptance, and the engine never checks filesystem existence. Legacy string evidence is free text and is not path-checked.
81
+ - **Text**: `title`, `impact`, supplied `fixSketch`/`verification`, string evidence, structured evidence `description`s, and trace `scope`/`description` must contain visible content (at least one code point outside Unicode whitespace and default-ignorable code points; lone surrogates invalid). Multilingual content is never stripped.
82
+
83
+ Gate diagnostics use stable codes (`audit.finding.fingerprint.*`, `audit.finding.severity.*`, `audit.finding.trace.*`, `audit.finding.path.*`, `audit.finding.evidence.*`, `audit.finding.text.*`) and `findings[index].field` paths only — never raw submitted values, so credential material cannot leak through error output.
84
+
85
+ ### Rendering consequences
86
+
87
+ Structured evidence renders `file:line — description` (or `file — description` when `line` is omitted); string evidence keeps its existing rendering. Supplied `fingerprint`, `severity`, and `confidence` persist into both the plan and the README index (index columns appear when any displayed row carries them, appended after the Evidence column: `# | Finding | Category | Impact | Effort | Risk | Confidence | Evidence [| Fingerprint][| Likelihood | Severity impact | Severity]`). In the plan Status block the `- **Confidence**:` line is emitted for any enriched finding — non-default confidence always persists, and an enriched finding (fingerprint, severity, trace, or object evidence present) keeps even the default `MED`; only a legacy finding without enriched metadata omits the line. When the finding carries evidence, the Status block also includes `- **Evidence**: <first evidence item>` (the first item, rendered like the Evidence bullets); this line feeds the README index Evidence column so the cell survives a no-new-findings rebuild. Supplied `trace` persists into the plan only, as the `## Trace` section — it has no index column. A scaffold re-run with no new findings rebuilds the README index from existing plan files without rewriting them, so persisted metadata survives. The engine neither enforces finding/lead mutual exclusion (a claim is either a finding or a Needs-verification lead by reviewer judgement — the lead carrier `{lead, how, evidence?}` has no fingerprint field) nor validates coverage; both stay with the auditor.
88
+
45
89
  ## Direction findings — adaptations
46
90
 
47
91
  Direction findings (`DIR-NN`) use the same format with two field changes: