design-playbook 0.7.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.
Files changed (47) hide show
  1. package/LICENSE +28 -0
  2. package/NOTICE +37 -0
  3. package/README.md +143 -0
  4. package/commands/design-io.md +8 -0
  5. package/commands/ui-review.md +8 -0
  6. package/commands/ux-spec.md +8 -0
  7. package/mcp/__init__.py +0 -0
  8. package/mcp/_transport.py +242 -0
  9. package/mcp/evidence/README.md +40 -0
  10. package/mcp/evidence/__init__.py +0 -0
  11. package/mcp/evidence/server.py +450 -0
  12. package/mcp/evidence/test_server_stdio.py +645 -0
  13. package/mcp/preview/__init__.py +0 -0
  14. package/mcp/preview/browser.py +661 -0
  15. package/mcp/preview/confirm.py +255 -0
  16. package/mcp/preview/control.py +1293 -0
  17. package/mcp/preview/i18n.py +162 -0
  18. package/mcp/preview/server.py +126 -0
  19. package/mcp/preview/test_browser_control.py +663 -0
  20. package/mcp/preview/test_server_stdio.py +630 -0
  21. package/mcp/preview/test_transaction.py +436 -0
  22. package/mcp/preview/transaction.py +536 -0
  23. package/mcp/preview/util.py +19 -0
  24. package/mcp/test_transport.py +39 -0
  25. package/package.json +42 -0
  26. package/skills/craft-guard/SKILL.md +59 -0
  27. package/skills/craft-guard/references/craft.md +29 -0
  28. package/skills/craft-guard/references/detectors.md +124 -0
  29. package/skills/design-baseline/SKILL.md +134 -0
  30. package/skills/design-baseline/agents/openai.yaml +4 -0
  31. package/skills/design-baseline/references/design-template.md +73 -0
  32. package/skills/design-baseline/references/extraction-guidance.md +39 -0
  33. package/skills/design-baseline/scripts/design_baseline.py +780 -0
  34. package/skills/design-playbook/SKILL.md +219 -0
  35. package/skills/native-craft/SKILL.md +59 -0
  36. package/skills/native-craft/references/native-feel.md +79 -0
  37. package/skills/reference-intake/SKILL.md +86 -0
  38. package/skills/reference-intake/references/contract-template.md +82 -0
  39. package/skills/ui-evaluator/SKILL.md +110 -0
  40. package/skills/ui-evaluator/references/rubric.md +45 -0
  41. package/skills/ui-picker/SKILL.md +63 -0
  42. package/skills/ui-picker/references/components.md +31 -0
  43. package/skills/ui-picker/references/design.md +21 -0
  44. package/skills/ui-picker/references/domain.md +26 -0
  45. package/skills/ui-picker/references/template.md +24 -0
  46. package/skills/ux-spec/SKILL.md +51 -0
  47. package/skills/ux-spec/references/spec-template.md +43 -0
@@ -0,0 +1,219 @@
1
+ ---
2
+ name: design-playbook
3
+ description: Orchestrate outcome-first Design I/O for product UI. Use when building or revising a page, dashboard, list, or settings surface from a short ask, or recirculating a failed design review through declarations and evidence.
4
+ ---
5
+
6
+ # design-playbook
7
+
8
+ **Design I/O** — same process every run: inject **declarations** (what good is), run **contracts** (how work enters the pipeline), **recirculate** failures to the declaration that owns them.
9
+
10
+ Not a style library. For palettes/type catalogs use other packs; here the product pipeline and acceptance are the product.
11
+
12
+ ## Run contract
13
+
14
+ Keep each control in one authoritative place:
15
+
16
+ | Control | Single source | Required content |
17
+ | --- | --- | --- |
18
+ | **Goal** | `spec` L1 | User-visible outcome, target user and scene, non-goals |
19
+ | **Success** | `spec` L6 | Observable pass/fail criteria; every top-level L6 item is `Given -> When -> Then` |
20
+ | **Evidence** | `spec` L6 + evaluator ledger | Exactly one `L6.<n>` ledger row per criterion; planning-only uses declaration coverage, implementation uses rendered states, interaction/test results, and applicable code checks |
21
+ | **Stop** | this orchestrator | Pass; smallest missing decision; unavailable required evidence or authority; repeated blocker |
22
+ | **Confirm** | this orchestrator + user decision | Any consequential action not already authorized |
23
+
24
+ Answer, review, diagnose, and plan requests end with findings or a plan. Build and fix requests continue through in-scope local edits and the most relevant available validation. Ask the smallest question only when the answer changes the goal, scope, platform, success criteria, or authority; otherwise record a conservative assumption in L1.
25
+
26
+ Pause for explicit confirmation before an external, destructive, costly, or scope-expanding action that the request did not already authorize. This includes adding a dependency, changing an API/backend/data contract, deploying or publishing, and accepting a blocking finding. When required evidence or authority is unavailable, stop with the exact blocker and the smallest next decision. If the same blocking finding survives two repair -> re-evaluate cycles without new evidence, stop recirculating and report it.
27
+
28
+ ## Steps
29
+
30
+ > **Stage-list mirror (monorepo maintainers only):** repo-root `scripts/run_status.py` → `STAGES` mirrors this section's steps and artifact filenames for status/resume narration. **Not shipped** with the installable plugin package (`packages/design-playbook/`). Plugin users ignore this pointer. If you add/remove a step or change an artifact filename here, update that monorepo table.
31
+
32
+ Do in order. Data flow:
33
+
34
+ `design-baseline? → reference-intake? → ux-spec? → plan? → (native-craft?) → ui-picker → (preview*) → fill → craft-guard → (observe*) → ui-evaluator`
35
+
36
+ - `?` = conditional entry/route
37
+ - `*` = run only when the matching MCP tool is available (`preview_prototype` for preview, `execute_capture_plan` for observe); otherwise skip
38
+ - When you skip a step, say so in one line — step name + reason + how to enable, with the gate label when one applies. Matters most for `preview*`/`observe*` adapter absence, e.g. `-> preview*: adapter absent, skipped (G5 not triggered; enable via packages/design-playbook/mcp/preview/ or host MCP)`. Other conditional skips may use the same shape; entry lines are optional (keep output lean). Narration only — not a run-contract control.
39
+ - Do not code a pretty shell until the active step’s completion criterion is met
40
+
41
+ ### 1. Entry routing
42
+
43
+ **SSOT for this decision is this skill only** (not `commands/design-io.md`).
44
+
45
+ - **Existing-product UI build/fix** (meaningful first-party pages/components/theme/styles already exist) and no usable verified baseline binding under `.scratch/<run>/design-baseline/state.json` → step **1A. `design-baseline`**
46
+ - **Reference materials present** (screenshot, design file path, URL, or product/brand analogy) and no usable `.scratch/<run>/reference/contract.md` → step **2. `reference-intake`**
47
+ - **Missing `spec`** (no usable six-layer `spec.md` for this run) → step **3. `ux-spec`**
48
+ - **Spec present** → step **4. plan** (do not re-run `ux-spec` unless structural conflict forces it)
49
+ - No meaningful existing UI, or no UI mutation in scope → skip `design-baseline` with one-line narration
50
+ - No reference materials → skip `reference-intake` with one-line narration
51
+
52
+ ### 1A. `design-baseline` (existing-product UI only)
53
+
54
+ Invoke **design-baseline** before reference intake or specification work. Call the deep module `prepare` → (user confirm/waive) → `confirm` as needed; immediately before Fill call `verify`. Discover and validate project `DESIGN.md`; if missing or incomplete, generate only run-local `DESIGN.draft.md` + `evidence.json` from first-party UI evidence and wait for confirmation before a durable write.
55
+
56
+ Gate artifact is only `.scratch/<run>/design-baseline/state.json` (`schema: design-baseline/v1`). A valid existing baseline becomes `status: ready` with `decision.kind: existing`. A generated draft requires `confirm(..., "accept")` (`ready` + `accepted`) or explicit `confirm(..., "waive", reason=...)` (`waived`). `needs_confirmation` and `ambiguous` block Fill. A draft alone is not authority.
57
+
58
+ **Done when:** **design-baseline**'s own completion criteria hold (that skill is SSOT). Smoke: `state.json` exists; candidate conflicts and stale source hashes are exposed by prepare/verify; status is `ready` or `waived`; no valid baseline was silently replaced.
59
+
60
+ ### 2. `reference-intake` (when reference materials present)
61
+
62
+ Invoke **reference-intake**. Produce `.scratch/<run>/reference/contract.md` + `manifest.json` (ADR-0011).
63
+
64
+ **Done when:** **reference-intake**'s own completion criteria hold (that skill is SSOT). Smoke: sources inventoried; observed vs inferred labeled; Keep/Change/Do not copy present for product-analogy, third-party URL, **and third-party screenshot/design**; license/brand risks recorded. Does not write `spec.md` or a decision report.
65
+
66
+ Then continue to **3. `ux-spec`** (or **4. plan** when spec already exists).
67
+
68
+ ### 3. `ux-spec` (when spec is missing)
69
+
70
+ Invoke **ux-spec**. Produce six-layer `spec.md`. When `.scratch/<run>/reference/contract.md` exists, **ux-spec** must read it first (functional constraints, non-goals, always/ask/never) — do not wait for plan.
71
+
72
+ **Done when:** **ux-spec**'s own completion criteria hold (that skill is SSOT). Smoke: L1–L6 present; L5 substantive, not "show loading"; every top-level L6 item uses ordered `Given -> When -> Then`, names its evidence, and names the capture seed where the proof is a runtime state; reference constraints folded when a contract exists.
73
+
74
+ Then continue to **4. plan**.
75
+
76
+ ### 4. plan (pipeline step — pure orchestration)
77
+
78
+ Not a run-contract control and **not** a machine gate. Does not become Goal / Success / Evidence / Stop / Confirm SSOT.
79
+
80
+ Write a light handoff at `.scratch/<run>/plan.md` (required on disk). When `.scratch/<run>/reference/contract.md` exists, the handoff must point to it (path only; do not paste the full contract) and fold its functional constraints into the description→spec map and its visual cues/exclusions into the ui-picker input pack. Minimum three blocks:
81
+
82
+ 1. **本次 run 范围** — pointers to L2 / scenes / non-goals (do not copy L1–L6 wholesale)
83
+ 2. **用户描述 → spec 映射** — which L1/L2/L6 this ask touches; unmapped items → conservative assumptions
84
+ 3. **ui-picker 输入包** — scene hints, constraints, explicit exclusions
85
+
86
+ **禁止:** paste the full spec; pre-write a decision report inside plan.
87
+
88
+ **描述 × spec 分轨:**
89
+
90
+ - **Structural conflict** (L1 outcome, L6 criteria, platform/permission/data contract, overturned non-goal) → stop; revise `ux-spec` or user Confirm of an exception recorded in plan
91
+ - **Presentation preference** (scene density, region weight, component role preference without L6 change) → put in the ui-picker input pack; `ui-picker` decides
92
+ - **Unmapped description** → mapping table as conservative assumption; do not silently edit L1
93
+
94
+ **Done when:** `plan.md` exists with the three blocks; ui-picker can consume the input pack without re-deriving scope from chat.
95
+
96
+ ### 5. Shell → conditional `native-craft` → `ui-picker`
97
+
98
+ Native desktop order: `ux-spec` → `native-craft` → `ui-picker` → `fill` → `craft-guard` → `ui-evaluator`. (Conditional entry `?` and optional adapters `preview*`/`observe*` are shown in the full sequence above, step 0.)
99
+
100
+ - Invoke **native-craft** only for an explicit native-desktop target or a request for native-feel. Web and mobile Web skip `native-craft`; a Web UI that merely resembles a desktop admin tool is still Web.
101
+ - If the target platform is unclear, ask once before choosing the route. Do not assume native desktop.
102
+ - For native desktop, require the native decision gate and render-surface seam before invoking **ui-picker**. If `native-craft` cannot load or does not produce them, stop and report what is missing; do not silently choose a Web shell.
103
+ - Pass the decision gate and seam to **ui-picker** as required shell context. The caller does not reconstruct or reinterpret them.
104
+
105
+ Invoke **ui-picker**. Map scene → template + component semantics. Read its `references/` only as that skill directs. When `.scratch/<run>/reference/contract.md` exists, pass its visual cues / exclusions (via plan input pack and/or direct path) into **ui-picker**.
106
+
107
+ **Done when:** **ui-picker**'s own completion criteria hold (that skill is SSOT). Smoke: the decision report names scene, density, template, regions, components, and risks; coding has not started before that report exists. For native desktop, it also consumes the declared render-surface seam.
108
+
109
+ `ui-picker` **stops at the decision report** — it has no preview step.
110
+
111
+ ### 6. preview* (optional external MCP adapter)
112
+
113
+ After the decision report exists, probe MCP `tools/list` for **`preview_prototype`**.
114
+
115
+ - **Absent** → skip preview; go to Fill (current behavior). No preview artifacts required.
116
+ - **Present** → run the preview loop in this orchestrator (not inside `ui-picker`):
117
+ 1. Host agent generates a disposable prototype HTML (structure-semantics floor: readable scene, named template regions, key component roles as placeholders). Path under `.scratch/<run>/preview/round-{n}.html`.
118
+ 2. Call `preview_prototype` with `path` (preferred) or `html`, plus `summary`, `round`, `report_ref` (current decision report), optional `options`.
119
+ 3. Adapter shows the prototype, collects feedback, applies the **feedback floor** (ADR-0008: non-empty feedback, OR ≥1 anchor with non-empty selector + non-empty comment), writes confirm/log under `.scratch/<run>/preview/`. A confirm that fails the floor is written with `confirmed: false` + `floor_failure` reason — it does **not** count as a confirm.
120
+ 4. On “需要修改” (or `confirmed: false` from a floor failure): append feedback to `preview/log.md`, revise the decision report in place (mark round), generate next prototype; **same blocker two repair rounds without new evidence → stop the loop and report**.
121
+ 5. On confirmed: the tool's `confirmed=true` is **not authoritative on its own** — the confirm record must also carry `floor_pass: true`. Proceed to Fill only with the confirmed + floor-passed decision report (+ plan pointers).
122
+
123
+ **Native desktop:** still run Web preview when the adapter exists; coverage is **render-surface seam and above** only. Note that limitation once in `preview/log.md`. Do not skip preview solely because the route is native (skip only when the adapter is missing).
124
+
125
+ **Hard boundary:** never copy `preview/round-*.html`, preview-only assets, fake data shells, **`reference/assets/`**, or **`reference/example.html`** (or any other file under `.scratch/<run>/reference/`) into the Fill source tree. Fill consumes report + `spec` semantics, not prototype or reference media files.
126
+
127
+ **Re-Fill signal (preview after Fill already exists):** if a Fill surface (code under the host tree or `filled-ui.*`) already exists and a later preview round **revises the decision report** (new round, structural/component change absorbed into `decision-report.md`), you **must re-Fill** (or explicitly record user acceptance that the existing Fill already matches the new report) **before** observe* / ui-evaluator. Do not run observe against a Fill that predates the current confirmed report. Log the re-Fill (or acceptance) once in `preview/log.md`.
128
+
129
+ **Done when:** either preview was skipped (no adapter), or a `confirm-round-*.json` with `confirmed: true` **and `floor_pass: true`** matches the current decision report (G5 when `validate_run.py` is given `--preview-dir` / `--decision-report`). A confirmed record without `floor_pass` fails G5 — empty/garbage feedback is a silent false-pass that must not reach Fill. When Fill already existed, the re-Fill signal above is satisfied.
130
+
131
+ ### 7. Fill
132
+
133
+ Implement structure from the decision report + `spec` + confirmed project `DESIGN.md` when bound. Prefer project tokens: visual values via `var(--*)`; missing tokens → `gaps.log` (or project equivalent), not raw hex/px/ms.
134
+
135
+ **Hard boundary (design baseline):** for existing-product UI work, do not enter Fill unless `design_baseline.verify(project_root, run_root)` succeeds with `status` `ready` (bound path + sha256) or `waived` (non-empty reason). A draft alone is not authority.
136
+
137
+ **Hard boundary (reference):** never copy `.scratch/<run>/reference/assets/`, `reference/example.html`, or third-party brand media inventoried by reference-intake into the host Fill tree. Honor Do not copy via report + `spec` only.
138
+
139
+ If a reused host component conflicts with spec L5, record the conflict and recirculate to `spec` via the authoritative map in `ui-evaluator` before choosing a minimal patch or explicit acceptance.
140
+
141
+ Load on demand (only if the fill needs them):
142
+
143
+ - domain / risk / sensitive fields → `ui-picker/references/domain.md`
144
+ - token roles / gaps → `ui-picker/references/design.md`
145
+ - component pairs → `ui-picker/references/components.md`
146
+
147
+ **Done when:** with a codebase — main flow renders and every L5 state named in the spec has a concrete UI path (not a blank region); planning-only — every L5 state has a named concrete UI landing, no blank region.
148
+
149
+ ### 8. Craft → `craft-guard`
150
+
151
+ Invoke **craft-guard**. Apply loading tiers, motion purpose, hierarchy, CJK type. For native desktop, `craft-guard` owns shared UI above the render-surface seam and defers to `native-craft` below it. If a finding crosses the seam, split it into separate point-backs to the owning declarations.
152
+
153
+ **Done when:** **craft-guard**'s own completion criteria hold (that skill is SSOT). Smoke: every wait/fail path maps to a loading tier; every animation states its purpose; L4 interactive-zone affordance resolved; residual issues handed to `ui-evaluator` with source `craft`.
154
+
155
+ ### 9. observe* (optional external MCP adapter)
156
+
157
+ After craft, probe MCP `tools/list` for **`execute_capture_plan`**.
158
+
159
+ - **Absent** → skip; `ui-evaluator` ledger `observed` stays free-text (current behavior). G6 not triggered.
160
+ - **Present** → for each L6 criterion whose proof is a runtime state, run the evidence loop in this orchestrator (not inside any skill):
161
+ 1. **Derive** a capture plan from L6 `Given -> When -> Then` (in memory, not on disk): `Given`/`When` → `state` + `actions`; `Then` → required proof (already in the ledger `required` field). Do not add or remove verification intent; L6 wins on conflict.
162
+ 2. **Execute**: call `execute_capture_plan({url, type, state, actions, artifact_path})`. The provider returns `{artifact, observed_state, result, error, written_path}` and never sees the criterion. Prefer `written_path` (absolute) when locating the file; if it points outside `.scratch/<run>/`, fix `DESIGN_PLAYBOOK_RUN_ROOT` / cwd before binding. `artifact_path` must start with `evidence/` (e.g., `evidence/empty-state.png`, not `empty-state.png`) — the provider resolves it under `<run_root>/evidence/` and refuses absolute paths, `..` segments, or anything that escapes that subtree (`mcp/evidence/server.py` `_resolve_artifact_path`); a bare filename is rejected because it would land outside the evidence subtree.
163
+ 3. **Bind** (orchestrator owns the manifest; provider never writes it). After **each** successful or failed capture, **immediately append** one line to `.scratch/<run>/evidence/manifest.jsonl` — do not batch-rewrite the file at the end. Rules:
164
+ - **`observed_state` / `result` / `error`**: copy the provider return **verbatim**. If the provider returns `unknown`, write `unknown` — never overwrite with the requested `state` (request intent lives only under `capture.state`).
165
+ - **Embedded capture snapshot**: store the full call parameters used (`url` including query string, `type`, `state`, `actions`, `artifact_path`). Omit nothing that would be needed to re-run the capture.
166
+ - **`ts`**: wall-clock of **this** capture's completion (ISO-8601). Distinct captures must not share one batch timestamp.
167
+ - Also record: criterion ref, `artifact` (run-root-relative), optional `artifact_sha256`, optional `written_path` from the provider.
168
+ 4. **Manual provider**: when no ecosystem provider is present but a human operates + screenshots to `artifact_path`, write the same-format manifest entry (`capture.provider: "manual"`); `observed_state` is what the human actually saw, not the planned label.
169
+ - v1 capture types: `screenshot` / `a11y tree` / `interaction trace`.
170
+
171
+ **Capture surface (url choice — honesty, not a machine gate):**
172
+
173
+ 1. **Prefer the live host** — running Fill surface (dev server route / real app URL) that implements the decision report.
174
+ 2. **Semantic mirror** (static HTML / fixture that only *looks like* Fill) is allowed only when the live host is unavailable or unsafe. Then **all** of:
175
+ - every manifest entry's capture snapshot includes `note` (or equivalent) with **`surface: mirror`** and a one-line reason;
176
+ - **ui-evaluator** must emit a finding (severity at least **low**) that observe used a mirror, `source` = `observe* seam` (or preview*/observe* seam), and the fix is "re-capture on live host when available";
177
+ - do **not** claim G6/process Pass as proof that the Fill tree was runtime-verified.
178
+ 3. **Mirror `data-state` (recommended):** when using a semantic mirror, set the page state the provider can read so `observed_state` is not forced to `unknown`. The evidence adapter reads `body[data-state]` or `[data-state]` (see `mcp/evidence/server.py` `_read_observed_state`). Example:
179
+
180
+ ```html
181
+ <body data-state="empty">
182
+ <!-- empty-state UI for L6 empty criterion -->
183
+ </body>
184
+ ```
185
+
186
+ Prefer one root marker that matches the capture plan's `state` intent. Still **never invent** `observed_state` in the manifest — copy the provider return verbatim (unknown stays unknown).
187
+
188
+ Evidence is captured, not judged — copy provider returns verbatim here; `pass`/`fail` authority is the evaluator's (step 9 / `ui-evaluator`). Full authority model (three ledgers: spec names what to prove, manifest what happened, evaluator what it means): SSOT `ui-evaluator` step 2.
189
+
190
+ **Done when:** either observe was skipped (no provider, ledger `observed` free-text), or each runtime-proven criterion has a manifest entry whose artifact exists (G6 when `validate_run.py` is given `--evidence-dir`); and if any capture used a mirror surface, the point-back includes the required mirror finding.
191
+
192
+ ### 10. Accept → `ui-evaluator`
193
+
194
+ Invoke **ui-evaluator**. Issues must **point back** to a declaration.
195
+
196
+ **Done when:** the report includes the criterion-shaped evidence ledger (`criterion / required / observed / result`) and findings as `issue / source / fix / severity`; the authoritative verdict completion criterion in `ui-evaluator` is met; **and** you show the user a short **run artifact index** (paths under `.scratch/<run>/`) so declaration products are discoverable — at minimum: `design-baseline/` (if triggered), `reference/` (if any), `spec.md`, `plan.md`, `decision-report.md`, `preview/` (if any), Fill surface path, `evidence/` (if any), `point-back.md`. One block is enough; do not only leave paths buried in tool logs.
197
+
198
+ Machine seam (optional local check): `python scripts/validate_run.py <spec.md> <point-back.md> [--preview-dir <preview/>] [--decision-report <report>] [--evidence-dir <evidence/>] [--run-root <run>]`.
199
+
200
+ ## Recirculate
201
+
202
+ When a finding has no owner or you need the observable -> declaration routing, use the **authoritative recirculate map in `ui-evaluator`** (do not duplicate it here). Fix only the owning layer, then resume from the step that consumes it.
203
+
204
+ ## Contracts on this pipeline
205
+
206
+ | Contract | Skill |
207
+ | --- | --- |
208
+ | Discover/validate/generate the project visual baseline | `design-baseline` |
209
+ | Reference intake (observed/inferred + Keep/Change/Do not copy) | `reference-intake` |
210
+ | Write the functional declaration | `ux-spec` |
211
+ | Choose shell + component meaning | `ui-picker` |
212
+ | Craft / feedback quality | `craft-guard` |
213
+ | Native-feel desktop declaration (render seam + conventions) | `native-craft` |
214
+ | Acceptance + point-back critique | `ui-evaluator` |
215
+
216
+ `plan`, `preview*`, and `observe*` are orchestrator steps (plus optional external MCPs for preview and observe), not rows in this table. `design-baseline?` is a conditional existing-product orchestrator gate (ADR-0012); `reference-intake?` is a conditional skill step (ADR-0011), not a machine gate.
217
+
218
+ Slash (installed plugin, namespaced): `/design-playbook:design-io` · `/design-playbook:ux-spec` · `/design-playbook:ui-review`.
219
+ With `claude --plugin-dir` the same command files apply under the plugin namespace.
@@ -0,0 +1,59 @@
1
+ ---
2
+ name: native-craft
3
+ description: Native-feel declaration for desktop apps. Use when a cross-platform desktop app (macOS+Windows) must feel indistinguishable from native. Invoked before ui-picker for native-desktop targets, not web pages.
4
+ ---
5
+
6
+ # native-craft
7
+
8
+ **Native-feel** is a Design I/O **platform declaration**: it fixes the render-surface seam and the native conventions before any web shell is picked. It is the native-desktop twin of `craft-guard` (which owns web craft); the two do not overlap.
9
+
10
+ Inspired by yetone/native-feel-skill (MIT) - see `NOTICE`. Depth lives in [`references/native-feel.md`](references/native-feel.md); do not load it until this skill applies.
11
+
12
+ ## When to apply / skip
13
+
14
+ - **Apply** when the target is a desktop app that must feel native on macOS + Windows (launcher, system utility, all-day productivity tool).
15
+ - **Skip** when: one OS only (build pure native), Electron-is-fine polish bar, internal tool, cold-start < 100 ms hard, or pure web/mobile. The decision gate in `references/native-feel.md` rules this stack **out** for those shapes - say so directly.
16
+
17
+ ## The declaration: place the seam at the rendering surface
18
+
19
+ The cross-platform boundary is drawn at the **WebView surface**, nowhere else.
20
+
21
+ - **Below the seam** - windowing, hotkeys, materials, file dialogs, tray, a11y, input methods, context menu - **native**, written twice in idiomatic Swift / C#. No abstraction is fast or correct enough.
22
+ - **Above the seam** - React tree, business logic, extension API, AI orchestration - **shared**, written once in TS.
23
+
24
+ Test every platform decision: *is this above or below the rendering surface?* Below -> write it twice. Above -> write it once. Refuse to draw the line elsewhere.
25
+
26
+ ## Native-feel tenets (cite by short name when advising)
27
+
28
+ - **Seam at render surface** - the boundary altitude where neither side can mimic the other.
29
+ - **Adopt the platform, don't compete** - the OS draws blur, scrollbars, dark mode, focus rings better than you. "Let the OS do it" *is* the implementation; custom is last resort.
30
+ - **Perception is performance** - users feel promises kept (keystroke, frame, latency), not MB/FPS. Define the perception target before optimizing.
31
+ - **Cross boundaries intentionally** - every IPC is a design decision: async, batched, schema-typed, observable. Never treat IPC like a function call.
32
+ - **Iteration loop is the product** - hot-reload (~200 ms) vs native recompile (~30 s) is 150x; the cross-platform tax buys this, protect it.
33
+
34
+ ## Pipeline integration
35
+
36
+ `native-craft` runs at **plan / shell**, before `ui-picker`:
37
+
38
+ 1. Run the decision gate (`references/native-feel.md`). If it rules native-feel **out**, stop - tell the user to use Electron / pure native / web.
39
+ 2. Declare the seam: what is native (below) vs shared TS (above).
40
+ 3. Declare native conventions (materials, cursor, windowing, keyboard, drag-drop) - the audit in `references/native-feel.md`.
41
+ 4. Hand to `ui-picker` for the shared UI shell; `craft-guard` still applies **above** the seam but must defer to native conventions below it (e.g. no `cursor: pointer`, no web toasts, OS materials).
42
+
43
+ ## Done when
44
+
45
+ - The decision gate has been run and native-feel is either confirmed in-scope or ruled out (with the reason stated).
46
+ - The seam is declared: every platform-touching feature labeled native (below) or shared (above), no "convenient" third line.
47
+ - Native conventions that block native-feel are listed for the surface in scope (cursor, context menu, materials, window controls, scroll, motion, keyboard, drag-drop).
48
+ - `craft-guard` rules above the seam and native conventions below it are reconciled - no conflict where web idiom would telegraph "web app".
49
+
50
+ ## Recirculate
51
+
52
+ | Observable | Declaration |
53
+ | --- | --- |
54
+ | App feels like a web page (cursor:pointer, web toasts, box-shadow windows) | `native-craft` conventions |
55
+ | Wrong seam (UI in native, or platform code in TS) | `native-craft` seam |
56
+ | Slow perception despite low memory | `native-craft` perception tenet |
57
+ | Used this stack for a one-OS / Electron-fine / <100ms app | `native-craft` decision gate (rule out) |
58
+
59
+ Depth: decision gate + native-conventions audit -> [`references/native-feel.md`](references/native-feel.md). Full evidence (WebView survival, IPC contract, memory truths) -> original `native-feel-skill` (user installs separately).
@@ -0,0 +1,79 @@
1
+ # native-feel - decision gate + conventions audit
2
+
3
+ Condensed in our voice from yetone/native-feel-skill (MIT). Full depth (WebView survival, IPC contract, memory truths, Raycast evidence) lives in the original skill - install it separately if you need it.
4
+
5
+ ## Decision gate - run first, rule OUT honestly
6
+
7
+ | Question | Ruled OUT (use instead) |
8
+ | --- | --- |
9
+ | One OS only? | Pure native (Swift/AppKit or C#/WinUI) - cross-platform tax not worth it |
10
+ | "Nice but Electron is fine"? | Electron + good designer - this stack's polish budget is 5-10x Electron |
11
+ | Internal tool, no end-user polish? | Web app / Electron |
12
+ | Cold start must be < 100 ms? | Pure native - WebView+Node boot floor is ~150-300 ms even prewarmed |
13
+ | Pure web or mobile? | Not this skill |
14
+
15
+ Proceed only when: macOS + Windows (optionally Linux), native-feel is a competitive differentiator, and warm-start < 50 ms / cold < 500 ms is acceptable.
16
+
17
+ ## The seam (the one structural decision)
18
+
19
+ The below/above split is declared in `SKILL.md` — that list is authoritative. This reference adds the IPC rule:
20
+
21
+ One schema for all IPC -> generate typed clients per runtime (UniFFI for Rust<->Swift/Kotlin/C#). Hand-written marshalling drifts in a sprint.
22
+
23
+ ## Native-conventions audit - the "not a web page" checklist
24
+
25
+ Each item telegraphs "web app" when wrong. None changes a benchmark; all change what the user feels. A skeptic should conclude "regular Mac/Windows app" in 30 s.
26
+
27
+ ### Input & cursor
28
+ - No `cursor: pointer` on hoverable rows. Native list rows don't change cursor.
29
+ - No text selection on chrome (labels, button text, headings) - only content areas select.
30
+ - Native context menu, not WebKit's: override `willOpenMenu` (Mac) / `CoreWebView2.ContextMenuRequested` (Win).
31
+ - No spellcheck underlines / dictionary popups / link previews on chrome.
32
+ - IME composition window at the caret, not above the WebView (test Pinyin/kana).
33
+
34
+ ### Windowing & focus
35
+ - Native window behavior: ⌘W / Alt-F4 close, ⌘M / Win+Down minimize.
36
+ - Dock/Taskbar click re-shows last window, doesn't spawn a new one.
37
+ - Settings in a native window (⌘, on Mac), not an in-app modal.
38
+ - Native `NSAlert` / `MessageBox` for confirms - no backdrop-blur modal overlays.
39
+ - OS notification center - no web toasts.
40
+ - Real title bar / chromeless region; drag works on full title bar.
41
+ - Traffic lights left (Mac) / min-max-close right (Win); Mac green = zoom not fullscreen.
42
+ - Window remembers size/position across launches, per-screen.
43
+
44
+ ### Materials & visual
45
+ - Platform material for window background: Mac `NSVisualEffectView` / `NSGlassEffectView` (Liquid Glass, macOS 26+); Win `DWMWA_SYSTEMBACKDROP_TYPE` mica / acrylic.
46
+ - Dark mode follows system, no per-frame flicker.
47
+ - Accent color follows system accent - don't hardcode brand blue.
48
+ - System font: Mac `-apple-system, BlinkMacSystemFont`; Win `'Segoe UI Variable', 'Segoe UI'`.
49
+ - No `box-shadow` for window shadows, no `border-radius` for window rounding - the OS draws those.
50
+
51
+ ### Scrolling
52
+ - Overlay scrollbars on Mac (fade out) - let WebKit do it.
53
+ - No smooth-scroll JS polyfills (`behavior: 'auto'`).
54
+ - Scroll position persists across in-window navigation.
55
+
56
+ ### Motion
57
+ - Cut between views by default - no route fades.
58
+ - Honor `prefers-reduced-motion`.
59
+ - Window resize animated by OS, not JS layout animations.
60
+ - No loading skeletons for sub-200 ms ops (web idiom) - spinner or nothing.
61
+ - No spring/bounce on simple state changes; reserve spring for grab-and-drag.
62
+
63
+ ### Keyboard
64
+ - Full keyboard nav; focus rings match platform.
65
+ - Native shortcuts (⌘F Mac, Ctrl+F Win); Escape always does something.
66
+ - Type-ahead in lists.
67
+
68
+ ### File / drag-and-drop
69
+ - Native drag-and-drop with file URLs (`NSPasteboard` / `IDataObject`), not browser drag API.
70
+ - Drop files onto dock icon opens them (`application:openFiles:`).
71
+
72
+ ## How to use this reference
73
+
74
+ 1. Run the decision gate. If ruled out, stop and say so.
75
+ 2. If in scope, declare the seam for the feature in question.
76
+ 3. Walk the audit for the surface in scope; each unchecked item is a native-feel bug.
77
+ 4. Reconcile with `craft-guard`: craft rules apply above the seam, native conventions below it. Where they'd conflict (e.g. craft says skeleton, native says no skeleton <200ms), native-feel wins below the seam.
78
+
79
+ Full depth (WebKit/WebView2 throttling fixes, IPC typing, memory measurement truths, Raycast binary evidence) -> install `native-feel-skill` and consult its `references/03-webview-survival.md`, `04-ipc-contract.md`, `05-memory-truths.md`, `07-evidence-raycast.md`.
@@ -0,0 +1,86 @@
1
+ ---
2
+ name: reference-intake
3
+ description: Reference intake contract from screenshot, URL, design file, or product analogy. Use when the user supplies visual/product reference before build, or when Keep/Change/Do not copy boundaries are missing for a design ask.
4
+ ---
5
+
6
+ # reference-intake
7
+
8
+ Turn raw reference material into a **run-local declaration input** the pipeline can consume without re-deriving from chat.
9
+
10
+ Not a style library. Not a code export. Not a Pass/Fail gate.
11
+
12
+ Authority boundary (ADR-0011):
13
+
14
+ | This skill owns | Does not own |
15
+ | --- | --- |
16
+ | Observed vs inferred evidence about the reference | `spec.md` L1→L6 |
17
+ | Keep / Change / Do not copy | decision report / Fill source |
18
+ | License and brand risk notes | evaluator verdict |
19
+
20
+ ## When to apply / skip
21
+
22
+ **Apply** when the ask includes at least one of:
23
+
24
+ - local screenshot / mock / design export path
25
+ - URL of a live product or design
26
+ - existing in-repo design artifact the user points at
27
+ - explicit product or brand analogy ("like Linear", "参考飞书设置页")
28
+
29
+ **Skip** when the ask is text-only product requirements with no reference material. Narrate once: `-> reference-intake?: no reference materials, skipped`.
30
+
31
+ ## Steps
32
+
33
+ ### 1. Inventory sources
34
+
35
+ List every reference source. For each source record:
36
+
37
+ - `kind`: `screenshot` | `url` | `design_file` | `product_analogy` | `other`
38
+ - locator (path or URL or product name)
39
+ - for files: SHA-256 when the file is readable; for URLs: the exact URL string
40
+ - `captured_at` (ISO-8601) and `tool` (how it was collected)
41
+
42
+ Write `manifest.json` under `.scratch/<run>/reference/` using the shape in [`references/contract-template.md`](references/contract-template.md). Copy durable local media into `reference/assets/` only when needed for later human review; never into the host Fill tree.
43
+
44
+ **Done when:** every cited source appears in `manifest.json` with kind + locator; file sources that exist on disk carry `sha256`.
45
+
46
+ ### 2. Separate observed from inferred
47
+
48
+ Read the sources. Fill **every required heading** from [`references/contract-template.md`](references/contract-template.md) (SSOT for section names and bullet prompts). Do not invent alternate headings.
49
+
50
+ Mark every claim as **observed** or **inferred**. Unlabeled claims are invalid; rewrite them before emit.
51
+
52
+ **Done when:**
53
+
54
+ - `contract.md` has every template heading (including always/ask/never hints and Unresolved questions)
55
+ - Keep / Change / Do not copy are each non-empty whenever any source is a product analogy, third-party URL, or third-party screenshot/design (first-party user-owned assets may put `none — first-party owned` under Do not copy only, with an ownership note)
56
+ - at least one license/brand risk line exists (`none identified` only for pure first-party assets the user owns)
57
+
58
+ ### 3. Emit and stop
59
+
60
+ Write:
61
+
62
+ ```text
63
+ .scratch/<run>/reference/contract.md
64
+ .scratch/<run>/reference/manifest.json
65
+ .scratch/<run>/reference/assets/ # optional
66
+ ```
67
+
68
+ Optional disposable `example.html` may be generated under `reference/` only as a later preview input. It is **not** a Fill source (same hard boundary as `preview/round-*.html`).
69
+
70
+ Stop. Do not write `spec.md`, do not pick components, do not implement UI.
71
+
72
+ **Done when:** both `contract.md` and `manifest.json` exist; steps 1→2 Done-when criteria still hold in the files.
73
+
74
+ ## Scope fence
75
+
76
+ | In | Out → |
77
+ | --- | --- |
78
+ | Source inventory + hashes | L6 acceptance → `ux-spec` |
79
+ | Keep / Change / Do not copy | template/component identity → `ui-picker` |
80
+ | Functional constraints derived from reference | coding / Fill |
81
+ | License and brand risk notes | visual similarity score as gate |
82
+ | | third-party skill or brand-kit port into the plugin |
83
+
84
+ ## Handoff
85
+
86
+ After emit, the orchestrator continues to `ux-spec?` (or `plan?` when spec already exists). Consumers must cite `reference/contract.md` rather than re-describing the screenshot from memory.
@@ -0,0 +1,82 @@
1
+ # Reference contract template
2
+
3
+ Emit as `.scratch/<run>/reference/contract.md`. Headings are required.
4
+
5
+ ## Source summary
6
+
7
+ - Ask (one line):
8
+ - Sources (ids matching `manifest.json`):
9
+ - Captured at (ISO-8601; may match `manifest.json` top-level `captured_at`):
10
+
11
+ ## Evidence (observed)
12
+
13
+ - ...
14
+
15
+ ## Inferred (labeled)
16
+
17
+ - ... | confidence: high|medium|low | why:
18
+
19
+ ## Keep
20
+
21
+ - ...
22
+
23
+ ## Change
24
+
25
+ - ...
26
+
27
+ ## Do not copy
28
+
29
+ - ...
30
+
31
+ ## Functional constraints for ux-spec
32
+
33
+ - Goal / scene hints:
34
+ - States or edges implied:
35
+ - Non-goals implied by Do not copy:
36
+ - always / ask / never hints:
37
+
38
+ ## Visual cues for ui-picker
39
+
40
+ - Density:
41
+ - Scene class hints:
42
+ - Region weight / hierarchy:
43
+ - Explicit exclusions:
44
+
45
+ ## License / brand risks
46
+
47
+ - ...
48
+
49
+ ## Unresolved questions
50
+
51
+ - ... (or `none`)
52
+
53
+ ---
54
+
55
+ ## manifest.json shape
56
+
57
+ Write beside this contract as `.scratch/<run>/reference/manifest.json`:
58
+
59
+ ```json
60
+ {
61
+ "schema": "design-playbook.reference.manifest/v1",
62
+ "run_id": "<run>",
63
+ "captured_at": "2026-07-22T00:00:00+08:00",
64
+ "tool": "reference-intake",
65
+ "sources": [
66
+ {
67
+ "id": "src-1",
68
+ "kind": "screenshot",
69
+ "locator": "assets/hero.png",
70
+ "sha256": "<hex or null>",
71
+ "note": "optional"
72
+ }
73
+ ]
74
+ }
75
+ ```
76
+
77
+ Rules:
78
+
79
+ - `kind` is one of: `screenshot`, `url`, `design_file`, `product_analogy`, `other`
80
+ - file locators are run-root-relative under `reference/` when copied into `assets/`
81
+ - URL locators are absolute strings; `sha256` is null for pure URLs and product analogies
82
+ - do not put host Fill paths into `sources`
@@ -0,0 +1,110 @@
1
+ ---
2
+ name: ui-evaluator
3
+ description: Run evidence-backed UI acceptance. Use after generating a page, or when the user wants a design review or recirculatable critique against declared goals and success criteria.
4
+ ---
5
+
6
+ # ui-evaluator
7
+
8
+ **Evaluator** contract: turn **declarations** into checks. Do not invent new taste standards. Every issue **points back** to a declaration.
9
+
10
+ ## Steps
11
+
12
+ ### 1. Bind declarations
13
+
14
+ Identify which of these apply to this surface (repo files, prior turns, or design-playbook defaults):
15
+ `spec` · `domain` · `craft` · `design` · `components` · `template`.
16
+
17
+ When a verified `.scratch/<run>/design-baseline/state.json` binds a baseline (`status: ready`, path from `baseline.path`), include it as the project-specific visual declaration. It can support design-drift findings but is never L6 runtime proof by itself. An explicit `status: waived` disables baseline-drift checks for that run; it does not waive `spec`, accessibility, or craft checks.
18
+
19
+ When `.scratch/<run>/reference/contract.md` exists (ADR-0011), you **may** use it as supporting context for findings about copied brand chrome, distinctive illustration, or other **Do not copy** breaches (`source` = `reference` or the owning declaration). It is **never** L6 proof and never a Pass/Fail gate by itself.
20
+
21
+ **Done when:** the check set is named; if `spec` L6 exists, every criterion and its required proof are on the list.
22
+
23
+ ### 2. Run checks
24
+
25
+ Walk every applicable row (exhaustive for bound declarations):
26
+
27
+ | Check | Source |
28
+ | --- | --- |
29
+ | Empty / loading / error / permission | `spec` |
30
+ | Risk color, secrets, dangerous ops | `domain` |
31
+ | AI slop, hierarchy, purposeless motion | `craft` |
32
+ | New surface drifts from confirmed project visual roles/patterns | bound `<binding.path>` |
33
+ | Raw hex / px / ms (unlogged) | `design` |
34
+ | Badge/Tag, Dialog/Drawer, … | `components` |
35
+ | Shell matches scene | `template` |
36
+ | Each L6 acceptance item | `spec` |
37
+ | Required proof exists for each L6 item | `spec` |
38
+
39
+ Dimension selection, 回流示例, the preview-seam health check (required when the run produced `preview/` artifacts), and the **observe\* mirror-surface** finding (required when any manifest capture notes `surface: mirror`): [`references/rubric.md`](references/rubric.md).
40
+
41
+ When `.scratch/<run>/craft-guard.md` exists, consume all eight `CRAFT-01` through `CRAFT-08` rows as supporting craft input. Detector rows are advisory: verify their rendered/source evidence and exception checks, then use the authoritative recirculate map below to choose declaration source and assign severity. A detector never decides source, severity, or verdict. Carry every `blocked` row into evaluation as a craft proof gap. Implemented UI cannot claim complete craft Pass while required rendered or source proof is blocked; planning-only work may record an explicit `N/A` rationale without claiming rendered inspection. Keep craft detector rows out of the G6 manifest and L6 evidence ledger: they are craft-stage audit records, not runtime artifacts or criterion results.
42
+
43
+ Record an evidence ledger before writing findings. Every L6 criterion has exactly one row:
44
+
45
+ ```text
46
+ criterion: L6.<n>
47
+ required: <declared proof>
48
+ observed: <artifact path, interaction, check result, or missing>
49
+ result: pass|fail|blocked|N/A
50
+ ```
51
+
52
+ `observed` is either an **artifact path** (relative to the run root, e.g. `evidence/L6.3-error.png`) when a runtime capture was bound by a manifest, or **free-text** describing a manual observation. Both are legitimate; the machine seam (G6) only validates artifact-path references. When using an artifact path, keep the path as the **leading token** of the line (e.g. `observed: evidence/L6.3-error.png`); trailing commentary is tolerated by G6 — it reads the leading token, breaking on whitespace, `(` / `(`, or `,` / `,` / `:` / `:`. Other punctuation (em dash, slashes, etc.) will be treated as part of the path, so put elaboration on a separate `note:` line for clarity when unsure.
53
+
54
+ Evidence is captured, not judged. A manifest entry records that an artifact was collected at a state — it does not say the criterion passed. `pass`/`fail` is this evaluator's verdict against `required` vs `observed`; a screenshot can prove a criterion false. Three ledgers, each one authority: `spec` L6 names **what to prove**; the manifest records **what happened**; this ledger decides **what it means**. Providers produce artifacts; the manifest binds them to criteria; the evaluator decides.
55
+
56
+ For implemented UI, visible-state proof is a rendered inspection at the declared target viewport; behavior proof is an interaction trace or automated check; code-health proof is the relevant available test, type/lint, or affected build result. Planning-only proof is declaration coverage and must not claim a render or test occurred. Non-L6 declaration checks may be supporting observations or findings; they do not enter the machine ledger.
57
+
58
+ **Done when:** every bound row was considered; every L6 criterion has exactly one non-empty `criterion / required / observed / result` row keyed as `L6.<n>`; results use only `pass|fail|blocked|N/A`; unavailable required proof is `blocked`, not skipped.
59
+
60
+ ### 3. Emit point-back findings
61
+
62
+ ```text
63
+ issue: <observable>
64
+ source: <declaration>
65
+ fix: <next edit>
66
+ severity: high (blocking)|high|med|low
67
+ ```
68
+
69
+ Order: **blocking** first (broken L5/L6, unsafe dangerous ops, removed focus rings), then polish.
70
+
71
+ **Done when:** every finding has all four fields; no “generally improve the design” lines.
72
+
73
+ ### 4. Verdict
74
+
75
+ - Emit exactly one `## Verdict` section containing exactly one anchored verdict: `Pass` or `Recirculate`.
76
+ - **Pass:** zero blocking; every L6 criterion has exactly one evidence row; every required evidence row passes (every evidence result is `pass`); token gaps are logged or fixed.
77
+ - **Recirculate:** each blocking `source` names the step/declaration to reopen in design-playbook; `fail` or `blocked` evidence remains visible.
78
+
79
+ For a repaired blocker, record exactly one closure line whose issue text is identical to the finding:
80
+
81
+ ```text
82
+ - closes: <exact issue value> -> recirculate -> fix -> re-eval -> 0 blocking
83
+ ```
84
+
85
+ **Done when:** the explicit verdict is structurally unique; blocking sources are non-empty; every blocking finding has exactly one matching closure before `Pass`. A blocking finding cannot be waived inside a Pass artifact. Without a user in the loop, blocking findings remain in recirculate and the run requests a decision; only after an explicit user decision that updates the owning declaration or severity — recorded against the user's statement or decision record — may the evaluator re-evaluate; the final Pass artifact contains no blocking severity.
86
+
87
+ The artifact shape behind this verdict is machine-checkable: `scripts/validate_run.py` gates L1-L6, ordered `Given -> When -> Then` in every top-level L6 item, one non-empty four-field evidence row per `L6.<n>`, allowed evidence results, all-pass evidence for `Pass`, four non-empty finding fields, one explicit verdict, and one exact issue-linked closure per blocking finding. These checks are the completion criteria above, not extra prose.
88
+
89
+ ## Recirculate map (authoritative)
90
+
91
+ Single source of truth for the observable -> declaration routing. The orchestrator and other skills point here; do not duplicate it.
92
+
93
+ | Observable | Declaration |
94
+ | --- | --- |
95
+ | Happy path only; empty/fail/auth missing | `spec` |
96
+ | Wrong business meaning / risk / secrets | `domain` |
97
+ | AI slop, flat hierarchy, purposeless motion | `craft` |
98
+ | New UI visually conflicts with confirmed existing-product baseline | bound `<binding.path>` |
99
+ | Scattered hex/px/ms | `design` |
100
+ | Badge↔Tag, Dialog↔Drawer mixups | `components` |
101
+ | Wrong page shell (e.g. list as card wall) | `template` |
102
+ | Desktop app feels like a web page / wrong seam | `native-craft` |
103
+ | Copied third-party brand / Do not copy breach | `reference` (supporting) → fix in Fill / re-intake |
104
+ | Critique with no owner | re-run `ui-evaluator` |
105
+
106
+ Fix only the owning layer, then resume from the pipeline step that consumes it.
107
+
108
+ ## Guard
109
+
110
+ Prefer positive fixes in `fix`. Reserve bans for non-negotiables (e.g. open dangerous action without confirm) and always pair with the required behavior.