Skip to content

Commit d09bfb8

Browse files
committed
feat(platform-checklist): planned items — selector, status command, RUNNER/README/skills
The selector resolves one selector against two pools: the runnable one it has always returned, and the planned one it now reports beside it. A planned id must never reach a runner (whatever verdict came back would be about nothing), but dropping it silently would answer 'nothing here' about an area whose gap the ledger is deliberately carrying. `pnpm gen:checklist-status` is the 平台功能清单 + 实现状态 view: per-area active/planned counts and every planned id, plus `--out <dir>` rendering the wiki page set — one index and one page per area, planned first. Published on a schedule to the wiki, never committed: a generated page in the tree is a third artifact to keep fresh whose stale copy reads exactly as authoritative as a current one. Claude-Session: https://claude.ai/code/session_01AmH9bKvGoLjiY86Q4Z3og2 Co-authored-by: Claude <noreply@anthropic.com>
1 parent 3c9b2d7 commit d09bfb8

7 files changed

Lines changed: 571 additions & 7 deletions

File tree

‎.claude/skills/checklist-author/SKILL.md‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,15 @@ metadata:
4141
3. **本技能的简报是假设,源码才是真相** —— 每个测试项按 README.md「Item anatomy」
4242
的契约写;缺 fixture 记 `blocked`/`knownGaps`,永不伪造覆盖。
4343

44+
## 缺项写成 `planned` 项,不写成注释
45+
46+
sweep 发现「定义要求、平台还没有」的能力,写成该区的 `status: "planned"` 项(title ·
47+
priority · personas,`since: null` 或目标 release,⛔ 无 steps);注释没有 id,派发时指
48+
不到。它是 `coverage.json` 的合法映射目标但⛔ 不算覆盖:只映射到它的种类仍报 UNMAPPED。
49+
50+
**`planned → active` 只由一次判 pass 的运行记录兑现** —— steps/acceptance 随实现 PR 落
51+
地,跑过才翻状态(bump `revision`、追 `history`);⛔ 代码落地就翻 = 账本开始虚报覆盖。
52+
4453
## 规模指引
4554

4655
一轮全量 sweep ≈ 5 个 hunter + 8 个 writer agent。范围化的问题(「X 有测试吗?」)

‎.claude/skills/checklist-test/SKILL.md‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,10 @@ node scripts/checklist-select.mjs <selector> --json
5555
`blocked` 时才传 `--include-blocked`。**把解析器报出的 `revision` 钉进运行记录**:
5656
判定只对它运行时所对的那个 revision 有效。
5757

58+
**`planned` 项任何选择器都不返回,也没有开关让它返回**:能力不存在,无 steps 可驱动、
59+
无 oracle 可查,解析器单独列出命中项。运行记录逐项记 `planned`,⛔ 永不 pass / fail /
60+
blocked / not-run,⛔ 不进标题计数;与 blocked 的分界是 fixture 债 vs 平台债。
61+
5862
## 1. 规划这一轮 —— 只构建需要的,钉住的先跑
5963

6064
读命中项的 `surface`:

‎docs/qa/platform-checklist/README.md‎

Lines changed: 60 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ next-sequential numbers do.
4646
"id": "approvals.per-group-signoff", // "<area>.<slug>" — immutable, globally unique, never reused
4747
"title": "Per-group sign-off (会签) needs one approval from EACH group",
4848
"since": "v16", // release that introduced the capability
49-
"status": "active", // active | draft | retired
49+
"status": "active", // active | draft | planned | retired (see "Implementation status")
5050
"revision": 1, // bumps on any semantic edit
5151
"priority": "P1", // P0 = release-gating smoke · P1 = core · P2 = extended
5252
"surface": "browser", // browser | api | cli | build | mixed (the 15.1 plan's 🖥/🔌 lanes)
@@ -297,6 +297,65 @@ Three things worth knowing before you meet it:
297297
showcase-side fixture gaps #3358 uncovered (#3408, #3409, #3415) each cost a sweep to
298298
rediscover; recording the gap on the item is what stops that.
299299

300+
## Implementation status — `planned` is how the ledger holds a capability gap
301+
302+
The ledger used to record only capabilities that already work, so a missing piece of a
303+
listed capability had nowhere to live: the backlog sweep had nothing to point a `Path:`
304+
at, and the platform's implementation status lived in nobody's head. The North Star's
305+
definition line (「做出来的是什么」…「缺任何一样就不是这个应用」) makes such a piece a
306+
**requirement**, so it belongs on the ledger — as a fourth status, not as a second
307+
document. A separate feature list would drift against this one with no gate able to say
308+
which is wrong (the ADR-0136 lesson), and the reading entry carries numbers only because
309+
a command produces them.
310+
311+
**`status: "planned"`** — the definition requires this capability and the platform does
312+
not yet implement or verify it. What a planned item carries, and what it deliberately
313+
does not:
314+
315+
| field | on a planned item |
316+
|:---|:---|
317+
| `id` · `title` · `priority` · `surface` · `revision` · `history` | as for any item — an id is picked once and is immutable, so it can be pointed at from the day the gap is found |
318+
| `personas` | **required** — who the capability is for is knowable the day the gap is found, and is what makes the gap readable to the next sweep |
319+
| `since` | `null` (no target release chosen) **or the TARGET release** — never a release that already shipped without it |
320+
| `steps` | **none.** There is nothing to drive. Steps arrive in the PR that implements the capability, in the same edit that promotes the item |
321+
| `acceptance` | not required — no oracle can be consulted yet. Clauses drafted early are still validated |
322+
323+
Three consequences, all mechanical:
324+
325+
- **A planned item never runs.** `scripts/checklist-select.mjs` resolves it in no
326+
selector's runnable set, and there is no flag that makes it one. The selector reports
327+
it separately and the run record carries the verdict `planned` — ⛔ never `pass`,
328+
`fail` or `blocked` ([RUNNER.md](./RUNNER.md) "Verdicts"). A blocked item is a real
329+
test the environment cannot run today; a planned item has nothing to run at all.
330+
- **A planned item is not coverage.** It is a legal `coverage.json` map target — that is
331+
where a capability-gap card points — and it contributes zero: a kind whose *only*
332+
items are planned is reported **UNMAPPED**, exactly as if the entry were empty.
333+
Otherwise `planned` would be the cheapest way to green an untested kind, and the
334+
ratchet would measure intentions instead of tests.
335+
- **Promotion `planned → active` is earned, not declared.** It takes a run record in
336+
which the item **passed**: steps and acceptance arrive with the implementation, the
337+
item is run, and only then does the status flip (bumping `revision` and appending to
338+
`history` like any other semantic edit). ⛔ Flipping the status because the code
339+
landed is how a ledger starts reporting coverage it does not have.
340+
341+
**Reading the status** — one command, and the human entry point it publishes:
342+
343+
```bash
344+
pnpm gen:checklist-status # per-area active/planned counts + every planned id
345+
pnpm gen:checklist-status --out <dir> # also render the wiki pages into <dir>
346+
```
347+
348+
The wiki index **[Platform-Checklist](https://github.com/objectstack-ai/objectstack/wiki/Platform-Checklist)**
349+
is the 「平台功能清单 + 实现状态」 reading entry: one row per area with its active and
350+
planned counts, linking to one `Checklist-<area>` page per area that lists every item
351+
(`id · title · priority · status · personas`) with the planned ones in their own section
352+
first. It is regenerated on a schedule by
353+
[`.github/workflows/checklist-status.yml`](../../../.github/workflows/checklist-status.yml)
354+
and is never committed here — ⛔ there is no `STATUS.md` in this tree and no gate paired
355+
with one, by maintainer ruling: staleness is tolerated, and a generated page in the tree
356+
is a third artifact to keep fresh whose stale copy reads exactly as authoritative as a
357+
current one.
358+
300359
## Capability coverage — every capability the platform has gets tested
301360

302361
`coverage.json` makes "凡是有的能力, 都要测试" mechanical instead of aspirational. The

‎docs/qa/platform-checklist/RUNNER.md‎

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,12 +35,28 @@ Per **item**, derived — never hand-assigned:
3535
- `partial` — some passed, none failed (the "proved half, left it unticked" state from
3636
#3358, now first-class instead of a prose apology);
3737
- `fail` — any clause failed;
38-
- `blocked` / `not-run` — nothing consulted.
38+
- `blocked` / `not-run` — nothing consulted;
39+
- `planned` — the item's `status` is `planned`: the definition requires the capability
40+
and the platform does not verify it yet. Read off the item, never derived from a run.
3941

4042
**No verdict without evidence.** A clause with no captured artifact is `not-run`, not
4143
`pass`. Evidence means: the API/network trace, the screenshot, the log excerpt, or the
4244
test-run output the clause's `evidence` field names.
4345

46+
**A `planned` item is never driven, and never scores.** `scripts/checklist-select.mjs`
47+
returns it in no selector's runnable set — it prints the matches under a separate
48+
PLANNED heading and hands the runner only what can run. Carry each one into the run
49+
record as `planned`, with no clause table: there are no `steps` to follow and no
50+
`acceptance` to consult, so ⛔ it is never `pass`, never `fail`, never `blocked` and
51+
never `not-run`. The distinction that matters is against `blocked`: a blocked item is a
52+
**real test** the environment cannot run today, so its gap is a fixture debt and the
53+
right answer is to provision it; a planned item has **nothing to run at all**, so its
54+
gap is a platform debt and the right answer is to build the capability. Scoring one as
55+
the other sends the next sweep to fix the wrong thing. A planned item earns `active`
56+
only through a run record in which it passed — see the README's "Implementation status".
57+
The completion criterion is unchanged by them: an area is run when every item the
58+
selector handed you has a verdict; planned ids are listed, not chased.
59+
4460
## The accuracy rules
4561

4662
1. **Oracle hierarchy** — server truth (`api`, `network`, `build`, `test`) outranks
@@ -429,6 +445,13 @@ QA run · <selector> (<judged>/<total>) · <framework-sha8> · <YYYY-MM-DD> · <
429445
**An omitted bucket means "not declared", never zero** — the roll-up renders the two
430446
differently, so write `0 FAIL` when you mean zero.
431447

448+
**⛔ `planned` items appear nowhere in this title** — not inside `<total>`, and not as a
449+
sixth bucket. The counts vocabulary is closed at the five verdicts above, and a planned
450+
item was never a judgeable unit of this run: putting one in `<total>` deflates the
451+
coverage claim the record is read on, and inventing a `PLANNED` segment is a deviation
452+
the roll-up refuses to parse. They are named in the body, under the scope section that
453+
already records the selector.
454+
432455
**Retired phrasings — ⛔ none of these may be written again:** `(FULL area)`, `(N items)`,
433456
`(N of M items)`, `(N/M items consulted)`, omitting the parenthetical entirely, and the
434457
trailing `(11 not-run)` spelling of NOT-RUN. `(FULL area)` is why the ruling exists: it

‎package.json‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@
107107
"check:scripts-symbol-anchors": "node scripts/symbol-anchors.mjs --self-test && node scripts/check-scripts-symbol-anchors.mjs --self-test && node scripts/check-scripts-symbol-anchors.mjs",
108108
"check:spec-docblock-symbol-anchors": "node scripts/symbol-anchors.mjs --self-test && node scripts/check-spec-docblock-symbol-anchors.mjs --self-test && node scripts/check-spec-docblock-symbol-anchors.mjs",
109109
"check:platform-checklist": "node scripts/checklist-select.mjs --self-test && node scripts/check-platform-checklist.mjs --self-test && node scripts/check-platform-checklist.mjs",
110+
"gen:checklist-status": "node scripts/gen-checklist-status.mjs --self-test && node scripts/gen-checklist-status.mjs",
110111
"check:org-identifier": "node scripts/check-org-identifier.mjs --self-test && node scripts/check-org-identifier.mjs",
111112
"check:runner-env-posture": "node scripts/check-runner-env-posture.mjs --self-test && node scripts/check-runner-env-posture.mjs",
112113
"check:cli-test-child-env": "node scripts/check-cli-test-child-env.mjs --self-test && node scripts/check-cli-test-child-env.mjs",

0 commit comments

Comments
 (0)