Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
15 commits
Select commit Hold shift + click to select a range
16f6591
feat(qa): 平台功能标准测试清单 — AI 可执行的常备台账 (docs/qa/platform-checklist)
claude Aug 7, 2026
f09f690
feat(qa): 补齐清单缺失能力域 — ai 与 integration-system (11 域 46 项)
claude Aug 7, 2026
2ce429a
feat(qa): 深化 dashboards 与 i18n 域到深测契约 (rev 2)
claude Aug 7, 2026
a930325
feat(qa): 深化 identity-auth/automation/access-security/records-forms 域…
claude Aug 7, 2026
2b57510
feat(qa): 深化 platform-core 与 approvals 域(检查点,80 项)
claude Aug 7, 2026
512b4be
feat(qa): 平台测试清单缺口全排查 — 补齐至 170 项,修正 3 处错误豁免
claude Aug 8, 2026
5d50fc0
feat(qa): 变体新鲜度门 + hook/页面/字段下钻 + sweep 自动化配方(177 项)
claude Aug 8, 2026
8966865
feat(qa): coverage-sweep 内部 skill — 一句话手动触发清单缺口重扫
claude Aug 8, 2026
99a053d
docs(qa): FOLLOW-UPS 头部措辞随提交状态更新
claude Aug 8, 2026
cbe2802
docs(qa): 首次真实执行记录 — platform-core.console-login (pass) + 证据
claude Aug 8, 2026
fb94c6f
Merge remote-tracking branch 'origin/main' into claude/platform-test-…
claude Aug 8, 2026
a3e1bbf
docs(qa): 测试结果不进仓库 — runs/ 改为格式契约 + git-ignore 结果
claude Aug 8, 2026
af45409
feat(qa): checklist-run 执行 skill + 选择器解析脚本
claude Aug 8, 2026
d167ff9
chore(qa): 平台测试清单校验改为定期手动,从 CI 移除(维护者决定)
claude Aug 8, 2026
3af66ea
docs(qa): README 记录「运行节奏」并全面校准去-CI 后的措辞
claude Aug 8, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
114 changes: 114 additions & 0 deletions .claude/skills/checklist-run/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
---
name: checklist-run
description: >
Execute the platform test checklist (docs/qa/platform-checklist/) against a real
running app and produce a run record. Use whenever the maintainer says "测一下
<功能>", "跑这个测试项", "run the checklist for <area/item>", "test this feature
file", "验证 <功能点>", or points at a framework source file and asks whether it
still works. Takes a SELECTOR (item id · area · capability kind · priority · a
release · or a source-file path) and drives every matched item through its steps
following RUNNER.md. The companion to `coverage-sweep` (which AUTHORS items); this
one RUNS them. NOT a customer-published skill — internal agent tooling (lives in
.claude/, never in the published `skills/` dir).
metadata:
# Hides this skill from interactive `npx skills add objectstack-ai/objectstack`
# discovery — every SKILL.md outside `skills/` must carry this marker
# (template-consistency.test.ts enforces it).
internal: true
---

# Checklist run — execute selected items against a live app

You resolve a **selector** to a set of checklist items, boot the app in isolation, drive
each item's steps in the browser / over the API, and emit a **run record**. The method
for judging each clause (verdicts, oracle hierarchy, evidence, the anti-false-positive
self-check) is **`docs/qa/platform-checklist/RUNNER.md`** — read it first and obey it;
this skill is the trigger, the selection contract, and the isolation/parallelism plan,
not a second copy of the runner protocol.

Environment know-how (boot, the dist build model, the vendored-console trap, browser
escape hatches) is the **`dogfood-verification`** skill — read it too. You are not
reinventing how to boot; you are executing a specific list against a boot.

## 0. Resolve the selector — deterministic, no guessing

Never eyeball which items to run. Ask the resolver:

```
node scripts/checklist-select.mjs <selector> --json
```

Selectors (one per run):

| selector | runs |
|---|---|
| `platform-core.console-login` (bare id) | that one item |
| `area:records-forms` (or bare `records-forms`) | every item in the area |
| `capability:hook` | items mapped to a metadata kind in `coverage.json` |
| `priority:P0` | the standing smoke |
| `surface:api` | every API-surface item (cheap — no browser build needed) |
| `since:v17` | everything introduced in a release (the release-sweep filter) |
| **`file:packages/plugins/plugin-approvals/src/approval-service.ts`** | **items whose `source[]` cites that file — "test whatever covers this file"** |
| `all` | the whole checklist |

`--json` gives the runnable list (id · priority · surface · revision). **Blocked items are
excluded by default** — they can't run on stock fixtures; pass `--include-blocked` only to
record them as `blocked` with their fixture reason. **Pin the `revision`** the resolver
reports into the run record: a verdict is only valid for the revision it ran against.

## 1. Plan the run by surface — build only what you need

Read the matched items' `surface`:

- **All `api` / `build` / `cli`** → no console build. Boot the framework (`objectstack dev`)
and drive REST/CLI. Fast (~minutes).
- **Any `browser` / `mixed`** → you need the vendored console dist. It builds SEPARATELY
from the showcase workspace closure (`pnpm objectui:build` from the pinned `.objectui-sha`);
the first boot 404s `/_console/` until it exists (dogfood §2 — a real precondition, record
it, don't fake a block). Budget the build (~10–30 min on a cold monorepo); it dominates
wall time, the browser driving is minutes.

Build once, up front, for the whole run.

## 2. Isolate, then execute (per dogfood §0)

- Own free non-default port + own file DB **per concurrently-running item**
(`--seed-admin -d file:/tmp/<run>/<item>.db`). Two runs sharing a port/DB/browser tab is
the `shared-browser-tab` trap.
- **Parallelism:** fan API-surface items out in parallel (each its own port, cheap). Run
browser items **few-at-a-time** (2–3), each its own port + browser context — a single
machine's CPU and one shared display contend past that. When dispatching runner
subagents, **they must be `opus`**, each given: the item JSON, RUNNER.md, the
dogfood skill, its own port/DB, and the results-out-of-repo rule (§4).
- Execute each item's `steps` faithfully; judge each `acceptance` clause and each
`negative` against its declared `oracle`, capturing the `evidence` the clause names.
**Server truth outranks pixels; DOM only after a screenshot confirms render; a `fail`
needs reproduction ×2 + the automation self-check + a filed issue** (RUNNER §rules).

## 3. When the run teaches you something about the ITEM

A run that discovers the item's `steps` are wrong (a moved route, a renamed key, an
expiry path that needs localStorage cleared too) is the checklist working. That is a
checklist EDIT — do it in a **worktree** (PD#11): revise the item, bump `revision`, append
a `history` entry, keep `node scripts/check-platform-checklist.mjs` green, and land it on a
task branch. Product defects found while running go to `FOLLOW-UPS.md` (or a filed issue)
as expected-fail probes — never tick a clause green over a real defect.

## 4. The run record — results do NOT go in the repo

Write one JSON per run in the shape RUNNER.md defines (env with framework sha +
`.objectui-sha` + port + db; per-clause verdicts each naming its evidence; derived item
verdict; issues). **`runs/` is git-ignored** — the record and its screenshots stay in the
executing environment / the tracking issue / an external QA store, never committed. The
committed source is the checklist under `areas/`; a run is a dated assertion about one
build and belongs with that build's artifacts. Report the per-clause verdict table + the
evidence paths + the env-setup-vs-test time split back to the maintainer.

## Guardrails

- **Don't fake coverage.** Missing fixture → `blocked(fixture)` with the reason; unbuilt
console → build it or record `blocked(environment)`; a half-proven item is `partial`,
not `pass`. A blocked verdict WITH evidence is a successful run; a faked pass is not.
- **Don't run blocked items as if runnable** — the resolver hides them for this reason.
- **One selector, one run record.** For a release sweep, run `since:vN` and `priority:P0`
as separate records rather than smearing them together.
61 changes: 61 additions & 0 deletions .claude/skills/coverage-sweep/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
---
name: coverage-sweep
description: >
Re-audit the platform test checklist (docs/qa/platform-checklist/) for coverage
gaps and author the missing items — the five-angle capability sweep. Use whenever
the maintainer says "跑一轮 coverage sweep", "run a coverage sweep", "排查测试清单
遗漏", "审计测试覆盖", or asks whether some platform surface "有测试吗" and the
answer needs verifying rather than recalling. Also the right tool after a large
platform surface lands or before a major release. NOT a customer-published skill —
this is internal agent tooling (lives in .claude/, never in the published
`skills/` dir).
metadata:
# Hides this skill from interactive `npx skills add objectstack-ai/objectstack`
# discovery — every SKILL.md outside `skills/` must carry this marker
# (template-consistency.test.ts enforces it).
internal: true
---

# Coverage sweep — keep the platform test checklist honest

The canonical method lives in **`docs/qa/platform-checklist/SWEEP.md`** — read it
first and follow it; this skill is the trigger and the orchestration contract, not a
second copy of the procedure.

## What you are producing

A delta on `docs/qa/platform-checklist/`: new/extended items in `areas/*.json`, a
reconciled `coverage.json`, defects/docs-drift appended to `FOLLOW-UPS.md` — all
validating green under `node scripts/check-platform-checklist.mjs`, landed on a task
branch per AGENTS.md (worktree-first, PD#11).

## Orchestration contract

1. **Worktree first** (PD#11): `git worktree add ../objectstack-<task> -b <branch> main`.
All edits there. Read the checklist's current state before dispatching anything.
2. **Five READ-ONLY gap hunters in parallel** — one per SWEEP.md angle (console UI /
spec enums / routes & runtime / built-in apps / docs claims). Each gets: the current
item-id list, the already-known waivers and blocked items (don't re-report), and the
output contract `surface | evidence path | coverage verdict | proposed id | sketch |
fixture?`. Hunters write NO files.
3. **Dedupe into a scratch register** (delete it before landing). Cross-angle
duplicates are high-priority signal, not noise.
4. **Per-area writer agents** — one agent per `areas/*.json` file so writers never
collide; nobody but the orchestrator touches `coverage.json` or `scripts/`.
Every item follows README.md's deep-test contract; missing fixtures become
`blocked`/`knownGaps`, never faked coverage. Writers ground every endpoint, enum,
and error code in source before asserting — treat this skill's own briefs as
hypotheses, source as truth.
5. **Reconcile centrally**: un-waive any kind a hunter proved has a stock fixture
(four of six waivers were stale in the 2026-08 sweep — re-audit every waiver every
time), map new items in `coverage.json`, pin `enumSource` on any new variants
matrix (see README "Variants stay fresh automatically").
6. **Validate + land**: validator green, then commit on the task branch. Product
defects and docs-drift go to `FOLLOW-UPS.md`; security-sensitive findings are
NEVER filed publicly without the maintainer's decision.

## Scale guidance

A full sweep is ~5 hunter + ~8 writer agents. For a scoped question ("X 有测试吗?"),
run ONE hunter on the relevant angle, verify against the checklist, and author only
what's missing — same contract, smaller fleet.
8 changes: 8 additions & 0 deletions .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,14 @@ jobs:
- name: ADR anchors + number uniqueness (governed code names its decision)
run: pnpm check:adr-anchors

# NOTE: the standing platform test checklist (docs/qa/platform-checklist/)
# is validated by `pnpm check:platform-checklist`, but by MAINTAINER
# DECISION it is NOT wired into CI — it runs on a periodic manual cadence
# (before a release / after a large platform surface lands), not on every
# PR. The checklist is a QA ledger, not a code gate; keeping it out of the
# per-PR path means an unrelated PR is never blocked by checklist drift.
# Run it by hand: `pnpm check:platform-checklist`. See that dir's README.

# #3280/#3290 org-identifier guard: `organizationId` is the blessed
# developer-facing name for the caller's active org in hook/action bodies;
# the `session.tenantId` alias was REMOVED in v11 (#3290). Keeps our own
Expand Down
69 changes: 69 additions & 0 deletions docs/qa/platform-checklist/FOLLOW-UPS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
# Follow-ups — open items from the capability-coverage sweep (2026-08-08)

Decision register from the capability-coverage sweep. The gap items found by the
five-angle sweep have all been authored into `areas/*.json` (checklist grew 84 → 170
items; the `api`/`datasource`/`mapping` coverage waivers were corrected). What remains
here is what the sweep surfaced that is **not** a checklist item: product defects to
decide on, and docs that promise retired capabilities.

## 1. Product defects found during the sweep (decide handling)

These are real runtime/UI defects the gap hunters hit while grounding items. Each is
captured inside the relevant checklist item as an **expected-fail probe** (so a run
records the actual behavior instead of ticking green), but they are defects, not test
gaps. Security-sensitive ones were deliberately NOT filed publicly — your call.

| # | defect | evidence | captured in | sensitivity |
|---|---|---|---|---|
| D1 | **Saved-report schedule routes lack an owner check** — `report-service.ts` `unscheduleReport`/`listSchedules` ignore `_context`, so user B can delete user A's report schedule (cross-owner destructive access). The read/run/delete routes ARE owner-gated (deny-as-404); only the schedule routes leak. | packages/plugins/plugin-reports/src/report-service.ts (unscheduleReport/listSchedules) | dashboards.saved-report-ownership (known-gap probe clause) | **SECURITY — not filed publicly; awaiting your decision** |
| D2 | **AppManagementPage enable/disable/set-default/delete are client-only stubs** — the handlers call `toast.success()` with a `TODO: Replace with real API call` and issue no request; an admin sees "success" while nothing changes. | objectui apps/console/src/pages/system/AppManagementPage.tsx | platform-core.app-management-toggle (expected-fail probe) | UX-integrity — safe to file |
| D3 | **`useGlobalUndo.executeOp` issues a bare `ds.update` with no `ifMatch`** — record undo can silently clobber a concurrent edit (no OCC guard on the undo path). | objectui react/src/hooks/useGlobalUndo.ts | records-forms.record-edit-undo (observe-and-flag clause) | correctness — safe to file |
| D4 | **`SharedViewLink` builds dead `/share/<object>/<view>?token=` URLs** — client-generated token, no matching console route (only `/s/:token`), no server persistence. Registered but unused. | objectui plugin-view/src/SharedViewLink.tsx | — (not an item; demo-grade) | low — file a cleanup issue |
| D5 | **List "Share" button is a no-op** — renders when `schema.sharing` is set but has no onClick. | objectui plugin-list/src/ListView.tsx | — | low — file a cleanup issue |
| D6 | **`/api/v1/datasources` admin CRUD has no route ledger** — mounted by serve.ts, absent from rest-route-ledger.ts (tranche-3 discipline gap). | packages/services/service-datasource/src/admin-routes.ts | integration-system.datasource-admin-lifecycle (source note) | low — internal discipline |
| D7 | **Parent-only PATCH does not revalidate a stale dependent child** — `evaluateOptionVisibility` skips fields absent from the payload, so changing only the parent leaves a now-invalid child value in place server-side; integrity rests entirely on the client clear. | packages/objectql/src/validation/rule-validator.ts (`!(name in data) continue`) | records-forms.cascading-multilevel-and-clear (knownGap) | integrity — safe to file |
| D8 | **Lookup cascade scope is existence-only server-side** — `assertReferencesResolve` accepts any EXISTING id regardless of `lookupFilters` scope (a cross-account contact that exists is accepted on direct POST). May be by-design (filters = UI courtesy) — needs a maintainer ruling: declared ≠ enforced, or documented courtesy. | packages/objectql/src/engine.ts (assertReferencesResolve) | records-forms.cascading-multilevel-and-clear (knownGap) | integrity/design — needs ruling |

## 2. Docs promise capabilities the runtime doesn't deliver (PD#10, docs side — file docs issues)

The capability docs advertise features that were retired or never shipped. Under Prime
Directive #10 ("never advertise a capability the runtime doesn't deliver") these are
docs bugs, not checklist items.

- **Recycle bin / soft delete** — promised in `content/docs/capabilities/{data,integrations}.mdx`,
but `enable.trash` was RETIRED ("every delete has always been a hard delete; soft delete
parked at #3146", object.zod.ts retired-key guidance). → fix docs or ship the feature.
- **Recently-visited lists** — `enable.mru` retired/never implemented. The console DOES ship a
recents rail (UnifiedSidebar) — reconcile whether the doc claim maps to that surface or a dead one.
- **TV display pages / discussion threads** — promised (analytics.mdx, build-without-code.mdx).
Discussion = the real chatter surface (now covered by records-forms.record-discussion-mentions);
display pages have no spec surface found → confirm removal or file.
- **Five data-depth scopes** (permissions.mdx) — `own_and_reports`/`unit`/`unit_and_below` are
ENTERPRISE (hierarchy-security). The open checklist correctly drives own/org only. Optional both-sides
probe: authoring an intermediate depth in the open edition must degrade LOUDLY (ADR-0049), not
silently to `own` — could become a checklist item if you want it.

## 3. Fixtures worth adding (would un-block currently-blocked items)

The 8 `blocked` items are blocked on missing stock fixtures, not on the platform. Adding
these to the showcase would make them runnable:

- a `publicSharing.enabled` object → unblocks `access-security.share-link-capability-tokens`.
- one configured OIDC/social IdP → unblocks `identity-auth.oauth-app-consent-loop`,
`linked-accounts-social`, and the existing `sso-enforced-first-paint`.
- a gantt view with `dependenciesField` + `lockField` + `parentField` → unblocks the
fixture-gated variants of `records-forms.gantt-interactions`.
- a not-auto-bound audience suggestion → unblocks the confirm/dismiss half of
`access-security.suggested-binding-loop`.
- the `IMPORT_CONSOLE_LIVE` import-harness backend → unblocks `records-forms.import-job-undo-cancel`.
- an approval-escalation clock-control/`runEscalations()` harness → unblocks `approvals.sla-escalation`.
- a second signed-up (non-admin) user in seeds, or a documented sign-up step in the runner →
removes the recurring "needs a 2nd user" knownGap on several persona-gated items.

## 4. Notes

- `PENDING-GAPS.md` (the full deduped gap register that drove the authoring) can be deleted
once you've reviewed §1–§2 above — it was scaffolding; this file is the durable residue.
- The checklist itself (`areas/*.json`, `coverage.json`, `README.md`, `RUNNER.md`,
`scripts/check-platform-checklist.mjs`) ships in this branch; this file carries the
decisions that remain with the maintainer.
Loading
Loading