@sushanglewis/lincoln 1.8.3 → 1.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/framework/.claude/agents/pm.md +3 -0
- package/framework/.claude/schemas/stage-definition.schema.json +2 -0
- package/framework/.claude/skills/build-product-prototype/prompts/main.md +16 -15
- package/framework/.claude/skills/clarify-requirements/prompts/main.md +36 -16
- package/framework/.claude/skills/draft-product-design/prompts/main.md +1 -1
- package/framework/.claude/skills/lc-workflow/validators/validate.py +100 -26
- package/framework/.claude/skills/plan-tdd-development/SKILL.md +1 -0
- package/framework/.claude/skills/plan-tdd-development/prompts/main.md +34 -4
- package/framework/.claude/stages/clarify.yaml +3 -0
- package/framework/.claude/stages/tdd-development-plan.yaml +6 -1
- package/framework/.claude/workflows/interview-to-knowledge.yaml +5 -0
- package/framework/.claude-plugin/marketplace.json +2 -2
- package/framework/.claude-plugin/plugin.json +1 -1
- package/framework/.version-bump.json +1 -1
- package/framework/README.en.md +21 -2
- package/framework/README.md +21 -2
- package/framework/RELEASE.md +44 -0
- package/framework/manifest.json +25 -25
- package/framework/scripts/lincoln_index.py +3 -24
- package/framework/scripts/lincoln_render.py +6 -4
- package/framework/scripts/validate_stage.py +96 -15
- package/framework/tools/lincoln/package-lock.json +2 -2
- package/framework/tools/lincoln/package.json +1 -1
- package/framework/tools/lincoln-installer/package.json +1 -1
- package/package.json +1 -1
|
@@ -20,6 +20,9 @@ extends:
|
|
|
20
20
|
3. 维护跨子页面影响关系:PRD、用户故事、相关者分析、数据模型等核心文档变更时,在门户中标注受影响的设计文档与原型页面,使下游阶段知悉影响范围。
|
|
21
21
|
4. 对 designer 的输入完整性负责:PRD、用户故事、相关者分析、数据模型、流程图、页面地图必须完整、一致、无歧义;designer 回退确认时,PM 优先澄清,而非让 designer 自行假设。
|
|
22
22
|
5. 在 `product-prototype` 阶段以 reviewer 身份确认原型的功能、字段、流程与需求文档一致。
|
|
23
|
+
6. PRD 质量把关:输出 PRD 前完成「需求清晰度六问」自检(用户有哪些、场景是什么、边界在哪里、如何管异常、如何体验好、最终谁决策),六问全部有明确答案才允许定稿。
|
|
24
|
+
7. PRD 只承载业务内容:技术调研、技术方案、架构/接口/数据库设计等只进设计阶段的归档页,不写入 PRD;过程性疑问(待确认、TODO 之类)在对话中解决后才以结论形式落文档。
|
|
25
|
+
8. 交叉验证决策权:研发计划阶段以测试场景反向挑战 PRD 发现的缺口,必须由人类 PM 决策闭环——修复(PRD 升版重冻结)或接受(记录理由与影响)二选一;PM 未决策不得视为闭环。
|
|
23
26
|
|
|
24
27
|
## 专属规则
|
|
25
28
|
|
|
@@ -60,7 +60,7 @@ You may still read the categorized example templates under `.claude/templates/is
|
|
|
60
60
|
|
|
61
61
|
Use the structured renderer with `--data` containing `sections`, `pages`, and `interactions` as appropriate.
|
|
62
62
|
|
|
63
|
-
5. **Plan prototype generation granularity.** Before writing prototype pages, derive a **角色×场景矩阵** from `scenarios.html` and `page-map.html`: rows are the user roles/personas, columns are the key scenarios (including boundary cases such as empty, loading, error, success). Every cell that a real user can reach must map to exactly one prototype sub-page; the matrix itself is written into `ui-spec.html` (章节 `## 角色×场景矩阵`) and serves as the splitting contract. Then group the sub-pages into cohesive clusters (e.g. onboarding, main shell, settings, overlays, tray). Generate one cluster at a time. After each cluster, verify that every page has complete
|
|
63
|
+
5. **Plan prototype generation granularity.** Before writing prototype pages, derive a **角色×场景矩阵** from `scenarios.html` and `page-map.html`: rows are the user roles/personas, columns are the key scenarios (including boundary cases such as empty, loading, error, success). Every cell that a real user can reach must map to exactly one prototype sub-page; the matrix itself is written into `ui-spec.html` (章节 `## 角色×场景矩阵`) and serves as the splitting contract. Then group the sub-pages into cohesive clusters (e.g. onboarding, main shell, settings, overlays, tray). Generate one cluster at a time. After each cluster, verify that every page has complete hotspot annotations (numbered badges covering functionality, fields, boundary cases, and exception flows) before starting the next cluster.
|
|
64
64
|
|
|
65
65
|
Each cluster is organized by one main shell page (e.g. `pages/prototype/{app,web,mobile}/main/page.html`) whose `navItems` link to all sub-pages of that cluster, so the portal viewer can walk every role/scenario state from a single entry page.
|
|
66
66
|
|
|
@@ -85,15 +85,13 @@ You may still read the categorized example templates under `.claude/templates/is
|
|
|
85
85
|
navItems:
|
|
86
86
|
- {id: dashboard, label: 概览, href: ./dashboard/page.html}
|
|
87
87
|
- {id: list, label: 列表, href: ./list/page.html}
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
doc-exceptions: "..."
|
|
96
|
-
doc-refs: "flows.html | page-map.html"
|
|
88
|
+
hotspots:
|
|
89
|
+
- target: "metric-revenue" # CSS selector, data-uid, or element id
|
|
90
|
+
title: "营收指标卡"
|
|
91
|
+
text: "展示本月营收与环比;点击进入详情页。"
|
|
92
|
+
- target: "#global-search"
|
|
93
|
+
title: "全局搜索"
|
|
94
|
+
text: "支持名称/拼音/首字母搜索,回车跳转首个结果。"
|
|
97
95
|
components:
|
|
98
96
|
- type: html
|
|
99
97
|
uid: dashboard-metrics
|
|
@@ -112,15 +110,17 @@ You may still read the categorized example templates under `.claude/templates/is
|
|
|
112
110
|
- Link to `../../../assets/prototype.css` and `../../../assets/prototype.js` (depth 3) or `../../../../assets/...` (depth 4) depending on its path.
|
|
113
111
|
- Include theme sync support (built into the structured template).
|
|
114
112
|
- Include `<meta name="prototype-base" content="../../../">` (or `../../../../` for depth 4).
|
|
115
|
-
- Include `<meta name="page-uid" content="...">`, `<meta name="nav-group" content="...">`, and `<meta name="nav-label" content="...">` (injected by the renderer from CLI args;
|
|
116
|
-
- Provide
|
|
117
|
-
- State the applicable role(s), scenario, page state (default / empty / loading / error / success), and which functions are visible and usable in that state via the
|
|
113
|
+
- Include `<meta name="page-uid" content="...">`, `<meta name="nav-group" content="...">`, and `<meta name="nav-label" content="...">` (injected by the renderer from CLI args; hotspots come from the YAML `hotspots` block and are rendered as numbered badges via driver.js).
|
|
114
|
+
- Provide a `hotspots` array so numbered on-page badges explain functionality, layout, fields, boundary cases, and exception flows (hover for the popover, click to pin; the prototype toolbar can hide badges).
|
|
115
|
+
- State the applicable role(s), scenario, page state (default / empty / loading / error / success), and which functions are visible and usable in that state via the hotspot `title`/`text` entries, so each sub-page precisely depicts what a given role sees and can do in that case.
|
|
116
|
+
- Depict only the final system as it will ship: no demo-only buttons, explanatory text rows, or walkthrough-only jumps. Explanations belong in the `hotspots` badges rendered on the page, never as on-screen prose.
|
|
117
|
+
- Declare the user story and business-flow step the page implements in the hotspot `text` entries, so the prototype aligns bidirectionally with the PRD: every flow step has at least one page, and every page belongs to a flow step or story.
|
|
118
118
|
- Use `LincolnPrototype.ui` builders and `prototype.css` tokens for every visual element so both light and dark themes render correctly; verify both themes by toggling the portal theme switch.
|
|
119
119
|
- Assign a stable `data-uid` attribute to every interactive element, screen region, and WebView placeholder.
|
|
120
120
|
- Override `LincolnPrototype.data` in the page script for issue-specific mock data (user, org, webviews, settings, unread items).
|
|
121
121
|
|
|
122
122
|
7. Optionally create or update `{process_slug}/designs/<design_id>/prototype.pen` with Pencil tools if the PM explicitly asks for a Pencil prototype. If you use Pencil tools, call `get_editor_state(include_schema: true)` first and use `snapshot_layout` to check for clipping/overlap.
|
|
123
|
-
8. Update the root `{process_slug}/pages/docs/prd.html`
|
|
123
|
+
8. Update the「相关产物链接」section of the root `{process_slug}/pages/docs/prd.html` (PRD sections are matched by name — numbering is cosmetic) with the `ui-spec.html`, `fields.html`, and HTML prototype links. If the PRD already has an approved snapshot, warn the PM that changes require a version bump and re-freeze via `python scripts/lincoln_prd.py freeze`.
|
|
124
124
|
9. Ask the PM to open `{process_slug}/index.html` in a browser, toggle the theme switch in the macOS menubar, and review both light and dark renderings of each prototype page inside the portal.
|
|
125
125
|
10. When the PM confirms the prototype, add `<!-- prototype-status: approved -->` to `ui-spec.html`.
|
|
126
126
|
11. Run `python scripts/stage_loader.py --stage product-prototype --action record-artifacts`.
|
|
@@ -141,6 +141,7 @@ You may still read the categorized example templates under `.claude/templates/is
|
|
|
141
141
|
- Keep controls and states complete enough for implementation: default, hover/focus where relevant, disabled, empty, loading, error, and success.
|
|
142
142
|
- Every interactive element must carry a stable `data-uid`. Do not generate random UIDs that change on every render.
|
|
143
143
|
- Prototype links use relative paths so they work both inside the portal iframe and when opened standalone.
|
|
144
|
-
- Do not add
|
|
144
|
+
- Do not add custom chrome to the portal. The portal sidebar is collapsible and supports focus mode out of the box; prototype pages open in focus mode automatically.
|
|
145
145
|
- The system tray is portal-level chrome. Never redraw the tray inside an app prototype page with `.menubar`, `.tray-icon`, `.tray-panel`, `bindTray`, or `trayMenu`. Tray scenario pages must be thin controllers that post `lincoln-tray-state` messages to the portal.
|
|
146
|
+
- 原型即最终系统样貌:禁止仅用于演示的模拟按钮、屏内说明文字与演示跳转;解释一律走 `hotspots` 数字角标(driver.js,hover 弹出说明)呈现;每个原型页面经角标 `text` 标注所属用户故事与业务流程步骤,与 PRD 文字双向对齐。
|
|
146
147
|
- After approval, tell the user to run: `claude plan-tdd-development <session_id> <design_id>`.
|
|
@@ -80,7 +80,17 @@ Produce a clear, agreed-upon `{process_slug}/pages/docs/requirements.html` and t
|
|
|
80
80
|
--markdown {process_slug}/pages/docs/user-stories.md
|
|
81
81
|
```
|
|
82
82
|
|
|
83
|
-
9.
|
|
83
|
+
9. 需求清晰度六问自检(Six-Question Gate)。起草原稿前逐项回答,答案落入 PRD 对应章节;任何一项答不出,先按 Human Interaction Rules 问人类 PM,禁止带着未决问题落笔:
|
|
84
|
+
|
|
85
|
+
| 六问 | 落笔位置 | 答不出时的动作 |
|
|
86
|
+
|---|---|---|
|
|
87
|
+
| 用户有哪些? | 用户故事 | 请 PM 补充分角色 |
|
|
88
|
+
| 场景是什么?何时触发、如何完成? | 业务流程图 | 与 PM 走查主流程 |
|
|
89
|
+
| 边界在哪里?明确不做什么 | 需求背景(非目标)+ 业务规则 | 请 PM 划定非目标 |
|
|
90
|
+
| 如何管异常?覆盖每个失败的系统行为 | 业务规则(异常处理) | 逐场景请 PM 裁决 |
|
|
91
|
+
| 如何体验好?质量与体验判据 | 非功能需求(体验要求) | 请 PM 给出体验判据 |
|
|
92
|
+
| 最终谁决策? | 版本说明(决策人) | 请 PM 指定决策人 |
|
|
93
|
+
10. Generate the root-level PRD at `{process_slug}/pages/docs/prd.html` using the Markdown-first renderer:
|
|
84
94
|
|
|
85
95
|
```bash
|
|
86
96
|
python scripts/lincoln_render.py \
|
|
@@ -90,25 +100,33 @@ Produce a clear, agreed-upon `{process_slug}/pages/docs/requirements.html` and t
|
|
|
90
100
|
--markdown {process_slug}/pages/docs/prd.md
|
|
91
101
|
```
|
|
92
102
|
|
|
93
|
-
Write `{process_slug}/pages/docs/prd.md`
|
|
94
|
-
|
|
95
|
-
- `## 1.
|
|
96
|
-
- `## 2.
|
|
97
|
-
- `## 3.
|
|
98
|
-
- `## 4.
|
|
99
|
-
- `## 5.
|
|
100
|
-
- `## 6.
|
|
101
|
-
- `## 7.
|
|
102
|
-
- `## 8.
|
|
103
|
-
- `## 9.
|
|
104
|
-
- `## 10.
|
|
103
|
+
Write `{process_slug}/pages/docs/prd.md` following this progressive skeleton. Keep all thirteen chapters in this order as the default narrative; you may insert additional business chapters inside the skeleton when this requirement's shape calls for it. Section headings are matched by name — numbering is cosmetic, not part of the contract:
|
|
104
|
+
|
|
105
|
+
- `## 1. 版本说明` — 当前版本、文档状态、决策人(最终谁拍板)。
|
|
106
|
+
- `## 2. 修订记录` — 版本/日期/作者/变更摘要 表格。
|
|
107
|
+
- `## 3. 功能列表` — 功能/优先级/状态/对应用户故事 表格。
|
|
108
|
+
- `## 4. 需求背景`
|
|
109
|
+
- `## 5. 用户故事`
|
|
110
|
+
- `## 6. 功能拆解`
|
|
111
|
+
- `## 7. 业务流程图` (use ` ```mermaid ` diagrams)
|
|
112
|
+
- `## 8. 验收标准`
|
|
113
|
+
- `## 9. 业务规则`(边界与异常处理)
|
|
114
|
+
- `## 10. 非功能需求`(含体验要求)
|
|
115
|
+
- `## 11. 关联系统/接口`
|
|
116
|
+
- `## 12. 相关产物链接`
|
|
117
|
+
- `## 13. 风险与开放问题`
|
|
118
|
+
|
|
119
|
+
PRD 正文禁则(读者是研发团队,文档只讲业务场景):
|
|
120
|
+
- 不写技术调研、技术方案、架构设计、接口设计、数据库设计等内容——这类内容只进 design 阶段维护的归档页 `feasibility.html`。
|
|
121
|
+
- 不写任何过程性提醒("待确认""待人类 PM 确认""TODO"、agent 注等)——疑问在对话中解决,解决后才落文档。
|
|
122
|
+
- 同一信息只用一种方式描述:文字、表格、mermaid 图组合使用,但不重复表达同一内容。
|
|
105
123
|
|
|
106
124
|
It must also carry:
|
|
107
125
|
- `<!-- version: v1.0 -->` marker (added automatically by the renderer from the Markdown file or `--version`).
|
|
108
126
|
- Meta tags: `doc-title`, `nav-group="Docs"`, `doc-version="v1.0"`, and a stable `doc-uid` (all injected by the renderer).
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
127
|
+
11. When the PM confirms, add an approval marker inside `requirements.html`: `<!-- status: approved -->`.
|
|
128
|
+
12. After human approval, run `python scripts/lincoln_prd.py freeze` to create the immutable snapshot `{process_slug}/pages/docs/snapshots/prd-v1.0.html`.
|
|
129
|
+
13. Run `python scripts/stage_loader.py --stage clarify --action record-artifacts` to persist the artifact paths and refresh `{process_slug}/assets/js/package-data.js`.
|
|
112
130
|
|
|
113
131
|
## Human Interaction Rules
|
|
114
132
|
|
|
@@ -122,6 +140,8 @@ Produce a clear, agreed-upon `{process_slug}/pages/docs/requirements.html` and t
|
|
|
122
140
|
- Every document page opens with `## 页面意图` (目标读者 + 本页回答什么问题) and `## 边界` (本页不覆盖什么 + 单一事实来源链接).
|
|
123
141
|
- Content follows standard Markdown: a single H1, H2 chapters, and tables/Mermaid over long prose. Never duplicate content across pages — link to the single source of truth instead.
|
|
124
142
|
- These pages are business-facing: 需求背景、用户角色、旅程、业务状态、业务流程、验收标准. Technical research or solution comparisons performed while clarifying are only background for the PM's decision — do not register them into any page; they may be summarized later into the archived `feasibility.html` by the design stage.
|
|
143
|
+
- 过程性内容不进入任何页面:待确认事项、agent 提醒、对话记录只留在对话里,解决后才以结论形式落文档。
|
|
144
|
+
- 同一信息只描述一次:文字、表格、mermaid 图可以组合使用,但不得用多种方式重复表达同一内容;需要引用时链接到单一事实来源。
|
|
125
145
|
|
|
126
146
|
## 认知象限确认(Johari)
|
|
127
147
|
|
|
@@ -79,7 +79,7 @@ Create `{process_slug}/pages/docs/` HTML design documents with enough product, d
|
|
|
79
79
|
4. Create the PM→UX handoff contract at `{process_slug}/handoffs/pm-to-ux/pm-to-ux.handoff.yaml` referencing the approved design docs and PRD versions.
|
|
80
80
|
5. Create the human-readable PM→UX handoff portal page at `{process_slug}/pages/docs/handoff-pm-to-ux-v1.0.html` and the narrative master handoff document at `{process_slug}/handoffs/pm-to-ux/master-handoff-pm-to-ux-v1.0.md`. These summarize core decisions, scope, open questions, and the context pack for the receiving UX Agent.
|
|
81
81
|
6. Keep all documents traceable to the approved requirement and transcript timestamps where available.
|
|
82
|
-
7. Update the root `{process_slug}/pages/docs/prd.html`
|
|
82
|
+
7. Update the「相关产物链接」section of the root `{process_slug}/pages/docs/prd.html` (PRD sections are matched by name — numbering is cosmetic) with links to the new design documents and handoff artifacts. If the PRD already has an approved snapshot (`pages/docs/snapshots/prd-v*.html`), warn the PM that any content change requires bumping the version marker and re-freezing via `python scripts/lincoln_prd.py freeze`.
|
|
83
83
|
8. Ask the PM to review `design-review.html` and linked docs (they can open `{process_slug}/index.html` in a browser).
|
|
84
84
|
9. When the PM confirms, add `<!-- status: approved -->` to `design-review.html`.
|
|
85
85
|
10. Run `python scripts/stage_loader.py --stage product-design-docs --action record-artifacts`.
|
|
@@ -526,22 +526,15 @@ def validate_prototype_artifact_complete(design_id: str):
|
|
|
526
526
|
if missing_nav_label:
|
|
527
527
|
fail(f"Prototype pages missing nav-label meta: {', '.join(missing_nav_label)}")
|
|
528
528
|
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
("doc-layout", 'doc-layout'),
|
|
532
|
-
("doc-fields", 'doc-fields'),
|
|
533
|
-
("doc-boundaries", 'doc-boundaries'),
|
|
534
|
-
("doc-exceptions", 'doc-exceptions'),
|
|
535
|
-
]
|
|
536
|
-
annotation_gaps: dict[str, list[str]] = {}
|
|
529
|
+
# Explanations are numbered on-page hotspots (driver.js), not doc-* metas.
|
|
530
|
+
hotspot_gaps: dict[str, str] = {}
|
|
537
531
|
for p in html_files:
|
|
538
532
|
text = p.read_text(encoding="utf-8")
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
fail(f"Prototype pages missing required annotation meta tags:\n" + "\n".join(messages))
|
|
533
|
+
if '"hotspots"' not in text:
|
|
534
|
+
hotspot_gaps[str(p.relative_to(process_root()))] = '"hotspots"'
|
|
535
|
+
if hotspot_gaps:
|
|
536
|
+
messages = [f"{path}: missing {key} in pageData" for path, key in hotspot_gaps.items()]
|
|
537
|
+
fail(f"Prototype pages missing required hotspot annotations:\n" + "\n".join(messages))
|
|
545
538
|
|
|
546
539
|
# Anti-pattern: in-app tray redraw. The system tray is portal-level chrome.
|
|
547
540
|
in_app_tray_markers = [
|
|
@@ -640,33 +633,75 @@ def check_tdd_plan_complete(design_id: str):
|
|
|
640
633
|
# PRD checks
|
|
641
634
|
# ---------------------------------------------------------------------------
|
|
642
635
|
|
|
636
|
+
# 递进骨架(issue-129):版本说明 → 修订记录 → 功能列表 → 需求展开。
|
|
637
|
+
# 章节编号不参与匹配,PM 可按需求类型在骨架内增插业务章节。
|
|
643
638
|
REQUIRED_PRD_SECTIONS = [
|
|
644
|
-
"
|
|
645
|
-
"
|
|
646
|
-
"
|
|
647
|
-
"
|
|
648
|
-
"
|
|
649
|
-
"
|
|
650
|
-
"
|
|
651
|
-
"
|
|
652
|
-
"
|
|
653
|
-
"
|
|
639
|
+
"版本说明",
|
|
640
|
+
"修订记录",
|
|
641
|
+
"功能列表",
|
|
642
|
+
"需求背景",
|
|
643
|
+
"用户故事",
|
|
644
|
+
"功能拆解",
|
|
645
|
+
"业务流程图",
|
|
646
|
+
"验收标准",
|
|
647
|
+
"业务规则",
|
|
648
|
+
"非功能需求",
|
|
649
|
+
"关联系统/接口",
|
|
650
|
+
"相关产物链接",
|
|
651
|
+
"风险与开放问题",
|
|
652
|
+
]
|
|
653
|
+
|
|
654
|
+
PRD_SECTION_PATTERNS = [
|
|
655
|
+
(name, re.compile(rf"^##\s*(?:\d+\s*[.、.]?\s*)?{re.escape(name)}\s*$", re.MULTILINE))
|
|
656
|
+
for name in REQUIRED_PRD_SECTIONS
|
|
654
657
|
]
|
|
655
658
|
|
|
659
|
+
# PRD 正文禁则:过程性疑问在对话中解决,技术内容归归档页(issue-129 第 4/6 条)。
|
|
660
|
+
PRD_FORBIDDEN_PROCESS_RE = re.compile(r"待确认|待人类|待补充|待\s*PM|TODO|FIXME", re.IGNORECASE)
|
|
661
|
+
PRD_FORBIDDEN_TECH_HEADING_RE = re.compile(
|
|
662
|
+
r"^##+\s*.*(技术调研|技术方案|可行性研究|架构设计|接口设计|数据库设计)",
|
|
663
|
+
re.MULTILINE,
|
|
664
|
+
)
|
|
665
|
+
|
|
666
|
+
CROSS_VALIDATION_PASS_RE = re.compile(
|
|
667
|
+
r"<!--\s*cross-validation:\s*pass\s+prd-version:\s*(v\d+\.\d+)\s*-->"
|
|
668
|
+
)
|
|
669
|
+
|
|
670
|
+
|
|
671
|
+
def _prd_hygiene_violations(content: str) -> list[str]:
|
|
672
|
+
violations = sorted(set(PRD_FORBIDDEN_PROCESS_RE.findall(content)))
|
|
673
|
+
violations.extend(sorted(set(PRD_FORBIDDEN_TECH_HEADING_RE.findall(content))))
|
|
674
|
+
return violations
|
|
675
|
+
|
|
656
676
|
|
|
657
677
|
def check_prd_has_required_sections(path: str):
|
|
658
678
|
target = PROJECT_ROOT / path
|
|
659
679
|
if not target.exists():
|
|
660
680
|
fail(f"PRD missing: {target}")
|
|
661
681
|
|
|
662
|
-
|
|
663
|
-
missing = [
|
|
682
|
+
content = read_document_file(target)
|
|
683
|
+
missing = [name for name, pattern in PRD_SECTION_PATTERNS if not pattern.search(content)]
|
|
664
684
|
if missing:
|
|
665
685
|
fail(f"PRD missing required sections: {', '.join(missing)}")
|
|
666
686
|
|
|
667
687
|
pass_check("PRD has all required sections")
|
|
668
688
|
|
|
669
689
|
|
|
690
|
+
def check_prd_content_hygiene(path: str):
|
|
691
|
+
target = PROJECT_ROOT / path
|
|
692
|
+
if not target.exists():
|
|
693
|
+
fail(f"PRD missing: {target}")
|
|
694
|
+
|
|
695
|
+
violations = _prd_hygiene_violations(read_document_file(target))
|
|
696
|
+
if violations:
|
|
697
|
+
fail(
|
|
698
|
+
"PRD contains content that belongs in conversation or archived pages, "
|
|
699
|
+
f"not the PRD: {', '.join(violations)}"
|
|
700
|
+
)
|
|
701
|
+
|
|
702
|
+
pass_check("PRD content hygiene OK")
|
|
703
|
+
|
|
704
|
+
|
|
670
705
|
def check_prd_snapshot_present(path: str):
|
|
671
706
|
target = PROJECT_ROOT / path
|
|
672
707
|
if not target.exists():
|
|
@@ -692,6 +727,43 @@ def check_prd_snapshot_present(path: str):
|
|
|
692
727
|
pass_check(f"PRD snapshot present: {snapshot_path}")
|
|
693
728
|
|
|
694
729
|
|
|
730
|
+
def check_prd_cross_validated(path: str):
|
|
731
|
+
target = PROJECT_ROOT / path
|
|
732
|
+
if not target.exists():
|
|
733
|
+
fail(f"Cross-validation report missing: {target}")
|
|
734
|
+
|
|
735
|
+
match = CROSS_VALIDATION_PASS_RE.search(read_document_file(target))
|
|
736
|
+
if not match:
|
|
737
|
+
fail(
|
|
738
|
+
"Cross-validation report has no pass marker. Every gap must be closed by a "
|
|
739
|
+
"human-PM decision (PRD fix with re-freeze, or accepted with recorded "
|
|
740
|
+
"rationale) before adding '<!-- cross-validation: pass prd-version: vX.Y -->'."
|
|
741
|
+
)
|
|
742
|
+
|
|
743
|
+
prd_path = target.parent / "prd.html"
|
|
744
|
+
if not prd_path.exists():
|
|
745
|
+
fail(f"PRD missing next to cross-validation report: {prd_path}")
|
|
746
|
+
|
|
747
|
+
if extract_markdown_version is None:
|
|
748
|
+
fail("markdown version extraction is not available")
|
|
749
|
+
|
|
750
|
+
prd_version = extract_markdown_version(prd_path)
|
|
751
|
+
if prd_version != match.group(1):
|
|
752
|
+
fail(
|
|
753
|
+
f"Cross-validation pass marker is for PRD {match.group(1)}, but the PRD is now "
|
|
754
|
+
f"{prd_version or 'unversioned'}. Re-run cross-validation against the current PRD."
|
|
755
|
+
)
|
|
756
|
+
|
|
757
|
+
violations = _prd_hygiene_violations(read_document_file(prd_path))
|
|
758
|
+
if violations:
|
|
759
|
+
fail(
|
|
760
|
+
f"PRD regressed after cross-validation pass: {', '.join(violations)}. "
|
|
761
|
+
"Fix the PRD, bump its version, re-freeze, and re-run cross-validation."
|
|
762
|
+
)
|
|
763
|
+
|
|
764
|
+
pass_check(f"PRD cross-validation pass marker matches PRD {prd_version}")
|
|
765
|
+
|
|
766
|
+
|
|
695
767
|
# ---------------------------------------------------------------------------
|
|
696
768
|
# Registry
|
|
697
769
|
# ---------------------------------------------------------------------------
|
|
@@ -729,6 +801,8 @@ EXIT_CHECKS = {
|
|
|
729
801
|
"tdd_plan_complete": check_tdd_plan_complete,
|
|
730
802
|
"prd_has_required_sections": check_prd_has_required_sections,
|
|
731
803
|
"prd_snapshot_present": check_prd_snapshot_present,
|
|
804
|
+
"prd_content_hygiene": check_prd_content_hygiene,
|
|
805
|
+
"prd_cross_validated": check_prd_cross_validated,
|
|
732
806
|
}
|
|
733
807
|
|
|
734
808
|
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
# plan-tdd-development
|
|
2
2
|
|
|
3
|
-
You are executing the Lincoln workflow step `tdd-development-plan`: turn the confirmed product design and prototype into a TDD development plan.
|
|
3
|
+
You are executing the Lincoln workflow step `tdd-development-plan`: turn the confirmed product design and prototype into a TDD development plan, then reverse-drive the PRD with test scenarios so gaps close before development.
|
|
4
4
|
|
|
5
5
|
## Goal
|
|
6
6
|
|
|
7
|
-
Create `{process_slug}/pages/docs/tdd-plan.html` as the bridge from product design to OpenSpec and GitHub Issues.
|
|
7
|
+
Create `{process_slug}/pages/docs/tdd-plan.html` as the bridge from product design to OpenSpec and GitHub Issues, and produce `{process_slug}/pages/docs/prd-cross-validation.html` so every test scenario (正常/边界/异常) challenges the PRD — SDD tests reverse-drive PRD completeness.
|
|
8
8
|
|
|
9
9
|
## Input
|
|
10
10
|
|
|
@@ -16,11 +16,13 @@ Create `{process_slug}/pages/docs/tdd-plan.html` as the bridge from product desi
|
|
|
16
16
|
在执行本 prompt 前:
|
|
17
17
|
1. 调用 `superpowers:writing-plans` 规划 `tdd-plan.html` 结构。
|
|
18
18
|
2. 遵循 `superpowers:test-driven-development` 方法论:确保每个任务切片都是“先写失败测试 → 最小实现 → 重构”序列。
|
|
19
|
-
3. 在 `tdd-plan.html` 的 Markdown source 头部加入:
|
|
19
|
+
3. 在 `tdd-plan.html` 的 Markdown source 头部加入:
|
|
20
20
|
`> **Required sub-skill:** Use superpowers:test-driven-development for all implementation`
|
|
21
21
|
|
|
22
22
|
## Steps
|
|
23
23
|
|
|
24
|
+
### Phase 1 — TDD 计划
|
|
25
|
+
|
|
24
26
|
1. Verify that `{process_slug}/pages/docs/ui-spec.html` contains `<!-- prototype-status: approved -->` or `[x] PM 已确认原型`.
|
|
25
27
|
2. Confirm `{process_slug}/designs/<design_id>/prototype.pen`, `{process_slug}/pages/docs/fields.html`, and the design review package exist.
|
|
26
28
|
3. Read the confirmed design docs and inspect the `.pen` through Pencil tools if visual structure is needed.
|
|
@@ -34,11 +36,39 @@ Create `{process_slug}/pages/docs/tdd-plan.html` as the bridge from product desi
|
|
|
34
36
|
- Task slices suitable for OpenSpec tasks and GitHub Issues
|
|
35
37
|
- Risks, dependencies, and out-of-scope items
|
|
36
38
|
5. Add `<!-- status: ready-for-openspec -->` when the plan is complete.
|
|
37
|
-
|
|
39
|
+
|
|
40
|
+
### Phase 2 — PRD 交叉验证(测试反向驱动 PRD)
|
|
41
|
+
|
|
42
|
+
6. **场景回问**:从 tdd-plan 的测试场景(正常、边界、异常)逐一回问 `{process_slug}/pages/docs/prd.html`:该场景涉及的业务规则与验收标准在 PRD 中是否有明确答案?逐项记录「场景 → PRD 答案(章节 + 条文)」,PRD 无答案者标记为缺口。
|
|
43
|
+
7. **对齐闭环**:建立业务流程步骤 ↔ 原型页面双向对齐矩阵——PRD 业务流程图中的每个步骤至少有 1 个原型页面承载;每个原型页面归属于某流程步骤或用户故事。任一侧对不上即为缺口(流程中断或原型多余)。
|
|
44
|
+
8. **渲染报告**:写 `{process_slug}/pages/docs/prd-cross-validation.md`(顶部含 `<!-- version: v1.0 -->`),用 `lincoln_render.py` 渲染为 `{process_slug}/pages/docs/prd-cross-validation.html`:
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
python scripts/lincoln_render.py \
|
|
48
|
+
--stage tdd-development-plan \
|
|
49
|
+
--target {process_slug}/pages/docs/prd-cross-validation.html \
|
|
50
|
+
--title "PRD 交叉验证报告" \
|
|
51
|
+
--markdown {process_slug}/pages/docs/prd-cross-validation.md
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
报告必须含:场景 → PRD 答案映射表、缺口清单、决策记录表(缺口 / 闭环方式 / PM 决策 / 理由)、PRD 修订回执。
|
|
55
|
+
9. **闭环缺口**:每个缺口由人类 PM 决策二选一——
|
|
56
|
+
- **修复**:修订 `prd.md`,bump `<!-- version: vX.Y -->`,运行 `python scripts/lincoln_prd.py freeze` 重新冻结,然后重跑 Phase 2;
|
|
57
|
+
- **接受**:在决策记录表中记录理由与影响,经人类 PM 明确认可。
|
|
58
|
+
|
|
59
|
+
全部缺口闭环后,在报告 markdown 末尾追加 `<!-- cross-validation: pass prd-version: vX.Y -->`(vX.Y = 当前 PRD 版本)并重新渲染。未闭环前禁止写入该标记。
|
|
60
|
+
10. Run `python scripts/stage_loader.py --stage tdd-development-plan --action record-artifacts`.
|
|
61
|
+
|
|
62
|
+
## Output Artifacts
|
|
63
|
+
|
|
64
|
+
- `{process_slug}/pages/docs/tdd-plan.html`
|
|
65
|
+
- `{process_slug}/pages/docs/prd-cross-validation.html`(含场景→PRD 答案映射、缺口清单、决策记录、闭环标记)
|
|
38
66
|
|
|
39
67
|
## Rules
|
|
40
68
|
|
|
41
69
|
- Keep the plan executable by an engineer without requiring extra product decisions.
|
|
42
70
|
- Every task slice must map back to a design artifact and acceptance criterion.
|
|
43
71
|
- Do not generate OpenSpec artifacts in this step.
|
|
72
|
+
- 交叉验证发现的 PRD 缺口不得自行编造业务答案,必须交回人类 PM 决策(修复或接受)。
|
|
73
|
+
- pass 标记中的版本必须与当前 PRD 版本一致;PRD 后续任何改动都会使标记失效,须重跑 Phase 2。
|
|
44
74
|
- After completion, tell the user to run: `claude propose-with-openspec <session_id> <design_id> <change_name>`.
|
|
@@ -18,6 +18,7 @@ variables:
|
|
|
18
18
|
- process_slug
|
|
19
19
|
artifact_templates:
|
|
20
20
|
'{process_slug}/pages/docs/tdd-plan.html': .claude/templates/issue-package/page-doc.html.tpl
|
|
21
|
+
'{process_slug}/pages/docs/prd-cross-validation.html': .claude/templates/issue-package/page-doc.html.tpl
|
|
21
22
|
agent:
|
|
22
23
|
primary: lc-engineer
|
|
23
24
|
reviewers:
|
|
@@ -36,11 +37,12 @@ skills:
|
|
|
36
37
|
artifacts:
|
|
37
38
|
required:
|
|
38
39
|
- '{process_slug}/pages/docs/tdd-plan.html'
|
|
40
|
+
- '{process_slug}/pages/docs/prd-cross-validation.html'
|
|
39
41
|
references:
|
|
40
42
|
skill_prompt: .claude/skills/plan-tdd-development/SKILL.md
|
|
41
43
|
agent_guide: .claude/agents/engineer.md
|
|
42
44
|
context:
|
|
43
|
-
goal: 基于已确认的产品设计文档、字段规格、UI 规格和 Pencil 原型,生成可执行的测试驱动研发计划(`tdd-plan.html`),作为从产品设计到 OpenSpec
|
|
45
|
+
goal: 基于已确认的产品设计文档、字段规格、UI 规格和 Pencil 原型,生成可执行的测试驱动研发计划(`tdd-plan.html`),作为从产品设计到 OpenSpec 变更提案的桥梁;随后执行 PRD 交叉验证——用测试场景反向挑战 PRD(业务规则、验收标准、流程与原型对齐),全部缺口经人类 PM 决策闭环后方可离开本阶段。所有产物通过 {process_slug}/index.html 门户访问。
|
|
44
46
|
execution: "调用 plan-tdd-development skill 基于已确认的产品设计文档、字段规格、UI 规格和 Pencil 原型,生成包含验收映射、测试场景与任务切片的 tdd-plan.html,作为 OpenSpec 提案的输入。"
|
|
45
47
|
gates:
|
|
46
48
|
entry:
|
|
@@ -49,3 +51,6 @@ gates:
|
|
|
49
51
|
- product-prototype
|
|
50
52
|
exit:
|
|
51
53
|
- check: artifacts_present
|
|
54
|
+
- check: prd_cross_validated
|
|
55
|
+
args:
|
|
56
|
+
- '{process_slug}/pages/docs/prd-cross-validation.html'
|
|
@@ -54,6 +54,8 @@ workflow:
|
|
|
54
54
|
exit_checks:
|
|
55
55
|
- check: prd_has_required_sections
|
|
56
56
|
args: ["{process_slug}/pages/docs/prd.html"]
|
|
57
|
+
- check: prd_content_hygiene
|
|
58
|
+
args: ["{process_slug}/pages/docs/prd.html"]
|
|
57
59
|
- check: human_approved
|
|
58
60
|
args: ["{session_id}"]
|
|
59
61
|
- check: prd_snapshot_present
|
|
@@ -114,9 +116,12 @@ workflow:
|
|
|
114
116
|
action: plan-tdd-development
|
|
115
117
|
artifacts:
|
|
116
118
|
- "{process_slug}/pages/docs/tdd-plan.html"
|
|
119
|
+
- "{process_slug}/pages/docs/prd-cross-validation.html"
|
|
117
120
|
exit_checks:
|
|
118
121
|
- check: tdd_plan_complete
|
|
119
122
|
args: ["{design_id}"]
|
|
123
|
+
- check: prd_cross_validated
|
|
124
|
+
args: ["{process_slug}/pages/docs/prd-cross-validation.html"]
|
|
120
125
|
|
|
121
126
|
- id: propose
|
|
122
127
|
name: OpenSpec 提案
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
{
|
|
10
10
|
"name": "lincoln",
|
|
11
11
|
"description": "AI-Native product R&D collaboration system for Conductor + Claude Code. Orchestrates stages, gates, artifacts, skills, and roles across product, design, and engineering into a repeatable workflow from recordings to GitHub Issues to Obsidian knowledge base.",
|
|
12
|
-
"version": "1.
|
|
12
|
+
"version": "1.9.0",
|
|
13
13
|
"author": {
|
|
14
14
|
"name": "Lincoln contributors"
|
|
15
15
|
},
|
|
@@ -28,5 +28,5 @@
|
|
|
28
28
|
]
|
|
29
29
|
}
|
|
30
30
|
],
|
|
31
|
-
"version": "1.
|
|
31
|
+
"version": "1.9.0"
|
|
32
32
|
}
|
package/framework/README.en.md
CHANGED
|
@@ -12,9 +12,9 @@ Lincoln is an AI-Native R&D workflow system spanning **IDEs, agent harnesses, co
|
|
|
12
12
|
|
|
13
13
|
## Latest Release
|
|
14
14
|
|
|
15
|
-
[](RELEASE.md)
|
|
16
16
|
|
|
17
|
-
**v1.
|
|
17
|
+
**v1.9.0** is released: the issue-package portal is rebuilt as a docsify-style documentation framework — collapsible/filterable sidebar, focus mode, full Markdown rendering via marked, and driver.js numbered hotspot annotations, all openable via file:// with zero services (#131).
|
|
18
18
|
|
|
19
19
|
See the full release notes in [RELEASE.md](RELEASE.md).
|
|
20
20
|
|
|
@@ -101,6 +101,12 @@ lincoln update # upgrade to the latest release and re-sync
|
|
|
101
101
|
lincoln update --check # check for a newer version without installing
|
|
102
102
|
```
|
|
103
103
|
|
|
104
|
+
Or upgrade the package directly via npm and then re-sync:
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
npm install -g @sushanglewis/lincoln@latest && lincoln update
|
|
108
|
+
```
|
|
109
|
+
|
|
104
110
|
On an older install (e.g. from the `lincoln-install` era), or if npm reports `EEXIST: file already exists`, a leftover bin from an old package is blocking the install — clean up first, then reinstall:
|
|
105
111
|
|
|
106
112
|
```bash
|
|
@@ -169,6 +175,17 @@ Every requirement maps to one GitHub issue and one Lincoln feature branch. The `
|
|
|
169
175
|
- `pages/prototype/` — interactive HTML prototypes
|
|
170
176
|
- `handoffs/` — stage handoff documents
|
|
171
177
|
|
|
178
|
+
### Docsify-Style Portal & Numbered Hotspot Annotations
|
|
179
|
+
|
|
180
|
+
The `index.html` portal adopts a docsify-style documentation framework: content is baked at generation time with zero runtime requests, so packages open directly via **file://** and can also be pushed to static hosting for shareable links (`#/pages/docs/prd.html` deep links included):
|
|
181
|
+
|
|
182
|
+
- **Sidebar**: collapsible groups with remembered state, a filter box for instant page location, collapse/expand-all
|
|
183
|
+
- **Focus mode**: hides the sidebar while reading docs or reviewing prototypes, giving prototypes full-width space (Esc to exit)
|
|
184
|
+
- **Documents**: marked (MIT) renders full Markdown — images, tables, task lists, and mermaid diagrams, all synced with the portal's light/dark theme
|
|
185
|
+
- **Prototype annotations**: declare `hotspots` in the page YAML to render numbered badges on elements; hover for the explanation without on-screen prose
|
|
186
|
+
|
|
187
|
+
Run `lincoln update` after upgrading to sync the latest portal templates and runtimes into every harness and project.
|
|
188
|
+
|
|
172
189
|
## Natural-Language Interaction
|
|
173
190
|
|
|
174
191
|
Lincoln is AI-Native — **you don't need to type terminal commands**. Describe your intent in plain language and the Agent translates and executes the right scripts for you.
|
|
@@ -236,6 +253,8 @@ Before submitting a PR, please read:
|
|
|
236
253
|
|
|
237
254
|
| Version | Date | Key capabilities |
|
|
238
255
|
|---|---|---|
|
|
256
|
+
| v1.9.0 | 2026-10-12 | Docsify-style issue-package portal: collapsible/filterable sidebar, focus mode, full Markdown rendering via marked, driver.js numbered hotspot annotations, hash deep links (#131) |
|
|
257
|
+
| v1.8.4 | 2026-10-08 | PM-stage PRD quality gates: six-question clarity check, 13-section progressive PRD skeleton, content-hygiene and cross-validation checks, final-system-only prototype discipline (#129) |
|
|
239
258
|
| v1.8.3 | 2026-10-08 | Less-is-more artifact discipline (hook-injected); platform-aware install hints for non-npm CLIs; `lincoln update` refreshes external skill libraries |
|
|
240
259
|
| v1.7.0 | 2026-09-05 | SecurityAnalyzer gating; Trace 2.0 event log; friction scoring/knowledge recall/trace condensation/MR mining; parallel specialist delegation; stakeholder analysis model |
|
|
241
260
|
| v1.6.3 | 2026-08-15 | Markdown-first issue-package documents; `--render-stage` batch rendering; local `mermaid.min.js` runtime |
|
package/framework/README.md
CHANGED
|
@@ -12,9 +12,9 @@ Lincoln 是一个贯穿 **IDE、Agent Harness、代码托管、知识管理、
|
|
|
12
12
|
|
|
13
13
|
## 最新版本
|
|
14
14
|
|
|
15
|
-
[](RELEASE.md)
|
|
16
16
|
|
|
17
|
-
**v1.
|
|
17
|
+
**v1.9.0** 已发布:issue-package 门户升级为 docsify 风格文档框架——可折叠可过滤目录、focus 模式、marked 完整 Markdown 渲染、driver.js 数字角标注解,零服务 file:// 直接打开(#131)。
|
|
18
18
|
|
|
19
19
|
查看完整发布说明:[RELEASE.md](RELEASE.md)
|
|
20
20
|
|
|
@@ -101,6 +101,12 @@ lincoln update # 升级本体 + 同步框架 + 刷新外部技能库
|
|
|
101
101
|
lincoln update --check # 只检查是否有新版本,不安装
|
|
102
102
|
```
|
|
103
103
|
|
|
104
|
+
也可以直接用 npm 把本体升到最新再同步框架:
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
npm install -g @sushanglewis/lincoln@latest && lincoln update
|
|
108
|
+
```
|
|
109
|
+
|
|
104
110
|
技能库同步遵循 dependencies.yaml 的 pin 策略:本地有未提交修改的仓库会被跳过并警告(不会被强制覆盖),非 npm 管理的 CLI(如 gh)缺失时只提示安装命令。同步失败降级为警告,不会阻断升级。
|
|
105
111
|
|
|
106
112
|
如果还是旧版安装(例如 bin 名是 `lincoln-install` 的时代),或更新时 npm 报 `EEXIST: file already exists`,说明有旧包残留的 bin 文件挡住了安装,先清理再重装:
|
|
@@ -171,6 +177,17 @@ AI 生成的交接产物容易内容堆积、重点淹没。Lincoln 把「少即
|
|
|
171
177
|
- `pages/prototype/` — 可交互原型 HTML 页面
|
|
172
178
|
- `handoffs/` — 阶段交接文档
|
|
173
179
|
|
|
180
|
+
### docsify 式门户与数字角标注解
|
|
181
|
+
|
|
182
|
+
`index.html` 门户采用 docsify 风格的文档框架,生成时烘焙、运行时零外部请求,**file:// 双击即可浏览**,也可由流水线推到静态托管后直接分享链接(`#/pages/docs/prd.html` 支持深链定位):
|
|
183
|
+
|
|
184
|
+
- **目录**:分组折叠(状态记忆)、过滤框实时定位、一键收起/展开;文档与原型分组清晰
|
|
185
|
+
- **Focus 模式**:阅读文档或验收原型时隐藏目录,原型获得全屏空间(Esc 退出)
|
|
186
|
+
- **文档**:marked(MIT)完整渲染 Markdown——图片、表格、任务列表、mermaid 流程图,随门户明暗主题切换
|
|
187
|
+
- **原型注解**:页面 YAML 声明 `hotspots` 后在元素上生成数字角标,hover 查看说明,解释不再占用屏内空间
|
|
188
|
+
|
|
189
|
+
升级到本版本后执行 `lincoln update` 即同步最新门户模板与运行时。
|
|
190
|
+
|
|
174
191
|
## 自然语言交互
|
|
175
192
|
|
|
176
193
|
Lincoln 是 AI-Native 工作流——**你不需要在终端输入任何命令**。直接用自然语言描述意图,Agent 会自动翻译成对应脚本并代为执行。
|
|
@@ -238,6 +255,8 @@ Lincoln 的 `.claude/` 是开放的系统提示层,欢迎基于同一套元模
|
|
|
238
255
|
|
|
239
256
|
| 版本 | 日期 | 关键能力 |
|
|
240
257
|
|---|---|---|
|
|
258
|
+
| v1.9.0 | 2026-10-12 | docsify 式 issue-package 门户:可折叠过滤目录、focus 模式、marked 完整 Markdown 渲染、driver.js 数字角标注解、hash 深链(#131) |
|
|
259
|
+
| v1.8.4 | 2026-10-08 | PM 阶段 PRD 质量门控:需求六问自检、13 章递进骨架、PRD 内容卫生与交叉验证校验、原型最终样貌纪律(#129) |
|
|
241
260
|
| v1.8.3 | 2026-10-08 | 少即是多制品生成纪律(hook 注入);非 npm CLI 平台化安装提示;`lincoln update` 同步刷新外部技能库 |
|
|
242
261
|
| v1.7.0 | 2026-09-05 | SecurityAnalyzer 安全门控;Trace 2.0 事件日志;摩擦学习/知识召回/Trace 浓缩/MR 挖掘;并行专家子代理委托;相关者分析模型 |
|
|
243
262
|
| v1.6.3 | 2026-08-15 | Markdown-first issue-package 文档;`--render-stage` 批量渲染;Mermaid 图表本地渲染 |
|
package/framework/RELEASE.md
CHANGED
|
@@ -1,3 +1,47 @@
|
|
|
1
|
+
# Lincoln v1.9.0 Release Notes
|
|
2
|
+
|
|
3
|
+
**Release date:** 2026-10-12
|
|
4
|
+
|
|
5
|
+
## Highlights
|
|
6
|
+
|
|
7
|
+
- **docsify 式 issue-package 门户 + 数字角标注解 (#131 / LEW-76)** — issue-package 的 `index.html` 人读门户整体升级为 docsify 风格的文档承载框架,生成时烘焙、运行时零请求,继续支持 file:// 双击打开与静态托管零部署:
|
|
8
|
+
- **docsify 式门户风格** — 白色侧边栏顶部药丸搜索框、加粗分组标题带 ▸/▾ 折叠箭头、激活页浅绿底 + 绿色左边条;文档内容区去掉窗口卡片,860px 居中大号排版(h1 34px、1.75 行距)、蓝色竖条引用样式;顶部精简为 40px 白色工具条(聚焦/主题/托盘/日期)。
|
|
9
|
+
- **可折叠、可定位的目录** — 目录分组独立折叠(状态存 localStorage)+ 一键全部收起/展开;过滤框按标题/路径匹配并自动展开命中组、高亮定位;文档与原型分组一目了然。右侧说明面板与「必要产物清单」从目录栏移除。
|
|
10
|
+
- **Focus 模式** — 菜单栏按钮或打开原型页自动进入:目录整体隐藏、内容 100% 宽,原型获得最大展示空间,Esc 退出。
|
|
11
|
+
- **完整 Markdown 渲染(marked)** — vendor `marked` v18(MIT,docsify 同款渲染器)替换手写解析器:图片、表格、任务列表等原生元素全支持;mermaid 代码块自动渲染并随门户明暗主题切换;`<script id="docSource">` 内嵌契约不变,全部 PRD 门控校验零回归。
|
|
12
|
+
- **数字角标注解(driver.js)** — vendor `driver.js` v1.9(MIT);原型 YAML 新增 `hotspots: [{target, title, text}]`,在搜索框、字段、卡片等元素上渲染数字角标,hover 弹出说明、点击固定、可整体隐藏,解释不再依赖门户右侧面板,也不再以屏内文字出现。
|
|
13
|
+
- **hash 深链路由** — `#/pages/docs/prd.html` 形式的地址可直接定位到任意文档或原型页,Agent share 链接给人类验收更友好。
|
|
14
|
+
- 顺带修复:结构化原型模板资源引用深度(depth-3 → depth-4)与技能规范不一致导致 `prototype.js` 404 的问题。
|
|
15
|
+
|
|
16
|
+
## Migration Notes
|
|
17
|
+
|
|
18
|
+
- 无破坏性变更:新门户由 `lincoln_index.py` 重新生成 `package-data.js` 后自动生效;进行中的 issue 工作包在包目录内重跑 `scripts/lincoln_index.py`(或任一 `stage_loader.py` 产物记录动作)即完成升级。
|
|
19
|
+
- 原型页的 `doc-*` 注解 meta 已被 `hotspots` 取代:`prototype_artifact_complete` 校验器现要求页面数据含 `hotspots`;存量原型页在下一次 `build-product-prototype` 重渲染时自动迁移。
|
|
20
|
+
- 升级方式:`npm install -g @sushanglewis/lincoln@latest` 后执行 `lincoln update`,即把新门户模板、marked/driver.js 运行时与校验器同步进各 harness 与项目。
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
# Lincoln v1.8.4 Release Notes
|
|
25
|
+
|
|
26
|
+
**Release date:** 2026-10-08
|
|
27
|
+
|
|
28
|
+
## Highlights
|
|
29
|
+
|
|
30
|
+
- **PM 阶段 PRD 质量整治 (#129)** — 把「PM 产出的 PRD 必须经得起研发团队与测试挑战」从 prompt 建议升级为可执行的门控:
|
|
31
|
+
- **需求清晰度六问自检** — clarify-requirements 起草原稿前必须逐项回答:用户有哪些、场景是什么、边界在哪里、如何管异常、如何体验好、最终谁决策;答不出先问人类 PM,禁止带未决问题落笔。
|
|
32
|
+
- **13 章递进骨架** — PRD 固定为「版本说明 → 修订记录 → 功能列表 → 需求背景 → … → 风险与开放问题」的渐进结构,章节按名字匹配、编号只是装饰,PM 可按需求形态在骨架内增插业务章节。
|
|
33
|
+
- **双注册表内容卫生校验** — 两份校验器(stage gates 与 workflow exit_checks)同步新增 `prd_content_hygiene`:PRD 正文禁止过程性短语(待确认/待 PM/TODO 等)与技术性章节(技术调研/架构设计/数据库设计等),技术内容只进 design 阶段的归档页 feasibility.html。
|
|
34
|
+
- **SDD 测试反向驱动交叉验证** — plan-tdd-development 新增 Phase 2:从测试场景(正常/边界/异常)逐一回问 PRD 业务规则与验收标准,并建立业务流程步骤 ↔ 原型页面双向对齐矩阵;缺口必须由人类 PM 决策闭环(修复 = PRD 升版重冻结,或接受 = 记录理由与影响),全部闭环后交叉验证报告才能写入 pass 标记,`prd_cross_validated` 校验器强制 pass 标记版本与当前 PRD 版本严格相等并对 PRD 复检卫生,防止后续阶段编辑回潮。
|
|
35
|
+
- **原型只画最终样貌** — build-product-prototype 明确禁止仅用于演示的模拟按钮、屏内说明文字与演示跳转,解释一律走 `doc-*` 注释由门户右侧面板呈现;每个原型页面标注所属用户故事与业务流程步骤,与 PRD 文字双向对齐。
|
|
36
|
+
- **PM 角色质量职责** — lc-pm 新增三条专属职责:输出 PRD 前完成六问自检、PRD 只承载业务内容、交叉验证缺口必须经人类 PM 决策才允许闭环。
|
|
37
|
+
|
|
38
|
+
## Migration Notes
|
|
39
|
+
|
|
40
|
+
- 无破坏性变更:新校验只在 `clarify` 与 `tdd-development-plan` 阶段出口执行,既有已审批产物不受影响;重跑这两个阶段时需满足新版骨架与卫生规则。
|
|
41
|
+
- 升级后执行 `lincoln update` 即把新 prompt、校验器与门控接线同步进各 harness;进行中的 issue 工作包从下一阶段起自动适用新规则。
|
|
42
|
+
|
|
43
|
+
---
|
|
44
|
+
|
|
1
45
|
# Lincoln v1.8.3 Release Notes
|
|
2
46
|
|
|
3
47
|
**Release date:** 2026-10-08
|
package/framework/manifest.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "1.
|
|
3
|
-
"generatedAt": "2026-10-
|
|
2
|
+
"version": "1.9.0",
|
|
3
|
+
"generatedAt": "2026-10-12T03:56:22.196Z",
|
|
4
4
|
"files": [
|
|
5
5
|
{
|
|
6
6
|
"path": ".claude-plugin/hooks.json",
|
|
@@ -8,11 +8,11 @@
|
|
|
8
8
|
},
|
|
9
9
|
{
|
|
10
10
|
"path": ".claude-plugin/marketplace.json",
|
|
11
|
-
"sha256": "
|
|
11
|
+
"sha256": "1fa447cb84c758beb21134854712cb938b5bc0e7dc01bccf48047c6b63f118ba"
|
|
12
12
|
},
|
|
13
13
|
{
|
|
14
14
|
"path": ".claude-plugin/plugin.json",
|
|
15
|
-
"sha256": "
|
|
15
|
+
"sha256": "9eaf9b63df95f32433d655a46a0f976dddedde2e9201908228835cfbc50ff897"
|
|
16
16
|
},
|
|
17
17
|
{
|
|
18
18
|
"path": ".claude/agents/architect.md",
|
|
@@ -56,7 +56,7 @@
|
|
|
56
56
|
},
|
|
57
57
|
{
|
|
58
58
|
"path": ".claude/agents/pm.md",
|
|
59
|
-
"sha256": "
|
|
59
|
+
"sha256": "eaf7c02ca5b268376e3df7ebe5862bee739688748b8a9472818117b74cfdd627"
|
|
60
60
|
},
|
|
61
61
|
{
|
|
62
62
|
"path": ".claude/agents/qa.md",
|
|
@@ -152,7 +152,7 @@
|
|
|
152
152
|
},
|
|
153
153
|
{
|
|
154
154
|
"path": ".claude/schemas/stage-definition.schema.json",
|
|
155
|
-
"sha256": "
|
|
155
|
+
"sha256": "153a15c095706c259fabb313227eb183615b84596020f70326ea5e3541f9797e"
|
|
156
156
|
},
|
|
157
157
|
{
|
|
158
158
|
"path": ".claude/schemas/workflow-stage.schema.json",
|
|
@@ -172,7 +172,7 @@
|
|
|
172
172
|
},
|
|
173
173
|
{
|
|
174
174
|
"path": ".claude/skills/build-product-prototype/prompts/main.md",
|
|
175
|
-
"sha256": "
|
|
175
|
+
"sha256": "eb2a23f58f8b0bf28263b03484ff0c672a72f3f10e300fecabb35097cfe5f994"
|
|
176
176
|
},
|
|
177
177
|
{
|
|
178
178
|
"path": ".claude/skills/clarify-requirements/SKILL.md",
|
|
@@ -180,7 +180,7 @@
|
|
|
180
180
|
},
|
|
181
181
|
{
|
|
182
182
|
"path": ".claude/skills/clarify-requirements/prompts/main.md",
|
|
183
|
-
"sha256": "
|
|
183
|
+
"sha256": "c96b1972e085fdea5c28c152c69fa8421d814fc9ce4a7169fa5c8f94619ef36c"
|
|
184
184
|
},
|
|
185
185
|
{
|
|
186
186
|
"path": ".claude/skills/dependencies.yaml",
|
|
@@ -192,7 +192,7 @@
|
|
|
192
192
|
},
|
|
193
193
|
{
|
|
194
194
|
"path": ".claude/skills/draft-product-design/prompts/main.md",
|
|
195
|
-
"sha256": "
|
|
195
|
+
"sha256": "77aa515515dbe87216041f064a480ddac269889e2e5f296843537ab92d794c44"
|
|
196
196
|
},
|
|
197
197
|
{
|
|
198
198
|
"path": ".claude/skills/lc-analyze-frameworks/SKILL.md",
|
|
@@ -348,15 +348,15 @@
|
|
|
348
348
|
},
|
|
349
349
|
{
|
|
350
350
|
"path": ".claude/skills/lc-workflow/validators/validate.py",
|
|
351
|
-
"sha256": "
|
|
351
|
+
"sha256": "5668b9ef229e5c42cfb41cfe75e1c4b7bb5cafdf727f2e18b4257a3a9c41bf4f"
|
|
352
352
|
},
|
|
353
353
|
{
|
|
354
354
|
"path": ".claude/skills/plan-tdd-development/SKILL.md",
|
|
355
|
-
"sha256": "
|
|
355
|
+
"sha256": "71c9497cd6bb6cbf0486fd3955bdf41579c6c2691f4c060a343d4712ba485143"
|
|
356
356
|
},
|
|
357
357
|
{
|
|
358
358
|
"path": ".claude/skills/plan-tdd-development/prompts/main.md",
|
|
359
|
-
"sha256": "
|
|
359
|
+
"sha256": "9a5a3a5b84c27e52026e8f6d01f3c350d5ac478af4f349c6756a5b40e2150705"
|
|
360
360
|
},
|
|
361
361
|
{
|
|
362
362
|
"path": ".claude/skills/process-interview/SKILL.md",
|
|
@@ -404,7 +404,7 @@
|
|
|
404
404
|
},
|
|
405
405
|
{
|
|
406
406
|
"path": ".claude/stages/clarify.yaml",
|
|
407
|
-
"sha256": "
|
|
407
|
+
"sha256": "506491928f9e68fb643912046b66b565035ed9c2b397a9613d3541cd46d35327"
|
|
408
408
|
},
|
|
409
409
|
{
|
|
410
410
|
"path": ".claude/stages/explore-opensource.yaml",
|
|
@@ -496,7 +496,7 @@
|
|
|
496
496
|
},
|
|
497
497
|
{
|
|
498
498
|
"path": ".claude/stages/tdd-development-plan.yaml",
|
|
499
|
-
"sha256": "
|
|
499
|
+
"sha256": "2e684504983e2b9488ffc885a018832883395fd8b0b9beadf4cd6f30e73d06bf"
|
|
500
500
|
},
|
|
501
501
|
{
|
|
502
502
|
"path": ".claude/stages/workflow-router.yaml",
|
|
@@ -528,7 +528,7 @@
|
|
|
528
528
|
},
|
|
529
529
|
{
|
|
530
530
|
"path": ".claude/workflows/interview-to-knowledge.yaml",
|
|
531
|
-
"sha256": "
|
|
531
|
+
"sha256": "8f858613592282504701141d65c36cc44c82d4ea47c14b890db90559a30f9e9c"
|
|
532
532
|
},
|
|
533
533
|
{
|
|
534
534
|
"path": ".claude/workflows/oss-first-design.yaml",
|
|
@@ -540,7 +540,7 @@
|
|
|
540
540
|
},
|
|
541
541
|
{
|
|
542
542
|
"path": ".version-bump.json",
|
|
543
|
-
"sha256": "
|
|
543
|
+
"sha256": "6abdf12c5f36113acbfc13f2261cded6efb331cea7e6649a0157740b5cc4349a"
|
|
544
544
|
},
|
|
545
545
|
{
|
|
546
546
|
"path": "CLAUDE.md",
|
|
@@ -556,15 +556,15 @@
|
|
|
556
556
|
},
|
|
557
557
|
{
|
|
558
558
|
"path": "README.en.md",
|
|
559
|
-
"sha256": "
|
|
559
|
+
"sha256": "386c243a93a88b13a8e7fde9d6e189014fdc9909246fe38c510a47f278450af6"
|
|
560
560
|
},
|
|
561
561
|
{
|
|
562
562
|
"path": "README.md",
|
|
563
|
-
"sha256": "
|
|
563
|
+
"sha256": "28e96b5cbbf45f3d710d8d9811a200bc38f6ca5fba804712b103364e33c91f91"
|
|
564
564
|
},
|
|
565
565
|
{
|
|
566
566
|
"path": "RELEASE.md",
|
|
567
|
-
"sha256": "
|
|
567
|
+
"sha256": "ad4237d5b283200e49a7b3b2f2a4387cf12e69e6732c5794bec1a55a3d2da5ee"
|
|
568
568
|
},
|
|
569
569
|
{
|
|
570
570
|
"path": "SKILL.md",
|
|
@@ -684,7 +684,7 @@
|
|
|
684
684
|
},
|
|
685
685
|
{
|
|
686
686
|
"path": "scripts/lincoln_index.py",
|
|
687
|
-
"sha256": "
|
|
687
|
+
"sha256": "d9212b5fee3808d031f849b79e2cfb734834859ed126fa319d5c2d0024cacf2b"
|
|
688
688
|
},
|
|
689
689
|
{
|
|
690
690
|
"path": "scripts/lincoln_mr_mine.py",
|
|
@@ -704,7 +704,7 @@
|
|
|
704
704
|
},
|
|
705
705
|
{
|
|
706
706
|
"path": "scripts/lincoln_render.py",
|
|
707
|
-
"sha256": "
|
|
707
|
+
"sha256": "923ff42a64b36c955bab4e0213f7191f1971cb981b2c5a70ff2410e3feea9a3a"
|
|
708
708
|
},
|
|
709
709
|
{
|
|
710
710
|
"path": "scripts/lincoln_role.py",
|
|
@@ -776,7 +776,7 @@
|
|
|
776
776
|
},
|
|
777
777
|
{
|
|
778
778
|
"path": "scripts/validate_stage.py",
|
|
779
|
-
"sha256": "
|
|
779
|
+
"sha256": "45259fce089fb9fc72df5b1a1c00a3c782008662e9de551aff45aff44a732019"
|
|
780
780
|
},
|
|
781
781
|
{
|
|
782
782
|
"path": "tools/lincoln-installer/.gitignore",
|
|
@@ -788,7 +788,7 @@
|
|
|
788
788
|
},
|
|
789
789
|
{
|
|
790
790
|
"path": "tools/lincoln-installer/package.json",
|
|
791
|
-
"sha256": "
|
|
791
|
+
"sha256": "03b08328e3ccd97f7d989b5c0cac5017c7b09d9ba1685eb8cd36730468829a91"
|
|
792
792
|
},
|
|
793
793
|
{
|
|
794
794
|
"path": "tools/lincoln-installer/src/cli.tsx",
|
|
@@ -1072,11 +1072,11 @@
|
|
|
1072
1072
|
},
|
|
1073
1073
|
{
|
|
1074
1074
|
"path": "tools/lincoln/package-lock.json",
|
|
1075
|
-
"sha256": "
|
|
1075
|
+
"sha256": "4ac004e537b8f6db8348deb2e6821a079f1bd18f893be87a8a03c910e36516f9"
|
|
1076
1076
|
},
|
|
1077
1077
|
{
|
|
1078
1078
|
"path": "tools/lincoln/package.json",
|
|
1079
|
-
"sha256": "
|
|
1079
|
+
"sha256": "da614ee0caea0ae442defae9bf1a90ce857d2b9de15c9983f2ee9d209410f094"
|
|
1080
1080
|
},
|
|
1081
1081
|
{
|
|
1082
1082
|
"path": "tools/lincoln/src/cli.tsx",
|
|
@@ -29,8 +29,6 @@ PACKAGE_DATA_FILENAME = "assets/js/package-data.js"
|
|
|
29
29
|
ARCHIVE_NAV_GROUP = "归档"
|
|
30
30
|
VERSION_COMMENT_RE = re.compile(r"<!--\s*version:\s*(v\d+\.\d+)\s*-->", re.IGNORECASE)
|
|
31
31
|
STATUS_COMMENT_RE = re.compile(r"<!--\s*status:\s*(\w+)\s*-->", re.IGNORECASE)
|
|
32
|
-
LIST_META_KEYS = ("doc-fields", "doc-stories", "doc-rules", "doc-boundaries", "doc-exceptions", "doc-refs")
|
|
33
|
-
LIST_SEPARATOR = "|"
|
|
34
32
|
|
|
35
33
|
|
|
36
34
|
class MetaParser(HTMLParser):
|
|
@@ -71,13 +69,6 @@ def package_relative(path: str, process_slug: str) -> str:
|
|
|
71
69
|
return path[len(prefix) :] if path.startswith(prefix) else path
|
|
72
70
|
|
|
73
71
|
|
|
74
|
-
def split_list_meta(value: str) -> list[str]:
|
|
75
|
-
"""Split a pipe-separated meta value into a list of trimmed non-empty items."""
|
|
76
|
-
if not value:
|
|
77
|
-
return []
|
|
78
|
-
return [item.strip() for item in value.split(LIST_SEPARATOR) if item.strip()]
|
|
79
|
-
|
|
80
|
-
|
|
81
72
|
def extract_html_meta(full_path: Path) -> dict[str, Any]:
|
|
82
73
|
"""Parse meta tags and version/status comments from an HTML page."""
|
|
83
74
|
if not full_path.exists():
|
|
@@ -91,20 +82,15 @@ def extract_html_meta(full_path: Path) -> dict[str, Any]:
|
|
|
91
82
|
|
|
92
83
|
version_match = VERSION_COMMENT_RE.search(text)
|
|
93
84
|
status_match = STATUS_COMMENT_RE.search(text)
|
|
94
|
-
|
|
85
|
+
return {
|
|
95
86
|
"title": parser.meta.get("doc-title") or parser.title or full_path.stem,
|
|
96
87
|
"nav_label": parser.meta.get("nav-label", ""),
|
|
97
88
|
"nav_group": parser.meta.get("nav-group", "Docs"),
|
|
98
89
|
"version": parser.meta.get("doc-version") or (version_match.group(1) if version_match else None),
|
|
99
90
|
"status": status_match.group(1) if status_match else "",
|
|
100
91
|
"uid": parser.meta.get("doc-uid") or parser.meta.get("page-uid", ""),
|
|
101
|
-
"
|
|
102
|
-
"layout": parser.meta.get("doc-layout", ""),
|
|
92
|
+
"markdown": extract_html_markdown(full_path) if 'id="docSource"' in text else "",
|
|
103
93
|
}
|
|
104
|
-
for key in LIST_META_KEYS:
|
|
105
|
-
field = key.replace("doc-", "")
|
|
106
|
-
meta[field] = split_list_meta(parser.meta.get(key, ""))
|
|
107
|
-
return meta
|
|
108
94
|
|
|
109
95
|
|
|
110
96
|
def extract_html_markdown(full_path: Path) -> str:
|
|
@@ -196,15 +182,8 @@ def build_package_data(
|
|
|
196
182
|
"stage": entry.get("stage", ""),
|
|
197
183
|
"gate_passed": entry.get("gate_passed", False),
|
|
198
184
|
"human_confirmed": entry.get("human_confirmed", False),
|
|
199
|
-
"purpose": entry.get("purpose", ""),
|
|
200
|
-
"layout": entry.get("layout", ""),
|
|
201
|
-
"stories": entry.get("stories", []),
|
|
202
|
-
"fields": entry.get("fields", []),
|
|
203
|
-
"rules": entry.get("rules", []),
|
|
204
|
-
"boundaries": entry.get("boundaries", []),
|
|
205
|
-
"exceptions": entry.get("exceptions", []),
|
|
206
|
-
"refs": entry.get("refs", []),
|
|
207
185
|
"uid": entry.get("uid", ""),
|
|
186
|
+
"markdown": entry.get("markdown", ""),
|
|
208
187
|
}
|
|
209
188
|
)
|
|
210
189
|
|
|
@@ -519,7 +519,7 @@ def _inject_meta(html: str, name: str, content: str) -> str:
|
|
|
519
519
|
|
|
520
520
|
|
|
521
521
|
def _inject_annotation_metas(html: str, page_data: dict[str, Any]) -> str:
|
|
522
|
-
"""Inject
|
|
522
|
+
"""Inject doc annotation meta tags from page_data.annotations into <head>."""
|
|
523
523
|
annotations = page_data.get("annotations") if isinstance(page_data, dict) else None
|
|
524
524
|
if not isinstance(annotations, dict):
|
|
525
525
|
return html
|
|
@@ -716,9 +716,11 @@ def render_page(
|
|
|
716
716
|
uid_kind = _resolve_meta_uid_kind(template, target)
|
|
717
717
|
html = _inject_meta(html, uid_kind, uid)
|
|
718
718
|
|
|
719
|
-
# Inject
|
|
720
|
-
#
|
|
721
|
-
|
|
719
|
+
# Inject doc annotation metas for doc pages only. Prototype pages carry
|
|
720
|
+
# their explanations as on-page hotspots (window.LincolnHotspots) instead.
|
|
721
|
+
is_prototype = "/prototype/" in target or PAGE_UID_META_RE.search(template)
|
|
722
|
+
if not is_prototype:
|
|
723
|
+
html = _inject_annotation_metas(html, page_data or {})
|
|
722
724
|
|
|
723
725
|
html = _ensure_version_comment(html, version)
|
|
724
726
|
|
|
@@ -324,37 +324,82 @@ def check_portal_index_fresh() -> None:
|
|
|
324
324
|
# ---------------------------------------------------------------------------
|
|
325
325
|
|
|
326
326
|
|
|
327
|
+
# 递进骨架(issue-129):版本说明 → 修订记录 → 功能列表 → 需求展开。
|
|
328
|
+
# 章节编号不参与匹配,PM 可按需求类型在骨架内增插业务章节。
|
|
327
329
|
REQUIRED_PRD_SECTIONS = [
|
|
328
|
-
"
|
|
329
|
-
"
|
|
330
|
-
"
|
|
331
|
-
"
|
|
332
|
-
"
|
|
333
|
-
"
|
|
334
|
-
"
|
|
335
|
-
"
|
|
336
|
-
"
|
|
337
|
-
"
|
|
330
|
+
"版本说明",
|
|
331
|
+
"修订记录",
|
|
332
|
+
"功能列表",
|
|
333
|
+
"需求背景",
|
|
334
|
+
"用户故事",
|
|
335
|
+
"功能拆解",
|
|
336
|
+
"业务流程图",
|
|
337
|
+
"验收标准",
|
|
338
|
+
"业务规则",
|
|
339
|
+
"非功能需求",
|
|
340
|
+
"关联系统/接口",
|
|
341
|
+
"相关产物链接",
|
|
342
|
+
"风险与开放问题",
|
|
338
343
|
]
|
|
339
344
|
|
|
345
|
+
PRD_SECTION_PATTERNS = [
|
|
346
|
+
(name, re.compile(rf"^##\s*(?:\d+\s*[.、.]?\s*)?{re.escape(name)}\s*$", re.MULTILINE))
|
|
347
|
+
for name in REQUIRED_PRD_SECTIONS
|
|
348
|
+
]
|
|
349
|
+
|
|
350
|
+
# PRD 正文禁则:过程性疑问在对话中解决,技术内容归归档页(issue-129 第 4/6 条)。
|
|
351
|
+
PRD_FORBIDDEN_PROCESS_RE = re.compile(r"待确认|待人类|待补充|待\s*PM|TODO|FIXME", re.IGNORECASE)
|
|
352
|
+
PRD_FORBIDDEN_TECH_HEADING_RE = re.compile(
|
|
353
|
+
r"^##+\s*.*(技术调研|技术方案|可行性研究|架构设计|接口设计|数据库设计)",
|
|
354
|
+
re.MULTILINE,
|
|
355
|
+
)
|
|
356
|
+
|
|
357
|
+
CROSS_VALIDATION_PASS_RE = re.compile(
|
|
358
|
+
r"<!--\s*cross-validation:\s*pass\s+prd-version:\s*(v\d+\.\d+)\s*-->"
|
|
359
|
+
)
|
|
360
|
+
|
|
361
|
+
|
|
362
|
+
def _read_prd_text(target: Path) -> str:
|
|
363
|
+
if target.suffix == ".html":
|
|
364
|
+
return extract_html_markdown(target)
|
|
365
|
+
return target.read_text(encoding="utf-8")
|
|
366
|
+
|
|
367
|
+
|
|
368
|
+
def _prd_hygiene_violations(text: str) -> list[str]:
|
|
369
|
+
violations = sorted(set(PRD_FORBIDDEN_PROCESS_RE.findall(text)))
|
|
370
|
+
violations.extend(sorted(set(PRD_FORBIDDEN_TECH_HEADING_RE.findall(text))))
|
|
371
|
+
return violations
|
|
372
|
+
|
|
340
373
|
|
|
341
374
|
def check_prd_has_required_sections(path: str) -> None:
|
|
342
375
|
target = PROJECT_ROOT / path
|
|
343
376
|
if not target.exists():
|
|
344
377
|
fail(f"PRD missing: {target}")
|
|
345
378
|
|
|
346
|
-
|
|
347
|
-
text = extract_html_markdown(target)
|
|
348
|
-
else:
|
|
349
|
-
text = target.read_text(encoding="utf-8")
|
|
379
|
+
text = _read_prd_text(target)
|
|
350
380
|
|
|
351
|
-
missing = [
|
|
381
|
+
missing = [name for name, pattern in PRD_SECTION_PATTERNS if not pattern.search(text)]
|
|
352
382
|
if missing:
|
|
353
383
|
fail(f"PRD missing required sections: {', '.join(missing)}")
|
|
354
384
|
|
|
355
385
|
pass_check("PRD has all required sections")
|
|
356
386
|
|
|
357
387
|
|
|
388
|
+
def check_prd_content_hygiene(path: str) -> None:
|
|
389
|
+
target = PROJECT_ROOT / path
|
|
390
|
+
if not target.exists():
|
|
391
|
+
fail(f"PRD missing: {target}")
|
|
392
|
+
|
|
393
|
+
violations = _prd_hygiene_violations(_read_prd_text(target))
|
|
394
|
+
if violations:
|
|
395
|
+
fail(
|
|
396
|
+
"PRD contains content that belongs in conversation or archived pages, "
|
|
397
|
+
f"not the PRD: {', '.join(violations)}"
|
|
398
|
+
)
|
|
399
|
+
|
|
400
|
+
pass_check("PRD content hygiene OK")
|
|
401
|
+
|
|
402
|
+
|
|
358
403
|
def check_prd_snapshot_present(path: str) -> None:
|
|
359
404
|
target = PROJECT_ROOT / path
|
|
360
405
|
if not target.exists():
|
|
@@ -374,6 +419,40 @@ def check_prd_snapshot_present(path: str) -> None:
|
|
|
374
419
|
pass_check(f"PRD snapshot present: {snapshot_path}")
|
|
375
420
|
|
|
376
421
|
|
|
422
|
+
def check_prd_cross_validated(path: str) -> None:
|
|
423
|
+
target = PROJECT_ROOT / path
|
|
424
|
+
if not target.exists():
|
|
425
|
+
fail(f"Cross-validation report missing: {target}")
|
|
426
|
+
|
|
427
|
+
match = CROSS_VALIDATION_PASS_RE.search(_read_prd_text(target))
|
|
428
|
+
if not match:
|
|
429
|
+
fail(
|
|
430
|
+
"Cross-validation report has no pass marker. Every gap must be closed by a "
|
|
431
|
+
"human-PM decision (PRD fix with re-freeze, or accepted with recorded "
|
|
432
|
+
"rationale) before adding '<!-- cross-validation: pass prd-version: vX.Y -->'."
|
|
433
|
+
)
|
|
434
|
+
|
|
435
|
+
prd_path = target.parent / "prd.html"
|
|
436
|
+
if not prd_path.exists():
|
|
437
|
+
fail(f"PRD missing next to cross-validation report: {prd_path}")
|
|
438
|
+
|
|
439
|
+
prd_version = _extract_document_version(prd_path)
|
|
440
|
+
if prd_version != match.group(1):
|
|
441
|
+
fail(
|
|
442
|
+
f"Cross-validation pass marker is for PRD {match.group(1)}, but the PRD is now "
|
|
443
|
+
f"{prd_version or 'unversioned'}. Re-run cross-validation against the current PRD."
|
|
444
|
+
)
|
|
445
|
+
|
|
446
|
+
violations = _prd_hygiene_violations(_read_prd_text(prd_path))
|
|
447
|
+
if violations:
|
|
448
|
+
fail(
|
|
449
|
+
f"PRD regressed after cross-validation pass: {', '.join(violations)}. "
|
|
450
|
+
"Fix the PRD, bump its version, re-freeze, and re-run cross-validation."
|
|
451
|
+
)
|
|
452
|
+
|
|
453
|
+
pass_check(f"PRD cross-validation pass marker matches PRD {prd_version}")
|
|
454
|
+
|
|
455
|
+
|
|
377
456
|
# ---------------------------------------------------------------------------
|
|
378
457
|
# Registry
|
|
379
458
|
# ---------------------------------------------------------------------------
|
|
@@ -397,6 +476,8 @@ EXIT_CHECKS = {
|
|
|
397
476
|
"handoff_versions_match": check_handoff_versions_match,
|
|
398
477
|
"prd_has_required_sections": check_prd_has_required_sections,
|
|
399
478
|
"prd_snapshot_present": check_prd_snapshot_present,
|
|
479
|
+
"prd_content_hygiene": check_prd_content_hygiene,
|
|
480
|
+
"prd_cross_validated": check_prd_cross_validated,
|
|
400
481
|
"portal_index_exists": check_portal_index_exists,
|
|
401
482
|
"artifact_meta_complete": check_artifact_meta_complete,
|
|
402
483
|
"ids_valid": check_ids_valid,
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sushanglewis/lincoln",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.9.0",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@sushanglewis/lincoln",
|
|
9
|
-
"version": "1.
|
|
9
|
+
"version": "1.9.0",
|
|
10
10
|
"license": "MIT",
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"ink": "^5.2.0",
|