---
name: catalyst-component-story-composer
description: >
  Author and maintain `*.stories.ts` files for `@aurora/components` following a single procedure
  that EXTRACTS mechanical facts from the component source and DEMANDS semantic intent from the
  developer. Trigger: When adding, extending, or enriching `@aurora/components/` with a new
  exported component; when editing a component whose inputs/outputs/behavior changed and the story
  is now stale; when batch-documenting components in Framework mode — EVEN IF the user doesn't use
  the word "story" (e.g. "extend @aurora/components with HlmToggle", "add this component to the
  platform", "publish X into @aurora"). Must also fire on explicit phrases: "document X",
  "generate story for X", "create a story for X", "update X's story". For CONSUMING existing
  stories while composing screens, use `catalyst-component-catalog` instead.
license: MIT
metadata:
  author: aurora
  version: '2.1'
---

## When to Use

- Adding a new `@aurora/components/<area>/<name>` component and writing its colocated story
- Editing a component whose `inputs`/`outputs`/behavior changed and whose story is now stale
- Asked to "document X", "generate story for X", "create a story for X", "update X's story"
- Batch-documenting the catalog (e.g. "document all `data-table/components/`")
- Working in **Framework mode** (building the platform), not Solution mode

**Skip this skill** when consuming an existing component in a screen — that is consumption, covered by `catalyst-component-catalog`.

## Core Principle

A good story has two kinds of content:

- **Mechanical facts** — selector, class name, input types, output shapes. The `.component.ts` already contains them. The skill READS them; the developer does NOT retype them.
- **Semantic intent** — when to use, what sibling it competes with, which state matters enough to show as a variant, what a variant demonstrates in prose. The code CANNOT know these. The skill DEMANDS them from the developer.

The skill follows the **same procedure regardless of the developer's clarity**. What changes is the shape of the dialogue, not the script:

- **Clear developer**: answers the semantic questions in one pass → skill writes the story.
- **Unclear developer**: each semantic question triggers a brief discussion. The skill proposes options, asks clarifying follow-ups, and pushes back on "skip it" until the skip is justified. Only then writes.

Never skip a semantic question silently. If the final answer is "no applicable value", it is recorded as a justified omission (e.g. `parameters.aurora` is not declared because no sibling exists to disambiguate against).

## Runtime Status — Storybook is NOT installed

This project treats `*.stories.ts` files as **structured TypeScript documentation**, not as input to a running Storybook dev server. Authoring implications:

1. Import `Meta` / `StoryObj` from the **local shim** at `frontend/src/@aurora/story.types` — NOT from `@storybook/angular`.
2. **Never** declare a `play` function. Describe interactive behavior in prose inside `parameters.docs.description.story`.
3. **Never** import from `storybook/test` or use `fn()` in `args`. Outputs go in `argTypes` with `action: '<name>'`.

Full rationale in `openspec/changes/adopt-storybook-component-catalog/` (proposal + design > scope reduction 2026-04-20). The CSF3 shape is preserved so a future reintroduction of Storybook is additive.

## The Procedure

### Phase A — Extract mechanical facts (no questions to dev)

#### A.1 Read the component source

`Read` `frontend/src/@aurora/components/<area>/<name>.component.ts`. Extract verbatim:

- Class name (e.g. `TableSearchComponent`)
- `@Component` selector
- Every `readonly foo = input<T>(...)` / `input.required<T>(...)` — name, type, default
- Every `readonly bar = output<T>(...)` — name, payload type
- The `imports` array — signal of runtime dependencies
- Any context injection (`injectFlexRenderContext`, `inject(Table<T>)`, required `TemplateRef` inputs) — marks the component as **context-dependent**

#### A.2 Derive `Meta.title`

Rule: `@aurora/<area>/<name>` where `<area>` is the path between `components/` and the file, and `<name>` is the kebab-cased component name without `.component.ts`.

| Component path | Title |
|---|---|
| `frontend/src/@aurora/components/breadcrumb/breadcrumb.component.ts` | `@aurora/components/breadcrumb` |
| `frontend/src/@aurora/components/data-table/components/table-search.component.ts` | `@aurora/components/data-table/table-search` |

#### A.3 Calculate the shim import path

Count directories between the story file and `frontend/src/@aurora/`; emit that many `../` + `story.types`.

| Story file | Import |
|---|---|
| `frontend/src/@aurora/components/breadcrumb/breadcrumb.stories.ts` | `'../../story.types'` |
| `frontend/src/@aurora/components/data-table/components/table-search.stories.ts` | `'../../../story.types'` |

#### A.4 Build `argTypes` and `args` from inputs

For each input, emit an `argType` entry with `control` mapped from `T`:

| Angular input `T` | `control` |
|---|---|
| `string` | `'text'` |
| `number` | `{ type: 'number', min: 0, step: 1 }` (tune `step` for semantic values, e.g. ms → 100) |
| `boolean` | `'boolean'` |
| `'a' \| 'b' \| 'c'` | `{ type: 'select', options: ['a', 'b', 'c'] }` |
| `T[]`, object, generic `T` | `'object'` |
| `Date` | `'date'` |
| `Signal<T>`, `WritableSignal<T>` | Unwrap to inner `T`, apply rules above |
| `TemplateRef`, `Type<T>`, `Table<T>` | Do NOT expose as arg — marks context-dependence |

Set each input's default in `args`. For `input.required<T>()` without a default, pick a representative value (`'Sample'`, `42`, `true`, first option of a union).

#### A.5 Declare outputs with `action`

For each `output<T>()`, add to `argTypes` with `action: '<outputName>'`. Do NOT add to `args` (no `fn()` — Storybook not installed).

---

### Phase B — Demand semantic intent (always asked; dialogue shape varies)

Phase A produces a mechanical skeleton. Phase B fills in what the code cannot answer. The skill MUST ask every question below. If the developer's answer is unclear or "no sé", the skill discusses until a clear answer OR a justified skip is reached.

#### B.1 Sibling disambiguation (drives `parameters.aurora`)

1. `Glob frontend/src/@aurora/components/<area>/**/*.component.ts` — list siblings in the same area.
2. **If ≥1 sibling exists:**
   - Ask: _"This component shares `<area>` with `<sibling-list>`. Which one could a developer confuse with `<this>`, and why?"_
   - If dev answers with a named sibling + reason → fill `parameters.aurora.whenNot` referencing that sibling by name.
   - If dev says "ninguno se parece" → push back once: _"¿Aún si el nombre fuera ambiguo? ¿Alguien podría empezar por `<sibling>` y acabar cambiando a `<this>`?"_ If still no → justified skip, do not declare `parameters.aurora`.
3. **If no siblings exist:**
   - Do not ask. `parameters.aurora` is skipped by construction.

#### B.2 Primary use case (`parameters.aurora.when`)

Only ask if B.1 produced a `whenNot`. Otherwise skip (aurora metadata requires all three fields or none).

- Ask: _"En una frase, ¿cuándo debería un desarrollador elegir este componente?"_
- If vague ("para mostrar datos"): push back with _"¿Qué contexto concreto? ¿Qué problema resuelve que otro no?"_
- If dev says "no sé": guide — _"Piensa en las últimas 3 pantallas donde lo usarías o lo has usado. ¿Qué tienen en común?"_ Extract the common thread.

#### B.3 Non-trivial variant

Ask regardless of B.1/B.2 outcomes. Every story MUST export at least `Default` + one meaningful variant.

- Ask: _"Además de Default, nombra un estado que cambie cómo se comporta o se ve este componente: disabled, un valor edge, contenido traducido, algo largo/corto, loading, error..."_
- If dev names one → use it.
- If dev says "no sé": offer 2–3 options inferred from the code:
  - An input with a literal-union type → variant for each option (pick one).
  - A numeric input with a "significant" value (debounce, timeout, limit) → variant with a boundary value.
  - Boolean input → variant with the opposite default.
  - Transloco/string inputs → variant with translated content.
- If dev rejects all options → push back: _"Si solo tuvieras Default, ¿qué información perdería alguien leyendo la story?"_ Use the answer.

#### B.4 Variant description (`parameters.docs.description.story`)

- Ask: _"Describe en una frase qué demuestra esta variante y por qué importa."_
- If dev is terse ("muestra X") → propose a fuller version: _"¿Funciona algo así: 'Demuestra X cuando Y, útil para Z'?"_
- If the component has interactive behavior, include in the prose what a `play` interaction WOULD assert when Storybook is reintroduced (this is the canonical record for now).

#### B.5 Context-dependent confirmation

Only triggered if Phase A.1 detected context injection (`injectFlexRenderContext`, required `Table<T>`, etc.).

- Ask: _"Este componente necesita `<context>` para renderizar. ¿Escribo un proxy host que falsea ese contexto, o prefieres un TODO stub que queda pendiente?"_
- If dev says proxy → emit a minimal host `@Component` inside the story file wrapping the target; document in `parameters.docs.description.component`.
- If dev says TODO → emit a `Default` story with `// TODO: wire proxy host — see STORIES.md > Proxy Host Pattern` and leave `args` empty. FLAG to the user that the story is incomplete until the proxy is wired.

---

### Phase C — Produce + verify

#### C.1 Write the file

Path: replace `.component.ts` → `.stories.ts` next to the component. Shape per `STORIES.md`.

#### C.2 Typecheck

```
pnpm --filter aurora-front exec tsc --noEmit -p tsconfig.app.json
```

MUST pass clean. Common failures:

- Wrong shim depth (Phase A.3) → adjust `../` count.
- Arg value doesn't match unwrapped input type → verify Signal unwrap (see `STORIES.md`).
- Output declared in `args` instead of `argTypes.action` → move it.

#### C.3 Report

Summarize to the developer:
- What was extracted (Phase A) and what was demanded (Phase B).
- Any justified skips (e.g. "no `parameters.aurora` — no sibling to disambiguate").
- File path written.
- If Phase B.5 ended as a TODO stub, flag it.

## Worked Example

See `frontend/src/@aurora/components/data-table/components/table-search.stories.ts` — the seed. Traceable to the procedure:

- **Phase A**: inputs `placeholder: string`, `debounce: number`, output `search: string` → `argTypes` with `'text'` + `{ type: 'number', step: 100 }` + `action: 'search'`.
- **Phase B.1**: siblings include `table-filters.component.ts` → `whenNot` points to `TableFiltersComponent`.
- **Phase B.2**: primary use case → "free-text search box that debounces user input and emits a single value...".
- **Phase B.3**: variants chosen → `TranslatedPlaceholder` (Transloco content) and `FastDebounce` (`debounce: 50`, significant boundary).
- **Phase B.4**: each variant has `parameters.docs.description.story` in prose, including what a future `play` interaction would assert.
- **Phase B.5**: not triggered — no context injection.
- **Phase C**: typechecks clean.

## Related Skills

- **`catalyst-component-catalog`** — sibling skill for **consuming** existing stories when composing screens. Fires in Solution mode; this one fires in Framework mode.
- `transloco-i18n` — when your story demonstrates translated content.
- `catalyst-data-table-management` — data-table family specifics.
- `typescript` — strict patterns when refining a generated story.
