@sema-agent/server 1.196.0 → 1.198.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 (38) hide show
  1. package/dist/config.d.ts.map +1 -1
  2. package/dist/config.js +10 -1
  3. package/dist/config.js.map +1 -1
  4. package/dist/http/server.js +1 -0
  5. package/dist/http/server.js.map +1 -1
  6. package/dist/main.js +24 -5
  7. package/dist/main.js.map +1 -1
  8. package/dist/plugins/remote-scratchpad.d.ts +8 -0
  9. package/dist/plugins/remote-scratchpad.d.ts.map +1 -0
  10. package/dist/plugins/remote-scratchpad.js +56 -0
  11. package/dist/plugins/remote-scratchpad.js.map +1 -0
  12. package/dist/task-cwd.d.ts +8 -0
  13. package/dist/task-cwd.d.ts.map +1 -1
  14. package/dist/task-cwd.js +14 -0
  15. package/dist/task-cwd.js.map +1 -1
  16. package/package.json +2 -1
  17. package/skills/code-review.md +28 -0
  18. package/skills/commit-push-pr.md +77 -0
  19. package/skills/dataviz/SKILL.md +112 -0
  20. package/skills/dataviz/references/anti-patterns.md +119 -0
  21. package/skills/dataviz/references/choosing-a-form.md +57 -0
  22. package/skills/dataviz/references/color-formula.md +113 -0
  23. package/skills/dataviz/references/components.md +39 -0
  24. package/skills/dataviz/references/interaction.md +60 -0
  25. package/skills/dataviz/references/marks-and-anatomy.md +97 -0
  26. package/skills/dataviz/references/palette.md +149 -0
  27. package/skills/dataviz/scripts/validate_palette.js +262 -0
  28. package/skills/find-skills.md +148 -0
  29. package/skills/init.md +28 -0
  30. package/skills/keybindings-help.md +294 -0
  31. package/skills/loop.md +50 -0
  32. package/skills/run-skill-generator.md +493 -0
  33. package/skills/run.md +148 -0
  34. package/skills/schedule.md +48 -0
  35. package/skills/security-review.md +181 -0
  36. package/skills/simplify.md +64 -0
  37. package/skills/update-config.md +93 -0
  38. package/skills/verify.md +334 -0
package/dist/task-cwd.js CHANGED
@@ -11,6 +11,20 @@ export function isValidCwd(cwd) {
11
11
  export function cwdHonored(config) {
12
12
  return config.remoteExec?.provider === "host" && config.requirePrincipal !== true;
13
13
  }
14
+ export function inProcessSingleUserLane(config) {
15
+ return config.remoteExec === undefined && config.requirePrincipal !== true;
16
+ }
17
+ export function satisfiedByProcessCwd(requested, realpath, cwd = process.cwd) {
18
+ try {
19
+ return realpath(requested) === realpath(cwd());
20
+ }
21
+ catch {
22
+ return false;
23
+ }
24
+ }
25
+ export function shellEnvMismatchCount(requested, procEnv) {
26
+ return Object.keys(requested).filter((k) => procEnv[k] !== requested[k]).length;
27
+ }
14
28
  export const MAX_ADDITIONAL_DIRS = 64;
15
29
  export function parseAdditionalDirectories(raw) {
16
30
  if (!Array.isArray(raw))
@@ -1 +1 @@
1
- {"version":3,"file":"task-cwd.js","sourceRoot":"","sources":["../src/task-cwd.ts"],"names":[],"mappings":"AAiBA,OAAO,IAAI,MAAM,WAAW,CAAC;AAG7B,MAAM,CAAC,MAAM,aAAa,GAAG,KAAK,CAAC;AAQnC,MAAM,UAAU,UAAU,CAAC,GAAY;IACrC,OAAO,CACL,OAAO,GAAG,KAAK,QAAQ;QACvB,GAAG,CAAC,MAAM,GAAG,CAAC;QACd,GAAG,CAAC,MAAM,IAAI,aAAa;QAC3B,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC;QACnB,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC;QACpB,CAAC,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,CACnC,CAAC;AACJ,CAAC;AAMD,MAAM,UAAU,UAAU,CAAC,MAA0E;IACnG,OAAO,MAAM,CAAC,UAAU,EAAE,QAAQ,KAAK,MAAM,IAAI,MAAM,CAAC,gBAAgB,KAAK,IAAI,CAAC;AACpF,CAAC;AAGD,MAAM,CAAC,MAAM,mBAAmB,GAAG,EAAE,CAAC;AAYtC,MAAM,UAAU,0BAA0B,CAAC,GAAY;IACrD,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC;QAAE,OAAO,SAAS,CAAC;IAC1C,MAAM,GAAG,GAAa,EAAE,CAAC;IACzB,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;IAC/B,KAAK,MAAM,KAAK,IAAI,GAAG,EAAE,CAAC;QACxB,IAAI,GAAG,CAAC,MAAM,IAAI,mBAAmB;YAAE,MAAM;QAC7C,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,IAAI,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC;YAAE,SAAS;QACpD,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QAChB,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAClB,CAAC;IACD,OAAO,GAAG,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC;AAC1C,CAAC;AAeD,MAAM,UAAU,sBAAsB,CACpC,UAA8B,EAC9B,GAA+C;IAE/C,OAAO,UAAU,IAAI,CAAC,GAAG,CAAC,SAAS,KAAK,UAAU,CAAC,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;AAClF,CAAC"}
1
+ {"version":3,"file":"task-cwd.js","sourceRoot":"","sources":["../src/task-cwd.ts"],"names":[],"mappings":"AAiBA,OAAO,IAAI,MAAM,WAAW,CAAC;AAG7B,MAAM,CAAC,MAAM,aAAa,GAAG,KAAK,CAAC;AAQnC,MAAM,UAAU,UAAU,CAAC,GAAY;IACrC,OAAO,CACL,OAAO,GAAG,KAAK,QAAQ;QACvB,GAAG,CAAC,MAAM,GAAG,CAAC;QACd,GAAG,CAAC,MAAM,IAAI,aAAa;QAC3B,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC;QACnB,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC;QACpB,CAAC,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,CACnC,CAAC;AACJ,CAAC;AAMD,MAAM,UAAU,UAAU,CAAC,MAA0E;IACnG,OAAO,MAAM,CAAC,UAAU,EAAE,QAAQ,KAAK,MAAM,IAAI,MAAM,CAAC,gBAAgB,KAAK,IAAI,CAAC;AACpF,CAAC;AAWD,MAAM,UAAU,uBAAuB,CAAC,MAA0E;IAChH,OAAO,MAAM,CAAC,UAAU,KAAK,SAAS,IAAI,MAAM,CAAC,gBAAgB,KAAK,IAAI,CAAC;AAC7E,CAAC;AAKD,MAAM,UAAU,qBAAqB,CAAC,SAAiB,EAAE,QAA+B,EAAE,MAAoB,OAAO,CAAC,GAAG;IACvH,IAAI,CAAC;QACH,OAAO,QAAQ,CAAC,SAAS,CAAC,KAAK,QAAQ,CAAC,GAAG,EAAE,CAAC,CAAC;IACjD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAKD,MAAM,UAAU,qBAAqB,CAAC,SAAiC,EAAE,OAA2C;IAClH,OAAO,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC;AAClF,CAAC;AAGD,MAAM,CAAC,MAAM,mBAAmB,GAAG,EAAE,CAAC;AAYtC,MAAM,UAAU,0BAA0B,CAAC,GAAY;IACrD,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC;QAAE,OAAO,SAAS,CAAC;IAC1C,MAAM,GAAG,GAAa,EAAE,CAAC;IACzB,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;IAC/B,KAAK,MAAM,KAAK,IAAI,GAAG,EAAE,CAAC;QACxB,IAAI,GAAG,CAAC,MAAM,IAAI,mBAAmB;YAAE,MAAM;QAC7C,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,IAAI,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC;YAAE,SAAS;QACpD,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QAChB,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAClB,CAAC;IACD,OAAO,GAAG,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC;AAC1C,CAAC;AAeD,MAAM,UAAU,sBAAsB,CACpC,UAA8B,EAC9B,GAA+C;IAE/C,OAAO,UAAU,IAAI,CAAC,GAAG,CAAC,SAAS,KAAK,UAAU,CAAC,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;AAClF,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sema-agent/server",
3
- "version": "1.196.0",
3
+ "version": "1.198.0",
4
4
  "description": "Sema Server — the server/API implementation layer for Sema, wiring core, registry, model providers, and cloud agent execution. Built on @sema-agent/core.",
5
5
  "type": "module",
6
6
  "license": "BUSL-1.1",
@@ -17,6 +17,7 @@
17
17
  "!dist/sema-server*",
18
18
  "!dist/run-local-linux-x64",
19
19
  "!dist/run-local-darwin-arm64",
20
+ "skills",
20
21
  "deploy/sema-up"
21
22
  ],
22
23
  "bin": {
@@ -0,0 +1,28 @@
1
+ ---
2
+ name: code-review-rubric
3
+ description: How to produce a prioritized, actionable code review (severity, path:line, concrete fixes).
4
+ scenarios: code-review
5
+ ---
6
+ # Code review rubric
7
+
8
+ Produce a review that an engineer can act on immediately.
9
+
10
+ ## Output shape
11
+ 1. **Summary** — 2–3 lines: overall health + the single most important thing to fix.
12
+ 2. **Findings**, grouped by severity:
13
+ - **Blocker** — correctness/security bug, data loss, or a crash path. Must fix before merge.
14
+ - **Major** — likely bug, missing error handling, race, leak, or a design problem.
15
+ - **Minor** — style, naming, small simplification, nit.
16
+ 3. Each finding: `path:line — what's wrong — concrete fix`. Cite the actual code; reference real symbols.
17
+
18
+ ## What to look for
19
+ - **Correctness**: edge cases, off-by-one, race conditions, resource leaks, missing/incorrect error paths, unhandled rejections.
20
+ - **Security**: authz/ownership checks, input validation, injection (SQL/command/path), secrets in code/logs, unsafe deserialization, SSRF.
21
+ - **Architecture**: module boundaries, coupling, data flow, error propagation, concurrency model, idempotency of side effects.
22
+ - **Performance**: hot paths, N+1 queries, unnecessary allocations/copies, blocking IO on the request path, missing caching/pooling.
23
+ - **Tests**: are the risky paths covered? Do tests assert behavior, not implementation?
24
+
25
+ ## Discipline
26
+ - **Read before you judge.** Use `repo_tree` to map the repo, then `repo_read_file` for the files you assess. Never invent files, lines, or symbols.
27
+ - Prefer a few high-signal findings over an exhaustive list of nits.
28
+ - When delegating to specialist reviewers, give each a clear scope; merge their findings, de-duplicate, and resolve disagreements.
@@ -0,0 +1,77 @@
1
+ ---
2
+ name: commit-push-pr
3
+ description: Commit the current changes, push the branch, and open (or update) a pull request — one clean commit, a short PR title, and a Summary / Test plan body.
4
+ ---
5
+
6
+ ## Context
7
+
8
+ Gather the real state first — run these and read the output:
9
+
10
+ ```bash
11
+ whoami
12
+ git status
13
+ git diff HEAD
14
+ git branch --show-current
15
+ git log --oneline <base>..HEAD # commits that will ride in the PR
16
+ git diff <base>...HEAD # the FULL diff the PR will contain
17
+ gh pr view --json number 2>/dev/null || true # does a PR already exist?
18
+ ```
19
+
20
+ `<base>` is the default branch (usually `main` or `master`; check
21
+ `git remote show origin` if unsure).
22
+
23
+ ## Git Safety Protocol
24
+
25
+ - NEVER update the git config
26
+ - NEVER run destructive/irreversible git commands (like push --force, hard reset, etc) unless the user explicitly requests them
27
+ - NEVER skip hooks (--no-verify, --no-gpg-sign, etc) unless the user explicitly requests it
28
+ - NEVER force push to main/master; warn the user if they request it
29
+ - Do not commit files that likely contain secrets (.env, credentials.json, etc)
30
+ - Never use git commands with the -i flag (like git rebase -i or git add -i) since they require interactive input which is not supported
31
+
32
+ ## Your task
33
+
34
+ Analyze all changes that will be included in the pull request, making
35
+ sure to look at all relevant commits (NOT just the latest commit, but
36
+ ALL commits that will be included in the pull request from the
37
+ `git diff <base>...HEAD` output above).
38
+
39
+ Based on the above changes:
40
+
41
+ 1. Create a new branch if you are currently on the default branch
42
+ (prefix the branch name with the username from `whoami`, e.g.
43
+ `username/feature-name`).
44
+ 2. Create a single commit with an appropriate message:
45
+
46
+ ```bash
47
+ git commit -m "$(cat <<'EOF'
48
+ Commit message here.
49
+ EOF
50
+ )"
51
+ ```
52
+
53
+ 3. Push the branch to the repo's remote (usually `origin`; use the
54
+ remote this repo is actually configured with), with `-u` on first
55
+ push.
56
+ 4. If a PR already exists for this branch (check the `gh pr view`
57
+ output above), update the PR title and body using `gh pr edit` to
58
+ reflect the current diff. Otherwise, create a pull request using
59
+ `gh pr create` with the multi-line body syntax shown below.
60
+ - IMPORTANT: Keep PR titles short (under 70 characters). Use the
61
+ body for details.
62
+
63
+ ```bash
64
+ gh pr create --title "Short, descriptive title" --body "$(cat <<'EOF'
65
+ ## Summary
66
+ - What changed and why, in a few bullets
67
+
68
+ ## Test plan
69
+ - How this was verified (commands run, flows driven, or why not applicable)
70
+ EOF
71
+ )"
72
+ ```
73
+
74
+ You have the capability to call multiple tools in a single response.
75
+ You MUST do all of the above in a single message.
76
+
77
+ Return the PR URL when you're done, so the user can see it.
@@ -0,0 +1,112 @@
1
+ ---
2
+ name: dataviz
3
+ description: Use this skill whenever you are about to create ANY chart, graph, plot, dashboard, or data visualization, in ANY output medium - an HTML or React artifact, inline SVG, plotting code in any library (matplotlib, plotly, d3, Recharts, ...), or an image/PNG you will render and share. Read it BEFORE writing the first line of chart code, choosing chart colors, building a stat tile / meter / KPI row, or laying out a dashboard. Produces visualizations that read as one system - elegant, accessible, consistent in light and dark - using a brand-neutral placeholder palette you swap for your own. Teaches a design-system-agnostic method: a form heuristic, a color formula with a runnable validator, mark specs, and interaction rules. A validated default palette is documented in `references/palette.md` - swap that file's values for your brand's. Triggers on: "chart", "graph", "plot", "data viz", "visualization", "dashboard", "analytics", "visualize data", "categorical colors", "sequential / diverging palette", "stat tile", "sparkline", "heatmap", "legend", "axis", "tooltip", "chart colors", "color by series".
4
+ ---
5
+
6
+ # Data Visualization
7
+
8
+ A chart is **read by people and executed by you**. This skill turns "make it look
9
+ good" into a procedure with checks, so the result is right by construction rather
10
+ than by taste.
11
+
12
+ **The method here is design-system-agnostic.** Nothing in the procedure, the form
13
+ heuristic, the six checks, or the mark specs is specific to one product. A design
14
+ system supplies a small set of *parameters* (its ramps, a categorical order, a
15
+ diverging pair, a status palette, a texture, its surfaces, its filter components);
16
+ the method consumes them unchanged. A **validated default palette** is the
17
+ reference instance, fully specified in `references/palette.md`. To target your
18
+ brand, read that file's structure and substitute its values — touch nothing else.
19
+
20
+ > The single most important habit: **the color part is computable, so compute it.**
21
+ > Never eyeball whether a palette is colorblind-safe — run `scripts/validate_palette.js`.
22
+
23
+ ## The procedure — do these in order
24
+
25
+ Color comes LAST. Most bad charts pick colors first.
26
+
27
+ 1. **Pick the form.** What is the data's job — magnitude, identity, polarity, a
28
+ single headline, change-over-time? The job picks the chart type, and sometimes
29
+ the answer is *not a chart* (a stat tile or hero number). → `references/choosing-a-form.md`
30
+ 2. **Assign color by the job it does.** Categorical (identity), sequential
31
+ (magnitude), diverging (polarity), or status (state) — each has one rule.
32
+ Assign categorical hues in fixed order, never cycled. → `references/color-formula.md`
33
+ 3. **VALIDATE the palette — run the script, don't reason about ΔE.**
34
+ `node scripts/validate_palette.js "<hex,hex,…>" --mode light` (relative to
35
+ this skill's base directory — or load it as `<script type="module">` in the
36
+ chart's own page, where it reads
37
+ `data-palette` off `<body>` and logs a `console.table` report). It returns
38
+ pass/fail on the lightness band, chroma floor, adjacent-pair CVD separation,
39
+ and contrast. Fix anything that FAILs before continuing. Re-run for
40
+ `--mode dark` with that mode's surface.
41
+ 4. **Apply mark specs & spacers.** Thin marks, 4px rounded data-ends anchored to
42
+ the baseline, 2px lines, ≥8px markers, a 2px surface gap between fills (stacked
43
+ segments and adjacent bars alike) and a 2px surface ring on overlapping marks,
44
+ selective direct labels. → `references/marks-and-anatomy.md`
45
+ 5. **Add the hover layer — by default.** An HTML/SVG chart *is* interactive; ship
46
+ a crosshair+tooltip on line/area and a per-mark hover tooltip on bar/dot/cell.
47
+ The only form that skips it is a bare stat tile with no plot. Hit targets bigger
48
+ than the mark; filters in one row above the charts. → `references/interaction.md`
49
+ 6. **Final accessibility pass.** For ≥ 2 series a legend is always present and ≤ 4
50
+ are also direct-labeled (a single series needs no legend box — the title names
51
+ it), so identity is never color-alone; a table view exists; dark mode is **selected** — its own
52
+ steps from the same ramps, validated against the dark surface, not an automatic
53
+ flip; texture is available for the CVD/print/forced-colors case.
54
+ 7. **Render it and look at it.** The validator checks color, not layout — open or
55
+ screenshot the output and eyeball it for label collisions, geometry, and overflow
56
+ before calling it done.
57
+
58
+ Then check the result against **`references/anti-patterns.md`** — it is the catalog
59
+ of what goes wrong. If your chart matches an entry, it's wrong.
60
+
61
+ ## Non-negotiables (true in every design system)
62
+
63
+ - **Assign categorical hues in fixed order, never cycled.** A 9th series is never a
64
+ generated hue — it folds into "Other," small multiples, or composite encoding.
65
+ - **One axis.** Never a dual-axis chart (two y-scales). Two measures of different
66
+ scale → two charts, small multiples, or indexed to a common base. *(This is the
67
+ #1 chart mistake — see anti-patterns.)*
68
+ - **Color follows the entity, never its rank.** A filter that changes the series
69
+ count must not repaint the survivors.
70
+ - **Sequential = one hue, light→dark. Diverging = two hues + a neutral gray
71
+ midpoint.** Never a rainbow; never a hue at the diverging midpoint.
72
+ - **Run the validator before shipping any categorical palette.** CVD ≥ 12 is the
73
+ target; 8–12 is a floor that is legal ONLY with secondary encoding. A contrast WARN
74
+ obligates visible labels or a table view — it is not dismissable.
75
+ - **Thin marks; a legend always present for ≥ 2 series (none for one), with
76
+ selective direct labels (never a number on every point); recessive grid/axes.**
77
+ - **Text wears text tokens, never the series color** — values, labels, and legends
78
+ stay in primary/secondary/muted ink; a colored mark beside them carries identity.
79
+ - **Status colors are reserved** (good/warning/serious/critical) and never reused
80
+ for "series 4"; they ship with an icon + label, never color alone.
81
+
82
+ ## Plugging in a design system
83
+
84
+ The method is invariant; only these parameters change per system. The reference
85
+ instance — every value filled in — is `references/palette.md`.
86
+
87
+ | Parameter | What the system provides |
88
+ |---|---|
89
+ | **Ramps** | the hue scales (named steps) the palette draws from |
90
+ | **Categorical theme** | the fixed hue order (a named theme); default + alternates |
91
+ | **Sequential hue** | the default single hue for magnitude |
92
+ | **Diverging pair** | two warm/cool poles + a neutral midpoint |
93
+ | **Status palette** | good / warning / serious / critical — steps distinct from categorical |
94
+ | **Texture fill** | one directional hand-drawn fill, used at 45° / 135° |
95
+ | **Surfaces** | light & dark chart-surface colors (the validator needs these) |
96
+ | **Filter controls** | date-range & dimension controls (behavioral spec in `interaction.md`) |
97
+
98
+ To onboard a new system: fill those rows, feed its ramps to the validator, and let
99
+ it snap each slot to the nearest passing step. Structure and rules stay as written.
100
+
101
+ ## Reference files
102
+
103
+ | File | What it answers |
104
+ |------|-----------------|
105
+ | `references/choosing-a-form.md` | Which chart type / is it even a chart? |
106
+ | `references/color-formula.md` | The four jobs, the six checks, snap-to-passing |
107
+ | `references/marks-and-anatomy.md` | Mark specs, spacers, labels, figures, hero number |
108
+ | `references/interaction.md` | Tooltips & hover, filters & time ranges |
109
+ | `references/components.md` | The pieces a chart is made of — build each in plain HTML |
110
+ | `references/anti-patterns.md` | **What goes wrong — check every chart against this** |
111
+ | `references/palette.md` | **The reference palette instance** — every parameter, filled in; swap for your brand's |
112
+ | `scripts/validate_palette.js` | Runnable six-checks validator (run it; don't eyeball) |
@@ -0,0 +1,119 @@
1
+ # Anti-patterns — what goes wrong
2
+
3
+ Check every chart against this list. If your output matches an entry, it is wrong —
4
+ fix it before shipping. These are real failure modes, each caught in shipping
5
+ dashboards.
6
+
7
+ ## Color & encoding
8
+
9
+ **❌ Dual-axis charts (two y-scales on one plot).**
10
+ Why it misleads: the alignment of the two scales is arbitrary, so the chart invents a
11
+ correlation that isn't in the data. Real example: an "Adoption" chart plotting Users
12
+ (0–30k) against Sessions (0–800k) — a reviewer flagged it as looking "hallucinated."
13
+ ✅ Do instead: two charts, small multiples, or index both series to a common base
14
+ (=100 at t0) on **one** axis.
15
+
16
+ **❌ Recolor-on-filter.** Assigning colors by current rank, so filtering out a series
17
+ repaints the survivors.
18
+ Why: a reader who learned "Acme is blue" is now misled.
19
+ ✅ Color follows the entity, not its row number. Survivors keep their hue.
20
+
21
+ **❌ Cycling / generating hues past 8.** A 9th categorical color, generated or reused.
22
+ Why: indistinguishable from an existing slot under CVD; breaks the order check.
23
+ ✅ Fold the tail into "Other," facet into small multiples, or use composite encoding.
24
+
25
+ **❌ Eyeballing colorblind-safety.** "These look different enough."
26
+ ✅ Run `scripts/validate_palette.js`. Adjacent ΔE ≥ 12, or 8–12 WITH secondary encoding.
27
+
28
+ **❌ A value-ramp on nominal categories.** Coloring each bar darker-where-bigger
29
+ when the categories have no natural order (products, teams, endpoints).
30
+ Why: it double-encodes bar length as hue, burns the only free channel on
31
+ information the chart already shows, and fails the categorical checks by design
32
+ (a ramp spans the lightness band and drops below the chroma floor).
33
+ ✅ One series → one color (slot 1) for every bar. Ordered categories (funnel,
34
+ tiers, age bands) → the ordinal ramp, validated with `--ordinal`.
35
+
36
+ **❌ Rainbow / non-neighbor sequential.** A multi-hue ramp for magnitude.
37
+ ✅ One hue, light→dark. (Analogous neighbors or semantic heat are the only multi-hue
38
+ sequential exceptions, always with a scale legend.)
39
+
40
+ **❌ A hue at the diverging midpoint, or two cool hues as the two poles.**
41
+ Why: the midpoint must read as "nothing"; poles must read as opposite. blue↔aqua
42
+ fails this (both cool); blue↔red or blue↔orange succeed (warm/cool).
43
+ ✅ Two hues that read as opposite + a neutral gray midpoint.
44
+
45
+ **❌ Status color used for a non-status series** (or a series color used for status).
46
+ ✅ Status tokens only when the color *means* good/bad; categorical when it's identity.
47
+
48
+ ## Form
49
+
50
+ **❌ Eight categorical hues when the story is one number.** The most common way a
51
+ chart misses its point.
52
+ ✅ Emphasis (highlight one, gray the rest), or a stat tile / hero number.
53
+
54
+ **❌ A one-bar bar chart, or a 2-slice pie.**
55
+ ✅ A stat tile. The number is the chart.
56
+
57
+ **❌ A donut/pie for comparing close values.**
58
+ ✅ A bar, or the numbers. Part-to-whole at a glance only, ≤ 6 segments.
59
+
60
+ **❌ More than ~7 color classes carrying meaning.**
61
+ ✅ A table, or table + chart. Past ~7 bins, adjacent classes blur.
62
+
63
+ ## Marks & chrome
64
+
65
+ **❌ Thick saturated blocks, heavy gridlines, no breathing room.** Reads loud, even
66
+ childish, at scale.
67
+ ✅ Thin marks, hairline recessive grid/axes, generous padding. Saturated fills are
68
+ for small marks and accents, never large blocks.
69
+
70
+ **❌ Dashed gridlines or axis rules.** Dashing adds visual noise and reads as
71
+ "projection" or "threshold" when it's just a grid.
72
+ ✅ Gridlines and axes are solid hairlines, one shade off the surface.
73
+
74
+ **❌ A number on every data point.** A value beside every dot or segment is chaos and goes unread.
75
+ ✅ A legend is always present for ≥ 2 series; direct-label *selectively* (the endpoint, the extreme, the one series that matters) and let the axis + tooltip carry the rest.
76
+
77
+ **❌ A border drawn around marks to separate them.**
78
+ ✅ A 2px surface gap between fills (stacked segments and adjacent bars alike) and a 2px surface ring (on overlapping markers).
79
+
80
+ **❌ A label clipped by, or overflowing, a too-small bar or stacked segment** —
81
+ including `overflow: hidden` cropping the first/last characters of an in-segment label.
82
+ ✅ Only render a label inside a mark when it fits with padding; otherwise move it
83
+ outside the bar end, or drop it to the tooltip/legend (the value stays in the table view).
84
+
85
+ **❌ A chart container whose fixed height excludes the x-axis band** — the plot
86
+ fits, the axis labels don't, so the card gets a tiny nested vertical scroll.
87
+ ✅ Size the container to include the axis labels (plot height + x-axis band),
88
+ or let the container grow with its content instead of fixing a height.
89
+
90
+ **❌ A display or serif face on the hero figure.** It reads as off-brand decoration.
91
+ ✅ The hero figure uses the same sans as everything else.
92
+
93
+ **❌ `tabular-nums` on a large standalone number.** Equal-width digits make `121`
94
+ look loose at display sizes.
95
+ ✅ Proportional figures on hero and stat-tile values; `tabular-nums` only where
96
+ numbers align vertically (table rows, axis ticks).
97
+
98
+ **❌ Texture on by default, or as decoration.** Dense angled fields are a vestibular
99
+ risk and read as noise on value scales.
100
+ ✅ Texture is opt-in (a11y setting, print, forced-colors), 45°/135° only, ordered on
101
+ value scales.
102
+
103
+ ## Interaction & accessibility
104
+
105
+ **❌ A tooltip as the only way to read a value.**
106
+ ✅ Tooltips enhance, never gate — every value is also reachable via direct labels or
107
+ the table view; keyboard focus shows the same as hover.
108
+
109
+ **❌ Pinpoint hover targets — an 8px scatter dot you must land on dead-center.**
110
+ ✅ The hit area includes the 2px gap and meets a ~24px minimum; dense scatter uses a nearest-point / Voronoi layer.
111
+
112
+ **❌ Per-chart filters, or filters inside a chart card.**
113
+ ✅ One filter row above everything it scopes; all charts re-render against the same slice.
114
+
115
+ **❌ Skeleton flash on refetch.**
116
+ ✅ Hold the previous render at reduced opacity — no layout jump.
117
+
118
+ **❌ No table view / color-only encoding on a continuous scale.**
119
+ ✅ Every chart has a table-view twin (the WCAG-clean equivalent).
@@ -0,0 +1,57 @@
1
+ # Choosing a form
2
+
3
+ Decide this **before** color. The data's job picks the form — and sometimes the
4
+ right form is not a chart.
5
+
6
+ ## Is it even a chart?
7
+
8
+ | The data is… | Use | Not |
9
+ |---|---|---|
10
+ | A single current value (+ maybe a trend) | **Stat tile** (value + delta + sparkline) | A one-bar bar chart |
11
+ | A handful of headline numbers | **KPI row** of stat tiles | A grouped bar chart |
12
+ | The one number a dashboard leads with | **Hero figure** (≥48px, sans) | — |
13
+ | A single ratio against a limit | **Meter** (same-ramp track) | A pie of 2 slices |
14
+ | More than ~7 classes that all carry meaning | A **table** (or table + chart) | More colors |
15
+
16
+ If a chart *is* right, pick the type by the job:
17
+
18
+ ## The job → the type
19
+
20
+ | Job (what the reader must do) | Default form | Color job |
21
+ |---|---|---|
22
+ | Compare magnitude, low → high | bar / column; **heatmap** for a grid | sequential (one hue) |
23
+ | Trend over time | line; area for a single series | sequential or 1 categorical |
24
+ | Tell distinct series apart | grouped/stacked bar, multi-line | **categorical** |
25
+ | One series is the point, rest are context | **emphasis** (highlight one, gray the rest) | 1 hue + gray |
26
+ | Above/below a baseline; Δ to target | diverging bar, or line vs baseline | diverging |
27
+ | Part-to-whole | **stacked bar** (go horizontal for many / long-named categories) | categorical |
28
+ | Ordered-scale share (Likert, sentiment, agree↔disagree) | **diverging stacked bar**, centered on neutral | diverging |
29
+ | Before → after per item | dumbbell | 1 hue, 2 shades |
30
+
31
+ ## The rules behind the table
32
+
33
+ - **Sequential is the safe default.** One hue, more-is-darker. It stays legible and
34
+ consistent and is hard to misread. Reach for it unless the data's job is
35
+ specifically *identity* or *polarity*.
36
+ - **Categorical is for when the series ARE the subject** — and it has a real cost:
37
+ it can bury the one data point that actually matters. If the story is "this one
38
+ went up," that's **emphasis**, not categorical.
39
+ - **Emphasis** = the most underused form. One series in the accent hue, the rest in
40
+ the de-emphasis gray. Often the honest answer to "make this chart clearer."
41
+ - **Texture is an opt-in expression, not a default form.** It earns its place only
42
+ for accessibility (full CVD), print/export, and `forced-colors`. Never decorative.
43
+ → see `marks-and-anatomy.md`.
44
+
45
+ ## Series-count ladder (categorical)
46
+
47
+ | Series | Treatment |
48
+ |---|---|
49
+ | 1–3 | color alone is comfortable for everyone; direct-label |
50
+ | 4 | the CVD floor enters — direct labels become mandatory, not a courtesy |
51
+ | 5–6 | soft cap; legend or small multiples |
52
+ | 7–8 | token ceiling; past it, fold the tail into "Other," facet into small multiples, or use composite encoding (hue × shape) |
53
+
54
+ Never solve "too many series" by generating more hues. A generated 9th hue is
55
+ indistinguishable from an existing one under CVD and breaks every check.
56
+
57
+ ---
@@ -0,0 +1,113 @@
1
+ # Color formula
2
+
3
+ Color is **not hand-picked**. Every chart color does exactly one of four jobs, and a
4
+ palette is legal only if it passes six checks. The checks are the product — they are
5
+ what makes a palette safe to change and what lets the same method run on any design
6
+ system's ramps.
7
+
8
+ ## The four jobs
9
+
10
+ | Job | What it encodes | Structure |
11
+ |---|---|---|
12
+ | **Categorical** | identity (which series) | 8 hues, fixed order, assigned in sequence, never cycled |
13
+ | **Ordinal** | position in a sequence (funnel stage, tier, bucket) | one hue, monotone lightness steps; light end still ≥ 2:1 on surface |
14
+ | **Sequential** | magnitude (how much) | one hue, steps 100→700, light→dark; flips anchor in dark |
15
+ | **Diverging** | polarity (which side of a baseline) | two hues + a neutral gray midpoint; equal steps per arm |
16
+ | **Status** | state (good→critical) | a small fixed scale, reserved meaning, always icon+label |
17
+
18
+ **Categorical or ordinal?** If swapping the category order would change the
19
+ meaning — funnel stages, size tiers (S/M/L), age bands, cohort buckets — it is
20
+ **ordinal** and takes a one-hue ramp so the reader sees the order in the color.
21
+ If swapping would not — product names, teams, regions, endpoints — it is
22
+ **nominal categorical** and each bar takes the *same* slot-1 hue (one series,
23
+ so no legend box — the title names it), or slots 1..N when there are N separate
24
+ series. Never color nominal bars by their value: that spends the identity channel
25
+ re-encoding what bar length already shows.
26
+
27
+ ## The six checks
28
+
29
+ Every categorical color — current or proposed — must pass all six.
30
+
31
+ 1. **Fixed hue anchors.** Eight families in a fixed order. The order is the
32
+ CVD-safety mechanism; it never changes. *(structural — enforced, not measured)*
33
+ 2. **Lightness band per mode.** OKLCH L ≈ 0.43–0.77 light; ≈ 0.48–0.67 dark. *(validator)*
34
+ 3. **Chroma floor.** OKLCH C ≥ ~0.10 — below it a hue reads as gray and stops doing
35
+ identity work. *(validator)*
36
+ 4. **CVD separation.** Machado-2009 ΔE ≥ 12 target / ≥ 8 floor (floor legal only with
37
+ secondary encoding), under protanopia & deuteranopia. *Adjacent* pairs for
38
+ stacks/bars/lines (only neighbors touch — assignment never skips); **all pairs for
39
+ scatter, bubble, choropleth, and small-multiples**, where any two marks can sit side
40
+ by side — pass `--pairs all` there or a real collapse stays hidden. *(validator)*
41
+ 5. **Contrast vs surface.** ≥ 3:1 for marks; conditionally relaxed where values are
42
+ readable another way (visible labels or the table view). *(validator)*
43
+ 6. **Documented palette only.** Every slot is a hex from the instance file
44
+ (`palette.md` or its equivalent) — no eyeballed values. *(structural; for a
45
+ customer's ramps, snap to nearest — below)*
46
+
47
+ ## Run the checks — never eyeball them
48
+
49
+ ```
50
+ node scripts/validate_palette.js \
51
+ "#2a78d6,#1baf7a,#eda100,#008300,#4a3aa7,#e34948,#e87ba4,#eb6834" --mode light
52
+ ```
53
+
54
+ (`scripts/` is relative to this skill's base directory, shown at the top of the prompt.)
55
+
56
+ (or load it as `<script type="module">` in the chart's own page — it reads
57
+ `data-palette` off `<body>` and logs a `console.table` report)
58
+
59
+ Reports each computable check (2–5) with PASS / WARN / FAIL plus the worst CVD pair.
60
+ Exit 0 = no hard FAIL (WARN bands — floor-band CVD 8–12 and sub-3:1 contrast relief —
61
+ still exit 0 and require secondary encoding); exit 1 on any FAIL. Run once per mode
62
+ (`--mode dark --surface "#1a1a19"`), and add
63
+ `--pairs all` for scatter / bubble / map / small-multiples charts (where any two marks
64
+ can be neighbors — the default adjacent check would hide a collapse). For an
65
+ **ordinal** ramp pass `--ordinal` — it switches to the ramp checks (monotone L,
66
+ adjacent ΔL ≥ 0.06, light-end contrast ≥ 2.0:1, single hue) instead of the
67
+ categorical six.
68
+ A WARN on CVD (8–12 floor) is legal **only** if you also ship secondary encoding
69
+ (direct labels, gaps, or texture). A WARN on contrast is **not dismissable** — it
70
+ obligates a relief channel (visible direct labels or the table view); shipping the
71
+ sub-3:1 fill with neither is a fail.
72
+
73
+ **Scope — what the validator does and doesn't cover.** These six checks validate a
74
+ *categorical* palette (series identity). They do **not** judge a lone status/text
75
+ color or a sequential ramp. For a single status or text color, run a WCAG *text*-
76
+ contrast check (4.5:1 normal, 3:1 large) — `validate_palette.js` exports
77
+ `contrast(a, b)` for exactly this. For sequential/diverging, the check is lightness
78
+ monotonicity across the ramp, not adjacency CVD — running the categorical validator on
79
+ a sequential ramp **will FAIL by design** (it spans the band; steps sit close), which
80
+ is expected, not a real failure; don't "fix" a good ramp to satisfy it.
81
+
82
+ ## Snap-to-passing (any design system)
83
+
84
+ Given a customer's ramps and a desired order:
85
+ 1. For each slot, pick the step whose OKLCH L sits in the mode's band and C ≥ floor.
86
+ 2. Run the validator. For any adjacent pair below ΔE 12, nudge one slot ± a step
87
+ (hold its hue, move its lightness) and re-run.
88
+ 3. Repeat until the worst adjacent pair clears the floor. Function preserved, the
89
+ customer's hues kept.
90
+
91
+ ## Themes
92
+
93
+ The slot **order** is a separable, named choice — a *theme* — on the same hues and
94
+ the same six checks. Each design system names a default order and any alternates;
95
+ swapping themes tunes the mood without touching the method. A surface adopts one
96
+ theme and freezes it; never mix themes within a dashboard. (See `palette.md`.)
97
+
98
+ **Deriving an order when a system has no theme yet:** don't guess. Enumerate candidate
99
+ orderings of the system's hues, run the validator on each, and pick the one that
100
+ maximizes the *minimum adjacent* CVD ΔE. (Seeding from a known-good order by hue-family
101
+ analogy, then optimizing, is fine — this is exactly how the default in
102
+ `palette.md` was derived.)
103
+
104
+ ## Status is fixed
105
+
106
+ Status never follows the theme — it is a small fixed scale (good → warning → serious
107
+ → critical) with reserved meaning, on steps deliberately distinct from the categorical
108
+ slots so a status color never impersonates a series, and always paired with an
109
+ icon + label (on a light surface warning and serious sit below 3:1 by design —
110
+ the pairing is the mitigation). (Exact steps in `palette.md`.) The collision rule: when a series *means* good/bad (error rate, pass/fail) it wears
111
+ status tokens; when it's just "series 4" it wears categorical — never both in one chart.
112
+
113
+ ---
@@ -0,0 +1,39 @@
1
+ # Components — the pieces a chart is made of
2
+
3
+ A chart is built from these parts, assembled in plain HTML/SVG. Tier 0 is the
4
+ foundation everything mounts on; the System tier is what makes the method
5
+ portable (and is, itself, this skill).
6
+
7
+ ## Tier 0 — Foundations
8
+ - **Color roles** — categorical (8 × light/dark), sequential ramps, diverging pairs,
9
+ status (4), de-emphasis / "Other", grayscale chart furniture (axis/grid/label/surface).
10
+ Defined as CSS custom properties at the top of the HTML — see `palette.md`.
11
+ - **Texture fill** — the directional fill + 45°/135° rotations.
12
+ - **Chart container** — a `<figure>` (or card `<div>`) that owns responsive
13
+ sizing, title/caption, and the **table-view toggle** (the accessibility twin
14
+ of every chart). **Any fixed height includes the x-axis band** (plot height
15
+ + axis labels) so the card never gets a nested vertical scroll; prefer
16
+ letting the container grow with its content.
17
+ - **Legend** (toggle-to-isolate, texture-aware swatches) · **Tooltip** · **Axis** · **Data label**.
18
+
19
+ ## Tier 1 — The charts people ask for
20
+ - **Bar chart** — grouped + stacked, thin-bar default, horizontal + vertical.
21
+ - **Line chart** — multi-series, soft-fill area variant, accessibility markers.
22
+ - **Stat tile** — value + delta + optional sparkline (the figure contract).
23
+ - **Meter / progress track** — same-ramp tracks.
24
+
25
+ ## Tier 2 — Rounding out the kit
26
+ - **Area chart** (stacked, band-edge = line) · **Sparkline** · **Heatmap**
27
+ - **Scale legend** (sequential / diverging) · **Chart filters / time range** · **Empty state**
28
+
29
+ ## System tier — becomes the skill
30
+ - **Six-checks validator** — `scripts/validate_palette.js` (palette validation).
31
+ - **Theming engine** — snap a customer's ramps to passing values (color-formula.md).
32
+ - **Chart-type heuristic** — pick the form (choosing-a-form.md).
33
+ - **Table-view generator** — the WCAG-clean equivalent of any chart.
34
+
35
+ Notes: part-to-whole rides on the stacked bar chart; donut stays deprioritized.
36
+ Small multiples is a layout pattern over these, not a separate piece. Scatter
37
+ joins Tier 2 if scatter-heavy surfaces land.
38
+
39
+ ---