orchestrix-skills 0.6.0 → 0.8.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.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "orchestrix-skills",
3
- "version": "0.1.0",
3
+ "version": "0.7.0",
4
4
  "description": "Capability-first AI development skill graph (Anthropic-native): plan → build with a warm-context orchestrator, contract-wired skills, and independent verification.",
5
5
  "author": "Orchestrix",
6
6
  "homepage": "https://orchestrix-mcp.youlidao.ai",
package/README.md CHANGED
@@ -33,7 +33,9 @@ the build loop runs lights-out, gated only by objective `verify`.
33
33
 
34
34
  ```bash
35
35
  npx orchestrix-skills install # default: Claude Code (.claude/skills/)
36
+ npx orchestrix-skills install --ide codex # Codex (.codex/skills/ + AGENTS.md)
36
37
  npx orchestrix-skills install --ide cursor
38
+ npx orchestrix-skills doctor --ide codex # validate an installation
37
39
  ```
38
40
 
39
41
  This copies the skills into your runtime's skills dir and scaffolds `knowledge/`
@@ -44,6 +46,26 @@ your own API key. No server, no license.
44
46
  For Claude Code you can also install via the plugin marketplace:
45
47
  `/plugin install orchestrix-skills`.
46
48
 
49
+ ## Runtime adapters
50
+
51
+ `skills/` is the runtime-neutral source of truth. `adapters/` describes how a
52
+ runtime maps generic capabilities to its tools. During a Codex install the CLI
53
+ removes Claude-only `allowed-tools`, preserves the Orchestrix contract, and adds
54
+ Codex runtime guidance. If the target has an unmanaged `AGENTS.md`, its content
55
+ is left untouched and the guidance is placed at `.codex/orchestrix/AGENTS.md`
56
+ for manual merging. Installer-created guidance uses a marked Orchestrix block,
57
+ so later installs refresh that block while preserving surrounding user
58
+ instructions.
59
+
60
+ `doctor` validates every installed Skill's required frontmatter, Codex
61
+ transformation, configuration shape, knowledge directory, and active root
62
+ guidance. A reference file that has not been merged into an existing root
63
+ `AGENTS.md` is reported as unhealthy rather than silently treated as active.
64
+
65
+ Codex uses isolated agents when the active session exposes them and otherwise
66
+ runs leaf skills sequentially. Cursor and Windsurf remain reference-rule
67
+ installs until those runtimes provide native compatible skill execution.
68
+
47
69
  ## How it works
48
70
 
49
71
  - **Every skill is a standard [Anthropic Agent Skill](https://agentskills.io/specification)**
@@ -0,0 +1,13 @@
1
+ {
2
+ "name": "claude",
3
+ "skillsDir": ".claude/skills",
4
+ "projectInstructions": "CLAUDE.md",
5
+ "nativeSkills": true,
6
+ "capabilities": {
7
+ "filesystem.read": "Read, Grep, Glob",
8
+ "filesystem.write": "Write, Edit",
9
+ "shell.execute": "Bash",
10
+ "web.read": "WebSearch, WebFetch",
11
+ "agent.spawn": "Task"
12
+ }
13
+ }
@@ -0,0 +1,11 @@
1
+ <!-- orchestrix:start -->
2
+ # Orchestrix runtime guidance
3
+
4
+ - Use the skills installed under `.codex/skills/`; start end-to-end work with `orchestrate`.
5
+ - Treat each skill's `metadata.contract` as Orchestrix workflow data. Codex skill selection still depends on `name` and `description`.
6
+ - Resolve logical knowledge and work namespaces through `core-config.yaml`.
7
+ - Map capability names in `metadata.requires.capabilities` to the tools available in the current Codex session.
8
+ - When isolated agents are available, dispatch independent leaf skills concurrently and await them. Otherwise execute leaf skills sequentially in the current context, reading only their declared inputs before each step.
9
+ - Independently run every objective verification command. Never accept an agent's success report as proof.
10
+ - Keep runtime evidence at the fixed `.orchestrate/` path described by the `orchestrate` skill.
11
+ <!-- orchestrix:end -->
@@ -0,0 +1,13 @@
1
+ {
2
+ "name": "codex",
3
+ "skillsDir": ".codex/skills",
4
+ "projectInstructions": "AGENTS.md",
5
+ "nativeSkills": true,
6
+ "capabilities": {
7
+ "filesystem.read": "runtime file tools or shell",
8
+ "filesystem.write": "runtime patch/edit tools",
9
+ "shell.execute": "runtime shell tool",
10
+ "web.read": "runtime web tools when enabled",
11
+ "agent.spawn": "optional collaboration tools; otherwise sequential fallback"
12
+ }
13
+ }
package/bin/install.js CHANGED
@@ -2,20 +2,31 @@
2
2
  // orchestrix-skills installer — zero dependencies.
3
3
  // Free path: copy skills into the runtime's skills dir + scaffold knowledge/.
4
4
  // No MCP, no license. Premium (hosted orchestrator / KB / 建造中心) is a separate opt-in.
5
- import { cpSync, existsSync, mkdirSync, readdirSync } from "node:fs";
5
+ import { cpSync, existsSync, mkdirSync, readFileSync, readdirSync, rmSync, statSync, writeFileSync } from "node:fs";
6
6
  import { dirname, join } from "node:path";
7
7
  import { fileURLToPath } from "node:url";
8
8
 
9
9
  const PKG = join(dirname(fileURLToPath(import.meta.url)), "..");
10
+ const MANAGED_START = "<!-- orchestrix:start -->";
11
+ const MANAGED_END = "<!-- orchestrix:end -->";
12
+ // Written into the skills dir after every successful install. Two consumers:
13
+ // this installer (prunes skills it placed that the package no longer ships) and
14
+ // hosts that auto-upgrade projects (compare `version` against the npm dist-tag
15
+ // to decide whether to reinstall — no second version constant to maintain).
16
+ const STAMP = ".orchestrix-skills.json";
10
17
 
11
18
  // Where each runtime auto-loads skills from (relative to the target project).
12
- const IDE_SKILLS_DIR = {
13
- claude: ".claude/skills",
14
- codex: ".codex/skills",
19
+ function adapter(name) {
20
+ return JSON.parse(readFileSync(join(PKG, "adapters", name, "runtime.json"), "utf8"));
21
+ }
22
+
23
+ const RUNTIMES = {
24
+ claude: adapter("claude"),
25
+ codex: adapter("codex"),
15
26
  // Cursor / Windsurf don't auto-load Anthropic skills; they read rule files.
16
27
  // For those, skills are copied as reference rules (best-effort) until native support lands.
17
- cursor: ".cursor/rules/orchestrix",
18
- windsurf: ".windsurf/rules/orchestrix",
28
+ cursor: { skillsDir: ".cursor/rules/orchestrix", nativeSkills: false },
29
+ windsurf: { skillsDir: ".windsurf/rules/orchestrix", nativeSkills: false },
19
30
  };
20
31
 
21
32
  function arg(name, fallback) {
@@ -28,6 +39,7 @@ function help() {
28
39
 
29
40
  Usage:
30
41
  npx orchestrix-skills install [--ide claude|codex|cursor|windsurf] [--dir <project>]
42
+ npx orchestrix-skills doctor [--ide claude|codex|cursor|windsurf] [--dir <project>]
31
43
 
32
44
  What it does (free, no license):
33
45
  1. Copies the skills into your runtime's skills dir (default: .claude/skills/)
@@ -37,21 +49,140 @@ Premium (hosted orchestrator, KB hosting, teams, 建造中心):
37
49
  see https://orchestrix-mcp.youlidao.ai`);
38
50
  }
39
51
 
52
+ function transformSkill(source, runtime) {
53
+ if (runtime !== "codex") return source;
54
+ return source
55
+ .replace(/^allowed-tools:.*\n/m, "")
56
+ .replace(
57
+ /^(---\n\n# )/m,
58
+ "---\n\n<!-- Codex adapter: tool access is governed by the active session. Map metadata.requires capabilities to available tools. -->\n\n# ",
59
+ );
60
+ }
61
+
62
+ function packageVersion() {
63
+ return JSON.parse(readFileSync(join(PKG, "package.json"), "utf8")).version;
64
+ }
65
+
66
+ function readStamp(target) {
67
+ try {
68
+ return JSON.parse(readFileSync(join(target, STAMP), "utf8"));
69
+ } catch {
70
+ return null; // absent, or written by a version that predates stamping
71
+ }
72
+ }
73
+
74
+ function installSkills(dir, runtimeName, runtime) {
75
+ const target = join(dir, runtime.skillsDir);
76
+ mkdirSync(target, { recursive: true });
77
+ const previous = readStamp(target);
78
+ const entries = readdirSync(join(PKG, "skills"), { withFileTypes: true });
79
+ for (const entry of entries) {
80
+ const source = join(PKG, "skills", entry.name);
81
+ const destination = join(target, entry.name);
82
+ if (!entry.isDirectory()) {
83
+ cpSync(source, destination);
84
+ continue;
85
+ }
86
+ mkdirSync(destination, { recursive: true });
87
+ for (const file of readdirSync(source, { withFileTypes: true })) {
88
+ if (file.isFile() && file.name === "SKILL.md") {
89
+ writeFileSync(join(destination, file.name), transformSkill(readFileSync(join(source, file.name), "utf8"), runtimeName));
90
+ } else {
91
+ cpSync(join(source, file.name), join(destination, file.name), { recursive: true });
92
+ }
93
+ }
94
+ }
95
+
96
+ const names = entries.filter((entry) => entry.isDirectory()).map((entry) => entry.name);
97
+ // Retire skills a PREVIOUS install of this package placed that it no longer
98
+ // ships. Only names recorded in our own stamp are candidates, so a skill the
99
+ // project or its host installed alongside ours is never touched.
100
+ let pruned = 0;
101
+ for (const name of previous?.skills ?? []) {
102
+ if (names.includes(name)) continue;
103
+ const stale = join(target, name);
104
+ if (!isDirectory(stale)) continue;
105
+ rmSync(stale, { recursive: true, force: true });
106
+ pruned += 1;
107
+ }
108
+ // Stamp LAST: a crash mid-copy leaves the older stamp in place, so the next
109
+ // run still sees a mismatch and reinstalls rather than declaring itself current.
110
+ writeFileSync(
111
+ join(target, STAMP),
112
+ `${JSON.stringify({ version: packageVersion(), ide: runtimeName, skills: names }, null, 2)}\n`,
113
+ );
114
+ return { count: names.length, pruned };
115
+ }
116
+
117
+ function installRuntimeGuidance(dir, runtimeName) {
118
+ if (runtimeName !== "codex") return;
119
+ const source = join(PKG, "adapters", "codex", "AGENTS.md");
120
+ const rootInstructions = join(dir, "AGENTS.md");
121
+ const referenceTarget = join(dir, ".codex", "orchestrix", "AGENTS.md");
122
+ mkdirSync(dirname(referenceTarget), { recursive: true });
123
+ cpSync(source, referenceTarget);
124
+ const guidance = readFileSync(source, "utf8");
125
+ if (!existsSync(rootInstructions)) {
126
+ writeFileSync(rootInstructions, guidance);
127
+ console.log("✓ AGENTS.md created with Codex runtime guidance");
128
+ } else {
129
+ const current = readFileSync(rootInstructions, "utf8");
130
+ const start = current.indexOf(MANAGED_START);
131
+ const end = current.indexOf(MANAGED_END);
132
+ if (start !== -1 && end > start) {
133
+ const updated = `${current.slice(0, start)}${guidance.trimEnd()}${current.slice(end + MANAGED_END.length)}`;
134
+ writeFileSync(rootInstructions, updated);
135
+ console.log("✓ Orchestrix block refreshed in AGENTS.md");
136
+ } else {
137
+ console.log("• AGENTS.md exists — left untouched; merge .codex/orchestrix/AGENTS.md to activate guidance");
138
+ }
139
+ }
140
+ }
141
+
142
+ function isFile(path) {
143
+ try {
144
+ return statSync(path).isFile();
145
+ } catch {
146
+ return false;
147
+ }
148
+ }
149
+
150
+ function isDirectory(path) {
151
+ try {
152
+ return statSync(path).isDirectory();
153
+ } catch {
154
+ return false;
155
+ }
156
+ }
157
+
158
+ function nonemptyFile(path) {
159
+ return isFile(path) && readFileSync(path, "utf8").trim().length > 0;
160
+ }
161
+
162
+ function validSkill(path, runtimeName, expectedName) {
163
+ if (!nonemptyFile(path)) return false;
164
+ const content = readFileSync(path, "utf8");
165
+ const frontmatter = content.match(/^---\n([\s\S]*?)\n---/);
166
+ if (!frontmatter) return false;
167
+ if (!new RegExp(`^name:\\s*${expectedName}\\s*$`, "m").test(frontmatter[1])) return false;
168
+ if (!/^description:\s*\S.+$/m.test(frontmatter[1])) return false;
169
+ return runtimeName !== "codex" || !/^allowed-tools:/m.test(frontmatter[1]);
170
+ }
171
+
40
172
  function install() {
41
173
  const ide = arg("ide", "claude");
42
174
  const dir = arg("dir", process.cwd());
43
- const rel = IDE_SKILLS_DIR[ide];
44
- if (!rel) {
45
- console.error(`Unknown --ide "${ide}". Options: ${Object.keys(IDE_SKILLS_DIR).join(", ")}`);
175
+ const runtime = RUNTIMES[ide];
176
+ if (!runtime) {
177
+ console.error(`Unknown --ide "${ide}". Options: ${Object.keys(RUNTIMES).join(", ")}`);
46
178
  process.exit(1);
47
179
  }
48
180
 
49
181
  // 1. Skills (capabilities) — always refreshed.
50
- const skillsTarget = join(dir, rel);
51
- mkdirSync(skillsTarget, { recursive: true });
52
- cpSync(join(PKG, "skills"), skillsTarget, { recursive: true });
53
- const count = readdirSync(join(PKG, "skills"), { withFileTypes: true }).filter((d) => d.isDirectory()).length;
54
- console.log(`✓ ${count} skills → ${rel}/`);
182
+ const { count, pruned } = installSkills(dir, ide, runtime);
183
+ console.log(`✓ ${count} skills ${runtime.skillsDir}/ (v${packageVersion()})`);
184
+ if (pruned > 0) console.log(`✓ ${pruned} retired skill(s) removed`);
185
+ installRuntimeGuidance(dir, ide);
55
186
 
56
187
  // 2. Knowledge (the brain) — scaffold only if absent; never clobber the user's brain.
57
188
  const knowledgeTarget = join(dir, "knowledge");
@@ -77,6 +208,52 @@ function install() {
77
208
  console.log(`\nDone. Start with the "orchestrate" skill. Premium hosting: https://orchestrix-mcp.youlidao.ai`);
78
209
  }
79
210
 
211
+ function doctor() {
212
+ const ide = arg("ide", "claude");
213
+ const dir = arg("dir", process.cwd());
214
+ const runtime = RUNTIMES[ide];
215
+ if (!runtime) {
216
+ console.error(`Unknown --ide "${ide}". Options: ${Object.keys(RUNTIMES).join(", ")}`);
217
+ process.exitCode = 1;
218
+ return;
219
+ }
220
+ const skillsTarget = join(dir, runtime.skillsDir);
221
+ const expectedSkills = readdirSync(join(PKG, "skills"), { withFileTypes: true })
222
+ .filter((entry) => entry.isDirectory())
223
+ .map((entry) => entry.name);
224
+ const configPath = join(dir, "core-config.yaml");
225
+ const configContent = nonemptyFile(configPath) ? readFileSync(configPath, "utf8") : "";
226
+ const checks = [
227
+ ["skills directory", isDirectory(skillsTarget), skillsTarget],
228
+ [
229
+ `all ${expectedSkills.length} skills valid`,
230
+ expectedSkills.every((name) => validSkill(join(skillsTarget, name, "SKILL.md"), ide, name)),
231
+ skillsTarget,
232
+ ],
233
+ ["core config valid", /(^|\n)knowledge:\s*(#.*)?\n/.test(configContent) && /(^|\n)work:\s*(#.*)?\n/.test(configContent), configPath],
234
+ ["knowledge brain", isDirectory(join(dir, "knowledge")), join(dir, "knowledge")],
235
+ ];
236
+ if (ide === "codex") {
237
+ const reference = join(dir, ".codex", "orchestrix", "AGENTS.md");
238
+ const rootInstructions = join(dir, "AGENTS.md");
239
+ const referenceContent = nonemptyFile(reference) ? readFileSync(reference, "utf8") : "";
240
+ const rootContent = nonemptyFile(rootInstructions) ? readFileSync(rootInstructions, "utf8") : "";
241
+ checks.push(["Codex guidance reference", referenceContent.includes(MANAGED_START) && referenceContent.includes(MANAGED_END), reference]);
242
+ checks.push(["root AGENTS guidance active", rootContent.includes(MANAGED_START) && rootContent.includes(MANAGED_END), rootInstructions]);
243
+ }
244
+ let healthy = true;
245
+ for (const [label, ok, path] of checks) {
246
+ healthy &&= ok;
247
+ console.log(`${ok ? "✓" : "✗"} ${label}: ${path}`);
248
+ }
249
+ if (healthy) console.log(`\nHealthy: ${ide} installation is complete.`);
250
+ else {
251
+ console.error(`\nUnhealthy: run "orchestrix-skills install --ide ${ide} --dir ${dir}".`);
252
+ process.exitCode = 1;
253
+ }
254
+ }
255
+
80
256
  const cmd = process.argv[2];
81
257
  if (cmd === "install") install();
258
+ else if (cmd === "doctor") doctor();
82
259
  else help();
package/package.json CHANGED
@@ -1,14 +1,18 @@
1
1
  {
2
2
  "name": "orchestrix-skills",
3
- "version": "0.6.0",
3
+ "version": "0.8.0",
4
4
  "description": "Capability-first AI development skill graph — Anthropic-native skills that run in any agent runtime.",
5
5
  "type": "module",
6
6
  "bin": {
7
7
  "orchestrix-skills": "bin/install.js"
8
8
  },
9
+ "scripts": {
10
+ "test": "node --test"
11
+ },
9
12
  "files": [
10
13
  "skills",
11
14
  "project-scaffold",
15
+ "adapters",
12
16
  "bin",
13
17
  ".claude-plugin"
14
18
  ],
@@ -13,13 +13,23 @@ typography:
13
13
  typeface: "" # named (not Inter/Roboto unless deliberate + justified)
14
14
  scale: [] # real sizes/weights, e.g. [12/400, 14/400, 16/500, 24/600, 40/700]
15
15
  line_height: ""
16
- color: # roles, not framework swatches
16
+ measure: "" # target line length for running text, e.g. 65ch
17
+ color: # roles, not framework swatches. THIS BLOCK IS THE LIGHT PALETTE.
17
18
  bg: ""
18
19
  surface: ""
19
20
  text: ""
20
- accent: ""
21
- states: { success: "", warning: "", error: "" }
22
- contrast_floor: "" # e.g. WCAG AA 4.5:1 body text
21
+ accent: "" # the one brand hue. Never used to signal state.
22
+ states: { success: "", warning: "", error: "" } # semantic; must read as distinct from accent
23
+ contrast_floor: "" # e.g. WCAG AA 4.5:1 body text, 3:1 UI + graphics
24
+ theme:
25
+ modes: "" # light | dark | both. "both" = both are designed and reviewed, not inverted.
26
+ selection: "" # how a mode is chosen: OS preference | explicit user toggle | both
27
+ dark: # required when modes includes dark. Same roles, re-picked — never a naive inversion.
28
+ bg: ""
29
+ surface: ""
30
+ text: ""
31
+ accent: "" # must still meet contrast_floor on the dark ground
32
+ states: { success: "", warning: "", error: "" }
23
33
  space:
24
34
  scale: [] # e.g. [4, 8, 12, 16, 24, 32, 48, 64]
25
35
  density: "" # tight | airy, tied to the product
@@ -27,6 +37,9 @@ motion:
27
37
  timing: "" # e.g. 150ms enter / 100ms exit
28
38
  easing: ""
29
39
  use_where: "" # and where motion is deliberately absent
40
+ reduced_motion: "" # what prefers-reduced-motion removes, and what must still work without it
41
+ focus:
42
+ visible_style: "" # the keyboard focus indicator every interactive element carries
30
43
 
31
44
  provenance: { source: design-system, added: "", approved_by: "" }
32
45
  ```
package/skills/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # Skills (v0.1)
1
+ # Skills
2
2
 
3
3
  Capability-oriented skills, not role-oriented agents. Each skill is a single
4
4
  capability with a clear contract. The `orchestrate` skill wires them together by
@@ -14,6 +14,12 @@ SDK runtime reads `name` + `description` + `allowed-tools` and ignores
14
14
  `metadata.contract`, so each skill runs anywhere. Our orchestrator additionally
15
15
  reads `metadata.contract` to wire, gate, and verify.
16
16
 
17
+ Runtime-neutral capability requirements live under
18
+ `metadata.requires.capabilities` using names such as `filesystem.read`,
19
+ `filesystem.write`, `shell.execute`, `web.read`, and optional `agent.spawn?`.
20
+ Adapters map those names to runtime tools. Runtime-specific tool names are not
21
+ part of the orchestration contract.
22
+
17
23
  ### The contract (6 fields)
18
24
 
19
25
  | Field | Meaning |
@@ -25,6 +31,16 @@ reads `metadata.contract` to wire, gate, and verify.
25
31
  | `verify` | Objective, automated success check. Never skipped. |
26
32
  | `accept` | Subjective human sign-off: `{ when, timing }`. |
27
33
 
34
+ **Optional entries** in `inputs` / `reads` / `updates` carry a trailing `?` and
35
+ **must be quoted** — `"qa_feedback?"`, not `qa_feedback?`. A bare `?` inside a
36
+ YAML flow sequence is not valid YAML; permissive loaders accept it, strict ones
37
+ reject the whole file.
38
+
39
+ **Names are semantic, not literal.** `inputs` name the role a skill needs
40
+ (`story`, `diff`, `spec`); `outputs` name the artifact produced
41
+ (`stories/<slug>.md`, `code_diff`). The orchestrator matches them by meaning —
42
+ do not expect string equality when reading the graph.
43
+
28
44
  `accept.timing`:
29
45
 
30
46
  - `deferred` (default) — batch the human check at the end (lights-out middle).
@@ -4,6 +4,8 @@ description: Use before any build, when an idea or intent must become an agreed,
4
4
  license: MIT
5
5
  allowed-tools: [Read, Write, Grep, Glob]
6
6
  metadata:
7
+ requires:
8
+ capabilities: [filesystem.read, filesystem.write]
7
9
  contract:
8
10
  inputs: [intent, project_context]
9
11
  reads: [taste/*, architecture/*, registry/*]
@@ -4,6 +4,8 @@ description: Use when verified work is ready to be recorded in version control.
4
4
  license: MIT
5
5
  allowed-tools: [Read, Bash]
6
6
  metadata:
7
+ requires:
8
+ capabilities: [filesystem.read, shell.execute]
7
9
  contract:
8
10
  inputs: [verified_changes, message_intent]
9
11
  reads: []
@@ -4,6 +4,8 @@ description: Use when an ACCEPTED deliverable must be shipped to a live environm
4
4
  license: MIT
5
5
  allowed-tools: [Read, Bash]
6
6
  metadata:
7
+ requires:
8
+ capabilities: [filesystem.read, shell.execute]
7
9
  contract:
8
10
  inputs: [accepted_deliverable, target]
9
11
  reads: [registry/deploy]
@@ -4,11 +4,13 @@ description: Use when a feature needs a new or changed architectural decision
4
4
  license: MIT
5
5
  allowed-tools: [Read, Write, Grep, Glob]
6
6
  metadata:
7
+ requires:
8
+ capabilities: [filesystem.read, filesystem.write]
7
9
  contract:
8
10
  inputs: [requirement, system_context]
9
11
  reads: [architecture/*, registry/api, registry/db]
10
12
  outputs: [specs/<slug>-arch.md]
11
- updates: [architecture/decisions, registry/api?, registry/db?]
13
+ updates: [architecture/decisions, "registry/api?", "registry/db?"]
12
14
  authority: "Write to the specs namespace (default docs/specs/) and update the architecture KB / registry. No source code, no production."
13
15
  verify: "States the alternatives and the rationale; consistent with existing decisions (or explicitly supersedes one); no placeholder."
14
16
  accept:
@@ -4,12 +4,14 @@ description: Use after a UI feature is built and running, before merging, to rev
4
4
  license: MIT
5
5
  allowed-tools: [Read, Bash, Grep, Glob]
6
6
  metadata:
7
+ requires:
8
+ capabilities: [filesystem.read, shell.execute]
7
9
  contract:
8
- inputs: [built_ui, ui_spec]
10
+ inputs: [built_ui, ui_spec, "screenshots?"]
9
11
  reads: [taste/design-system, taste/brand]
10
12
  outputs: [design_review_report]
11
- authority: "Read-only on code; render and screenshot the running UI. No edits, no commits, no production."
12
- verify: "Report is based on actual rendered screens (screenshots), not inferred from source; contains both verdicts (spec compliance AND visual quality)."
13
+ authority: "Read-only on code. Start and stop the app locally to render it, and drive it via browser automation or HTTP. No edits, no commits, no deploy, no external spend."
14
+ verify: "Every verdict rests on a rendered artifact, not on source; each objective check reports pass, fail, or untested-with-reason — never a pass without its evidence; contains all three verdicts (spec compliance, rendering + accessibility, visual quality); any process this skill started was cleaned up."
13
15
  accept:
14
16
  when: "never — findings route back to implement; the human reviews at the end batch."
15
17
  timing: deferred
@@ -18,45 +20,153 @@ metadata:
18
20
  # Design Review
19
21
 
20
22
  Review a built UI with a senior designer's eye. The visual counterpart of
21
- `review-code`: two verdicts, in order — does it match the spec, then is it
22
- world-class.
23
+ `review-code`: three verdicts, in order — does it match the spec, does it
24
+ actually render correctly, and is it world-class.
23
25
 
24
26
  **Core principle:** You cannot review design from source. Render it and look at
25
27
  the pixels. A claim about how it looks, without a screenshot, is a guess — the
26
28
  visual equivalent of claiming tests pass without running them.
27
29
 
30
+ ## The Iron Law
31
+
32
+ ```
33
+ UNRENDERED IS NOT PASSED. Every check ends in exactly one of:
34
+ pass (with evidence) | fail (with evidence) | untested (with the reason)
35
+ ```
36
+
37
+ A review run without a browser is a partial review, and says so. It is never a
38
+ clean one.
39
+
28
40
  ## Inputs
29
41
 
30
- - `built_ui` — the running app (url / dev server / screenshots of each screen).
31
- - `ui_spec` — `specs/<slug>-ui.md` it must satisfy.
42
+ - `built_ui` — the running app (a live URL, or a dev server this skill starts).
43
+ - `ui_spec` — `specs/<slug>-ui.md` it must satisfy, including its declared
44
+ **treatment** (`utility` / `product` / `editorial`).
45
+ - `screenshots?` — captures a prior `smoke-test` already took. Use them instead
46
+ of relaunching the app for the same screen.
32
47
  - Read `taste/design-system` + `taste/brand` — the bar to calibrate against.
33
48
  Deviations from the system are higher severity, not lower.
34
49
 
35
- ## Look at the real thing first
36
-
37
- Render every screen and state in the spec (loading, empty, error too). Capture
38
- screenshots. Review those, not the CSS.
50
+ ## How to render (do this before any verdict)
51
+
52
+ Prefer what already exists: if `screenshots` covers a screen, do not relaunch
53
+ the app for it. When you must launch it yourself, follow the same protocol
54
+ `smoke-test` uses — it is the same hazard.
55
+
56
+ 1. **Discover how to run it.** `registry/app` first; else the project's manifest
57
+ (`package.json` scripts, `Makefile`, `README`). Genuinely unguessable → every
58
+ render-dependent check is `untested: cannot launch`. Do not invent a server.
59
+ 2. **Launch in the background, capture logs** to
60
+ `.orchestrate/verify/design-review-server.log`. Record the PID. Pick a free
61
+ port if configurable, to avoid colliding with anything already running.
62
+ 3. **Wait for readiness, bounded** — poll the health endpoint / port / ready
63
+ line for up to ~60s. Not ready → the checks are `failed: app did not start`,
64
+ attach the server log, skip to cleanup.
65
+ 4. **Render** every screen and state in the spec (loading, empty, error too), in
66
+ every mode under the system's `theme.modes`, with the best driver available:
67
+ - **Browser automation** (a Playwright/Chrome MCP tool, if available in this
68
+ session) — the only driver that can reach computed style, keyboard focus,
69
+ a media-query override, or a viewport change.
70
+ - **Handed-in screenshots** — enough for layout, hierarchy, contrast, and the
71
+ greyscale check; not enough for focus, computed font, or media queries.
72
+ - **No driver at all** — state it once, mark the dependent checks `untested`,
73
+ and still deliver Verdict 1 and every judgment call the spec supports.
74
+ 5. **Capture evidence per screen** to
75
+ `.orchestrate/verify/design-review-<screen>-<mode>.png` (or `.log`).
76
+ 6. **ALWAYS clean up** — kill only the processes you started, and remove temp
77
+ state you created. Cleanup runs even when checks fail.
78
+
79
+ Record which modes you actually rendered. An unrendered mode is `untested`,
80
+ never a passing one.
81
+
82
+ ## When the design system predates these fields
83
+
84
+ A `taste/design-system` written before `theme`, `focus.visible_style`, and
85
+ `motion.reduced_motion` existed will not carry them — and an upgrade never
86
+ rewrites an existing brain, so this is the normal case for any project older
87
+ than those fields. **A missing field is unspecified, not satisfied:**
88
+
89
+ | Missing | Assume | Consequence |
90
+ | ----------------------- | ------------------------------------- | ------------------------------------------------------------------------------- |
91
+ | `theme` / `theme.modes` | light-only | Render light. Do not fail a dark mode that was never designed. |
92
+ | `color.contrast_floor` | WCAG AA — 4.5:1 text, 3:1 UI/graphics | Still measure. Label the floor `assumed` in the report. |
93
+ | `focus.visible_style` | any clearly visible indicator | Check 3 still runs; only the specific style is unspecified. |
94
+ | `motion.reduced_motion` | all non-essential animation stops | Check 5 still runs. |
95
+
96
+ Every assumption goes in the report's `assumptions` list, so the human sees
97
+ which bar was applied. Raise the gap **once** as a `Minor` finding so the system
98
+ gets backfilled — never once per screen.
39
99
 
40
100
  ## Verdict 1 — Spec compliance
41
101
 
42
102
  For each screen/flow/state in `ui_spec`: present and correct, missing, or wrong.
43
103
  Missing a screen or a non-happy state → spec verdict FAIL.
44
104
 
45
- ## Verdict 2 — Visual quality
105
+ ## Verdict 2 — Rendering & accessibility (objective)
106
+
107
+ These are measurements, not opinions. Measure them; do not eyeball them. Each
108
+ check reports `pass`, `fail`, or `untested` with its reason — **a check you
109
+ could not run is never a pass.** The `needs` tag names what the check requires;
110
+ without it, the check is `untested`. Any failure here is at least **Important**;
111
+ anything that makes content unreadable or a control unreachable is **Critical**.
112
+
113
+ 1. **Themes** *(needs: a render per declared mode)*. Render each screen in every
114
+ declared mode. Also render the *un-stamped* default state — where no explicit
115
+ theme is selected and only the OS preference applies — because a color
116
+ defined solely inside a `[data-theme]` or media block never applies there,
117
+ and the page renders one theme's text on the other theme's ground. Any text,
118
+ icon, border, or focus ring that vanishes or loses contrast in one mode is a
119
+ defect in that mode.
120
+ 2. **Contrast** *(needs: a screenshot or computed style)*. Compute the ratios
121
+ against `contrast_floor` for body text and for UI/graphic elements, in every
122
+ rendered mode. Report the number, not an impression.
123
+ 3. **Keyboard focus** *(needs: browser)*. Tab through every screen. Every
124
+ interactive element has a visible focus indicator, focus order follows visual
125
+ order, and no element traps focus. An invisible focus ring is Critical.
126
+ 4. **Overflow** *(needs: browser — viewport resize)*. At the narrowest supported
127
+ width, the page body must not scroll horizontally. Wide content — tables,
128
+ code, charts, diagrams — scrolls inside its own container. Check for
129
+ overlapping or clipped elements at each tested width.
130
+ 5. **Reduced motion** *(needs: browser — media override)*. Render with
131
+ `prefers-reduced-motion: reduce`. Animation is removed or reduced, and
132
+ nothing becomes unusable or permanently invisible — scroll-reveal content
133
+ that never reveals is Critical.
134
+ 6. **Fonts actually loaded** *(needs: browser — computed style)*. Confirm the
135
+ system's named typeface is the one rendering, not a silent fallback to a
136
+ system face. Compare computed `font-family` against what actually painted.
137
+ 7. **Spacing integrity** *(needs: a screenshot + the spacing scale)*. Gaps
138
+ between sibling groups match the scale — no collapsed or doubled margins, no
139
+ ad-hoc values off the scale.
140
+ 8. **State without color** *(needs: a screenshot)*. Convert it to greyscale. If
141
+ success, warning, and error can no longer be told apart, state is encoded in
142
+ color alone — a defect, not a style.
143
+
144
+ ## Verdict 3 — Visual quality (judgment)
46
145
 
47
146
  Against the design system and world-class craft:
48
147
 
148
+ - **Treatment match.** Compare against the treatment the spec declared. Flag
149
+ both directions: a `product` screen built with no hierarchy or presence, and a
150
+ `utility` screen wearing a hero, decorative motion, or ornament it did not
151
+ earn. **Over-design is a finding, at the same severity as under-design.**
49
152
  - **Hierarchy** obvious in one glance? **Spacing** on the system's scale?
50
153
  - **Consistency** with the system's type/color/tokens? Drift = defect.
51
154
  - Is the **memorable-thing** actually visible here?
52
155
  - **Non-happy states** real, not stubs (loading/empty/error)?
156
+ - **Copy** — do labels, confirmations, empty states, and errors match the spec
157
+ and `taste/brand`? A control whose label and confirmation use different verbs,
158
+ an error that only apologizes, or shipped placeholder text are all findings.
53
159
  - **Interaction quality** — jank, slow transitions, layout shift?
54
160
 
55
161
  ### Anti-slop check
56
162
 
57
- Flag any AI-default tell: Inter/Roboto where the system says otherwise ·
58
- purple-blue gradient heroes · three-column rounded-card grids · default Tailwind
59
- swatches · drop shadows on everything · emoji as icons · generic everything.
163
+ Flag any AI-default tell. The canonical list lives in the `design-system` skill;
164
+ short form: warm cream + serif + terracotta · near-black with one acid-green or
165
+ vermilion pop · purple-to-blue gradient hero · default framework swatches ·
166
+ Inter / Roboto / Space Grotesk where the system says otherwise · three-column
167
+ rounded-card grids · accent rail on a rounded card · `rounded-lg` and drop
168
+ shadows on everything · emoji as icons · everything centered · `01 / 02 / 03`
169
+ numbering on content that is not a sequence.
60
170
 
61
171
  Rate each finding **Critical** (must fix) · **Important** (fix before merge) ·
62
172
  **Minor** (note it).
@@ -64,20 +174,56 @@ Rate each finding **Critical** (must fix) · **Important** (fix before merge) ·
64
174
  ## Output: `design_review_report`
65
175
 
66
176
  ```yaml
177
+ coverage: partial # full | partial — partial whenever any check is untested
178
+ driver: browser # browser | screenshots | none
179
+ themes_rendered: [light] # modes actually rendered; [] if none
180
+ assumptions: ["contrast_floor absent from taste/design-system — applied WCAG AA 4.5:1"]
67
181
  spec_compliance: pass # pass | fail
182
+ rendering_a11y: # one verdict per check: pass | fail | untested (+ reason)
183
+ themes: pass
184
+ contrast: fail
185
+ focus: untested — no browser driver in this session
186
+ overflow: untested — no browser driver in this session
187
+ reduced_motion: untested — no browser driver in this session
188
+ fonts: untested — no browser driver in this session
189
+ spacing: pass
190
+ state_without_color: pass
68
191
  missing: [<screen/state not built>]
69
192
  findings:
70
- - { severity: Important, screen: settings, issue: "...", fix: "...", shot: "..." }
193
+ - {
194
+ severity: Important,
195
+ kind: rendering, # spec | rendering | a11y | visual | treatment | copy | anti-slop
196
+ screen: settings,
197
+ mode: dark,
198
+ issue: "...",
199
+ evidence: "measured 3.1:1 against a 4.5:1 floor",
200
+ fix: "...",
201
+ shot: ".orchestrate/verify/design-review-settings-dark.png",
202
+ }
71
203
  verdict: changes_requested # approved | changes_requested
204
+ cleanup: "started pid 48213, killed" # or "nothing started"
72
205
  ```
73
206
 
74
207
  ## Rules
75
208
 
76
- - **Evidence-based.** Every finding names the screen and, where possible, a
77
- screenshot. No "looks off" without showing it.
209
+ - **Evidence-based.** Every finding names the screen and, where applicable, the
210
+ theme mode and a screenshot. Objective findings carry the measurement. No
211
+ "looks off" without showing it.
212
+ - **`untested` never rounds up.** Do not infer a check from the CSS, from a
213
+ neighbouring screen, or from what the framework "usually" does. If `coverage`
214
+ is `partial`, say which checks are missing in one line — an `approved` verdict
215
+ must never imply coverage it does not have.
78
216
  - **Don't pre-judge.** Surface deviations; don't excuse one because the spec or a
79
217
  deadline pushed it.
80
218
  - **Findings route back, not to the human.** Critical/Important go to `implement`
81
219
  (re-run with this report as `qa_feedback`). The human sees the result at the
82
220
  end-of-run acceptance.
83
- - If it's clean and on-system, say so plainly and approve.
221
+ - If it's clean, on-system, and on-treatment, say so plainly and approve.
222
+
223
+ ## Red flags — stop
224
+
225
+ - Reporting `pass` on a check you could not run
226
+ - A measurement (contrast, computed font) stated without the number
227
+ - Leaving a dev server you started still running after the report
228
+ - Failing a dark mode the design system never declared
229
+ - Raising the same missing-KB-field finding once per screen
@@ -4,12 +4,14 @@ description: Use when a project has no design direction yet, or must (re)establi
4
4
  license: MIT
5
5
  allowed-tools: [Read, Write, WebSearch, Grep, Glob]
6
6
  metadata:
7
+ requires:
8
+ capabilities: [filesystem.read, filesystem.write, web.read]
7
9
  contract:
8
- inputs: [product_context, references?]
10
+ inputs: [product_context, "references?"]
9
11
  reads: [taste/brand, taste/design-system]
10
12
  outputs: [taste/design-system, taste/brand]
11
13
  authority: "Author the durable design KB (taste/design-system, taste/brand). High-authority, audited (knowledge write). No source code, no production."
12
- verify: "Specific, not generic: a named typeface, real type/space scales, named reference products, one memorable-thing. Covers type + color + space + motion. No AI-default tells (see anti-slop)."
14
+ verify: "Specific, not generic: a named typeface, real type/space scales, named reference products, one memorable-thing. Covers type + color + theme + space + motion + focus. Every mode listed under theme.modes has a full palette. No AI-default tells (see anti-slop)."
13
15
  accept:
14
16
  when: "Always — the aesthetic direction is foundational and brand-defining."
15
17
  timing: inline
@@ -47,27 +49,72 @@ point of view BEFORE specifying anything:
47
49
  ## Specify the system (specifics, not adjectives)
48
50
 
49
51
  - **Typography** — a named typeface (not Inter/Roboto unless deliberate and
50
- justified), a type scale with real sizes/weights, line-height rules.
52
+ justified), a type scale with real sizes/weights, line-height rules, and the
53
+ target measure for running text (~65 characters).
51
54
  - **Color** — a real palette with roles (bg, surface, text, accent, states), not
52
- default framework swatches; state the contrast floor.
55
+ default framework swatches; state the contrast floor as a number.
56
+ **Accent and semantic color are two different systems.** The accent is the one
57
+ brand hue; success/warning/error carry meaning. If the accent doubles as
58
+ "success", state cannot be read at a glance — pick again.
59
+ - **Theme** — decide `light | dark | both`, and how a mode is selected (OS
60
+ preference, an explicit user toggle, or both). If dark is in scope, specify a
61
+ **second full palette, role for role**. A dark palette is re-picked, not
62
+ inverted: an accent that holds 4.5:1 on white usually fails on near-black.
63
+ Deciding light-only is allowed — but it must be written down as a decision, so
64
+ `design-review` knows there is nothing else to check.
53
65
  - **Space** — a spacing scale; density posture (tight/airy) tied to the product.
54
66
  - **Layout** — grid and composition principles; how hierarchy is created.
55
- - **Motion** — timing, easing, and where motion is used (and where it is not).
67
+ - **Motion** — timing, easing, where motion is used (and where it is not), and
68
+ what `prefers-reduced-motion` removes while keeping the UI usable.
69
+ - **Focus** — the keyboard focus indicator every interactive element carries. An
70
+ invisible focus ring is a broken system, not a style choice.
56
71
 
57
- ## Anti-slop (forbidden defaultsname and avoid)
72
+ ## Anti-slop (the canonical list `design-ui` and `design-review` check it too)
58
73
 
59
- Inter/Roboto by default · purple-blue gradient heroes · three-column
60
- rounded-card feature grids · drop shadows on everything · default Tailwind
61
- palette (blue-500…) · emoji as product icons · centered everything · gradients /
62
- glassmorphism with no reason. If a choice is one of these, it must be a
63
- deliberate, justified decision not a default.
74
+ AI-generated design converges on a small set of looks. These are not banned —
75
+ they are **disqualified as defaults**. Picking one is allowed only as a stated,
76
+ justified decision, never as where you landed without choosing.
77
+
78
+ **Palettes** warm cream ground (`#F4F1EA` and neighbors) with a serif display
79
+ and a terracotta accent · near-black with one acid-green or vermilion pop ·
80
+ purple-to-blue gradient hero on white · default framework swatches
81
+ (`blue-500`, `slate-800`, …).
82
+
83
+ **Type** — Inter, Roboto, or Space Grotesk as the "safe" face · a display face
84
+ used at body sizes so its personality never shows.
85
+
86
+ **Layout** — three-column rounded-card feature grids · an accent bar/rail down
87
+ the side of a rounded card · `rounded-lg` on everything · drop shadow on
88
+ everything · everything centered · broadsheet hairline rules over dense columns.
89
+
90
+ **Ornament** — emoji as product icons or section markers · `01 / 02 / 03`
91
+ numbering on content that is not actually a sequence · gradients or
92
+ glassmorphism with no reason.
93
+
94
+ Structural devices must encode something true. Number a set of steps only when
95
+ order is information the reader needs; otherwise the numbers are decoration
96
+ pretending to be structure.
64
97
 
65
98
  ## Output: the durable KB (structured, with provenance)
66
99
 
67
100
  Write `taste/design-system` and `taste/brand` as structured entries (per the
68
101
  knowledge format: terse, chunked, each with `source`/`added`/`approved_by`).
69
102
  Record the memorable-thing, the references, the one distinctive rule, and each
70
- specified token/scale. This is the source `design-ui` reads.
103
+ specified token/scale. Leave no field of the seed blank: `theme`,
104
+ `motion.reduced_motion`, and `focus.visible_style` are decisions, and a blank
105
+ one reads to `design-review` as unspecified rather than as "not needed". This is
106
+ the source `design-ui` reads.
107
+
108
+ **Backfilling an older system.** An upgrade refreshes skills but never rewrites
109
+ an existing `knowledge/` — that brain belongs to the project. So a system
110
+ authored before `theme`, `focus.visible_style`, `motion.reduced_motion`, and
111
+ `typography.measure` existed will simply be missing them, and `design-review`
112
+ will report every dependent check against an assumed bar. When you re-run on
113
+ such a project, add those fields rather than re-authoring the whole system:
114
+ keep every existing value untouched, fill only the gaps, and give the new
115
+ entries their own `added` date and approver. Adding a dark palette to a
116
+ light-only product is a real design decision — put it through the same accept
117
+ gate as the rest, don't infer it.
71
118
 
72
119
  ## Self-critique before done
73
120
 
@@ -4,13 +4,15 @@ description: Use when a feature has a user interface, to design its screens and
4
4
  license: MIT
5
5
  allowed-tools: [Read, Write, Grep, Glob]
6
6
  metadata:
7
+ requires:
8
+ capabilities: [filesystem.read, filesystem.write]
7
9
  contract:
8
10
  inputs: [requirement, ui_context]
9
11
  reads: [taste/design-system, taste/brand]
10
12
  outputs: [specs/<slug>-ui.md]
11
- updates: [taste/design-system?, taste/brand?]
13
+ updates: ["taste/design-system?", "taste/brand?"]
12
14
  authority: "Write to the specs namespace (default docs/specs/) and design assets. No source code, no production."
13
- verify: "Every screen and flow maps to a requirement; expresses the design system (not generic defaults); passes the designer's-eye self-critique."
15
+ verify: "Every screen and flow maps to a requirement; a treatment is declared and held to; expresses the design system (not generic defaults); copy and non-happy states are specified for every screen; the design plan passed its critique before the spec was written."
14
16
  accept:
15
17
  when: "Visual direction — the look is foundational; everything downstream builds on it."
16
18
  timing: inline
@@ -34,33 +36,107 @@ aesthetic — that is exactly how product consistency dies.
34
36
  Carry the system's **memorable-thing** and **distinctive rule** through every
35
37
  screen. If this feature can't express them, say so.
36
38
 
39
+ ## Treatment — declare one before designing
40
+
41
+ Craft is constant. Visual ambition is not. **Over-design is a defect, not
42
+ enthusiasm**: a settings page with a full-bleed hero is as wrong as a landing
43
+ page without one. Pick exactly one treatment for this feature, write it at the
44
+ top of the spec, and hold every screen to it. `design-review` checks the built
45
+ UI against the treatment you declared, so declaring `utility` and shipping
46
+ ornament is a finding.
47
+
48
+ | Treatment | Use for | Budget |
49
+ | ----------- | ---------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------- |
50
+ | `utility` | internal tools, admin, settings, dense data screens | Information design only. Real hierarchy, spacing on the scale, system palette. No hero, no decorative motion, no ornament. |
51
+ | `product` | the core user-facing surfaces — **the default** | Full system expression. The memorable-thing is present but quiet. Motion is functional: state changes, transitions, feedback. |
52
+ | `editorial` | marketing, landing, launch, first-run onboarding | Opinionated composition. One deliberate aesthetic risk, spent in one place; everything around it stays quiet. |
53
+
54
+ When the requirement doesn't say, use `product`. Escalating to `editorial` is a
55
+ choice you must justify in one sentence in the spec.
56
+
37
57
  ## Process
38
58
 
39
- 1. **Map screens to requirements.** Every screen/state must trace to a
59
+ 1. **Map screens to requirements.** Every screen and state must trace to a
40
60
  requirement. Cut the rest (YAGNI).
41
- 2. **Apply the system, don't restate it.** Use its typeface, scale, palette,
42
- spacing, motion. Reuse existing components/tokens before proposing new ones.
43
- 3. **Specify each screen:** purpose, layout and hierarchy, the system tokens
61
+ 2. **Write the design plan, then attack it before specifying anything.**
62
+ Three to five lines: the treatment, the system tokens this feature leans on,
63
+ the layout concept, and where the memorable-thing shows up. Then read it back
64
+ and ask: *would this same plan work, unchanged, for any other feature in any
65
+ other product?* Whatever survives that substitution is generic — revise it,
66
+ and record in the spec what you changed and why. Catching this in the plan is
67
+ far cheaper than catching it in a finished spec.
68
+ 3. **Apply the system, don't restate it.** Use its typeface, scale, palette,
69
+ spacing, motion. Reuse existing components and tokens before proposing new
70
+ ones.
71
+ 4. **Specify each screen:** purpose, layout and hierarchy, the system tokens
44
72
  used, and the non-happy states — loading, empty, error. These are where UIs
45
73
  actually fail.
46
- 4. **Show, don't just tell.** When a layout choice is clearer shown than
47
- described, produce a mockup/wireframe, not prose.
74
+ 5. **Specify the copy** for every screen see below. A screen whose labels are
75
+ unwritten is not specified.
76
+ 6. **Cover every theme the system declares.** If `theme.modes` is `both`, each
77
+ screen's spec names the tokens it uses, not literal colors, and calls out any
78
+ place the two palettes need different treatment (elevation, dividers, images
79
+ on a dark ground). If the system is light-only, say so once and move on.
80
+ 7. **Show, don't just tell.** When a layout choice is clearer shown than
81
+ described, produce a mockup or wireframe, not prose.
82
+
83
+ ## Copy is design material
84
+
85
+ Words are part of the design, not filler dropped in later. Read `taste/brand`
86
+ for voice, then write the actual strings:
87
+
88
+ - **Name things the way the user names them**, not the way the system is built.
89
+ A person manages *notifications*, not *webhook config*.
90
+ - **A control says exactly what happens.** Button `Publish` → toast `Published`.
91
+ Label and confirmation must use the same verb.
92
+ - **Errors state what went wrong and what to do next.** No apologies, no
93
+ "something went wrong", no error code alone.
94
+ - **Empty states say what goes here and how to get the first one**, not "No
95
+ data".
96
+ - Active voice. Specific beats clever. Never ship lorem ipsum or placeholder
97
+ text into a spec — write the real string or mark it `TODO: copy`.
98
+
99
+ ## When the surface is operated, not read
100
+
101
+ A dashboard, console, or tool is scanned and acted on, not read top to bottom.
102
+ For those screens the craft shifts from typography to information design:
103
+
104
+ - Summary before detail. What needs attention resolves in one glance.
105
+ - **Encode state in form, not only in number** — a pill, a chip, a severity
106
+ stripe. Color alone is not an encoding; it fails for color-blind users and in
107
+ a screenshot.
108
+ - **Semantic color (success / warning / error) is separate from the accent hue**
109
+ and never counts as your accent.
110
+ - Charts and sparklines get the same care as type: a considered fill, a faint
111
+ grid, an emphasized endpoint. Numbers in columns get tabular figures.
112
+ - What is interactive must look interactive.
48
113
 
49
114
  ## Anti-slop (forbidden defaults)
50
115
 
51
- No Inter/Roboto unless the system says so · no purple-blue gradient heroes · no
52
- three-column rounded-card grids by reflex · no default Tailwind swatches · no
53
- drop shadows on everything · no emoji as icons. A default is only allowed as a
54
- deliberate, justified choice.
116
+ The canonical list lives in the `design-system` skill; `design-review` checks
117
+ the same one. Short form none of these may be where you landed without
118
+ choosing: warm cream + serif + terracotta · near-black with one acid-green or
119
+ vermilion pop · purple-to-blue gradient hero · default framework swatches ·
120
+ Inter / Roboto / Space Grotesk as the safe face · three-column rounded-card
121
+ grids · accent rail on a rounded card · `rounded-lg` and drop shadows on
122
+ everything · emoji as icons · everything centered · `01 / 02 / 03` numbering on
123
+ content that is not a sequence.
124
+
125
+ A default is only allowed as a deliberate, justified choice — and any deviation
126
+ from `taste/design-system` needs the same justification.
55
127
 
56
128
  ## Designer's-eye self-critique (mandatory gate before done)
57
129
 
58
- Look at the result and ask:
130
+ The plan critique in step 2 catches generic direction. This catches generic
131
+ execution. Look at the result and ask:
59
132
 
60
133
  - Does this look like it could ship from {the named references}, or like a
61
134
  generic AI UI? If the latter, fix it.
62
135
  - Is hierarchy obvious in one glance? Is spacing on the scale? Is the
63
136
  memorable-thing visible here?
137
+ - **Does the ambition match the declared treatment** — nothing under-designed,
138
+ and nothing decorated past its budget?
139
+ - Every screen: loading, empty, error specified? Copy written, not placeholder?
64
140
  - Did any anti-slop tell sneak in?
65
141
 
66
142
  Fix until it passes. This is the visual equivalent of `run-tests` — don't claim
@@ -71,13 +147,20 @@ done without running it.
71
147
  ```markdown
72
148
  # <Feature> — UI Spec
73
149
 
150
+ ## Treatment — utility | product | editorial, and why (one sentence).
151
+
152
+ ## Design plan — the plan from step 2, plus what the critique changed and why.
153
+
74
154
  ## Screens — each: purpose, the requirement it serves, layout + hierarchy.
75
155
 
76
156
  ## Flows — how the user moves between screens.
77
157
 
78
158
  ## States — loading / empty / error for each screen.
79
159
 
80
- ## System use tokens/components used; how the memorable-thing shows up here.
160
+ ## Copythe real strings per screen: labels, confirmations, empties, errors.
161
+
162
+ ## System use — tokens/components used; theme coverage; how the
163
+ memorable-thing shows up here.
81
164
 
82
165
  ## New patterns — anything the system lacked, with rationale (candidate for KB).
83
166
  ```
@@ -4,9 +4,11 @@ description: Use when a feature request or requirement must become an implementa
4
4
  license: MIT
5
5
  allowed-tools: [Read, Write, Grep, Glob]
6
6
  metadata:
7
+ requires:
8
+ capabilities: [filesystem.read, filesystem.write]
7
9
  contract:
8
10
  inputs: [requirement, context]
9
- reads: [taste/coding-standards, registry/api, registry/db, front-end-spec?]
11
+ reads: [taste/coding-standards, registry/api, registry/db, "front-end-spec?"]
10
12
  outputs: [stories/<slug>.md]
11
13
  authority: "Write one flat story file in the stories namespace (physical path from core-config.yaml; default docs/stories/). No folders. No source code. No production. No spend."
12
14
  verify: "Every requirement maps to at least one acceptance criterion; constraints are copied verbatim; no placeholders (no TBD/TODO/'handle edge cases')."
@@ -4,8 +4,10 @@ description: Use when implementing a feature or bugfix from a spec with acceptan
4
4
  license: MIT
5
5
  allowed-tools: [Read, Write, Edit, Bash]
6
6
  metadata:
7
+ requires:
8
+ capabilities: [filesystem.read, filesystem.write, shell.execute]
7
9
  contract:
8
- inputs: [story, acceptance_criteria, scope, qa_feedback?]
10
+ inputs: [story, acceptance_criteria, scope, "qa_feedback?"]
9
11
  reads: [taste/coding-standards, registry/api, registry/db]
10
12
  outputs: [code_diff, ac_traceability, test_files]
11
13
  authority: "Write src/ and tests/. No production, no deploy, no network spend."
@@ -4,8 +4,10 @@ description: Use when something is broken and the CAUSE is unknown — a repeate
4
4
  license: MIT
5
5
  allowed-tools: [Read, Bash, Grep, Glob]
6
6
  metadata:
7
+ requires:
8
+ capabilities: [filesystem.read, shell.execute]
7
9
  contract:
8
- inputs: [symptom, context?, prior_attempts?]
10
+ inputs: [symptom, "context?", "prior_attempts?"]
9
11
  reads: [registry/architecture, taste/coding-standards]
10
12
  outputs: [root_cause_report]
11
13
  authority: "Read code and run diagnostics/reproductions. Temporary instrumentation is allowed but MUST be reverted before finishing. No fixes — the fix belongs to a re-dispatched implement."
@@ -2,10 +2,12 @@
2
2
  name: map-codebase
3
3
  description: Use when entering an EXISTING codebase (brownfield) before designing or changing anything — build an evidence-based map of its architecture, conventions, and hazards.
4
4
  license: MIT
5
- allowed-tools: [Read, Bash, Grep, Glob]
5
+ allowed-tools: [Read, Write, Bash, Grep, Glob]
6
6
  metadata:
7
+ requires:
8
+ capabilities: [filesystem.read, filesystem.write, shell.execute]
7
9
  contract:
8
- inputs: [repo_path, focus?]
10
+ inputs: [repo_path, "focus?"]
9
11
  reads: []
10
12
  outputs: [codebase_map, registry_updates]
11
13
  authority: "Read-only on source; non-mutating commands only (ls, grep, git log, test discovery). Writes go ONLY to the registry/* namespace (physical path from core-config.yaml; default knowledge/registry/)."
@@ -4,9 +4,11 @@ description: Use when a goal must be delivered end-to-end by composing skills, w
4
4
  license: MIT
5
5
  allowed-tools: [Read, Write, Edit, Bash, Grep, Glob, Task]
6
6
  metadata:
7
- version: 5
7
+ version: 6
8
+ requires:
9
+ capabilities: [filesystem.read, filesystem.write, shell.execute, "agent.spawn?"]
8
10
  contract:
9
- inputs: [intent, constraints?]
11
+ inputs: [intent, "constraints?"]
10
12
  reads: [core-config, skill-registry, taste/*]
11
13
  outputs: [accepted_deliverable, run_ledger]
12
14
  authority: "Dispatch leaf skills, each within its own authority. Do not directly touch source, production, or spend — leaf skills do that, gated. Enforce every accept gate."
@@ -34,21 +36,22 @@ no step above intent.
34
36
  1. **Bind intent.** Read the human's goal and constraints. This is the only
35
37
  place intent enters. Then run the first-run preflight (below) before any
36
38
  wiring.
37
- 2. **Select.** Read the skill registry. Pick skills by their `description`
38
- (when-to-use). Load a skill's full `contract` only when it is a candidate —
39
- never load every contract at once.
39
+ 2. **Select.** Read the skill registry (the `description:` line of every
40
+ SKILL.md in the runtime's skills directory). Pick skills by that
41
+ when-to-use description. Load a skill's full `contract` only when it is a
42
+ candidate — never load every contract at once.
40
43
  3. **Wire (emergent, not hardcoded).** Build the path by matching one skill's
41
44
  `outputs` to the next skill's `inputs`. Skills do not know each other; only
42
45
  you do. Do not assume a fixed pipeline — wire what this intent needs.
43
46
  4. **Dispatch.** Hand the skill exactly the `inputs` it declares, as files —
44
47
  resolving each logical namespace it reads/writes to a physical path via
45
- `core-config.yaml` (see Namespace resolution). Run
46
- it as a fresh subagent for isolation. Choose the cheapest model that can do
47
- the step. **Dispatch independent steps in PARALLEL** (whose `inputs` don't
48
- depend on each other) as concurrent FOREGROUND subagents awaited together in
49
- the same turn, for speed. Keep dependent steps sequential. NEVER fire-and-forget
50
- a background subagent and end the turn waiting to be woken — run foreground and
51
- await; there is no reliable async wake.
48
+ `core-config.yaml` (see Namespace resolution). If the runtime supports isolated
49
+ agents, run each leaf as a fresh dispatch and choose the cheapest capable model.
50
+ Dispatch independent steps concurrently and await them in the same turn; keep
51
+ dependent steps sequential. Never fire-and-forget a background agent. If the
52
+ runtime has no isolated-agent capability, execute leaves sequentially in the
53
+ current context, reloading only the declared inputs before each step. Isolation
54
+ is preferred; it is not required for correctness.
52
55
  5. **Verify (gate) — executable, not prose.** Prove the skill's `verify` with a
53
56
  REAL command you run yourself via Bash, and capture the proof:
54
57
 
@@ -66,8 +69,12 @@ no step above intent.
66
69
  6. **Accept (gate).** Apply the rule below. Then continue — do not pause to ask
67
70
  "should I keep going?" mid-run.
68
71
  7. **Repeat** 3–6 until the intent is fulfilled.
69
- 8. **Final acceptance.** Present the batched deferred accepts and a final review
70
- to the human, once. Apply corrections (see Metabolism), then deliver.
72
+ 8. **Final acceptance.** FIRST re-read the intent from the `run_start` ledger
73
+ line and check the assembled result against IT every step passing its own
74
+ verify does not prove the composition serves the intent (steps can each be
75
+ right while the whole drifts). Then present the batched deferred accepts and
76
+ a final review to the human, once. Apply corrections (see Metabolism), then
77
+ deliver.
71
78
 
72
79
  ## Namespace resolution (`core-config.yaml`)
73
80
 
@@ -112,6 +119,27 @@ resolved paths):
112
119
 
113
120
  Both checks are per-run and idempotent: a populated brain makes them no-ops.
114
121
 
122
+ ## Hard wiring rules (what emergence cannot reach)
123
+
124
+ Output→input matching wires most of the graph. Two skills it structurally
125
+ CANNOT reach — wire these by rule, not by match:
126
+
127
+ 1. **`smoke-test` is the acceptance floor for runnable apps.** Nothing in the
128
+ graph outputs its `flows` or `run_instructions`, so no output→input match
129
+ will ever select it. If the deliverable is a runnable app or service and
130
+ this run changed it, wire `smoke-test` before final acceptance and derive
131
+ its inputs yourself: `flows` from the story's acceptance criteria (or from
132
+ the intent, when there is no story), `run_instructions` from `registry/app`
133
+ (or the project's own manifest). `run-tests` proves functions; `smoke-test`
134
+ proves the product — green unit tests are not this evidence. A `failed` or
135
+ `untested` verdict is a real result: carry it into final acceptance
136
+ verbatim, never round it up to passed.
137
+ 2. **`design-system` comes before `design-ui`.** `design-ui` READS
138
+ `taste/design-system` — it never produces it. If UI work is wired and the
139
+ resolved `taste/design-system` namespace is empty, wire `design-system`
140
+ first; otherwise `design-ui` dresses a project that has a brand in generic
141
+ defaults.
142
+
115
143
  ## Accept gate
116
144
 
117
145
  | Skill's `accept.timing` | Skill's `authority` | Action |
@@ -139,8 +167,9 @@ aimed at a symptom re-rolls the dice.
139
167
  STOP the run — do not burn a 4th attempt. Write a `gate` event to the ledger
140
168
  (`{"e":"gate","kind":"rework_exhausted","question":"step <n> (<skill>) failed 3
141
169
  attempts: <one-line why>"}`), summarize the three failures for the human, and
142
- report AWAIT. A step that cannot pass its own verify after three tries needs a
143
- human decision (wrong approach, wrong spec, or wrong verify), not more tokens.
170
+ stop for their decision. A step that cannot pass its own verify after three
171
+ tries needs a human (wrong approach, wrong spec, or wrong verify), not more
172
+ tokens.
144
173
 
145
174
  ## Metabolism — governed writeback
146
175
 
@@ -179,13 +208,18 @@ platform renders it as live progress). It is append-only JSONL: one JSON event
179
208
  per line, appended with `Bash` (`echo '<json>' >> .orchestrate/ledger.jsonl`).
180
209
  Never rewrite or delete lines. Timestamps: `date -u +%FT%TZ`.
181
210
 
211
+ **Quoting hazard:** the single-quoted `echo` breaks on `'` inside the JSON —
212
+ and a mangled line corrupts the run's only durable memory. Keep every free-text
213
+ field (`intent`, `question`, `title`) to one line with no single quotes:
214
+ rephrase (`don't` → `do not`) before writing, never fight the shell escaping.
215
+
182
216
  Events and when to write them:
183
217
 
184
218
  | Event | When | Shape |
185
219
  | ----- | ---- | ----- |
186
220
  | `run_start` | right after binding intent | `{"e":"run_start","run":"r-<yyyymmdd>-<slug>","intent":"...","ts":"..."}` |
187
221
  | `plan` | after wiring the graph, and EVERY time the graph changes | `{"e":"plan","run":"...","steps":[{"n":1,"skill":"research","title":"..."}, …]}` — full current plan; latest `plan` line wins; steps may be added, never removed |
188
- | `step` | immediately BEFORE each dispatch, and again after its verify | `{"e":"step","run":"...","n":3,"skill":"implement","status":"dispatched\|done\|failed","attempt":1,"evidence":"<file or one-line result>","ts":"..."}` — rework = same `n`, next `attempt` |
222
+ | `step` | immediately BEFORE each dispatch, and again after its verify | `{"e":"step","run":"...","n":3,"skill":"implement","status":"dispatched\|done\|failed\|skipped","attempt":1,"evidence":"<file or one-line result>","ts":"..."}` — rework = same `n`, next `attempt`; a step a replan made obsolete gets `skipped` with the reason in `evidence` (plan lines are never removed, so this is how an obsolete step closes) |
189
223
  | `gate` | when stopping at a human gate | `{"e":"gate","run":"...","kind":"inline_accept","question":"...","ts":"..."}` |
190
224
  | `run_end` | at delivery or abandonment | `{"e":"run_end","run":"...","result":"delivered\|paused\|abandoned","ts":"..."}` |
191
225
 
@@ -194,6 +228,24 @@ step is required and should be the step's verify log path
194
228
  (`.orchestrate/verify/step-<n>-attempt-<k>.log`); a `done` with no evidence is
195
229
  a false claim.
196
230
 
231
+ ## Resume — cold re-entry (deterministic, not from memory)
232
+
233
+ Whenever you enter with an existing ledger — after compaction, an interrupted
234
+ session, or a wake-up — do NOT continue from what you remember. Replay:
235
+
236
+ 1. Read `.orchestrate/ledger.jsonl`. The active run is the last `run_start`
237
+ with no matching `run_end`. Its `intent` line — not your recollection — is
238
+ what you are delivering. No active run → this is a fresh start.
239
+ 2. Rebuild state from events alone: the latest `plan` wins; `done` and
240
+ `skipped` steps are closed; prior `attempt` values count toward each step's
241
+ cap of 3.
242
+ 3. **A dangling `dispatched`** (no `done`/`failed`/`skipped` after it) means
243
+ that attempt was cut off mid-flight. Trust it in NEITHER direction: run that
244
+ step's verify command now. Pass → append its `done` with the evidence.
245
+ Fail → re-dispatch as the next attempt.
246
+ 4. Continue the loop from the first open step. If the run was stopped at a
247
+ `gate`, re-ask that gate's question — never assume it was answered.
248
+
197
249
  ## Context discipline (stay lean)
198
250
 
199
251
  - **Files, not paste.** Move artifacts between steps as files. Never paste a
@@ -209,6 +261,8 @@ a false claim.
209
261
  - Hardcoding a fixed skill order instead of wiring outputs→inputs
210
262
  - Pasting a step's full output into your context instead of handing a file
211
263
  - Re-dispatching a step the ledger already marks done
264
+ - Resuming from memory instead of replaying the ledger — or trusting a
265
+ dangling `dispatched` in either direction without running its verify
212
266
  - Dispatching a step without first writing its `dispatched` ledger line
213
267
  - Ending a run without a `run_end` ledger line
214
268
  - Marking a step done on the subagent's say-so, without your own verify command
@@ -217,4 +271,7 @@ a false claim.
217
271
  - Appending to `taste/*` without reading it first (duplicate/contradiction risk)
218
272
  - Dispatching a design/build skill in an existing codebase while `registry/*`
219
273
  is empty (first-run preflight skipped)
274
+ - Delivering a runnable app this run changed with no `smoke-test` verdicts
275
+ (unit tests are not that evidence)
276
+ - Dispatching `design-ui` while the resolved `taste/design-system` is empty
220
277
  - Marking the run complete without every step's `verify` evidence
@@ -2,8 +2,10 @@
2
2
  name: research
3
3
  description: Use when a planning or design decision needs external facts — market, competitor, feasibility, or a library/tech choice — before committing.
4
4
  license: MIT
5
- allowed-tools: [Read, WebSearch, WebFetch]
5
+ allowed-tools: [Read, Write, WebSearch, WebFetch]
6
6
  metadata:
7
+ requires:
8
+ capabilities: [filesystem.read, filesystem.write, web.read]
7
9
  contract:
8
10
  inputs: [question, scope]
9
11
  reads: []
@@ -4,6 +4,8 @@ description: Use after implementing a task or feature and before merging, to che
4
4
  license: MIT
5
5
  allowed-tools: [Read, Bash, Grep, Glob]
6
6
  metadata:
7
+ requires:
8
+ capabilities: [filesystem.read, shell.execute]
7
9
  contract:
8
10
  inputs: [diff, spec]
9
11
  reads: [taste/coding-standards]
@@ -4,8 +4,10 @@ description: Use before claiming any work is complete, fixed, or passing, and be
4
4
  license: MIT
5
5
  allowed-tools: [Read, Bash]
6
6
  metadata:
7
+ requires:
8
+ capabilities: [filesystem.read, shell.execute]
7
9
  contract:
8
- inputs: [target, expected_outcome?]
10
+ inputs: [target, "expected_outcome?"]
9
11
  reads: []
10
12
  outputs: [verification_report]
11
13
  authority: "Run test, lint, and build commands. Read-only on source. No edits, no production."
@@ -4,8 +4,10 @@ description: Use when the deliverable is a runnable app or service and its real
4
4
  license: MIT
5
5
  allowed-tools: [Read, Bash, Grep, Glob]
6
6
  metadata:
7
+ requires:
8
+ capabilities: [filesystem.read, shell.execute]
7
9
  contract:
8
- inputs: [run_instructions, flows, qa_feedback?]
10
+ inputs: [run_instructions, flows, "qa_feedback?"]
9
11
  reads: [registry/app]
10
12
  outputs: [smoke_report, verify_evidence]
11
13
  authority: "Start and stop the app locally; drive it via browser automation, HTTP, or CLI. Read-only on source. No deploy, no external spend, no mutations outside the app's own local state."