dflow-sdd-ddd 0.14.0 → 0.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +915 -0
- package/CONTRIBUTING.md +11 -1
- package/README.en.md +160 -212
- package/README.md +93 -147
- package/TEMPLATE-COVERAGE.md +13 -6
- package/TEMPLATE-LANGUAGE-GLOSSARY.md +15 -1
- package/bin/dflow.js +90 -4
- package/docs/closeout-check.en.md +204 -0
- package/docs/closeout-check.md +204 -0
- package/docs/commands.en.md +112 -0
- package/docs/commands.md +103 -0
- package/docs/doctor-uncertainty.en.md +212 -0
- package/docs/doctor-uncertainty.md +212 -0
- package/docs/evaluating-dflow.en.md +29 -11
- package/docs/evaluating-dflow.md +8 -6
- package/docs/release-versioning-policy.md +6 -0
- package/docs/upgrading.en.md +196 -0
- package/docs/upgrading.md +197 -0
- package/docs/using-with-claude-code.en.md +13 -4
- package/docs/using-with-claude-code.md +13 -4
- package/docs/using-with-codex.en.md +13 -4
- package/docs/using-with-codex.md +13 -4
- package/docs/using-with-github-copilot.en.md +13 -4
- package/docs/using-with-github-copilot.md +13 -4
- package/lib/closeout-check.js +690 -0
- package/lib/doc-shapes.json +1195 -0
- package/lib/doctor-checks.js +2500 -24
- package/lib/init.js +2861 -143
- package/lib/render-diagrams.js +1474 -0
- package/lib/render.js +865 -49
- package/package.json +2 -2
- package/templates/brownfield/references/drift-verification.md +4 -0
- package/templates/brownfield/references/finish-feature-flow.md +648 -98
- package/templates/brownfield/references/finish-feature-follow-up.md +60 -0
- package/templates/brownfield/references/finish-feature-minimal-host.md +427 -0
- package/templates/brownfield/references/finish-feature-post-hoc-hotfix.md +95 -0
- package/templates/brownfield/references/finish-feature-record-correction.md +45 -0
- package/templates/brownfield/references/git-integration.md +205 -26
- package/templates/brownfield/references/init-project-flow.md +23 -3
- package/templates/brownfield/references/modify-existing-flow.md +439 -88
- package/templates/brownfield/references/modify-existing-follow-up.md +122 -0
- package/templates/brownfield/references/modify-existing-post-hoc-hotfix.md +200 -0
- package/templates/brownfield/references/new-feature-flow.md +110 -37
- package/templates/brownfield/references/new-phase-flow.md +59 -9
- package/templates/brownfield/references/pr-review-checklist.md +328 -10
- package/templates/brownfield/scaffolding/AI-AGENT-GUIDE.md +151 -34
- package/templates/brownfield/scaffolding/CLAUDE-md-snippet.md +1 -1
- package/templates/brownfield/scaffolding/Git-principles-gitflow.md +85 -8
- package/templates/brownfield/scaffolding/Git-principles-trunk.md +88 -10
- package/templates/brownfield/scaffolding/_conventions.md +50 -28
- package/templates/brownfield/scaffolding/_overview.md +1 -0
- package/templates/brownfield/templates/_index.md +158 -10
- package/templates/brownfield/templates/analysis.md +79 -0
- package/templates/brownfield/templates/behavior.md +1 -0
- package/templates/brownfield/templates/context-definition.md +1 -0
- package/templates/brownfield/templates/context-map.md +2 -1
- package/templates/brownfield/templates/glossary.md +1 -0
- package/templates/brownfield/templates/lightweight-spec.md +154 -11
- package/templates/brownfield/templates/models.md +1 -0
- package/templates/brownfield/templates/phase-spec.md +9 -1
- package/templates/brownfield/templates/rules.md +1 -0
- package/templates/brownfield/templates/tech-debt.md +1 -0
- package/templates/common/references/ddd-modeling-guide.md +33 -16
- package/templates/{greenfield → common}/references/dflow-feedback-flow.md +2 -1
- package/templates/common/references/flow-rationale-registry.md +146 -0
- package/templates/common/skill/SKILL.md +13 -11
- package/templates/greenfield/references/drift-verification.md +4 -0
- package/templates/greenfield/references/finish-feature-flow.md +639 -100
- package/templates/greenfield/references/finish-feature-follow-up.md +60 -0
- package/templates/greenfield/references/finish-feature-minimal-host.md +383 -0
- package/templates/greenfield/references/finish-feature-post-hoc-hotfix.md +95 -0
- package/templates/greenfield/references/finish-feature-record-correction.md +45 -0
- package/templates/greenfield/references/git-integration.md +193 -26
- package/templates/greenfield/references/init-project-flow.md +23 -3
- package/templates/greenfield/references/modify-existing-flow.md +395 -87
- package/templates/greenfield/references/modify-existing-follow-up.md +104 -0
- package/templates/greenfield/references/modify-existing-post-hoc-hotfix.md +200 -0
- package/templates/greenfield/references/new-feature-flow.md +116 -35
- package/templates/greenfield/references/new-phase-flow.md +58 -9
- package/templates/greenfield/references/pr-review-checklist.md +311 -8
- package/templates/greenfield/scaffolding/AI-AGENT-GUIDE.md +146 -32
- package/templates/greenfield/scaffolding/CLAUDE-md-snippet.md +5 -2
- package/templates/greenfield/scaffolding/Git-principles-gitflow.md +84 -8
- package/templates/greenfield/scaffolding/Git-principles-trunk.md +94 -14
- package/templates/greenfield/scaffolding/_conventions.md +50 -28
- package/templates/greenfield/scaffolding/_overview.md +6 -2
- package/templates/greenfield/templates/_index.md +145 -10
- package/templates/greenfield/templates/aggregate-design.md +1 -0
- package/templates/greenfield/templates/analysis.md +79 -0
- package/templates/greenfield/templates/behavior.md +1 -0
- package/templates/greenfield/templates/context-definition.md +1 -0
- package/templates/greenfield/templates/context-map.md +2 -1
- package/templates/greenfield/templates/events.md +4 -1
- package/templates/greenfield/templates/glossary.md +1 -0
- package/templates/greenfield/templates/lightweight-spec.md +154 -11
- package/templates/greenfield/templates/models.md +1 -0
- package/templates/greenfield/templates/phase-spec.md +9 -1
- package/templates/greenfield/templates/rules.md +1 -0
- package/templates/greenfield/templates/tech-debt.md +1 -0
- package/templates/brownfield/references/dflow-feedback-flow.md +0 -251
package/docs/commands.md
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
# Dflow 指令參考
|
|
2
|
+
|
|
3
|
+
> **繁體中文** | [English](commands.en.md)
|
|
4
|
+
|
|
5
|
+
> **一般使用不需要這一頁。** 你把想做的事講出來,AI 會判斷該走哪一條 workflow,並在每個
|
|
6
|
+
> 決策點停下來問你——這是 Dflow 的預設用法,見 [README「你不用先學指令」](../README.md#你不用先學指令)。
|
|
7
|
+
>
|
|
8
|
+
> 這一頁是給以下三種情況看的:你想直接指定某條 flow、AI 選錯了你要糾正它、
|
|
9
|
+
> 或你在盤點 Dflow 到底涵蓋哪些情境。
|
|
10
|
+
|
|
11
|
+
## 指令一覽
|
|
12
|
+
|
|
13
|
+
Dflow 指令依角色分四類。
|
|
14
|
+
|
|
15
|
+
### 入口指令(從這裡開始一個 workflow)
|
|
16
|
+
|
|
17
|
+
啟動一次 workflow run;可在沒有任何既有 feature 的狀態下使用。三者彼此獨立、不互為前置。
|
|
18
|
+
|
|
19
|
+
| Flow | 何時用 | 典型產出 |
|
|
20
|
+
|---|---|---|
|
|
21
|
+
| `/dflow:new-feature` | 完全新功能、新增一條系統要實現的業務規則 | feature 目錄 + `_index.md` + 第 1 份 phase-spec(一律 T1) |
|
|
22
|
+
| `/dflow:modify-existing` | 改既有行為 — **不確定改動屬於哪類**時用,AI 內部會分流 | T1 → 升 new-phase / new-feature;T2 → lightweight-spec;T3 → `_index.md` inline 一行 |
|
|
23
|
+
| `/dflow:bug-fix` | 可清楚陳述預期行為的 defect | AI 判 tier(多為 T2 lightweight-spec)。無所屬 feature 的 orphan bug 會開一個 minimal(zero-phase)host:**功能性 bug** 走 `bugfix/BUG-{NUMBER}-{slug}`,其餘 standalone T2/T3 走 `feature/{SPEC-ID}-{slug}` |
|
|
24
|
+
|
|
25
|
+
⚠ `/dflow:bug-fix` 與 `/dflow:modify-existing` **走的是同一份 flow 文件**;tier 由
|
|
26
|
+
ceremony cascade 判定,不是由你選了哪個指令決定。所以這兩個名字選錯不會有後果。
|
|
27
|
+
|
|
28
|
+
### Feature 內指令(限 active feature)
|
|
29
|
+
|
|
30
|
+
只在已啟動的 active feature 內可用。指向 `completed/` 的 feature 會被拒絕。
|
|
31
|
+
|
|
32
|
+
| Flow | 何時用 | 典型產出 |
|
|
33
|
+
|---|---|---|
|
|
34
|
+
| `/dflow:new-phase` | active feature 需要再一個實作切片 | 新一份 `phase-spec-{date}-{slug}.md` + Implementation Tasks + 程式實作 / 驗證 + phase 標記完成(一律 T1) |
|
|
35
|
+
| `/dflow:finish-feature` | feature 全部 phase 完成、要收尾 | `git mv` 整個 feature dir 到 `completed/`、sync BR Snapshot 到 BC 層、Integration Summary(不 auto-merge) |
|
|
36
|
+
|
|
37
|
+
### 流程控制(管理進行中的 workflow run)
|
|
38
|
+
|
|
39
|
+
| Flow | 何時用 |
|
|
40
|
+
|---|---|
|
|
41
|
+
| `/dflow:status` | 看現在在哪個 workflow / Step / 進度 |
|
|
42
|
+
| `/dflow:next` | 確認過 Step Gate(等同自然語言「OK」/「繼續」) |
|
|
43
|
+
| `/dflow:cancel` | 放棄目前 workflow run、回到自由對話。已建立的 artifacts 保留 |
|
|
44
|
+
|
|
45
|
+
### 獨立工具(任何時候可呼叫,不綁定 feature 或 workflow)
|
|
46
|
+
|
|
47
|
+
| Flow | 何時用 | 典型產出 |
|
|
48
|
+
|---|---|---|
|
|
49
|
+
| `/dflow:verify` | 需要確認文件、程式、測試、債務紀錄是否一致 | 跨規格、領域文件、實作、測試、債務的 drift report |
|
|
50
|
+
| `/dflow:pr-review` | 變更已準備接受審查 | SDD/DDD 合規 review 清單,含風險、缺口、後續項目 |
|
|
51
|
+
| `/dflow:report-dflow-feedback` | 你或 AI 在使用中發現 Dflow 本身的問題 | sanitized 的本地草稿,逐欄對齊上游 issue 表單可直接貼上;不自動送出 |
|
|
52
|
+
|
|
53
|
+
## 該選哪個指令(rule of thumb)
|
|
54
|
+
|
|
55
|
+
**不確定就不用選** —— 把事情講出來,或直接下 `/dflow:modify-existing`,AI 會分流。
|
|
56
|
+
下表是你想跳過那一步、直接指定時用的。
|
|
57
|
+
|
|
58
|
+
| 我要做的事 | 直接下指令 |
|
|
59
|
+
|---|---|
|
|
60
|
+
| 完全新功能(與現有 feature 無關) | `/dflow:new-feature` |
|
|
61
|
+
| 為 active feature 加規劃中的下一個 phase | `/dflow:new-phase` |
|
|
62
|
+
| 修一個明確的 bug | `/dflow:bug-fix` |
|
|
63
|
+
| **不確定**怎麼分類、反正是改既有的 | `/dflow:modify-existing` |
|
|
64
|
+
| feature 全部 phase 都完成、要收尾 | `/dflow:finish-feature` |
|
|
65
|
+
| 跑變更 review | `/dflow:pr-review` |
|
|
66
|
+
| 檢查文件與程式碼 drift | `/dflow:verify` |
|
|
67
|
+
|
|
68
|
+
## 各 AI 工具怎麼輸入
|
|
69
|
+
|
|
70
|
+
`/dflow:*` 是 Dflow 的 canonical 共同詞彙;各 AI 工具的 `/` parser 行為不同。
|
|
71
|
+
實際輸入方式如下:
|
|
72
|
+
|
|
73
|
+
| 工具 | 建議叫法 |
|
|
74
|
+
|---|---|
|
|
75
|
+
| Claude Code(安裝 `--command-adapters` 後) | `/dflow:<id>`,例如 `/dflow:new-feature` |
|
|
76
|
+
| GitHub Copilot(VS Code Chat) | 命令入口用 `/dflow-<id>`(連字號,需 `--command-adapters`);也可自然語言自動觸發。`/dflow:<id>`(冒號)僅當文字稱呼、非命令 |
|
|
77
|
+
| GitHub Copilot CLI | 沒有 per-id 命令;先打 `/dflow` 喚起 skill,再用自然語言描述 workflow |
|
|
78
|
+
| Codex CLI | 不帶斜線的純文字 `dflow:<id>`,例如 `dflow:new-feature` |
|
|
79
|
+
|
|
80
|
+
若你的工具不支援自訂 slash command,把 workflow 名稱當成普通對話訊息輸入即可。Dflow 是
|
|
81
|
+
Markdown-based 的 workflow 材料加一個 scaffolding CLI,能與任何可讀專案指示與 repo
|
|
82
|
+
上下文的 AI 程式設計助理一起運作。
|
|
83
|
+
|
|
84
|
+
**沒裝 command adapters 也能用。** adapters 是 opt-in 的(`dflow configure-agents
|
|
85
|
+
--command-adapters`),它只是把這些名字變成工具原生的 `/` 選單項;自然語言觸發與純文字
|
|
86
|
+
輸入都不需要它。
|
|
87
|
+
|
|
88
|
+
## CLI 指令(在終端機執行,不是對 AI 講)
|
|
89
|
+
|
|
90
|
+
上面的 `/dflow:*` 是給 AI 助理的 workflow;下面五個是 `dflow` CLI 本身:
|
|
91
|
+
|
|
92
|
+
| 指令 | 用途 |
|
|
93
|
+
|---|---|
|
|
94
|
+
| `dflow init` | 在專案裡初始化 Dflow:問模式、Git policy、AI commit 標記、要設定哪些 AI 工具 |
|
|
95
|
+
| `dflow configure-agents` | 冪等地重新投影:加新的 AI 工具、刷新 workflow bundle;`--skills` 重生成 skill、`--command-adapters` 產生原生 `/` 命令 |
|
|
96
|
+
| `dflow doctor` | 唯讀健康檢查與漂移偵測 |
|
|
97
|
+
| `dflow render` | 把 `dflow/specs/` 轉成人類可讀的靜態 HTML |
|
|
98
|
+
| `dflow check-closeout` | 收尾 commit 的機械檢查:以結束碼當閘,接在 pre-commit hook 或 CI 上 |
|
|
99
|
+
|
|
100
|
+
每個指令的完整旗標見它自己的 `--help`(例如 `dflow render --help`)。
|
|
101
|
+
`init`、`configure-agents`、`render` 的使用情境與版控建議見 [README「開始使用」](../README.md#開始使用);
|
|
102
|
+
`doctor` 報出來的東西怎麼處理,見[升級既有 Dflow 專案](upgrading.md)與[當 `dflow doctor` 說它沒有把握](doctor-uncertainty.md);
|
|
103
|
+
`check-closeout` 檢查什麼、hook 與 CI 的接法範本,見[收尾 commit 的機械檢查](closeout-check.md)。
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
# When `dflow doctor` says it is not sure
|
|
2
|
+
|
|
3
|
+
> [繁體中文](doctor-uncertainty.md) | **English**
|
|
4
|
+
|
|
5
|
+
> This page tracks the source `main` branch and can describe behavior still listed under `## Unreleased` in the changelog. `@latest` installs the latest **published** CLI; it does not guarantee that every `main`-branch feature below has been released:
|
|
6
|
+
>
|
|
7
|
+
> ```bash
|
|
8
|
+
> npm install -g dflow-sdd-ddd@latest
|
|
9
|
+
> ```
|
|
10
|
+
>
|
|
11
|
+
> Everything on this page is done by editing your own Markdown, so the repairs remain forward-compatible. If your `dflow doctor` never prints an `[uncertain]` line, your installed CLI may predate the feature. Use this page as forward guidance; the reports appear only in a published release whose changelog includes them.
|
|
12
|
+
|
|
13
|
+
## What `uncertain` means
|
|
14
|
+
|
|
15
|
+
Most of this page is about one question `dflow doctor` answers about the two files whose content it makes claims about — `dflow/specs/shared/_conventions.md` and `dflow/specs/shared/AI-AGENT-GUIDE.md`: **are the rules and settings from the upstream template still in your file?** One id is about something else: the shape marker line in your spec docs (`unreadable-shape-marker`, below).
|
|
16
|
+
|
|
17
|
+
To answer it, doctor has to work out which section each line belongs to — which means reading Markdown block structure. Its reader is a deliberately small one, and there are shapes it is known to get wrong. Rather than guess, doctor now says so:
|
|
18
|
+
|
|
19
|
+
```text
|
|
20
|
+
[uncertain] dflow/specs/shared/_conventions.md, line 42: an HTML comment begins part-way through a line (inline-html-comment)
|
|
21
|
+
...
|
|
22
|
+
Ran, but cannot be trusted while this shape is present — their silence is NOT a pass, and anything they DO report may be an artefact of the shape: ...
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Three things follow from an `[uncertain]` line, and the second is the one people miss:
|
|
26
|
+
|
|
27
|
+
1. **`All checks passed` is not printed.** A project in this state never gets the same verdict a clean one gets.
|
|
28
|
+
2. **The named checks ran — but you cannot trust what they said, in either direction.** Doctor reports by exception, so a check that says nothing normally means "this is fine". For the checks listed in the finding, that silence means *nothing reliable was measured* — do not read it as a pass. The other direction holds too: a `warn` from one of those checks may be the shape confusing the reader rather than real drift.
|
|
29
|
+
3. **The exit code is still `0`.** Uncertainty is not a build failure. Doctor has never used a non-zero exit for a finding, and this did not change that.
|
|
30
|
+
|
|
31
|
+
## Why doctor doesn't just fix the shape instead
|
|
32
|
+
|
|
33
|
+
Some of these are fixable and some genuinely are not, and pretending otherwise is what this page exists to avoid:
|
|
34
|
+
|
|
35
|
+
- For some the fix is a rewrite of the reader itself, and rewrites of *this* reader have a measured record of introducing new defects while closing old ones.
|
|
36
|
+
- One would change what counts as a table, which would move an unrelated formatting check with it.
|
|
37
|
+
- And for at least one shape — the table-indent gap described at the end of this page, which is deliberately **not** reported — there is **no available arbiter** to implement against.
|
|
38
|
+
|
|
39
|
+
So the honest position is: **narrow the reader where that is safe, disclose what can be detected, and record the rest in the source.** Detecting *whether a shape is present* needs none of the block-boundary logic that could be wrong, which is why these warnings can be trusted even though the thing they warn about cannot.
|
|
40
|
+
|
|
41
|
+
## The shapes
|
|
42
|
+
|
|
43
|
+
Each heading is the detector id doctor prints in brackets.
|
|
44
|
+
|
|
45
|
+
> This list is **not** exhaustive. It covers the shapes that are both known and detectable today. Markdown has more edge cases than any list, and a shape that is absent here is not thereby certified safe — it is either not yet known, or known and judged not worth warning about (see the last section).
|
|
46
|
+
|
|
47
|
+
### `inline-html-comment`
|
|
48
|
+
|
|
49
|
+
**The shape.** An HTML comment that begins part-way through a line:
|
|
50
|
+
|
|
51
|
+
```markdown
|
|
52
|
+
Selected Git policy: `gitflow` <!-- was trunk, revisit in Q3 -->
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
**Why it cannot be read reliably.** Doctor classifies Markdown one line at a time. A comment that *starts* a line opens a block, and doctor correctly treats its contents as invisible. A comment that opens mid-line is not a block at all — it is an inline span — so its contents are counted as live document text.
|
|
56
|
+
|
|
57
|
+
**Which way it fails.** Silently. A rule you commented out mid-line still reads as present, so doctor reports your file as current when part of it has been switched off. This is the worst direction, which is why it is disclosed rather than left alone.
|
|
58
|
+
|
|
59
|
+
**How to rewrite it.** Move the comment to a line of its own **starting at column 0**, outside any list item or block quote. There it opens a real HTML block and its contents stop being read:
|
|
60
|
+
|
|
61
|
+
```markdown
|
|
62
|
+
<!-- was trunk, revisit in Q3 -->
|
|
63
|
+
Selected Git policy: `gitflow`
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Deleting it works too.
|
|
67
|
+
|
|
68
|
+
> ⚠ **Indenting it under a list item is not enough**, and this is worth stating because it is the obvious way to follow the instruction above. A comment indented under `- item` stays *inside* the item, where Dflow still reads it — see the next shape. The column is what matters, not the fact of being alone on the line.
|
|
69
|
+
|
|
70
|
+
Note that a comment inside a code span — `` `<!-- like this -->` `` — is *rendered*, so a reader does see it; that is not this shape and doctor does not report it.
|
|
71
|
+
|
|
72
|
+
> ⚠ **If the cited comment is inside a `<textarea>`, leave that line unchanged** — including when it is reported under *this* id, which is what happens when the tag and the comment share a line. A `<textarea>` holds raw text, so moving the line out would hide text the reader already sees. But do **not** ignore the overall uncertainty result: doctor reports only the first occurrence of each shape in a file, so this harmless line can shadow a later, genuinely hidden comment with the same id. Inspect the rest of the cited file for other apparent comment openers before trusting the affected checks.
|
|
73
|
+
|
|
74
|
+
> ⚠ **Renderer scope:** a markerless continuation of list-owned raw HTML is calibrated to the renderer Dflow ships (`dflow render`, powered by Marked). Another Markdown renderer may expose an escaped apparent opener there; if you publish through a different renderer, inspect its output before applying the repair.
|
|
75
|
+
|
|
76
|
+
> ⚠ **If the comment is inside an HTML block** (`<details>`, `<div>`, `<pre>`, …), moving it to column 0 is not the whole repair — you are still inside the block. See `comment-inside-container` below for the per-tag rule.
|
|
77
|
+
|
|
78
|
+
### `comment-inside-container`
|
|
79
|
+
|
|
80
|
+
**The shape.** An HTML comment on its own line, but inside a container. A list item and a block quote are the everyday cases:
|
|
81
|
+
|
|
82
|
+
```markdown
|
|
83
|
+
- Ceremony scaling
|
|
84
|
+
<!-- Escalate-only, no de-escalation. -->
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
An HTML block is one too, and it catches people out because the comment looks perfectly ordinary at column 0:
|
|
88
|
+
|
|
89
|
+
```markdown
|
|
90
|
+
<details>
|
|
91
|
+
<!-- Selected Git policy: `trunk` -->
|
|
92
|
+
</details>
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
**Why it cannot be read reliably.** Doctor does not parse the interior of a container as its own sequence of blocks, so a comment opened inside one never opens an HTML block as far as doctor is concerned. Its text stays in the pool of live document content. What makes something a container here is that behaviour, not its syntax — so treat the two examples above as illustrations rather than as the full set.
|
|
96
|
+
|
|
97
|
+
**Which way it fails.** Silently in `dflow render`. Its Marked renderer normally shows no comment text there; doctor reads the comment's contents as though you had written them as ordinary text. **This holds whether or not the comment is closed** — closing it changes nothing, because the problem is where it sits, not whether it ends. The `<details>` form is the more dangerous of the two, because a commented-out setting inside it can be *contradicted* by a visible line further down and doctor will still trust the hidden one.
|
|
98
|
+
|
|
99
|
+
> ⚠ **Renderer scope:** for a markerless continuation of list-owned raw HTML, the direction above is calibrated to `dflow render` (Marked). Some other Markdown renderers expose an escaped apparent opener instead. If you publish through another renderer, inspect that output before moving or deleting the comment.
|
|
100
|
+
|
|
101
|
+
**How to rewrite it.** Move the comment out of the enclosing container — or delete it. Leaving the container is the whole repair, and *how* you leave depends on which container you are in:
|
|
102
|
+
|
|
103
|
+
- **List item or block quote** — put the comment on a line of its own at column 0, with no list marker or `>` before it.
|
|
104
|
+
- **HTML block, most tags (`<details>`, `<div>`, …)** — a **blank line** is what ends the block; the closing tag does not. So put a blank line between the block and the comment, or move the comment above the block entirely.
|
|
105
|
+
- **`<pre>`** — the exception, and its rule is the opposite one: it ends at its own **closing tag**. Move the comment *below* `</pre>`. Adding a blank line inside the block does nothing, because a blank line does not end it.
|
|
106
|
+
|
|
107
|
+
⚠ **If the comment sits inside more than one container, the outermost one is the one you have to leave.** A `<pre>` inside a list item, a `<details>` inside a block quote: apply only the inner rule and the comment is still in the list item or the quote, and doctor reports the same finding again. Work outwards until the comment is at column 0 with nothing enclosing it.
|
|
108
|
+
|
|
109
|
+
⚠ The middle rule is the one that catches people: for `<details>` and friends, moving the comment below `</details>` with no blank line leaves it inside the block, and doctor will report the same finding again. It is the same blank-line rule described under `unclosed-html-block`. ⚠ And do not generalise it — apply it to `<pre>` and you will add a blank line, see the finding survive, and have no idea why.
|
|
110
|
+
|
|
111
|
+
#### `<textarea>` is not one of these shapes — and doctor sometimes reports it anyway
|
|
112
|
+
|
|
113
|
+
`<textarea>` looks like `<pre>` but behaves in the opposite way here. Its interior is **raw text**, so `<!-- like this -->` is displayed to the reader exactly as written. Nothing is hidden, so there is nothing to disclose — and **moving such a comment out would be the one edit that genuinely hides it.**
|
|
114
|
+
|
|
115
|
+
Doctor reports it anyway, deliberately. Suppressing it was tried three times and each attempt created a case where a *genuinely* hidden comment went unreported — the failure this whole check exists to prevent. Keeping the harmless line reported is safer than a clean verdict over a file doctor read wrong, so the exemption was removed rather than patched again.
|
|
116
|
+
|
|
117
|
+
> **If you get `comment-inside-container` and the cited comment is inside a `<textarea>`, leave that line unchanged, but keep the overall finding open.** Doctor reports only the first occurrence of this id in a file, so inspect the rest of the file for a later apparent comment opener before treating the affected checks as reliable.
|
|
118
|
+
|
|
119
|
+
#### A fenced example can still be reported
|
|
120
|
+
|
|
121
|
+
Doctor masks fenced code before looking for these shapes, so an example inside ```` ``` ```` normally does not fire. Its fence scanner reads raw document lines, though, and never strips a container prefix — so there are fences it does not recognise. The known ones:
|
|
122
|
+
|
|
123
|
+
- a fence opened **inside a block quote** (`> ` before the backticks);
|
|
124
|
+
- a fence whose **raw indent is four spaces or more**, which happens under an ordinary `- item` as soon as you indent the fence that far;
|
|
125
|
+
- a fence opened **on the list-marker line itself** (`` - ```md ``), whose raw line starts with `-`, so the un-indent repair below cannot reach it.
|
|
126
|
+
|
|
127
|
+
> ⚠ This list is **not** exhaustive. An earlier version said the scanner "misses exactly two shapes", and the very next review round found the third (`p084gate-x14`). The rule is that the fence's raw line does not start at column 0, or carries a container prefix — not a list you can check off.
|
|
128
|
+
|
|
129
|
+
A comment inside any of them is still reported, and there is a second consequence that is easy to miss: **the text inside an unmasked fence is read as ordinary section content.** If your example happens to quote a rule that has since been changed elsewhere in the file, doctor can read the example as the live rule and report the file as current. So an unmasked fence is not only noisy — it can also hide real drift.
|
|
130
|
+
|
|
131
|
+
They clear differently:
|
|
132
|
+
|
|
133
|
+
- **The indent case** — un-indent the fence to two or three spaces and the report stops. This is the one place where re-indenting helps, and it is the exception to the rule stated above.
|
|
134
|
+
- **The block quote case** — re-indenting does **not** help at any depth, because the line still starts with `>` and the fence scanner never sees the fence at all. Move the example out of the quote, or ignore the report.
|
|
135
|
+
- **The list-marker-line case** — un-indenting cannot help either, because the raw line begins with the marker. Move the fence to a line of its own below the marker, or ignore the report. ⚠ Do **not** follow the generic repair the CLI prints for this id here: moving or deleting the comment would remove example text your reader can see.
|
|
136
|
+
|
|
137
|
+
Re-indenting the **comment** inside the container never helps. ⚠ That is about the comment. Un-indenting a *fence* is a different edit and it does help — see the fenced-example note above, which is the one exception on this page.
|
|
138
|
+
|
|
139
|
+
### `unclosed-html-block`
|
|
140
|
+
|
|
141
|
+
**The shape.** An HTML block opened at the start of a line that never closes — most often a `<!--` left behind mid-edit.
|
|
142
|
+
|
|
143
|
+
**Why it cannot be read reliably.** Everything from that line to the end of the file is inside the block, so it is not section content and cannot be assessed.
|
|
144
|
+
|
|
145
|
+
**Which way it fails.** Both ways at once, and this is the one worth reading twice. A `missing` or `is missing the rule` finding below the block may be caused by the block rather than by real drift — *and* a rule that genuinely has drifted below it can go unreported entirely, because the block hides the text the check would have read. Treat every result about content below that line as unknown, not as passing.
|
|
146
|
+
|
|
147
|
+
**How to rewrite it.** Close the block. For an HTML comment that means adding `-->`; other block types that carry an end condition (`<script>`, `<style>`, `<pre>`, `<textarea>`, `<?`, `<!DOCTYPE`, `<) — doctor cannot read: the line where the marker belongs (the first line, or the one right after the frontmatter's closing `---`) is malformed (for example a number that is not a positive whole number, such as `0` or `01`); a line containing `dflow-shape:` sits anywhere else (quoted as an example, commented out, inside a list or a quote — all count; so does a marker under frontmatter that a byte-order mark at the start of the file hides, because `dflow render` does not see that frontmatter either); the doc has two or more lines containing `dflow-shape:`; or the line names a template this CLI does not ship (a typo, or a template only the other track has).
|
|
171
|
+
|
|
172
|
+
**Why it cannot be read reliably.** The marker is the only record of which template shape the doc was written against. With the line damaged, doctor does not guess: a guessed number could keep it silent about a doc that is behind, or have it report one that is not. Nor does doctor decide whether a marker line anywhere else is the live one: that would mean reading lists, quotes, comments and code blocks exactly as `dflow render` does, and it would rather say it cannot tell than report a doc it cannot read as passing.
|
|
173
|
+
|
|
174
|
+
**Which way it fails.** Only in silence, and only for the docs listed: doctor did not judge their shape, so saying nothing about it is not a pass. For a feature `_index.md` the same holds for the section-by-section comparison doctor otherwise runs on a dashboard without a marker. Every other check is unaffected.
|
|
175
|
+
|
|
176
|
+
**How to rewrite it.** Leave exactly one marker line in the doc, on the line where it belongs. A marker quoted as an example, or an old one commented out, counts too: reword it so it no longer contains `dflow-shape:`. If the doc was copied whole from the bundle's template copy and its first line is `<!-- dflow-generated: workflow-bundle -->`, delete that line and the blank line after it (doctor names this case). If the file starts with a byte-order mark (BOM) in front of its frontmatter, save it as UTF-8 without a BOM (doctor names this case too). If you know which template and number it had, write the line back with that number: the line in the installed template carries the current number, so copying it marks an older doc as current. If you are not sure of the number, remove the damaged lines and treat the doc as one without a marker — the one-time procedure in [Shape markers](upgrading.en.md#shape-markers) decides the number. ⚠ A doc in a zero-phase feature that has not closed out yet is listed apart: leave it alone until closeout, after which it moves to `features/completed/` and is no longer checked. A feature whose Phase Specs table is empty while its directory holds a phase spec is one doctor cannot place, and is listed apart too: leave it alone if it is a minimal host, and rewrite as above if it is not.
|
|
177
|
+
|
|
178
|
+
## Shapes that are known and deliberately not reported
|
|
179
|
+
|
|
180
|
+
Disclosure has a cost of its own: a warning that fires on correct files teaches people to ignore all of them.
|
|
181
|
+
|
|
182
|
+
**A malformed table delimiter row** — one whose cell count differs from the header row above it — is in this section too, and its reason is different enough to be worth stating.
|
|
183
|
+
|
|
184
|
+
GFM requires the delimiter row to carry exactly as many cells as the header, and treats a mismatched pair as ordinary prose: it is **not a table at all**. Doctor does not enforce that rule, so the two can disagree about whether this is a table, and then about where the section ends. The shape genuinely does make doctor misread, in **both** directions — the section can run on past where you see it end, or be cut short so content below is never examined.
|
|
185
|
+
|
|
186
|
+
So why is it not reported? **Because it was, and it could not be made to work.** Across six consecutive review rounds, every round found a document the detector stayed silent on — and silence here is worse than saying nothing, because it prints `All checks passed` over a file that has drifted. Narrowing it to the shapes where a divergence had been measured failed five times; widening it back to every mismatch failed on the sixth, because the remaining list had simply moved into the code that recognises a delimiter row. One of those rounds also measured the same silent failure with a delimiter row whose cell count was **correct**, which means the detector's scope was only ever part of the problem.
|
|
187
|
+
|
|
188
|
+
The durable fix is a different instrument rather than a better list of shapes: `marked` — the renderer `dflow render` uses — is already a dependency of this package, so the section boundary can be taken **from** the renderer instead of guessed alongside it and compared shape by shape. That is a separate design change with its own evaluation. Until then this page says plainly that the check is not made, rather than shipping one that goes quiet on documents nobody thought to try.
|
|
189
|
+
|
|
190
|
+
If you are chasing a drift result that makes no sense and your file has a delimiter row whose cell count differs from its header, that is worth fixing first.
|
|
191
|
+
|
|
192
|
+
Another known gap — **an indented continuation line inside a table** — is left unreported for a different reason. A prototype detector for it produced five false reports across 151 real files, all of them triggered by ordinary indented code and examples. There is also no reference implementation available that can settle who is right about that shape: the two that could arbitrate disagree, and one of them has no notion of tables at all. For it, the warning really would be worse than the gap.
|
|
193
|
+
|
|
194
|
+
It is not the only unreported one. Others are recorded in the source (`lib/doctor-checks.js`, under "what this deliberately does not implement") rather than here, because they have no detectable shape to key a report to — the interior of a nested container not being parsed as its own sequence of blocks is the broadest of them, and the two comment shapes above are the specific cases of it that *could* be detected.
|
|
195
|
+
|
|
196
|
+
So if you are chasing a drift result that makes no sense and none of the shapes above are present in your file, the table-indent gap is **one** candidate worth checking — not the last one. The end of this page is not the end of the list.
|
|
197
|
+
|
|
198
|
+
**One more deliberate silence, of a different kind: command files and skill files that are not there at all.** Everything above is about a file doctor might *misread*. This one is not a misreading — it is a whole layer doctor does not look for.
|
|
199
|
+
|
|
200
|
+
`dflow doctor` does check `.claude/commands/dflow/`, `.github/prompts/dflow-*.prompt.md` and the three `SKILL.md` files, but it judges **only the ones already present**: a partial set is reported, a `0.5.0`-era filename left behind is reported, and a Dflow-generated `SKILL.md` that has fallen behind this version is reported. **When none of them exist at all, it says nothing.**
|
|
201
|
+
|
|
202
|
+
**The failure this leaves open.** A project never ran `dflow configure-agents --command-adapters` — or ran it once and the files were not regenerated after a clone — so not one `/dflow:*` command is available, and `dflow doctor` reports `All checks passed` throughout. This is not hypothetical: one real project went six releases with no `.claude/commands/dflow/` files at all, and doctor was silent the whole way.
|
|
203
|
+
|
|
204
|
+
**Why it is deliberately not defended.** Doctor cannot separate two kinds of adopter — "I never wanted those files" and "I had them and they are gone" — because the two states are identical on disk, and **both are legitimate**. Dflow never records which tools a project intends to use (the boundary `PROPOSAL-058` set, which `checkRootAgentShims` also follows), while `PROPOSAL-037` positively **recommends** that adopters gitignore these generated files and regenerate them after a clone — so "not one of them present" in a fresh clone is exactly what following that advice looks like. A detector for this was specified three times, and each time a path was found where it fired on an innocent project; one version reported on **every fresh `dflow init`**. The sentence this section opens with — a warning that fires on correct files teaches people to ignore all of them — is about precisely this.
|
|
205
|
+
|
|
206
|
+
**Who carries the residual risk.** The adopter. Doctor states the boundary at the end of every run and names who picks it up: the AI you run Dflow with. ⚠ **That is a delegation, not a guarantee** — nothing makes it happen, and nothing verifies afterwards that it did.
|
|
207
|
+
|
|
208
|
+
**What would make us reconsider.** If command adapters became installed by default, the "I never wanted them" adopter would no longer exist and the detection collapses into the simple question "is it there?". That is a separate product decision — it would overturn `PROPOSAL-074`'s explicit choice to keep adapters opt-in — and it has not been made. If it is, this entry gets revisited with it.
|
|
209
|
+
|
|
210
|
+
## If none of this explains your result
|
|
211
|
+
|
|
212
|
+
Doctor is read-only; it never edits your files, so nothing here can have damaged anything. A drift report you cannot account for is worth reporting — include the `_conventions.md` section it points at, and the detector id if one was printed.
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
# 當 `dflow doctor` 說它沒有把握
|
|
2
|
+
|
|
3
|
+
> **繁體中文** | [English](doctor-uncertainty.en.md)
|
|
4
|
+
|
|
5
|
+
> 本頁隨源碼 `main` 更新,因此可能描述 changelog 仍列在 `## Unreleased` 的行為。`@latest` 安裝的是最新的**已發佈** CLI,不保證已包含下面每一項 `main` 功能:
|
|
6
|
+
>
|
|
7
|
+
> ```bash
|
|
8
|
+
> npm install -g dflow-sdd-ddd@latest
|
|
9
|
+
> ```
|
|
10
|
+
>
|
|
11
|
+
> 本頁所有修法都只是改你自己的 Markdown,因此可以當成向前相容的指引。如果你的 `dflow doctor` 從來不會印出 `[uncertain]`,已安裝的 CLI 可能早於這項功能。這時先把本頁當作未來版本指引;只有 changelog 已納入此功能的發佈版才會出現這類回報。
|
|
12
|
+
|
|
13
|
+
## `uncertain` 是什麼意思
|
|
14
|
+
|
|
15
|
+
這一頁大部分在講 `dflow doctor` 對兩個「它會針對內容做出宣稱」的檔案——`dflow/specs/shared/_conventions.md` 與 `dflow/specs/shared/AI-AGENT-GUIDE.md`——回答的那一個問題:**上游模板的規則與設定,在你的檔案裡還在不在?** 只有一個 id 講的是別的東西:你的規格文件裡那一行形狀標記(下面的 `unreadable-shape-marker`)。
|
|
16
|
+
|
|
17
|
+
要回答它,doctor 必須判斷每一行屬於哪一節,也就是要讀懂 Markdown 的區塊結構。它用的是一個刻意做得很小的讀取器,而有一批形狀是**已知它會讀錯**的。與其用猜的,doctor 現在會直接說出來:
|
|
18
|
+
|
|
19
|
+
```text
|
|
20
|
+
[uncertain] dflow/specs/shared/_conventions.md, line 42: an HTML comment begins part-way through a line (inline-html-comment)
|
|
21
|
+
...
|
|
22
|
+
Ran, but cannot be trusted while this shape is present — their silence is NOT a pass, and anything they DO report may be an artefact of the shape: ...
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
看到 `[uncertain]` 這行,有三件事成立,而第二件最常被忽略:
|
|
26
|
+
|
|
27
|
+
1. **不會印出 `All checks passed`。** 處在這個狀態的專案,永遠不會拿到跟真正乾淨的專案一樣的結論。
|
|
28
|
+
2. **被列名的那幾項檢查是有跑的——但它講的話,兩個方向都不能信。** doctor 是「有問題才講」的工具,所以平常一項檢查沒出聲就代表「這裡沒事」。但對 finding 裡列出的那幾項,沒出聲代表的是**沒有量到任何可信的東西**——不要當成通過。反方向也成立:那幾項裡如果真的冒出一條 `warn`,它也可能是那個形狀把讀取器搞混了,而不是真的漂移。
|
|
29
|
+
3. **exit code 仍然是 `0`。** 「不確定」不是建置失敗。doctor 從來沒有用非零 exit code 表達任何一條 finding,這次也沒有改變。
|
|
30
|
+
|
|
31
|
+
## 為什麼不乾脆把那些形狀修掉就好
|
|
32
|
+
|
|
33
|
+
有些修得動、有些真的修不動,而假裝都修得動正是本頁要避免的事:
|
|
34
|
+
|
|
35
|
+
- 有些的修法等於把讀取器本身重寫,而**這個**讀取器的重寫有紀錄可查:每次都在關掉舊缺陷的同時帶進新的。
|
|
36
|
+
- 有一種修下去會改變「什麼算是一張表格」,連帶把一項無關的格式檢查一起挪動。
|
|
37
|
+
- 而至少有一種形狀——本頁最後一節那個**刻意不回報**的表格縮排缺口——**找不到仲裁者**可以實作:能判定誰對的兩套參照實作彼此不一致,而其中一套根本沒有表格的概念。
|
|
38
|
+
|
|
39
|
+
所以誠實的做法是:**能安全收窄的地方就收窄,偵測得到的就說出來,其餘的記在原始碼裡。** 偵測「某個形狀在不在」完全不需要那套可能會錯的區塊邊界邏輯——這正是為什麼這些警告可以信,即使它們警告的那件事不能信。
|
|
40
|
+
|
|
41
|
+
## 各種形狀
|
|
42
|
+
|
|
43
|
+
每個標題就是 doctor 印在括號裡的偵測器 id。
|
|
44
|
+
|
|
45
|
+
> 這份清單**不是**窮舉。它涵蓋的是目前既已知、又偵測得到的形狀。Markdown 的邊界情況比任何清單都多,**沒有出現在這裡不等於已被驗證安全**——它可能是還沒被發現,也可能是已知但判斷不值得警告(見最後一節)。
|
|
46
|
+
|
|
47
|
+
### `inline-html-comment`
|
|
48
|
+
|
|
49
|
+
**形狀。** 從一行的中間才開始的 HTML 註解:
|
|
50
|
+
|
|
51
|
+
```markdown
|
|
52
|
+
Selected Git policy: `gitflow` <!-- was trunk, revisit in Q3 -->
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
**為什麼讀不準。** doctor 是一行一行分類 Markdown 的。**開頭就是**註解的那一行會開啟一個區塊,doctor 會正確地把它的內容當成看不見的。但從一行中間才開始的註解根本不是區塊,而是 inline span,所以它的內容會被算成活的文件內容。
|
|
56
|
+
|
|
57
|
+
**往哪個方向失敗。** 靜默。你在一行中間註解掉的規則,讀起來仍然「還在」,於是 doctor 會說你的檔案是最新的,而其實它有一部分已經被關掉了。這是最糟的方向,也是它必須被揭露、而不能放著不管的原因。
|
|
58
|
+
|
|
59
|
+
**怎麼改。** 把註解移到自己獨立的一行,而且**要從第 0 欄開始**、不在任何清單項或引用區塊裡面。在那個位置它才會開啟一個真正的 HTML 區塊,內容才會停止被讀取:
|
|
60
|
+
|
|
61
|
+
```markdown
|
|
62
|
+
<!-- was trunk, revisit in Q3 -->
|
|
63
|
+
Selected Git policy: `gitflow`
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
直接刪掉也可以。
|
|
67
|
+
|
|
68
|
+
> ⚠ **縮排到清單項底下是不夠的**,這句話值得特別寫出來,因為那正是照上面那句話做時最自然的動作。縮排在 `- item` 底下的註解仍然**留在那個項目裡面**,doctor 照樣會讀它——見下一個形狀。關鍵是**欄位**,不是「有沒有獨佔一行」。
|
|
69
|
+
|
|
70
|
+
注意:寫在 code span 裡的註解——`` `<!-- 像這樣 -->` ``——是**會被 render 出來**的,讀者看得到,那不是這個形狀,doctor 也不會回報它。
|
|
71
|
+
|
|
72
|
+
> ⚠ **如果這次指出的註解在 `<textarea>` 裡面,請保持那一行不變**——包括它被報在**這個** id 底下的時候(標籤和註解寫在同一行時就會這樣)。`<textarea>` 的內部是原始文字,把那一行移出去反而會藏起讀者原本看得到的文字。但**不要忽略整體的不確定結論**:doctor 對每種形狀在一個檔案裡只回報第一處,所以這個無害位置可能遮住後面同 id、真的被藏起來的註解。信任受影響的檢查以前,請繼續檢查該檔案其餘看似註解開頭的位置。
|
|
73
|
+
|
|
74
|
+
> ⚠ **Renderer 範圍:**清單所擁有的 raw HTML 若出現沒有 marker 的 continuation,這裡以 Dflow 隨附的 renderer(`dflow render`,由 Marked 驅動)為準。其他 Markdown renderer 可能會顯示被 escape 的註解開頭;若你用別的 renderer 發佈,套用修法前請先檢查它的輸出。
|
|
75
|
+
|
|
76
|
+
> ⚠ **如果註解在 HTML 區塊裡面**(`<details>`、`<div>`、`<pre>` …),只把它移到第 0 欄**不是完整的修法**——你仍然在那個區塊裡面。逐標籤的規則見下面的 `comment-inside-container`。
|
|
77
|
+
|
|
78
|
+
### `comment-inside-container`
|
|
79
|
+
|
|
80
|
+
**形狀。** 註解獨佔一行,但那一行在某個**容器**裡面。清單項和引用區塊是日常會遇到的兩種:
|
|
81
|
+
|
|
82
|
+
```markdown
|
|
83
|
+
- Ceremony scaling
|
|
84
|
+
<!-- Escalate-only, no de-escalation. -->
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
HTML 區塊也是一種容器,而它特別容易讓人踩到——因為那個註解就寫在第 0 欄,看起來完全正常:
|
|
88
|
+
|
|
89
|
+
```markdown
|
|
90
|
+
<details>
|
|
91
|
+
<!-- Selected Git policy: `trunk` -->
|
|
92
|
+
</details>
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
**為什麼讀不準。** doctor 不會把容器的**內部**當成獨立的一串區塊去解析,所以在裡面開啟的註解,對 doctor 而言從來沒有開啟過任何 HTML 區塊。它的文字仍然留在「活的文件內容」那一堆裡。決定「算不算容器」的是**這個行為**、不是它的語法長相——所以上面兩個例子請當成舉例,不是完整清單。
|
|
96
|
+
|
|
97
|
+
**往哪個方向失敗。** 在 `dflow render` 裡會靜默;它的 Marked renderer 通常不會顯示註解文字,doctor 卻把註解的內容當成你寫的一般文字在讀。**不管你有沒有把註解關起來,結論都一樣**——關起來也沒用,因為問題出在它待的位置,不是它有沒有結束。兩種形狀裡 `<details>` 那種更危險:被註解掉的設定可以被下面某一行**看得見的內容推翻**,而 doctor 仍然採信藏起來的那一個。
|
|
98
|
+
|
|
99
|
+
> ⚠ **Renderer 範圍:**對清單所擁有的 raw HTML 之 markerless continuation,上面的方向以 `dflow render`(Marked)為準。有些其他 Markdown renderer 反而會顯示被 escape 的註解開頭。若你用別的 renderer 發佈,移動或刪除註解前請先檢查那份輸出。
|
|
100
|
+
|
|
101
|
+
**怎麼改。** 把註解移出那個容器——或直接刪掉。**離開容器**就是整個修法,而「怎麼離開」取決於你在哪一種容器裡:
|
|
102
|
+
|
|
103
|
+
- **清單項或引用區塊**——把註解放到第 0 欄、獨佔一行,前面沒有清單標記、也沒有 `>`。
|
|
104
|
+
- **HTML 區塊,大多數標籤(`<details>`、`<div>` …)**——結束這種區塊的是**空行**,**不是結束標籤**。所以要在區塊和註解之間空一行,或乾脆把註解移到整個區塊上面。
|
|
105
|
+
- **`<pre>`**——它是例外,規則正好相反:它是靠**自己的結束標籤**收尾的。請把註解移到 `</pre>` **下面**。在區塊裡面加空行沒有用,因為空行結束不了它。
|
|
106
|
+
|
|
107
|
+
⚠ **如果註解同時待在好幾層容器裡面,你要離開的是最外面那一層。** 清單項裡面的 `<pre>`、引用區塊裡面的 `<details>`:只套用裡面那條規則,註解仍然在清單項或引用區塊裡,doctor 會再報一次同樣的 finding。請一層一層往外走,直到註解位在第 0 欄、外面什麼都沒有。
|
|
108
|
+
|
|
109
|
+
⚠ 中間那條才是最容易踩的:`<details>` 這一類,把註解移到 `</details>` **下面但沒有空行**,它仍然在區塊裡面,doctor 會再報一次同樣的 finding。這條就是 `unclosed-html-block` 那節講的同一個空行規則。⚠ 但**不要把它推廣**——套到 `<pre>` 上,你會加了空行、看著 finding 原封不動,然後完全不知道為什麼。
|
|
110
|
+
|
|
111
|
+
#### `<textarea>` 不屬於這裡講的形狀——但 doctor 有時仍然會報它
|
|
112
|
+
|
|
113
|
+
`<textarea>` 長得像 `<pre>`,行為卻正好相反。它的內部是**原始文字**,所以 `<!-- 像這樣 -->` 會**原封不動顯示給讀者看**。沒有任何東西被藏起來,也就沒有什麼要揭露的——**反過來說,把這種註解移出去,才是那個真正會把它藏起來的編輯。**
|
|
114
|
+
|
|
115
|
+
doctor 仍然會報它,而且是刻意的。要豁免它試過三次,每一次都反而造出「**真的**被藏起來的註解沒人回報」的情況——那正是這整個檢查存在要防的事。保留這個無害位置的回報,比「對一個 doctor 讀錯的檔案給出乾淨結論」安全得多,所以那個豁免被**移除**,而不是再補一次。
|
|
116
|
+
|
|
117
|
+
> **如果你收到 `comment-inside-container`、而這次指出的註解就在 `<textarea>` 裡面,請保持那一行不變,但整體 finding 仍不能結案。** Doctor 對這個 id 在一個檔案裡只回報第一處;信任受影響的檢查以前,請繼續找該檔案後面是否還有看似註解開頭的位置。
|
|
118
|
+
|
|
119
|
+
#### 用 fence 包起來的範例,仍然可能被報
|
|
120
|
+
|
|
121
|
+
doctor 在找這些形狀之前,會先把 fenced code 遮掉,所以放在 ```` ``` ```` 裡面的範例通常不會觸發。但它的 fence 掃描器讀的是**原始的文件行**、從來不會先剝掉容器前綴——所以有一類 fence 它認不出來。已知的有:
|
|
122
|
+
|
|
123
|
+
- 開在**引用區塊裡面**的 fence(反引號前面有 `> `);
|
|
124
|
+
- **原始縮排四格以上**的 fence——在普通的 `- item` 底下,只要你把 fence 縮到那麼深就會發生;
|
|
125
|
+
- 和清單標記**開在同一行**的 fence(`- ```md`)——這一種的原始行以 `-` 開頭,所以下面「縮到三格以內」的修法對它無效,要把 fence 移到自己獨立的一行。
|
|
126
|
+
|
|
127
|
+
> ⚠ 這份清單**不是窮舉**。前一版寫「剛好會漏掉兩種」,而下一輪 review 就找到第三種(`p084gate-x14`)。判準是「fence 的原始行沒有從第 0 欄開始、或前面有容器前綴」,不是一張可以核對完的清單。
|
|
128
|
+
|
|
129
|
+
這幾種情況裡的註解仍然會被報出來,而且還有第二個容易被忽略的後果:**沒被遮蔽的 fence,裡面的文字會被當成一般的節內容讀進去。** 如果你的範例剛好引用了一條後來在檔案別處被改掉的規則,doctor 可能會把範例當成活的規則、然後回報這個檔案是最新的。所以沒被遮蔽的 fence 不只是吵——**它也可能把真正的漂移藏起來**。
|
|
130
|
+
|
|
131
|
+
它們的解法**不一樣**:
|
|
132
|
+
|
|
133
|
+
- **縮排那種**——把 fence 的縮排退回兩到三格,回報就會停。這是唯一一個「重新縮排真的有用」的地方,也是上面那條規則的例外。
|
|
134
|
+
- **引用區塊那種**——重新縮排**沒有用**,縮多少都一樣,因為那一行開頭仍然是 `>`,fence 掃描器根本看不到這個 fence。要嘛把範例移出引用區塊,要嘛忽略它。
|
|
135
|
+
- **跟清單標記同一行那種**——重新縮排也沒用,因為原始行是以標記開頭的。把 fence 移到標記下面、自己獨佔一行,或者忽略它。⚠ 這裡**不要**照 CLI 對這個 id 印出來的通用修法做:移掉或刪掉那個註解,會把讀者看得到的範例文字一併拿掉。
|
|
136
|
+
|
|
137
|
+
把**註解**在容器裡面重新縮排,永遠沒有幫助。⚠ 這句講的是註解。把 **fence** 的縮排退回來是另一件事,而且那是有用的——見上面 fenced 範例那段,那是本頁唯一的例外。
|
|
138
|
+
|
|
139
|
+
### `unclosed-html-block`
|
|
140
|
+
|
|
141
|
+
**形狀。** 在一行開頭開啟、卻從未關閉的 HTML 區塊——最常見的是編輯到一半留下的 `<!--`。
|
|
142
|
+
|
|
143
|
+
**為什麼讀不準。** 從那一行到檔案結尾全都在那個區塊裡面,所以那些內容不算任何一節的內容,也就無從評估。
|
|
144
|
+
|
|
145
|
+
**往哪個方向失敗。** 兩個方向同時發生,這一條值得讀兩遍。區塊下方出現的 `missing` 或「少了那條規則」可能是這個未關閉區塊造成的、而不是真的漂移;**而且**真的漂移掉的規則如果在它下面,會完全沒有人回報,因為區塊把檢查本來要讀的文字藏起來了。請把這一行以下的所有結果都當成**未知**,不要當成通過。
|
|
146
|
+
|
|
147
|
+
**怎麼改。** 把區塊關起來。如果是 HTML 註解,就是補上 `-->`;其他**帶有結束條件**的區塊型別(`<script>`、`<style>`、`<pre>`、`<textarea>`、`<?`、`<!DOCTYPE`、`<)——doctor 看不準:標記該在的那一行(第一行;有 frontmatter 就是它收尾之後那一行)格式不對(例如號碼不是正整數,像 `0` 或 `01`);含 `dflow-shape:` 的行不在那個位置(當例子引用的、被註解掉的、放在清單或引用裡的都算;檔案開頭的 BOM(位元組順序標記)擋住 frontmatter 時,它後面那一行標記也算——`dflow render` 一樣看不到那段 frontmatter);文件裡有兩行以上含 `dflow-shape:`;或是那一行指名了這個 CLI 沒有出貨的範本(打錯字,或是只有另一軌才有的範本)。
|
|
171
|
+
|
|
172
|
+
**為什麼讀不準。** 標記是「這份文件是照哪一號範本形狀寫的」唯一的紀錄。那一行壞了,doctor 不去猜:猜出來的號碼可能讓它對一份其實落後的文件保持沉默,也可能讓它去報一份其實沒有落後的文件。doctor 也不去判斷一行不在標準位置的標記是不是有效的:那得跟 `dflow render` 對清單、引用、註解、程式碼區塊的讀法完全一致,而它寧可照實說看不準,也不把一份讀不到的文件報成通過。
|
|
173
|
+
|
|
174
|
+
**往哪個方向失敗。** 只會是沉默,而且只限列出來的那幾份:doctor 沒有判讀它們的形狀,所以它什麼都沒說不代表通過。如果是 feature 的 `_index.md`,doctor 對沒有標記的 dashboard 會做的逐段比對,這時也一樣沒有做。其他檢查都不受影響。
|
|
175
|
+
|
|
176
|
+
**怎麼改。** 每份文件只留一行標記,放在它該在的那一行。當例子引用的、被註解掉的舊標記也會被算進去:改寫成不含 `dflow-shape:` 的字樣。文件如果是從 bundle 的範本副本整份拷過來、第一行是 `<!-- dflow-generated: workflow-bundle -->`,刪掉那一行和它後面的空行(doctor 會點名這種情況)。檔案開頭如果有 BOM 擋在 frontmatter 前面,把檔案存成不帶 BOM 的 UTF-8(doctor 也會點名這種情況)。如果你確定它原本是哪支範本、第幾號,就照那個號碼把那一行寫回去:已安裝的範本裡那一行帶的是現行號碼,直接抄過來會把舊文件標成現行形狀。如果不確定號碼,把壞掉的行刪掉,當成沒有標記的文件處理——號碼由[形狀標記](upgrading.md#形狀標記)那一節的一次性補法決定。⚠ 還沒關帳的 zero-phase feature(沒有 phase、只掛一個小改動的 feature 目錄)裡的文件會被分開列:關帳之前不要動它,關帳後它會搬進 `features/completed/`,不再被檢查。Phase Specs 表是空的、目錄裡卻有 phase spec 的 feature,doctor 判不出是不是 minimal host,也會分開列:是 minimal host 就先別動,不是就照上面改。
|
|
177
|
+
|
|
178
|
+
## 已知、但刻意不回報的形狀
|
|
179
|
+
|
|
180
|
+
揭露本身也有代價:一個會在正確檔案上誤報的警告,會教會大家把所有警告都當耳邊風。
|
|
181
|
+
|
|
182
|
+
**畸形的表格分隔列**(格數和標題列不一樣)也在這一節裡,而它的理由不一樣,值得單獨說。
|
|
183
|
+
|
|
184
|
+
GFM 要求分隔列的格數必須和標題列相同,不相同時整塊就**不是表格**、會被當成普通段落文字。doctor 沒有強制這條規則,兩邊對「這裡是不是表格」看法不同,接下來對「這一節到哪裡結束」也就可能不同——所以這個形狀**真的會**害 doctor 讀錯,而且兩個方向都會(節延伸過頭,或提早切斷)。
|
|
185
|
+
|
|
186
|
+
那為什麼不回報?**因為做過了,而且做不好。** 這個偵測器寫出來之後,連續六輪 review 每一輪都找到一份它會漏掉的文件,而漏掉的後果一律是**靜默**——對已經漂移的檔案印出 `All checks passed`,比不回報更糟。收窄成「只報實測會讀錯的形狀」失敗五次;放寬成「任何格數不符都報」在第六次失敗,因為剩下的那個清單搬到了「怎麼認出一列分隔列」裡面。而其中一輪還量到:**分隔列格數正確時**同樣的靜默失敗也會發生,所以這個偵測器的範圍本來就只是問題的一部分。
|
|
187
|
+
|
|
188
|
+
耐久的解法是換工具,不是再寫一份更好的形狀清單:`marked`(就是 `dflow render` 用的那支 renderer)已經是這個套件的相依套件,所以節的邊界可以**直接向它拿**,而不是在旁邊猜、再逐個形狀比對差異。那是另一項設計改動,要走自己的評估,所以現在**先誠實地說「這裡我們不檢查」**,而不是留一個會靜默漏掉的檢查。
|
|
189
|
+
|
|
190
|
+
如果你在追一個說不通的漂移結果,而檔案裡有格數不符的表格分隔列——那是值得先改掉的地方。
|
|
191
|
+
|
|
192
|
+
還有一個已知缺口——**表格裡面的縮排續行**——是為了另一個理由刻意不回報。它的原型偵測器在 151 份真實檔案裡誤報了 5 次,全部都是被一般的縮排程式碼和範例觸發的。而且目前也沒有任何可用的參照實作能判定這個形狀誰對誰錯:能仲裁的兩套彼此不一致,其中一套還根本沒有表格的概念。對它而言,警告真的比缺口本身更糟。
|
|
193
|
+
|
|
194
|
+
**它不是唯一沒被回報的。** 其他的記在原始碼裡(`lib/doctor-checks.js` 的「what this deliberately does not implement」段),而不是記在本頁,因為它們沒有可以拿來觸發回報的具體形狀——其中範圍最廣的一條是「巢狀容器的內部不會被當成獨立的一串區塊解析」,而上面那兩個註解形狀,正是這一條裡**偵測得到**的具體子案。
|
|
195
|
+
|
|
196
|
+
所以,如果你正在追一個怎麼看都說不通的漂移結果,而上面那些形狀在你的檔案裡都不存在,表格縮排那條是**其中一個**值得檢查的候選——不是最後一個。**本頁的結尾不是清單的結尾。**
|
|
197
|
+
|
|
198
|
+
**還有一筆刻意不報的,和上面那些不同類:指令檔與 skill 檔「一支都沒有」。** 上面講的都是「同一份檔,doctor 可能讀錯」。這一筆不是讀錯,是**整層不看**。
|
|
199
|
+
|
|
200
|
+
`dflow doctor` 會檢查 `.claude/commands/dflow/`、`.github/prompts/dflow-*.prompt.md` 與三份 `SKILL.md`,但**只檢查已經存在的那些**:少了其中幾支會報、留著 `0.5.0` 舊檔名的殘留會報、Dflow 產生的 `SKILL.md` 落後於目前版本會報。**但整層一支都不存在時,它一個字都不會說。**
|
|
201
|
+
|
|
202
|
+
**失敗情境。** 一個專案從來沒跑過 `dflow configure-agents --command-adapters`,或跑過、而檔案在某次 clone 之後沒有重新產生,於是 `/dflow:*` 一支都打不出來——而 `dflow doctor` 從頭到尾回報 `All checks passed`。這不是假想:一個實際專案跨了六個版本、`.claude/commands/dflow/` 一支都沒有,doctor 全程沉默。
|
|
203
|
+
|
|
204
|
+
**為什麼決定不防。** doctor 分不出兩種人——「我本來就沒要那些檔」和「我有過、掉了」——這兩種狀態在磁碟上長得完全一樣,而且**兩種都合法**。Dflow 從來不記錄一個專案打算用哪些工具(這是 `PROPOSAL-058` 定下的界線,`checkRootAgentShims` 也照它走),而 `PROPOSAL-037` 反過來**建議**採用者把這些衍生檔 gitignore 掉、clone 之後再重新產生——所以一個乾淨 clone「一支都沒有」,正是照建議做出來的樣子。這條偵測規則被規格化過三次,三次都被找出會誤報無辜專案的路徑,其中一版連**每一次全新的 `dflow init`** 都會被報一條。本節開頭那句「會在正確檔案上誤報的警告,會教會大家把所有警告都當耳邊風」,講的就是這個。
|
|
205
|
+
|
|
206
|
+
**殘餘風險由誰承擔。** 採用者。doctor 每次執行都會在報告結尾把這條界線印出來,並指名由使用 Dflow 的 AI 接手確認——⚠ **但那是委派,不是保證**:沒有任何機制強制它發生,也沒有任何檢查驗證它做過。
|
|
207
|
+
|
|
208
|
+
**什麼條件下會重新考慮。** 如果 command adapter 改成預設安裝,「我本來就沒要」的那種人就不存在了,偵測會退化成「在不在」這個單純問題。那是一項獨立的產品決定(它會推翻 `PROPOSAL-074` 把 adapter 維持 opt-in 的明文決定),目前尚未拍板。真的走那條路的話,本頁這一筆要一併重看。
|
|
209
|
+
|
|
210
|
+
## 如果以上都解釋不了你看到的結果
|
|
211
|
+
|
|
212
|
+
doctor 是唯讀的,它不會改你的檔案,所以本頁提到的任何情況都不可能弄壞什麼東西。一個你解釋不了的漂移回報值得回報給我們——請附上它指到的那一節 `_conventions.md` 內容,以及有印出來的話,那個偵測器 id。
|
|
@@ -208,29 +208,47 @@ not to adopt.
|
|
|
208
208
|
|
|
209
209
|
## Cost Per Feature: A Rough Estimate
|
|
210
210
|
|
|
211
|
-
Dflow scales ceremony to change risk through three tiers (
|
|
212
|
-
|
|
213
|
-
|
|
211
|
+
Dflow scales ceremony to change risk through three tiers (the criteria live in
|
|
212
|
+
`AI-AGENT-GUIDE.md` § Ceremony Scaling; see
|
|
213
|
+
[`README.md` "Workflow Model"](../README.en.md#workflow-model) for the product
|
|
214
|
+
overview):
|
|
214
215
|
|
|
215
216
|
- **T1 Heavy** — new features, new phases, new Aggregates or Bounded
|
|
216
|
-
Contexts, architecture changes, new business rules
|
|
217
|
+
Contexts, architecture changes, new business rules, data-structure
|
|
218
|
+
changes, and any contract change that breaks a caller (API / event, or a
|
|
219
|
+
required env var / CLI flag / exit code). A full phase-spec
|
|
217
220
|
with domain modeling, behavior examples, an implementation plan, and
|
|
218
221
|
verification + finish checks. The cost is real but proportional to
|
|
219
222
|
the risk being managed.
|
|
220
223
|
- **T2 Light** — bug fixes (logic errors), UI verification adjustments,
|
|
221
|
-
small changes with a business-rule delta
|
|
224
|
+
small changes with a business-rule delta, non-breaking contract changes,
|
|
225
|
+
performance-only work. A lightweight spec, focused
|
|
222
226
|
verification, and confirmation that the fix lands in the correct
|
|
223
227
|
architectural layer.
|
|
224
|
-
- **T3 Trivial** —
|
|
225
|
-
|
|
226
|
-
|
|
228
|
+
- **T3 Trivial** — a local, meaning-preserving display copy / appearance tweak
|
|
229
|
+
(button colors, copy typos / wording, layout polish) — **no business-rule,
|
|
230
|
+
Domain, or data-structure changes**, and not high-consequence content.
|
|
231
|
+
"Local" means element level on a single screen / component, or a single
|
|
232
|
+
independently-consumed page / file such as a public README or an API
|
|
233
|
+
reference page; a whole-screen rewrite or a cross-screen sweep escalates to
|
|
234
|
+
T2, and so does a cross-page sweep. When hosted
|
|
235
|
+
under an existing feature it is one line in that `_index.md`, with no separate
|
|
236
|
+
spec file; if no owning feature exists, `/dflow:modify-existing` opens a
|
|
237
|
+
minimal (zero-phase) host and records the row there.
|
|
238
|
+
|
|
239
|
+
The list above is a summary. The one source that decides an actual change is
|
|
240
|
+
`AI-AGENT-GUIDE.md` § Ceremony Scaling — the ordered cascade, steps 0–4, first
|
|
241
|
+
match wins.
|
|
227
242
|
|
|
228
243
|
Tier choice is not always manual: `/dflow:new-feature` and
|
|
229
244
|
`/dflow:new-phase` default to T1; `/dflow:modify-existing` and
|
|
230
245
|
`/dflow:bug-fix` let the AI judge T1/T2/T3 based on what is actually
|
|
231
|
-
changing. Pure
|
|
232
|
-
|
|
233
|
-
|
|
246
|
+
changing. Pure formatting commits (e.g., `prettier`, `dotnet format`),
|
|
247
|
+
internal comments, and internal-doc typos can skip Dflow entirely and just
|
|
248
|
+
`git commit` (a user-visible typo goes through the cascade: T3 on a single screen or a single independently-consumed page, T2 once it sweeps several or touches high-consequence content). The reverse also holds — invisible
|
|
249
|
+
does not mean untracked: machine-consumed contracts, security / CVE and
|
|
250
|
+
compliance work, and deliberate runtime performance / resource / SLA changes
|
|
251
|
+
all stay inside Dflow.
|
|
234
252
|
|
|
235
253
|
## Project Language Compatibility
|
|
236
254
|
|