Files
paperclip/.agents/skills/directions-lab/references/design-proposals-storybook.md
T
TonioandClaude Opus 5.5 dfedd73f56 feat(skills): add the directions-lab skill and its lab generator (#17437)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - Design explorations for the board UI are drawn as coded "directions
labs": one page that shows what ships today next to numbered design
directions, on every surface a feature appears. The artifact directions
lab (#17213) is the first one, and it fed the Artifacts V1 work
(#17228).
> - That lab was built by hand. Each new lab would repeat the same
plumbing: the URL state, the toolbar, the theme and animation toggles,
the fetch stub, the frames, the capture script and the Storybook
registration.
> - Labs should also look and move the same way, so a shared link reads
the same to everyone, and the design-proposals Storybook should be the
place where every exploration is registered.
> - This pull request adds the `directions-lab` agent skill. It asks how
many directions and which surfaces, then a generator scaffolds a
complete, verified lab in one command and registers it in the
design-proposals Storybook.
> - The benefit is that a designer gets a working lab with production
next to the directions in minutes, and the agent spends its time on the
drawing, not on the plumbing.

## Linked Issues or Issue Description

Refs #17213 (the artifact directions lab, the precedent the templates
were derived from) and #17228 (Artifacts V1, which shipped a direction
from that lab).

**Subsystem affected**

Agent skills (`.agents/skills/`) for UI design work, the `ui/` Vite
build, and the design-proposals section of the Storybook
(`ui/storybook/stories/design-proposals/`).

**Problem or motivation**

A directions lab is the repo's way to compare design directions with
production, but there is no repeatable way to make one. The artifact lab
took many sessions of hand work, and a second lab would copy that work
file by file and drift from the first. The animation details, the
toggles and the Storybook metadata are easy to get wrong by hand.

**Proposed solution**

Add a skill that does the intake (two questions: how many directions,
which surfaces) and a generator, `scripts/scaffold.mjs`, that writes a
self-contained lab from templates derived from the artifact lab. The
generator verifies the lab with the catalog check, the lab's tests and a
Vite build, and removes everything again when a check fails. Every
direction carries the V1 MVP hover motion verbatim, and a test in the
skill fails when the precedent's motion block changes.

**Alternatives considered**

A shared runtime under `ui/src/directions-lab/` that every lab imports.
It removes the per-lab copies, but it is product code under typecheck
and a much larger change; it stays a follow-up. Instructions only, with
no generator: the agent would rebuild the plumbing every time and the
labs would drift.

**Roadmap alignment**

This fits the skills layer in `ROADMAP.md` (reusable procedures for
agents). It does not change the Skills Manager, Skill Studio or the
Skills Store, and it adds no product feature.

## What Changed

- Add `.agents/skills/directions-lab/` with `SKILL.md`,
`agents/openai.yaml` (Codex metadata) and the
`.claude/skills/directions-lab` symlink, the same layout as the other
repo skills.
- Add `scripts/scaffold.mjs` with the subcommands `new`, `check`,
`add-direction` and `list`, plus `scripts/lib/*`.
- `new` renders in memory and refuses comment lines, files of 600 lines
or more, and existing paths.
- It then writes the lab, two `.claude/launch.json` entries and one
`.gitignore` line.
  - It verifies the result, and rolls everything back on failure.
- Add 34 templates under `assets/templates/` and the motion block
`assets/motion/v1-mvp-motion.css`, derived from
`ui/src/artifact-directions` at the commit recorded in
`assets/PRECEDENT`.
- A generated lab has:
  - an html entry and a Vite config;
- a registry that the toolbar, keyboard digits, Board and capture script
read at runtime;
- a toolbar whose surfaces row is written only for a multi-surface lab;
- a Today kit stub, one kit per direction with a motion specimen, five
Vitest files and `shot.cjs`;
- a design-proposals story with meta id `design-<slug>-directions` and a
literal `parameters.paperclip` block (`status: "proposed"`, `home:
"design-proposals"`).
- Add ten references:
  - lab anatomy;
  - Today, fixtures, build and deploy;
  - the motion spec;
  - questions and defaults;
  - drawing phases;
  - design-proposals Storybook registration;
  - lifecycle;
  - repo gates;
  - the generator;
- `canonical-directory.md`, the paste-ready governance diffs that would
make the design-proposals section the source of truth. They are for a
separate pull request and are not applied here.
- Add three eval cases under `evals/`: a two-surface lab with three
directions, a one-surface lab with two directions, and a terse brief
that must make the skill ask how many directions.

## Verification

- `node --test .agents/skills/directions-lab/scripts/scaffold.test.mjs`:
29 tests pass in under a second. The tests cover flag parsing, context
derivation and refused inputs, ports, the write guards and the
shared-file edits. They also render the three eval shapes into a
temporary root, check the surfaces-row rule and the mode-B story status
rule, and run the motion drift test against
`ui/src/artifact-directions/directions/d5-v1-mvp/tokens.css`.
- A real scaffold into this checkout at the branch head:

  ```sh
node .agents/skills/directions-lab/scripts/scaffold.mjs new --slug
prsmoke --title "PR smoke" --surfaces
thread:Task-thread:960@tasks,panel:Right-panel:434@tasks --directions 3
  ```

The built-in checks passed: `node scripts/storybook-catalog.mjs
--check`, `npx vitest run src/prsmoke-directions` and the lab's Vite
build. The lab was then removed, and `git status` shows only the skill.
- Browser check on a scaffolded lab (composer, two surfaces, three
directions), done earlier in the same session:
- The toolbar shows both rows with Today last as `0.Today`, and the
theme and animation toggles.
- A hovered motion specimen computes `translateY(-6px)` on the front,
and the backings turn ±5° from their resting angles with a 300 ms
transition.
  - After pressing `a`, nothing moves.
- `add-direction` and `check --json` were proven on a second scaffolded
lab: the toolbar legend reads `0–3` from the registry without a hand
edit.
- The root Vitest run is unaffected: `vitest.config.ts` lists explicit
projects, and `.agents` is not one of them. The skill tests with `node
--test` like the sibling skills.
- Not run: `pnpm -r typecheck`, `pnpm build` and `pnpm build-storybook`.
This change adds only files under the git-ignored `.agents/` tree plus
one symlink, and touches no product code.

## Risks

Low risk. The pull request adds a skill and a symlink and changes no
product code, no dependencies and no lockfile. A lab that someone later
scaffolds lands under `ui/src/`, where `tsc -b` and `pnpm test` see it.
The generator verifies each lab and rolls back on failure, so a
half-written lab is not left behind. `/.agents/` is git-ignored, so
later edits to the skill must be force-added (`git add -f`), as for the
other repo skills. The templates are copies of the precedent. When the
artifact lab changes, the skill's drift test fails until the templates
are updated and `assets/PRECEDENT` is bumped.

> For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and
discuss it in `#dev` before opening the PR. Feature PRs that overlap
with planned core work may need to be redirected — check the roadmap
first. See `CONTRIBUTING.md`.

## Model Used

- Claude Fable 5.1 (Anthropic), model ID `claude-fable-5-1`, with
extended thinking, for most of the work: mapping the precedent, the
references, the generator design and implementation, and the proofs.
Claude Opus 5.5 (Anthropic), model ID `claude-opus-5-5`, for the final
cleanup and this pull request. Both ran in Claude Code with tool use
(file edits, shell, the in-app browser) and multi-agent workflows for
parallel mapping, judging and verification.

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [ ] All Paperclip CI gates are green
- [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-10-26 12:26:44 -07:00

29 KiB
Raw Blame History

Design-proposals Storybook - registering a lab and graduating a direction

For the agent scaffolding a new directions lab under ui/storybook/stories/design-proposals/. Line numbers confirmed at 5e26b733b; re-check after the cited files change. Working directory: repo root.

Contents

  1. The catalog gate
  2. The proposal theme
  3. Precedent: the two artifact lab stories
  4. The story file new writes
  5. Lifecycle: proposed, adopted, current, historical
  6. Source-of-truth note

1. The catalog gate

scripts/storybook-catalog.mjs stands between a story file and the sidebar. It parses every ui/storybook/stories/**/*.stories.(ts|tsx|mdx) with Storybook's CSF parser and never executes the module (lines 61-67). It reads only the default-export meta's parameters (line 46).

Rules for a story homed in design-proposals:

  • Explicit static meta id (109: "explicit meta ID is required"). Folder convention design-<slug> (18 of 20 files). Story ids are <meta id>--<kebab-case export name>; name does not change them.
  • title starts Design proposals/ (17, 106-108), then <Group>/<Suite>. Group vocabulary (maps/storybook-map/README.md:62-65, DESIGN.md:299-301): Foundations, Components, Layouts, Pages, Patterns, Guidelines, in that order. The pinned sort order in scripts/storybook-feature-index.mjs:11 adds "AI Generated Test Pages" between Pages and Patterns and is asserted by scripts/storybook-feature-index.test.mjs:19. An unlisted group (today "Artifact directions") validates and sorts after Guidelines in file-discovery order; to pin a lab group, extend line 11 and the test, never the generated preview.tsx. No empty groups (README.md:65).
  • File path starts ui/storybook/stories/design-proposals/ (107). Subfolders pass: files() recurses (19-24) and the check is startsWith.
  • Exactly one literal parameters.paperclip on the meta (45-59): parameters is an object literal (47) with one paperclip key (49), non-computed (51), and no ...spread or [computed] key after it (52-53), so put it last. Values are string, boolean, number, array or object literals; as and satisfies wrappers are unwrapped (26-43). Template literals, identifiers, calls and null throw "metadata must be literal; expressions are not executed". To omit a key, leave it out.
  • Closed field set (16): capabilities, status, implementation, boundary, notes, home, exception, replacement. Any other key throws "unknown paperclip field " (57).
  • Enums (13-15): status current | proposed | historical | mixed | unreviewed; implementation production | prototype | mixed | guide | unreviewed; boundary fixtures | fixture-server | static | unreviewed. Omitted status, implementation and boundary default to unreviewed, notes to "" (77).
  • capabilities is required even as [] (94); every id must exist in the feature index (102), no duplicates (104), and [] needs an exception (103). Ids are <feature>/<key> from the ## Sub-features bullets of maps/feature-map/<feature>.md (scripts/feature-map.mjs:28,34; example maps/feature-map/documents-artifacts.md:17-21). No design-proposals/* capability or recipe exists (145). Adding a capability means editing the recipe and its agent-access companion, then node scripts/check-maps.mjs; generateStorybook aborts on map errors first (184-185).
  • home may only be foundations or design-proposals (95). home: "design-proposals" forces status: "proposed" (99) and a nonempty exception (96, 100); scripts/storybook-catalog.test.mjs:101-115 asserts current, mixed, unreviewed and historical fail.
  • Uniqueness (112-114): meta ids unique among meta ids; story ids unique among story ids plus the generated feature-map-<featureId>--overview. Avoid a feature-map- prefix by convention. replacement, when present, names an existing story file (110).

Two failure modes: annotation() and literal() throw and abort the whole scan (26-59), so Storybook does not start and tsc never runs; validateCatalog collects errors and throws them joined (83-116, 189-190). A .mdx file passes the filename filter (64) but the parser throws a raw Babel error: emit TSX only, prose goes in parameters.docs.description.component. Only the meta block counts: a story-level parameters.paperclip is ignored, and a file whose only block is story-level fails "declare parameters.paperclip as literal metadata" (47) when the meta has no parameters, else "expected one parameters.paperclip annotation" (49). Storybook merges story parameters at runtime, so a story-level block still reaches the theme decorator (section 2).

How it runs:

  • Storybook start and build: ui/storybook/.storybook/main.ts:10 calls generateStorybook().
  • cd ui && pnpm typecheck is node ../scripts/storybook-catalog.mjs && tsc -b (ui/package.json:23): write mode, then tsc over src and storybook (ui/tsconfig.json:19).
  • CI: .github/workflows/pr-trusted.yml:332-333 runs pnpm run typecheck:build-gaps (package.json:25); scripts/run-typecheck-build-gaps.mjs:143-149,181-182 typechecks every workspace whose build script lacks tsc, and ui builds with vite build (ui/package.json:18). release-verify.yml:118 and docker-runner-check.yml:271 run pnpm -r typecheck.
  • pnpm test runs ui/storybook/map-tooling.test.ts:5-11, which shells out to node --test scripts/check-maps.test.mjs scripts/storybook-catalog.test.mjs scripts/storybook-feature-index.test.mjs. Those are fixture-based: they prove the checker, not your story, which only the paths above and --check validate.

--check: node scripts/storybook-catalog.mjs --check validates without writing (202-206). It still runs checkMaps and resolves storybook/internal/csf-tools through ui/package.json (62-63), so pnpm install comes first. Current output, exit 0: Storybook map: 316 files, 2973 stories. Structure only; no runtime proof. Every failure above is an uncaught throw: exit 1, stack on stderr, no summary line. Any flag other than --write or --check, or two flags, throws a usage error (204).

Every generated output is git-ignored and never committed: ui/storybook/generated/, ui/storybook/.storybook/preview.tsx, maps/generated/, ui/storybook-static/ (.gitignore:11-14; README.md:86-90). Add decorators and fixtures to preview-base.tsx only. Do not record review state in the annotation: fingerprint fails as an unknown field (storybook-catalog.test.mjs:49-50), as reviewedAt would (16, 57); evidence goes in the PR (README.md:107-113).

2. The proposal theme

One decorator, ui/storybook/.storybook/preview-base.tsx:843-857. Line 853, verbatim:

{context.parameters.paperclip?.home === "design-proposals" && context.parameters.designProposalTheme !== false ? <AvatarInitialProvider><div className="design-proposal-theme"><Story /></div></AvatarInitialProvider> : <Story />}

Opt out per meta or per story with parameters: { designProposalTheme: false }. The parameter exists only in that line (no doc names it); its one user is the TodayProductionPalette export the generator writes (section 4). Opting out also drops AvatarInitialProvider, the single-initial fallback-avatar rule for proposals (DESIGN.md:305-308).

All proposal CSS lives in ui/src/index.css in additive blocks keyed on the marker: html:has(.design-proposal-theme) 3916-3933 and 3938-4091 (conversation typography), html.dark:has(.design-proposal-theme) 4097-4111, html:not(.dark):has(...) 4114-4120, element rules 4121-4144 (body and #storybook-root fill 4123, inputs 4124-4125, focus 4129-4140, cards 4141-4144), group labels 4508-4509, canonical toolbar 4625-4698 and 4767. The scope is html:has(...), so the marker anywhere re-themes the whole iframe including Radix portals (comment 4093-4096); a column-wise production-vs-proposal split inside one themed story is impossible. Either opt out and scope a direction's tokens to [data-direction="<id>"] (section 3), or theme every column. Adopting a theme value means lifting it out of the scoped block into :root or .dark and deleting it there; "removing the story marker restores the existing production theme" (4095-4096).

What the theme changes: --font-mono and --default-mono-font-family become --font-paper-mono (Paper Mono; @font-face 126-132, token 214); --font-weight-medium collapses to normal and --font-weight-bold to semibold; --text-micro aliases --text-xs (3917-3922). In dark mode the Paper palette applies: --background: #1a1a1a, --muted-alt: #202020, card and popover equal background, --accent from --muted-highlight, --input from --border, --ring from --foreground (4098-4110). Light inherits production colors and only aliases --muted-alt and four --design-sidebar-* names (4115-4119). No radius changes.

Light and dark come from the toolbar global theme (default dark, 858-872), applied as .dark on <html>. Pin per story with globals: { theme: "light" } (ui/storybook/stories/design-proposals/bulk-agent-settings.stories.tsx:33). Proposals follow the toolbar and never lock a mode (DESIGN.md:150-151). The backgrounds addon is disabled (878-880).

Global for every file: a11y: { test: "error" } (875-877) with @storybook/addon-a11y (main.ts:25), so an axe violation in any direction fails the a11y run; override per story, never in preview-base. docs: { autodocs: true } (main.ts:30-32) plus docs.toc (888-890): every file gets a Docs page from parameters.docs.description.component. Opt-out precedents: tags: ["!autodocs"] (scripts/storybook-feature-index.mjs:49) and docs: { story: { inline: false } } (buttons.stories.tsx:23).

Viewport presets (892-908): mobile 390x844, tablet 834x1112, desktop 1440x960, plus MINIMAL_VIEWPORTS (mobile1 320x568, mobile2 414x896). Select with globals: { viewport: { value: "mobile", isRotated: false } }; add parameters.waitForViewport: true to defer mounting until the iframe has that width (847-850). Declare a custom width per story in parameters.viewport.options (ui/storybook/stories/documents-artifacts/rich-work-product-cards.stories.tsx:249-253). parameters.initialEntries seeds the MemoryRouter (default /PAP/storybook).

3. Precedent: the two artifact lab stories

Both live in ui/storybook/stories/design-proposals/, import the lab from ui/src/artifact-directions/ through the @ alias (main.ts maps @ to ui/src), and share one classification: status: "proposed", home: "design-proposals", implementation: "prototype", boundary: "fixtures", capabilities documents-artifacts/library, documents-artifacts/work-products, documents-artifacts/attachments.

artifact-directions-compare.stories.tsx: id design-artifact-directions-compare, title Design proposals/Artifact directions/Compare (38-40). exception (56): "Design exploration staged in the artifact directions lab: the shipped baseline and five proposed directions compared on static mocks of the task thread, the side panel and the Artifacts page." notes (57) end "nothing here establishes production behaviour or access checks." Mount (21-36): DirectionView({ direction, view }) holds useState<DirectionKit | null>; a useEffect keyed on direction calls installApiStub() (@/artifact-directions/api-stub, ref-counted, returns a restore function; api-stub.ts:50-71), then loadKit(direction) (an import.meta.glob over directions/*/index.tsx, kit.ts:157-163), and restores fetch in the cleanup. It renders <KindsSheet kit> or <SurfaceView kit surface> (frames/SurfaceView.tsx:9-31; surfaces thread | panel | gallery | stack). Controls (60-73): direction is a select over DIRECTION_IDS with labels from KIT_META[id].name (kit.ts:130,143-150); view is a select over [...SURFACE_IDS, "kinds"]; default { direction: "d4-hybrid", view: "gallery" }. One export, AnyDirection, named "Any direction, any surface" (79). The stub exists because the today kit mounts shipped components that fetch /api/; it layers over Storybook's own fetch fixtures (preview-base.tsx:95,785-788). The generator's story template (assets/templates/stories/compare.stories.tsx.mjs) is this file parameterised (section 4).

artifact-directions-hybrid.stories.tsx: id design-artifact-directions-hybrid, title Design proposals/Artifact directions/4.Hybrid (27-29; the suite name is the KIT_META name). Static import { kit as hybrid } (3); meta is satisfies Meta with no component (48). Four Surface · ... exports render <SurfaceView kit={hybrid} surface=... /> (56-74); Kinds renders <KindsSheet> (76-79). Five Slot · ... exports wrap one kit slot in Stage (87-95): <div className="min-h-screen bg-background p-6 text-foreground"> around <Slot kit={hybrid}> (frames/Slot.tsx:8-14 emits data-direction={kit.id}, which scopes a kit's tokens.css) at product widths 960, 1152 and TASK_DETAIL_PANE_WIDTH - 32 (84-85, 139-140). A slot with its own Controls is typed StoryObj<SlotArgs> with local args and argTypes (105-125). No stub: lab-only kits do not fetch.

Classification rule: a lab is implementation: "prototype" even when one direction mounts production primitives ("A prototype that imports production primitives remains a prototype of its outer subject", README.md:46-47), and boundary: "fixtures" because frames are static mocks on fixture data. A kit's tokens.css is tolerated as lab code: the token gate scans only ui/src/components and ui/src/pages (scripts/check-token-gates.mjs:79-80), but DESIGN.md:17 forbids a second token source, so adoption converts kit tokens into index.css tokens.

4. The story file new writes

File: ui/storybook/stories/design-proposals/<slug>-directions.stories.tsx, rendered by node .agents/skills/directions-lab/scripts/scaffold.mjs new from assets/templates/stories/compare.stories.tsx.mjs; <...> marks what the flags fill. Every paperclip value is emitted with JSON.stringify, so it stays a plain double-quoted string literal. The id is design-<slug>-directions (registry.storyId), chosen once; it is permanent.

import type { Meta, StoryObj } from "@storybook/react-vite";
import { useEffect, useState } from "react";
import { installApiStub } from "@/<slug>-directions/api-stub";
import "@/<slug>-directions/api-routes";
import { DIRECTION_IDS, KIT_META, SURFACE_IDS, loadKit, surfaceOf, type DirectionId, type DirectionKit, type SurfaceId } from "@/<slug>-directions/kit";
import { SurfaceView } from "@/<slug>-directions/frames/SurfaceView";

type Args = { direction: DirectionId; surface: SurfaceId; animation: boolean };

function DirectionView({ direction, surface, animation }: Args) {
  const [kit, setKit] = useState<DirectionKit | null>(null);
  useEffect(() => {
    const restoreFetch = installApiStub();
    let live = true;
    loadKit(direction).then((loaded) => {
      if (live) setKit(loaded);
    });
    return () => {
      live = false;
      restoreFetch();
    };
  }, [direction]);
  useEffect(() => {
    const previous = document.documentElement.dataset.labAnim;
    if (animation) document.documentElement.dataset.labAnim = "1";
    else delete document.documentElement.dataset.labAnim;
    return () => {
      if (previous === undefined) delete document.documentElement.dataset.labAnim;
      else document.documentElement.dataset.labAnim = previous;
    };
  }, [animation]);
  if (!kit) return null;
  return <SurfaceView kit={kit} surface={surface} />;
}

const meta = {
  id: "design-<slug>-directions",
  title: "Design proposals/<Group>/<Title> directions/Compare",
  component: DirectionView,
  argTypes: {
    direction: { control: { type: "select" }, options: DIRECTION_IDS, labels: Object.fromEntries(DIRECTION_IDS.map((id) => [id, KIT_META[id].name])), description: "Which direction's kit renders." },
    surface: { control: { type: "select" }, options: SURFACE_IDS, labels: Object.fromEntries(SURFACE_IDS.map((id) => [id, surfaceOf(id).label])), description: "Which product surface." },
    animation: { control: { type: "boolean" }, description: "The lab's hover-motion toggle (html[data-lab-anim])." },
  },
  args: { direction: "today", surface: "<first surface id>", animation: true },
  parameters: {
    layout: "fullscreen",
    docs: { description: { component: "Pick a direction and a surface. 0.Today mounts the shipped <Title> components on the lab's fixtures; 1–<n> are the explored directions. Page: https://paperclip-<slug>-directions.vercel.app." } },
    paperclip: {
      capabilities: ["<area>/<key>"],
      status: "proposed",
      home: "design-proposals",
      implementation: "prototype",
      boundary: "fixtures",
      exception: "Design directory entry for <Title>: the shipped baseline and <n> proposed directions compared on static mocks of <surface labels>.",
      notes: "Controls pick a direction kit and a surface. Today mounts the shipped <Title> components behind the lab's fetch stub with the <company> fixture world; the other kits are lab components. Frames are static mocks of the product chrome; nothing here establishes production behaviour or access checks.",
    },
  },
} satisfies Meta<typeof DirectionView>;

export default meta;
type Story = StoryObj<typeof meta>;

export const Compare: Story = { name: "Any direction, any surface" };
export const Today: Story = { name: "0.Today", args: { direction: "today" } };
export const D1: Story = { name: "1.<Name>", args: { direction: "d1-<name>" } };
export const TodayProductionPalette: Story = { name: "0.Today · production palette", args: { direction: "today" }, parameters: { designProposalTheme: false } };

One D<N> export per direction, named 1.Refine, 2.Rework, 3.Rethink unless --names says otherwise (two directions: Refine, Rethink). No light variants are emitted; the visual suite shoots both themes.

Rules the file encodes:

  • capabilities: --capabilities <area>/<key>,..., validated against the ## Sub-features of maps/feature-map/<area>.md through scripts/feature-map.mjs (lib/capabilities.mjs:35-48); the first entry is primary and decides the folder on retirement (section 5, item 7). Omit the flag for cross-cutting work: the story then carries capabilities: [] and the exception sentence (the generator says so on stderr), the Foundations precedent being colors.stories.tsx:14-17 ("Shared color foundation: production light palette and proposed Paper dark palette. This does not establish production feature coverage."). developer-labs/design (maps/feature-map/developer-labs.md:17) is what buttons.stories.tsx cites for a sandbox inventory.
  • exception reads as a stand-alone sentence: it fills the Capabilities column of maps/generated/storybook.md when capabilities is empty (storybook-catalog.mjs:152). The generated one is "Design directory entry for