@mstar-harness/opencode 0.6.0 → 0.6.1

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.
@@ -3,29 +3,30 @@ name: mstar-plan-artifacts
3
3
  description: Morning Star plan harness artifacts — `{PLAN_DIR}` main plans and `reports/`, `{KNOWLEDGE_DIR}` / `{ITERATION_DIR}` indexes, Done compaction, plus `{HARNESS_DIR}/status.json` and root `residual_findings` (severity SSOT, open/archived lifecycle, `notes.json`). Read when writing plans or QC reports, maintaining knowledge/iteration indexes, reading or writing `status.json` / R#, Done compaction, or mapping QC severity to JSON. Required for `@project-manager` on status, residuals, and InReview/QC waves; `@qc-specialist*` before `reports/**/*.md`; `@qa-engineer` before closing R#. Verdict rules in `mstar-review-qc`; paths in `mstar-plan-conventions`.
4
4
  ---
5
5
 
6
- ## Load order(必读顺序)
6
+ ## Load order
7
7
 
8
- **首次 Read skill 前:必须先 Read `mstar-harness-core`(SKILL.md),并按需 Read `mstar-plan-conventions`(路径符号)。** 涉及 Git 分支 / worktree / QC 检出 → **`mstar-branch-worktree`**。冲突时 **以 `mstar-harness-core` 为准**。
8
+ **Before first Read of this skill: Read `mstar-harness-core` (SKILL.md), and `mstar-plan-conventions` when path symbols matter.** Git branch / worktree / QC checkout → **`mstar-branch-worktree`**. On conflict, **`mstar-harness-core` wins**.
9
9
 
10
- ## Scopeplan 目录工件)
10
+ ## Scope (plan directory artifacts)
11
11
 
12
- | Topic | 详见 |
13
- |-------|------|
14
- | planreports 命名、QC 波次、residual plan 索引顺序 | `references/plan-files-and-reports.md` |
15
- | knowledge / iterations / specs 边界与索引 | `references/knowledge-and-designs.md` |
16
- | Done 行瘦身 Profile A/B | `references/done-compaction.md` |
17
- | `status.json`、residual severity、生命周期、`jq` | `references/status-and-residuals.md` |
18
- | 空仓库 `status.json` / `notes.json` 模板 | `templates/status.empty.json`、`templates/notes.empty.json`(见 `templates/README.md`) |
12
+ | Topic | See |
13
+ |-------|-----|
14
+ | Main plan, reports naming, QC waves, residual and plan index order | `references/plan-files-and-reports.md` |
15
+ | knowledge / iterations / specs boundaries and indexes | `references/knowledge-and-designs.md` |
16
+ | Done row compaction Profile A/B | `references/done-compaction.md` |
17
+ | `status.json`, residual severity, lifecycle, `jq` | `references/status-and-residuals.md` |
18
+ | Empty-repo `status.json` / `notes.json` templates | `templates/status.empty.json`, `templates/notes.empty.json` (`templates/README.md`) |
19
+ | Tech-debt rollup (read-only) | `scripts/tech-debt-rollup.sh` |
19
20
 
20
- **Out of scope:** 分支与 QC/QA 检出对齐 → **`mstar-branch-worktree`**;QC 清单与 verdict → **`mstar-review-qc`**;`{HARNESS_DIR}` 路径发现与目录初始化步骤 → **`mstar-plan-conventions`**。
21
+ **Out of scope:** branch and QC/QA checkout alignment → **`mstar-branch-worktree`**; QC checklist and verdict → **`mstar-review-qc`**; `{HARNESS_DIR}` discovery and init → **`mstar-plan-conventions`**.
21
22
 
22
- ## `status.json` open residual(摘要)
23
+ ## `status.json` and open residual (summary)
23
24
 
24
- - **`{HARNESS_DIR}/status.json`**:`plans[]` 行状态 + 根级 **`residual_findings[<plan-id>]`**(open 列表 **SSOT**)。
25
- - **Canonical**:新登记只写根级 `residual_findings`;**`metadata.residual_findings`** legacy 读,**禁止**双写。
26
- - **生命周期**:open → 验证关闭 → **`archived/residuals/<plan-id>.json`**;**`severity`** 机器枚举见 reference
27
- - **`notes.json`**、**`tech_debt_summary`**(可选聚合视图)。
25
+ - **`{HARNESS_DIR}/status.json`**: `plans[]` row status + root **`residual_findings[<plan-id>]`** (open list **SSOT**).
26
+ - **Canonical**: register new findings only at root `residual_findings`; **`metadata.residual_findings`** is legacy read-only — **do not** dual-write.
27
+ - **Lifecycle**: open → verified close → **`archived/residuals/<plan-id>.json`**; machine **`severity`** enum in reference.
28
+ - **`notes.json`**, optional **`tech_debt_summary`** (rollup view; compute via **`scripts/tech-debt-rollup.sh`**).
28
29
 
29
- 字段、severity 映射表、归档与 `jq` 示例 → **`references/status-and-residuals.md`**。
30
+ Field semantics, severity mapping, archive flow, and `jq` examples → **`references/status-and-residuals.md`**.
30
31
 
31
- **Templates(本 skill):** `templates/status.empty.json`、`templates/notes.empty.json` — 复制到 `{HARNESS_DIR}/`(说明见 `templates/README.md`)。
32
+ **Templates (this skill):** `templates/status.empty.json`, `templates/notes.empty.json` — copy into `{HARNESS_DIR}/` (`templates/README.md`).
@@ -1,14 +1,14 @@
1
- # `{HARNESS_DIR}/status.json` Residual FindingsMorning Star
1
+ # `{HARNESS_DIR}/status.json` and Residual Findings (Morning Star)
2
2
 
3
- > **Load order(与其它 `mstar-*` skill 一致)**:依赖本 reference SSOT / residual 前,须已 Read **`mstar-harness-core`** skill(SKILL.md;同仓分支与 worktree **`mstar-branch-worktree`**)。冲突以 **`mstar-harness-core`** 为准;专题索引见该 SKILL.md「Morning Star Skill 索引」。
3
+ > **Load order (same as other `mstar-*` skills):** Before changing SSOT / residual fields using this reference, Read **`mstar-harness-core`** (SKILL.md; same-repo branches and worktrees → **`mstar-branch-worktree`**). On conflict, **`mstar-harness-core` wins**; skill index in that SKILL.md.
4
4
 
5
- `status.json` 位于 **`{HARNESS_DIR}/status.json`**,是 **`plans[]` 行状态**与 **仍处 `open` residual findings** 的**单一事实来源(SSOT)**。
6
- **residual canonical / legacy fallback 定义**见 **`mstar-plan-artifacts` SKILL.md**「`status.json` open residual(摘要)」;本文件展开**字段、severity、生命周期、归档与 `jq` 示例**。
7
- **已关闭**的 residual **不应长期堆在**本文件中;权威档案见 `**{HARNESS_DIR}/archived/residuals/<plan-id>.json`**(见「Residual findings 生命周期」)。
5
+ `status.json` lives at **`{HARNESS_DIR}/status.json`**. It is the **single source of truth (SSOT)** for **`plans[]` row status** and **open residual findings**.
6
+ Canonical vs legacy residual definitions **`mstar-plan-artifacts` SKILL.md** (“`status.json` and open residual (summary)”); this file covers **fields, severity, lifecycle, archive, and `jq` examples**.
7
+ **Closed** residuals should not accumulate here long-term; authoritative archive → **`{HARNESS_DIR}/archived/residuals/<plan-id>.json`** (see “Residual findings lifecycle”).
8
8
 
9
- **编排语义(为何不是「多写几个字」)**:open 列表与 `archived/residuals/` 是跨会话、跨 agent 的**风险与决策交接面**。若非阻断结论只留在对话或单次 QC 报告里、**不进 SSOT**,下一任实现/审查方**无法可靠继承**已 defer、已风险接受或待跟进的约定;`Done` 也会与「已知债是否对仓库可见」**脱钩**,复盘或线上问题时常出现**无单一事实可引用**。因此 `**@project-manager`** 宜在审查收口后尽快把应跟踪项**登记为 open**;`**@qa-engineer`** PM 宜在验证或豁免决策明确后**及时关闭并归档**——节奏可按里程碑灵活安排,但**不应默认「口头说过即可」**。
9
+ **Why this matters:** The open list and `archived/residuals/` are the **cross-session handoff surface** for risk and decisions. Non-blocking conclusions that stay only in chat or a single QC report **without SSOT** cannot be inherited reliably; `Done` drifts from visible known debt. **`@project-manager`** should register trackable open items soon after review closure; **`@qa-engineer`** and PM should close and archive after verification or explicit waiver — flexible timing, but **not** “said in chat is enough.”
10
10
 
11
- ## 基本结构
11
+ ## Basic structure
12
12
 
13
13
  ```json
14
14
  {
@@ -17,7 +17,7 @@
17
17
  "plans": [
18
18
  {
19
19
  "id": "plan-id",
20
- "title": "计划标题",
20
+ "title": "Plan title",
21
21
  "file": "{PLAN_DIR}/plan-id-feature-name.md",
22
22
  "status": "Todo | InProgress | InReview | Blocked | Done",
23
23
  "owner": "@project-manager",
@@ -51,184 +51,156 @@
51
51
  }
52
52
  ```
53
53
 
54
- **空仓库可复制模板**:本 skill **`templates/status.empty.json`**;可选 **`templates/notes.empty.json`** → `{HARNESS_DIR}/notes.json`。说明见 **`templates/README.md`**。
54
+ **Empty-repo templates:** **`templates/status.empty.json`**; optional **`templates/notes.empty.json`** → `{HARNESS_DIR}/notes.json`. See **`templates/README.md`**.
55
55
 
56
- **已关闭条目**在以上字段之外补充:`lifecycle`、`closed_at`、`closure_note`;可选 `closure_evidence`、`superseded_by`。语义见下「Residual findings 生命周期」。
56
+ **Closed entries** add: `lifecycle`, `closed_at`, `closure_note`; optional `closure_evidence`, `superseded_by`. See “Residual findings lifecycle”.
57
57
 
58
- **Open 条目可选 `detail_doc`**:仓库内相对路径,指向 `**{PLAN_DIR}/residuals/<plan-id>/**` 下与该条 `**id**`(如 `R1`)配套的散文 `.md`(若启用散文层,见 `knowledge-and-designs.md`);未使用散文层则**省略**该键。
58
+ **Open `detail_doc` (optional):** repo-relative path under **`{PLAN_DIR}/residuals/<plan-id>/`** matching **`id`** (e.g. `R1`); omit if prose layer unused (`knowledge-and-designs.md`).
59
59
 
60
- ## Residual findingsseveritySSOT,机器字段)
60
+ ## Residual findings: `severity` (SSOT, machine field)
61
61
 
62
- `residual_findings[<plan-id>][]` 里每条记录的 `**severity`** 只能是本节定义的枚举值(若仓库仅有 legacy 键位,读法见文末 **`jq` 示例**)。QC 报告 Markdown 里的 **Critical / Warning / Suggestion** 是**章节标题**,**不得**原样当作 JSON 里的 `severity`(二者关系见下表)。
62
+ Each `residual_findings[<plan-id>][]` entry’s **`severity`** must be from this enum (legacy read paths → **`jq` examples** at end). QC report Markdown **Critical / Warning / Suggestion** are **section titles** — **do not** copy them verbatim into JSON `severity`.
63
63
 
64
- ### 1. 允许取值
64
+ ### 1. Allowed values
65
65
 
66
- 仅此五种,**必须小写英文**:
66
+ Only these five, **lowercase English**:
67
67
 
68
- `critical`、`high`、`medium`、`low`、`nit`
68
+ `critical`, `high`, `medium`, `low`, `nit`
69
69
 
70
- ### 2. 全序(从重到轻)
70
+ ### 2. Total order (heavy → light)
71
71
 
72
- `critical` `high` `medium` `low` `nit`
72
+ `critical` > `high` > `medium` > `low` > `nit`
73
73
 
74
- - `**nit` 恒轻于 `low`**,禁止把两档颠倒或等同。
75
- - **禁止**把 `severity` 写成 `warning`、`Major`、中文或其它未列出的字符串。
74
+ - **`nit` is always lighter than `low`** — never invert or equate.
75
+ - **Forbidden** in JSON: `warning`, `Major`, non-English, or any value not listed.
76
76
 
77
- ### 3. 各档含义与门禁关系
77
+ ### 3. Meaning and gate relationship
78
78
 
79
+ | `severity` | Meaning |
80
+ | ---------- | ------- |
81
+ | `critical` | Merge-blocking; maps to QC **Critical** findings. |
82
+ | `high` | Not blocking but high impact (security, correctness, data, significant tech debt); fix, escalate, or open residual with PM follow-up. |
83
+ | `medium` | Should address this or next milestone; may be open residual. |
84
+ | `low` | Small impact, cheap fix; may be open residual. |
85
+ | `nit` | Style, naming, wording, non-behavior doc nits; **lighter than `low`**. PM may omit from `residual_findings` if no tracking needed. |
79
86
 
80
- | `severity` | 含义要点 |
81
- | ---------- | ------------------------------------------------------------------------ |
82
- | `critical` | 合并阻断级;与 QC 报告 **Critical** findings 对应。 |
83
- | `high` | 非阻断但影响大(安全、正确性、数据、显著技术债);须修复、显式升级决策,或 open residual 并由 PM 明确跟进。 |
84
- | `medium` | 当前或下一里程碑应处理;可 open residual。 |
85
- | `low` | 影响面小、修复成本低;可 open residual。 |
86
- | `nit` | 最低档:风格、命名偏好、措辞、非行为文档笔误等;**轻于 `low`**。PM 判断无需跟踪时可不写入 `residual_findings`。 |
87
+ Summary vs `mstar-review-qc`: unresolved **`critical`** → usually `Request Changes`; **`high`** often “fix or explicit decision before merge”; **`medium` / `low` / `nit`** may ship with residual tracking (final **Verdict** = PM consolidation).
87
88
 
89
+ ### 4. QC report section → JSON `severity`
88
90
 
89
- `mstar-review-qc` 的对应关系(摘要):未解决的 `**critical**` 通常导致 `Request Changes`;`**high**` 常与「合并前须处理或显式拍板」同列;`**medium` / `low` / `nit**` 可作为带 residual 的跟踪项(具体 **Verdict** 以 PM 汇总为准)。
91
+ When registering into root **`residual_findings`** (template in `mstar-review-qc`):
90
92
 
91
- ### 4. QC 报告小节 JSON 里写什么 `severity`
93
+ | Report Findings section | JSON `severity` |
94
+ | ----------------------- | --------------- |
95
+ | **Critical** | Default `critical`. PM may record `high` if “not blocking this merge but follow up soon” — state reason in `title`/`scope`. |
96
+ | **Warning** | `high` or `medium`: security/correctness/data → `high`; other substantive non-blocking → `medium`; **when unsure, use `high`**. |
97
+ | **Suggestion** | `low` or `nit`: substantive improvement → `low`; pure style/optional → `nit`. |
92
98
 
93
- QC 报告模板见 `mstar-review-qc`。把 finding 登记进根级 `**residual_findings**` 时,按下表选择字段值:
99
+ **Common mistake:** report **Warning** is not a valid `severity` string; there is no `warning` in the enum (see legacy below).
94
100
 
101
+ ### 5. Legacy `"severity": "warning"`
95
102
 
96
- | 报告 Findings 小节 | 写入 JSON `severity` |
97
- | -------------- | --------------------------------------------------------------------------- |
98
- | **Critical** | 默认 `critical`。若 PM 明确记录「本次不阻断但须尽快跟进」,可记 `high` 并在 `title`/`scope` 写清理由。 |
99
- | **Warning** | `high` 或 `medium`:偏安全/正确性/数据 → `high`;其它实质性非阻断 → `medium`;**不确定时取 `high`**。 |
100
- | **Suggestion** | `low` 或 `nit`:有实质改进 → `low`;纯风格/可有可无 → `nit`。 |
101
-
102
-
103
- **易错点**:报告里的 **Warning** 不是合法 `severity` 字符串;合法值里**没有** `warning`(见第 5 节历史兼容)。
104
-
105
- ### 5. 历史数据中的 `"severity": "warning"`
106
-
107
- 旧 JSON 若出现 `**"severity": "warning"`**:读取、汇总、`tech_debt_summary` 统计时**一律视为 `low`**。**禁止**在新条目中使用该值。
103
+ In old JSON, **`"severity": "warning"`** is read and rolled up as **`low`**. **Forbidden** on new entries.
108
104
 
109
105
  ---
110
106
 
111
- ## `plans[].metadata` 标准可选字段
112
-
113
- 与业务仓库实践对齐的推荐键(均为可选;项目可只选子集):
114
-
115
-
116
- | 键 | 类型 | 用途 |
117
- | --------------------------------- | ------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |
118
- | `working_branch` | string | 本 plan 实现所用分支名,与 Assignment `**Working branch`** 对齐(SSOT) |
119
- | `spec_integration_branch` | string | (多 Plan 同源 **Spec** 时推荐)该 Spec 对应的 **集成分支** 名;各 Plan 实现分支收口时应 merge 回此线,而非默认直接进 `main`(见 `mstar-plan-conventions` SKILL.md「Spec 文档驱动的分支模型」) |
120
- | `merge_target` | string | 本 plan 成果 **下一跳** 预期合并到的分支:多 Plan + Spec 模型下 **通常为 `spec_integration_branch`**;最终进 `main` 仍走 **PR**(同 skill)。单 plan / 无 Spec 集成线时可为 `main` 或团队约定名 |
121
- | `branch_policy` | string | 与 `mstar-harness-core` 一致的一行策略说明(如 `direct on main — <reason>` 或 `create feature/x from main`) |
122
- | `phase` | string | 程序/路线图阶段标签(如 `Phase 0`、`v1.0`) |
123
- | `priority` | `high` | `medium` | `low` | PM 编排优先级 |
124
- | `description` / `scope` | string | 一句话范围或目标;**同一仓库内择一**为主,避免两键长期混填不同内容 |
125
- | `gates` | object | 门禁结果摘要;**推荐子键**(按需):`qc`、`qa`、`typecheck`、`tests`、`lint`… 值为短字符串(如 `PASS (…)`、`FAIL — see report`) |
126
- | `blocked_since` | `YYYY-MM-DD` | 当 `status` 为 `Blocked` 时建议填写 |
127
- | `blocked_reason` | string | 阻塞原因(可与顶层 `notes` 互引,metadata 便于机器过滤) |
128
- | `blocked_by_plan_id` | string | 若阻塞来自另一计划,填对方 `**plans[].id`** |
129
- | `dependency` | string | 其它依赖说明(接口、外部团队);与 `blocked_by_plan_id` 互补 |
130
- | `next_action` | string | 当前解锁后或审查后的下一步(给谁、做什么) |
131
- | `primary_spec` | string | 主规格/设计文档路径(仓库内相对路径;**常为** `{KNOWLEDGE_DIR}/...` 或 `{SPECS_DIR}/...`,其中 `{SPECS_DIR}` 支持 `specs/` 与 `designs/`);多文档可用 `spec_refs`(string[]) |
132
- | `iteration_compass` | string | 可选:本轮依赖的迭代/版本 compass(`{ITERATION_DIR}/...`) |
133
- | `iteration_refs` | string[] | 可选:多个迭代 compass 或 program 快照路径 |
134
- | `qc_status` / `tests` / `commits` | string | **InReview / Done** 前后的可验证快照(结论摘要、测试统计、commit 提示),**非**替代正式报告文件 |
135
-
136
-
137
- ### 建议补充:交付型状态账本(phase + batches + verification)
107
+ ## `plans[].metadata` standard optional fields
138
108
 
139
- plan 包含多批次推进或多角色协作时,推荐采用以下三组字段,减少“只知道状态,不知道为什么”的信息损失:
140
-
141
- | 键 | 类型 | 用途 |
109
+ | Key | Type | Purpose |
142
110
  | --- | --- | --- |
143
- | `phase` | string | 当前交付阶段标签(如 `spec-drafting`、`batches-done-pending-merge`、`awaiting-qa`) |
144
- | `batches` | array<object> | 批次执行账本:每批覆盖任务、owner、状态、commit 证据、自检结论 |
145
- | `verification` | object | 与该批次相关的命令级验证快照(如 `cargo_check_workspace: ok`) |
146
-
147
- **`batches[]` 推荐子字段**(按需):
148
-
149
- - `index`: number,批次序号(1-based)。
150
- - `covers`: string[],覆盖的任务 ID(如 `T3`、`T4`)。
151
- - `status`: `planned | in_progress | done | blocked`。
152
- - `owner`: string,负责角色。
153
- - `commits`: string[],commit 短哈希 + 一行摘要(便于审计)。
154
- - `a2_self_audit`(或同义键):string,规则自检结论。
155
- - `verification`: object,批次级验证结果(可覆盖全局 verification)。
156
-
157
- > 约束:`batches`/`verification` 是证据索引,不替代 `{PLAN_DIR}/reports/` 正式审查文档,也不替代根级 `residual_findings` 的风险追踪。
158
-
159
- ### `plans[].notes` `{HARNESS_DIR}/notes.json` 的协同
160
-
161
- 推荐保留双层日志语义,避免把所有轨迹塞进单条字符串:
111
+ | `working_branch` | string | Implementation branch; aligns with Assignment **`Working branch`** (SSOT) |
112
+ | `spec_integration_branch` | string | (Multi-plan same **Spec**) integration branch name; plan branches merge here before `main` (`mstar-plan-conventions`) |
113
+ | `merge_target` | string | Next merge target; multi-plan + Spec → usually `spec_integration_branch`; `main` via PR |
114
+ | `branch_policy` | string | One-line policy per `mstar-harness-core` |
115
+ | `phase` | string | Program/roadmap label |
116
+ | `priority` | `high` \| `medium` \| `low` | PM scheduling |
117
+ | `description` / `scope` | string | One-line scope; pick one key per repo |
118
+ | `gates` | object | Gate summary (`qc`, `qa`, `typecheck`, `tests`, `lint`, …) |
119
+ | `blocked_since` | `YYYY-MM-DD` | When `status` is `Blocked` |
120
+ | `blocked_reason` | string | Block reason |
121
+ | `blocked_by_plan_id` | string | Blocking **`plans[].id`** |
122
+ | `dependency` | string | Other dependencies |
123
+ | `next_action` | string | Next step after unblock/review |
124
+ | `primary_spec` | string | Main spec path (`{KNOWLEDGE_DIR}/…`, `{SPECS_DIR}/…`) |
125
+ | `iteration_compass` | string | Optional `{ITERATION_DIR}/…` |
126
+ | `iteration_refs` | string[] | Optional multiple compass paths |
127
+ | `qc_status` / `tests` / `commits` | string | InReview/Done snapshots; not a substitute for `{PLAN_DIR}/reports/` |
128
+
129
+ ### Optional delivery ledger (`phase` + `batches` + `verification`)
130
+
131
+ For multi-batch or multi-role plans:
132
+
133
+ | Key | Type | Purpose |
134
+ | --- | --- | --- |
135
+ | `phase` | string | Delivery phase label |
136
+ | `batches` | array\<object\> | Per-batch task coverage, owner, status, commits, self-audit |
137
+ | `verification` | object | Command-level verification snapshot |
162
138
 
163
- - `plans[].notes`:该 plan 的关键时间线(可用字符串数组,按时间追加)。
164
- - `{HARNESS_DIR}/notes.json`:跨 plan 程序里程碑。
139
+ Recommended `batches[]` subfields: `index`, `covers`, `status`, `owner`, `commits`, `a2_self_audit` (or synonym), `verification`.
165
140
 
166
- 若仓库历史上 `plans[].notes` 是字符串,也可保留;新项目建议从一开始使用字符串数组,记录“时间 + 事件 + 证据锚点(commit/PR/report)”。
141
+ > `batches` / `verification` are evidence indexes — not replacements for `{PLAN_DIR}/reports/` or root `residual_findings`.
167
142
 
168
- ## 根级 `metadata` 标准可选字段
143
+ ### `plans[].notes` vs `{HARNESS_DIR}/notes.json`
169
144
 
170
- `**residual_findings`**(SSOT)外,推荐可选:
145
+ - `plans[].notes`: per-plan timeline (string array recommended).
146
+ - `{HARNESS_DIR}/notes.json`: cross-plan program milestones.
171
147
 
148
+ Legacy string `plans[].notes` is OK; new repos should use arrays with time + event + evidence anchor.
172
149
 
173
- | 键 | 类型 | 用途 |
174
- | --------------------------- | ------ | ---------------------------------------------------------------------------------------------------------------- |
175
- | `versioning` | object | 跨 plan 约定,如 `phase_1_branch_prefix`、`release` 等(自由键,团队自定) |
176
- | `notes` | array | **遗留/不推荐**:曾用于程序级时间线;**新仓库请用** `**{HARNESS_DIR}/notes.json`**,以免 `status.json` 随日志变长。若仍存在,可与 `notes.json` 并存直至迁出 |
177
- | `residual_findings_history` | object | **遗留/不推荐**:同型剪切区;**新归档请用** `{HARNESS_DIR}/archived/residuals/<plan-id>.json` |
178
- | `tech_debt_summary` | object | **可选**:技术债**一览/rollup**,与 `**residual_findings` 互补**(见下专节);由 `**@project-manager`** 维护 |
150
+ ## Root `metadata` standard optional fields
179
151
 
152
+ | Key | Type | Purpose |
153
+ | --- | --- | --- |
154
+ | `versioning` | object | Cross-plan conventions (team-defined) |
155
+ | `notes` | array | **Legacy** — prefer **`{HARNESS_DIR}/notes.json`** |
156
+ | `residual_findings_history` | object | **Legacy** — prefer **`archived/residuals/<plan-id>.json`** |
157
+ | `tech_debt_summary` | object | Optional rollup over open R#; maintain via script (below) |
180
158
 
181
- ## 通用约束
159
+ ## General constraints
182
160
 
183
- - 每条 `plans[]` 可带 `**metadata` 对象**(可选)。无扩展需求时可省略该键或置 `{}`。
184
- - 初始化时若尚无 findings,使用平级 `"residual_findings": {}`;**勿**与 legacy 侧**双写**(定义见本 skill **`SKILL.md` 开篇**)。**程序时间线**请用可选 `**{HARNESS_DIR}/notes.json`**(见下),**勿**在 `status.json` 根级 `metadata` 中长期堆 `notes` 数组(遗留仓库若已有 `metadata.notes`,可择机迁出后删键)。
185
- - `**plans[].id`** 与根级 `**residual_findings**` 的键应对齐(同一 `plan-id`);若仍存在 legacy 侧残留映射,其 **plan-id 键名**也应与之一致,便于 `jq` 与报告目录 `reports/<plan-id>/` 一致。**不要**再存 `residual_findings_plan_id`(与 `id` 重复)。
186
- - `**residual_findings` 空键**:某 `plan-id` 下**已无 open 条目**时,应从根级 `**residual_findings**`(及 legacy 侧同键,若有)**删除该键**(勿保留 `"plan-id": []` 空数组),减少噪声与误读。**注意**:这仅指 residual 映射对象上的键;`**plans[]` 是否仍保留该 plan 行**由团队决定(Done 瘦行、冷快照与「滚动保留」见 `done-compaction.md`),二者独立。
187
- - `**residual_summary`(可选)**:单行人类可读摘要;**仅描述仍 open 且仍挂在** `residual_findings[<plan-id>]`(与 legacy 侧同口径若仍存在)**的项**(已关闭项应在 `**{HARNESS_DIR}/archived/residuals/`** 与可选 `**{HARNESS_DIR}/notes.json**` 中体现)。
161
+ - Each `plans[]` row may include optional **`metadata`** (`{}` or omit).
162
+ - Init with `"residual_findings": {}`; **no dual-write** with legacy side (see SKILL.md). Program timeline → **`notes.json`**, not long `metadata.notes` in `status.json`.
163
+ - **`plans[].id`** keys must align with root **`residual_findings`** keys (and `reports/<plan-id>/`). Do not store `residual_findings_plan_id`.
164
+ - **Empty `plan-id` key:** when no open items remain, **delete** the key from root **`residual_findings`** (and legacy side if present) — no `"plan-id": []`. Whether **`plans[]`** keeps the row is separate (`done-compaction.md`).
165
+ - **`residual_summary` (optional):** one-line human summary of **open** items only.
188
166
 
189
167
  ---
190
168
 
191
- ## Residual findings 生命周期(关闭、归档、移除)
192
-
193
- 本节约定技术债条目在**已修复、已豁免、被替代或误登**之后如何更新 JSON,与「登记」流程闭环。
169
+ ## Residual findings lifecycle (close, archive, remove)
194
170
 
195
- ### 条目状态 `lifecycle`(可选,默认视为 open
171
+ ### `lifecycle` (optional; default open)
196
172
 
173
+ | `lifecycle` | Meaning | `closure_note` should explain |
174
+ | ----------- | ------- | ----------------------------- |
175
+ | `open` | Not closed (omit field = open) | — |
176
+ | `resolved` | Fixed in code/config/docs and **verified** | What changed; how verified |
177
+ | `waived` | Explicit decision not to fix | Who decided; why; optional `tracking` Issue |
178
+ | `superseded` | Replaced by new finding/spec/refactor | `superseded_by` |
179
+ | `duplicate` | Duplicate of another R# | Canonical `id` or mistake note |
197
180
 
198
- | `lifecycle` | 含义 | 关闭时 `closure_note` 应说明 |
199
- | ------------ | ------------------------------ | ----------------------------------- |
200
- | `open` | 未关闭(**省略该字段时按 open 处理**,兼容旧数据) | — |
201
- | `resolved` | 已在代码/配置/文档中解决,且**已验证** | 改了什么、如何验证(可与 `closure_evidence` 互指) |
202
- | `waived` | 经明确决策**不修复**(承担风险或产品接受) | 谁决策、为何不修、是否登记 `tracking` Issue |
203
- | `superseded` | 被新 finding、新规格或重构方案取代 | 指向 `superseded_by`(另一条 `id` 或知识库路径) |
204
- | `duplicate` | 重复录入或与另一 R# 实质相同 | 指向 canonical 的 `id` 或说明误登 |
181
+ **On close:** set **`closed_at`** (`YYYY-MM-DD`) and **`closure_note`**; recommend **`closure_evidence`** (PR, commit, test, doc anchor).
205
182
 
183
+ ### Who updates when
206
184
 
207
- **关闭必填**:凡 `lifecycle` `resolved` / `waived` / `superseded` / `duplicate`,须填写 `**closed_at`**(`YYYY-MM-DD`)与 `**closure_note**`(一句即可)。**推荐**填写 `**closure_evidence`**(PR、commit、测试结果、文档锚点),以满足可审计与 `verification-before-completion` 一致。
185
+ | Action | Owner | When |
186
+ | ------ | ----- | ---- |
187
+ | Implement fix | `@fullstack-dev` / assignee | Completion Report cites R# + evidence |
188
+ | Verify | `@qa-engineer` | Regression / acceptance |
189
+ | Write `status.json` | **`@project-manager`** or **`@qa-engineer`** | After verification; waivers after PM + user/architect alignment |
208
190
 
209
- ### 谁更新、何时更新
191
+ Do not claim “R3 fixed” in chat/plan only without SSOT update.
210
192
 
193
+ PM should register open items after **`Approve with residuals`**; QA should state each related R# (open / resolved this round / needs waiver).
211
194
 
212
- | 动作 | 建议负责方 | 时机 |
213
- | ---------------- | ---------------------------------------------------------- | ------------------------------------ |
214
- | 实现修复 | `@fullstack-dev` / 对应 owner | Completion Report 中说明对应 R# 与证据 |
215
- | 验证 | `@qa-engineer` | 回归或验收中确认 R# 已满足 |
216
- | 写回 `status.json` | `**@project-manager**` 或 `**@qa-engineer**`(与 Done 收口权限一致) | 验证通过后同一提交或紧随其后;豁免/替代由 PM 与用户或架构对齐后写回 |
195
+ ### Recommended: archive to `archived/residuals/<plan-id>.json`
217
196
 
197
+ After **`closed_at`**, **`closure_note`**, and PM/QA confirm close:
218
198
 
219
- **不得**在未落盘的情况下,仅从对话或主 plan 文案中宣称「R3 已修」而不更新 SSOT。
199
+ 1. **Append** to **`{HARNESS_DIR}/archived/residuals/<plan-id>.json`**.
200
+ 2. **Remove** from open list (root **`residual_findings[<plan-id>]`**; legacy side if used). Delete empty **`plan-id`** keys.
201
+ 3. Update root **`updated_at`**; optional milestone in **`notes.json`**.
220
202
 
221
- **对 `@project-manager` / `@qa-engineer` 的预期(可灵活,须诚实)**:PM 在 `**Approve with residuals`** 或 consolidated QC 后**主动补齐** open 登记(或与 QC 报告交叉引用且 SSOT 中可追溯);QA 在验收叙述中**显式交代**每条相关 R#(仍 open / 本次已验证 resolved / 需 PM 与用户裁决豁免),避免「只写测试通过」而 SSOT 与报告对不上。长期 open 与已关闭双写、或主列表与归档文件**长期不一致**,会削弱 handoff 可信度,应在本里程碑内收敛。
222
-
223
- ### 推荐策略:关闭后迁入 `archived/residuals/<plan-id>.json`(控制 `status.json` 体积)
224
-
225
- 为避免 `**status.json` 随已关闭 R# 无限膨胀**,在 `**closed_at` / `closure_note`(及推荐 `closure_evidence`)已齐备** 且 `**@qa-engineer`** 或 `**@project-manager**` 已确认可关闭后:
226
-
227
- 1. **追加**到 `**{HARNESS_DIR}/archived/residuals/<plan-id>.json`**(文件名与 `**plans[].id**` 一致;遗留同目录布局下全路径可能为 `**plans/archived/residuals/<plan-id>.json**` 等,视解析结果而定)。
228
- 2. 从 **open 列表**(根级 **`residual_findings[<plan-id>]`**,条目若仅存于 legacy 侧则从该处)**删除**该条(主列表**只保留 open**)。若删除后该数组为空,**删除**对应的 `**plan-id` 键(见上文「空键」约定)。
229
- 3. 更新根级 `**updated_at`**;可选在 `**{HARNESS_DIR}/notes.json**` 追加一条里程碑(**优先**于根级 `metadata.notes`,见「`notes.json`」专节)。
230
-
231
- **归档文件格式**(每个 `plan-id` 一个 JSON 文件,可多次追加 `entries`):
203
+ Archive file shape (append to `entries`):
232
204
 
233
205
  ```json
234
206
  {
@@ -237,14 +209,7 @@ QC 报告模板见 `mstar-review-qc`。把 finding 登记进根级 `**residual_f
237
209
  "entries": [
238
210
  {
239
211
  "id": "R1",
240
- "title": "…",
241
212
  "severity": "medium",
242
- "source": "QC-#1",
243
- "scope": "…",
244
- "decision": "defer",
245
- "owner": "@fullstack-dev",
246
- "target": "…",
247
- "tracking": null,
248
213
  "lifecycle": "resolved",
249
214
  "closed_at": "2026-04-06",
250
215
  "closure_note": "…",
@@ -255,100 +220,88 @@ QC 报告模板见 `mstar-review-qc`。把 finding 登记进根级 `**residual_f
255
220
  }
256
221
  ```
257
222
 
258
- - **首次**为该 `plan-id` 归档时创建文件;**后续**关闭项 **append** 到同一文件的 `entries`(合并时读入—追加—写回,注意 JSON 格式化与冲突)。
259
- - 每条归档对象 **必须**含 `**archived_at`**(迁入文件当日 `YYYY-MM-DD`),与 `closed_at` 可不同。
260
- - **审计**:已关闭记录**只存在于**上述文件(及 QC `reports/` 原文);`status.json` 不再承载该条。
261
- - **一览**:若使用 `**metadata.tech_debt_summary`**,批量归档或关闭后应**刷新**聚合数字,与仍 open 的 R# 对齐(见「`metadata.tech_debt_summary`」专节)。
262
-
263
- ### 临时原位关闭(仅短过渡)
223
+ - Each archived entry needs **`archived_at`** (`YYYY-MM-DD`).
224
+ - Closed records live in archive + QC `reports/`; not in open list.
225
+ - After batch archive/close, **refresh `tech_debt_summary`** (script below).
264
226
 
265
- 在写入归档文件**之前**,可先在 **open 列表**(根级或仅存 legacy 侧时从该处)内补全 `lifecycle` / `closed_*`(便于同一次 PR 内 diff);**应在同一里程碑内**完成「迁入 `**{HARNESS_DIR}/archived/residuals/`** + 从主列表删除」,避免长期双写。
227
+ ### Short in-place close (transition only)
266
228
 
267
- ### 遗留:`metadata.residual_findings_history`(不推荐)
229
+ May set `lifecycle` / `closed_*` in open list for one PR; **same milestone** move to archive + delete from open list.
268
230
 
269
- 根级 `**residual_findings_history**` 曾用于在单文件内剪切已关闭项;**新仓库请优先**使用 `**{HARNESS_DIR}/archived/residuals/<plan-id>.json`**,以免 `status.json` 仍随历史变长。若仓库已存在 `residual_findings_history`,可由 `**@project-manager**` 择机迁到 `**{HARNESS_DIR}/archived/residuals/**` 后删除该键。
231
+ ### Legacy `metadata.residual_findings_history`
270
232
 
271
- ### 移除(硬删除)
233
+ Prefer **`archived/residuals/`**; migrate and delete history key when possible.
272
234
 
273
- - **禁止**对 `**open`** 条目从 `status.json` **硬删**。
274
- - 已归档条目**不要**从 `archived/residuals/*.json` **删除**;错关时追加更正说明条目或新开 R# 引用原 `id`。
275
- - 仅**误登且未进入任何留档**时,经 `**@project-manager`** 可从 `residual_findings` 删除;更稳妥为标 `**duplicate**` 后走关闭归档流程。
235
+ ### Hard delete
276
236
 
277
- ### 查询 open 项与已归档(示例)
237
+ - **Forbidden** for **open** entries.
238
+ - Do not delete archived entries; correct via new entry or new R# referencing old `id`.
239
+ - Mistaken open-only entry: PM may delete or mark **`duplicate`** then close/archive.
278
240
 
279
- 首条 `jq` `//` 右侧为 **legacy 读取路径**(键名与语义见本 skill **`SKILL.md` 开篇**)。
241
+ ### Query open and archived (examples)
280
242
 
281
243
  ```bash
244
+ # Replace .agents with your resolved {HARNESS_DIR}.
282
245
  jq '.residual_findings["01-data-infrastructure"] // .metadata.residual_findings["01-data-infrastructure"]' .agents/status.json
283
246
  jq '.entries[] | select(.id == "R1")' .agents/archived/residuals/01-data-infrastructure.json
284
- jq '.metadata.tech_debt_summary' .agents/status.json
247
+ bash skills/mstar-plan-artifacts/scripts/tech-debt-rollup.sh .agents/status.json
285
248
  ```
286
249
 
287
- ---
250
+ (`//` right-hand side = legacy read path.)
288
251
 
289
- ## `{HARNESS_DIR}/notes.json`(可选·程序时间线)
252
+ ---
290
253
 
291
- **定位**:**追加型**程序日志,记录合并收口、批量归档、`tech_debt_summary` 刷新等里程碑,**不**与 `**plans[].status` / open residual** 争 SSOT;目的是让 `**status.json` 保持短小**、仍可被 agent 按路径单独读取。
254
+ ## `{HARNESS_DIR}/notes.json` (optional program timeline)
292
255
 
293
- **推荐结构**(字段可按项目增减):
256
+ Append-only log for merge closure, batch archive, `tech_debt_summary` refresh, etc. Does not compete with **`plans[].status`** / open residual SSOT.
294
257
 
295
258
  ```json
296
259
  {
297
260
  "schema_version": 1,
298
261
  "updated_at": "YYYY-MM-DD",
299
262
  "entries": [
300
- {
301
- "at": "2026-04-08",
302
- "message": "Short milestone description",
303
- "plan_id": "01-data-infrastructure"
304
- }
263
+ { "at": "2026-04-08", "message": "Short milestone", "plan_id": "01-data-infrastructure" }
305
264
  ]
306
265
  }
307
266
  ```
308
267
 
309
- - `**entries**`:**按时间追加**;`plan_id` 可选(跨 plan 事件可省略或写多个说明在 `message` 内)。
310
- - `**updated_at`**:建议与**最后一条** `entries[].at` 同步,便于扫读。
311
- - **维护**:`**@project-manager`**(与写回 `status.json` 同权限节奏)。**禁止**为「改历史叙述」而重写已提交条目正文;更正走**新 `entries` 条**说明勘误。
312
- - **与 `plans[].notes`**:`plans[].notes` 仍是**单条计划**的短字符串(可选);本文件承载**跨计划 / 跨里程碑**的日志,二者不重复长文。
268
+ - **`@project-manager`** maintains; do not rewrite past `entries` — add correction as new entry.
269
+ - **`plans[].notes`**: per-plan; **`notes.json`**: cross-plan.
313
270
 
314
271
  ---
315
272
 
316
- ## `metadata.tech_debt_summary`(可选·技术债一览)
273
+ ## `metadata.tech_debt_summary` (optional rollup)
317
274
 
318
- **定位**:在根级 `**residual_findings**`(按 `plan-id` 分列的 **open** R#)之上,提供**跨 plan 的聚合视图**,便于路线图、排期与「还有多少 open」一眼扫读。**不替代**单条 R# 的权威字段;新增/关闭 R# 时仍以根级 `residual_findings` `**{HARNESS_DIR}/archived/residuals/`** 为准。
275
+ **Role:** Cross-plan aggregate over **open** R# in root **`residual_findings`** (and legacy read path if present). Does **not** replace per-entry SSOT.
319
276
 
320
- **维护**:`**@project-manager`** 在重大里程碑后更新(例如 QC 波次结束、批量归档 resolved、版本封板前)。可选在 `**{HARNESS_DIR}/notes.json**` 记一条「已刷新 `tech_debt_summary`」。
277
+ **Compute (canonical):** run the read-only script (do **not** hand-count):
321
278
 
322
- **推荐结构**(字段可按项目删减;`cross_cutting` 可省略):
279
+ ```bash
280
+ # From repo root; pass path to status.json if not .agents/status.json
281
+ bash skills/mstar-plan-artifacts/scripts/tech-debt-rollup.sh .agents/status.json
282
+ ```
283
+
284
+ - Prints computed `total_open`, `by_severity`, `by_target`, `by_plan`.
285
+ - Prints **PASS** / **DRIFT** vs stored `metadata.tech_debt_summary`.
286
+ - Script **does not write** `status.json` — PM copies computed values into `metadata.tech_debt_summary` after DRIFT or milestone refresh.
287
+
288
+ **When to refresh:** after QC waves, batch archive of resolved items, or release freeze. Optional `notes.json` entry: “refreshed tech_debt_summary”.
289
+
290
+ **Recommended stored shape** (`cross_cutting` optional; script does not compute `cross_cutting` — maintain manually if used):
323
291
 
324
292
  ```json
325
293
  {
326
294
  "tech_debt_summary": {
327
295
  "updated_at": "YYYY-MM-DD",
328
296
  "total_open": 29,
329
- "by_severity": {
330
- "critical": 0,
331
- "high": 10,
332
- "medium": 10,
333
- "low": 5,
334
- "nit": 1
335
- },
336
- "by_target": {
337
- "V1.0": 5,
338
- "V1.1": 18
339
- },
340
- "by_plan": {
341
- "domain-models": 4,
342
- "cli-daemon-foundation": 11,
343
- "cross-cutting": 1
344
- },
297
+ "by_severity": { "critical": 0, "high": 10, "medium": 10, "low": 5, "nit": 1 },
298
+ "by_target": { "V1.0": 5, "V1.1": 18 },
299
+ "by_plan": { "domain-models": 4, "cli-daemon-foundation": 11 },
345
300
  "cross_cutting": [
346
301
  {
347
302
  "id": "DEBT-X1",
348
- "title": "Short cross-plan theme (e.g. shared DB pooling)",
303
+ "title": "Cross-plan theme",
349
304
  "severity": "high",
350
- "scope": "crates/foo, crates/bar",
351
- "target": "V1.1 — unified strategy",
352
305
  "relates_to": ["CLI-R9", "SYNC-R4"]
353
306
  }
354
307
  ]
@@ -356,39 +309,30 @@ jq '.metadata.tech_debt_summary' .agents/status.json
356
309
  }
357
310
  ```
358
311
 
359
- - `**total_open` / `by_***`:应与当前 **open 列表**(根级 `**residual_findings**`;若仅存 legacy 侧则与该口径对齐)中所有 **open**(未归档)条目的数量与严重度**大致一致**;若有意的「跨 plan 合并视角」导致计数口径不同,在 `**{HARNESS_DIR}/notes.json`** 或 `cross_cutting` 中说明。
360
- - `**by_plan**`:键可为 **短标签** `**plans[].id` 前缀**,与仓库约定一致即可。
361
- - `**cross_cutting`**:用于**跨多 plan / 多条 R#** 的主题债(架构层、重复实现等);`**relates_to`** 列出对应 `**id**`(与 `residual_findings` 内 R# 对齐),避免与单条 R# 重复叙述时可只在此处保留总述。
312
+ - **`by_plan`** keys: short labels or `plans[].id` prefixes per repo convention.
313
+ - **`cross_cutting`**: themes spanning plans/R#; explain intentional count differences in `notes.json` or here.
362
314
 
363
315
  ---
364
316
 
365
- ## 合并前:`status.json` 须反映事实(建议门禁)
317
+ ## Pre-merge: `status.json` should match reality
366
318
 
367
- 在合并关闭计划相关工作的分支或打开 PR 前,`**@project-manager**`(或项目约定角色)宜核对:`plans[].status`、`plans[].metadata.gates`(若热行仍保留)、根级 `**residual_findings**`(核对是否与 legacy 侧误双写,见本 skill **`SKILL.md` 开篇**)、`**metadata.tech_debt_summary**`(若使用)、`**{HARNESS_DIR}/notes.json**`(若使用)与审查结论、CI/测试结果一致。**SSOT 与事实不一致时,不宜视为可合并**,应先修正。
319
+ Before merge/PR, **`@project-manager`** (or delegate) should verify: `plans[].status`, `metadata.gates`, root **`residual_findings`** (no accidental dual-write), **`tech_debt_summary`** (if used — run script), **`notes.json`** (if used), vs review/CI.
368
320
 
369
- **常见疏漏**(与业务无关的通用项):
321
+ **Common gaps:**
370
322
 
371
- - 关闭或新增 R# `**tech_debt_summary` 未刷新**(`total_open`、`by_severity` open 列表脱节)。
372
- - 仅在 `**plans[].notes`** 或对话中描述 finding,**未**写入根级 `**residual_findings[<plan-id>]`**(canonical,见本 skill **`SKILL.md` 开篇**)。
373
- - 团队若用 `**notes.json**`(或遗留 `**metadata.notes**`)作程序时间线:重大合并或批量归档后**未**追加条目,导致后续 agent 缺少上下文。
323
+ - R# added/closed but **`tech_debt_summary` not refreshed** (script shows DRIFT).
324
+ - Finding only in **`plans[].notes`** or chat, not in **`residual_findings[<plan-id>]`**.
325
+ - Major milestone with no **`notes.json`** entry when team uses program timeline.
374
326
 
375
- ## 兼容性与键名映射(推荐)
327
+ ## Compatibility: plan key names
376
328
 
377
- 不同仓库可能使用 `id` `plan_id` 作为计划键名。建议新仓统一用 `id`,并在迁移期遵循:
329
+ - Read: accept `id` or `plan_id`.
330
+ - Write: one canonical key (prefer `id`).
331
+ - Document canonical key in `.agents/AGENTS.md` if migrating.
378
332
 
379
- - 读取时允许 `id` / `plan_id` 双兼容;
380
- - 写回时只写一种(推荐 `id`);
381
- - 与目录键(如 `reports/<plan-id>/`、`residual_findings[<plan-id>]`)保持同一 canonical 值。
382
-
383
- 若短期不迁移,至少在仓库的 `.agents/AGENTS.md` 写清“canonical key 使用哪一个”,避免多 agent 混写。
384
-
385
- ## 常用查询示例
386
-
387
- 第二条 `jq` 中 `//` 右侧含义见上文「**查询 open 项与已归档(示例)**」。
333
+ ## Common queries
388
334
 
389
335
  ```bash
390
- # Replace .agents with your resolved {HARNESS_DIR} if different.
391
336
  jq '.plans[] | select(.id == "01-data-infrastructure")' .agents/status.json
392
337
  jq '.residual_findings["01-data-infrastructure"] // .metadata.residual_findings["01-data-infrastructure"]' .agents/status.json
393
338
  ```
394
-
@@ -0,0 +1,115 @@
1
+ #!/usr/bin/env bash
2
+ # Read-only rollup of open residual_findings into tech_debt_summary aggregates.
3
+ # Does not write status.json — PM applies output manually.
4
+ set -euo pipefail
5
+
6
+ STATUS_FILE="${1:-.agents/status.json}"
7
+
8
+ if [[ ! -f "$STATUS_FILE" ]]; then
9
+ echo "error: status file not found: $STATUS_FILE" >&2
10
+ exit 1
11
+ fi
12
+
13
+ if ! command -v jq >/dev/null 2>&1; then
14
+ echo "error: jq is required" >&2
15
+ exit 1
16
+ fi
17
+
18
+ # Merge canonical + legacy maps (canonical keys win). Legacy "warning" -> low.
19
+ # Skip entries with lifecycle != open (default open when omitted).
20
+ read -r -d '' JQ_FILTER <<'JQEOF' || true
21
+ def norm_sev:
22
+ if . == "warning" then "low"
23
+ elif . == null or . == "" then "medium"
24
+ else .
25
+ end;
26
+
27
+ def is_open:
28
+ (.lifecycle // "open") == "open";
29
+
30
+ (.residual_findings // {}) as $canon
31
+ | (.metadata.residual_findings // {}) as $legacy
32
+ | ($canon + $legacy) as $merged
33
+ | [
34
+ $merged
35
+ | to_entries[]
36
+ | .key as $plan
37
+ | .value[]
38
+ | select(is_open)
39
+ | . + { _plan_id: $plan }
40
+ ] as $items
41
+ | {
42
+ total_open: ($items | length),
43
+ by_severity: (
44
+ ["critical", "high", "medium", "low", "nit"]
45
+ | map(. as $s
46
+ | { ($s): ([$items[] | select((.severity | norm_sev) == $s)] | length) })
47
+ | add
48
+ ),
49
+ by_target: (
50
+ $items
51
+ | map(.target // "unspecified")
52
+ | group_by(.)
53
+ | map({ key: .[0], value: length })
54
+ | from_entries
55
+ ),
56
+ by_plan: (
57
+ $items
58
+ | group_by(._plan_id)
59
+ | map({ key: .[0]._plan_id, value: length })
60
+ | from_entries
61
+ )
62
+ }
63
+ JQEOF
64
+
65
+ COMPUTED=$(jq -c "$JQ_FILTER" "$STATUS_FILE")
66
+ STORED=$(jq -c '.metadata.tech_debt_summary // null' "$STATUS_FILE")
67
+
68
+ echo "=== tech_debt_summary (computed from open residual_findings) ==="
69
+ echo "$COMPUTED" | jq '.'
70
+
71
+ echo ""
72
+ echo "=== stored metadata.tech_debt_summary ==="
73
+ if [[ "$STORED" == "null" ]]; then
74
+ echo "(none)"
75
+ else
76
+ echo "$STORED" | jq '.'
77
+ fi
78
+
79
+ echo ""
80
+ echo "=== consistency check ==="
81
+
82
+ compare_field() {
83
+ local field="$1"
84
+ local computed stored
85
+ computed=$(echo "$COMPUTED" | jq -c ".$field")
86
+ if [[ "$STORED" == "null" ]]; then
87
+ echo "DRIFT: no stored tech_debt_summary (computed $field = $computed)"
88
+ return 1
89
+ fi
90
+ stored=$(echo "$STORED" | jq -c ".$field // null")
91
+ if [[ "$computed" == "$stored" ]]; then
92
+ echo "PASS: $field"
93
+ return 0
94
+ fi
95
+ echo "DRIFT: $field"
96
+ echo " computed: $computed"
97
+ echo " stored: $stored"
98
+ return 1
99
+ }
100
+
101
+ FAIL=0
102
+ compare_field total_open || FAIL=1
103
+ compare_field by_severity || FAIL=1
104
+ compare_field by_target || FAIL=1
105
+ compare_field by_plan || FAIL=1
106
+
107
+ if [[ "$FAIL" -eq 0 ]]; then
108
+ echo ""
109
+ echo "OVERALL: PASS"
110
+ exit 0
111
+ fi
112
+
113
+ echo ""
114
+ echo "OVERALL: DRIFT — refresh metadata.tech_debt_summary in status.json"
115
+ exit 1
@@ -1,6 +1,6 @@
1
1
  # Plan harness file templates
2
2
 
3
- Copy these into `{HARNESS_DIR}` when bootstrapping a project. Path symbols (`{HARNESS_DIR}`, `{PLAN_DIR}`, …) → **`mstar-plan-conventions`**. Field semantics and residual lifecycle → **`mstar-plan-artifacts/references/status-and-residuals.md`**.
3
+ Copy these into `{HARNESS_DIR}` when bootstrapping a project. Path symbols (`{HARNESS_DIR}`, `{PLAN_DIR}`, …) → **`mstar-plan-conventions`**. Field semantics and residual lifecycle → **`mstar-plan-artifacts/references/status-and-residuals.md`**. Optional rollup: **`../scripts/tech-debt-rollup.sh`** (read-only; see that reference).
4
4
 
5
5
  | File | Copy to | Notes |
6
6
  |------|---------|--------|
@@ -37,35 +37,41 @@ When multiple routes apply, set one `Primary` route in Assignment and treat othe
37
37
 
38
38
  ## Dev Triangle Balance (`fullstack-dev` / `fullstack-dev-2` / `frontend-dev`)
39
39
 
40
+ ### Default: spread backend/fullstack work across both tracks
41
+
42
+ When **>=2 independent** backend/fullstack units exist (on the task board or across sequential batches):
43
+
44
+ - **Parallel (preferred)** when units are parallelizable: dispatch **`fullstack-dev` + `fullstack-dev-2`** with explicit module boundaries and branch/worktree isolation.
45
+ - **Sequential rotation** when work is not concurrent: alternate `Execute as` between `fullstack-dev` and `fullstack-dev-2` by task/batch order (round-robin).
46
+
47
+ **Independence gate:** do **not** split genuinely dependent or sequential work across two dev IDs just to use both tracks.
48
+
49
+ **Single-id path needs justification:** collapsing >=2 independent units onto one backend-capable dev id requires `Dev owner tie-break: single id — <reason>` and Pre-Implement `single_stream_justified: yes` with that reason.
50
+
40
51
  ### When `frontend-dev` is required
41
52
 
42
53
  - Task category is `visual`, or acceptance depends on page/component/interaction/a11y/frontend performance.
43
54
  - UI-bearing fullstack work defaults to split ownership (`frontend-dev` for UI, `fullstack-dev` for API/domain).
44
55
 
45
- ### When `fullstack-dev-2` is required
56
+ ### When `fullstack-dev-2` is required (concurrent dual-track)
46
57
 
47
58
  Use a second implementation track when **any** applies:
48
59
 
49
60
  - Task board has >=2 independently parallelizable implementation units.
50
61
  - Medium+ fullstack scope and PM/user expects wall-clock acceleration.
51
- - Existing flow overloaded on one fullstack track while another independent module exists.
62
+ - One fullstack track is overloaded while another independent module exists.
52
63
 
53
64
  ### `single-stream` clarification
54
65
 
55
- `Dev routing: single-stream` means no concurrent multi-write dev tracks in this round.
66
+ `Dev routing: single-stream` means no concurrent multi-write dev tracks **in this round**.
56
67
  It does **not** force all future batches to one fixed developer ID.
57
68
 
58
- ### Round-robin default for equivalent backend/fullstack units
59
-
60
- If multiple equivalent non-UI units can be assigned to either `fullstack-dev` or `fullstack-dev-2`,
61
- default owner tie-break is round-robin by task order unless there is a documented override reason.
62
-
63
- Allowed override reasons:
69
+ ### Allowed override to single backend-capable dev id
64
70
 
65
71
  - User explicitly locks owner
66
- - Module ownership/continuity constraint
72
+ - Module ownership / continuity on one track
67
73
  - Hotfix stop-bleeding
68
- - Only one active write track in this round
74
+ - Only one active write track in this round (true dependency, not preference)
69
75
 
70
76
  Document override as `Dev owner tie-break: single id — <reason>`.
71
77
 
@@ -73,6 +73,8 @@ Pick one `Primary` route per Assignment; attach additional gates as needed.
73
73
  Detailed conflict priority and dev allocation:
74
74
  `references/project-manager/routing-and-dev-allocation.md`.
75
75
 
76
+ **Dev spread default:** when the task board has **>=2 independent** backend/fullstack units, prefer **`fullstack-dev` + `fullstack-dev-2`** (parallel with boundaries) or **round-robin** owners across sequential batches. Using a single backend-capable dev id for multiple independent units requires documented justification (`single_stream_justified` in Pre-Implement Gate Check).
77
+
76
78
  ---
77
79
 
78
80
  ## Non-Bypass Constraints
@@ -170,6 +172,7 @@ Anti-patterns:
170
172
  - Q10: Is Delegation consistent with Superpowers usage?
171
173
  - Q11: For non-trivial plan, is PM Task Board published with coverage?
172
174
  - Q12: In invoke-based hosts, were matching invokes actually issued?
175
+ - Q13: With **>=2 independent** backend/fullstack units, are owners spread across `fullstack-dev` and `fullstack-dev-2` (parallel or rotated), or is `single_stream_justified: yes` recorded with a real reason?
173
176
 
174
177
  ---
175
178
 
@@ -202,6 +205,7 @@ Hard block when:
202
205
  - Non-trivial plan has required field = `no`
203
206
  - Harness-active non-hotfix flow lacks on-disk main plan or status registration
204
207
  - `Task category: quick` is used on non-trivial work
208
+ - **>=2 independent** backend/fullstack units on the task board but `single_stream_justified: no` with no spread across `fullstack-dev` / `fullstack-dev-2` and no documented single-id override
205
209
 
206
210
  ---
207
211
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mstar-harness/opencode",
3
- "version": "0.6.0",
3
+ "version": "0.6.1",
4
4
  "description": "Morning Star harness OpenCode plugin (skills bootstrap and agent loading).",
5
5
  "license": "MIT",
6
6
  "repository": {