Skip to content
Registry

stories

v0.1.3

Component stories for any React project — builds CSF story files with the project's own Vite (monorepo-aware), indexes components and their states per git line, compares the working tree with a branch, a commit or a chat turn, renders deterministic screenshots and DOM/React trees through the browser worker, and injects an explorer and compare page into the console.

Install

bash
iii trigger compose::add worker=stories

Run in your project with the iii engine running. Exact versions are immutable and digest-pinned.

Agent-ready brief for v0.1.3
Install, config, dependencies, readme, skills and API reference, all in one place. Fetch as agent-context.md for an LLM to consume.
The same content rendered as discrete blocks below is exposed as a single markdown document at /registry/workers/stories.md. Paste it into an LLM prompt or pipe it through curl from a worker.

Install

iii trigger compose::add worker=stories@0.1.3

Configuration

iii-config.yaml
- data_path: data/stories
  keep_lines: 12
  viewport:
    dpr: 1
    height: 768
    width: 1024
  watch: true
  workspaces:
    - name: workspace
      path: .

Dependencies

Dependency · 2Version

Readme

README.md

stories

Component stories for any React project, rendered in the iii console. The worker finds CSF story files (*.stories.tsx and friends) in one or more workspaces, builds them with the project's own Vite (its plugins, aliases, CSS pipeline and React), and keeps an index per git line: the working tree, its previous build, any ref or commit, or a chat turn's before/after tree. Two lines compare into a list of components that changed directly (the story file or a direct import), indirectly (a deeper input), were added or removed. Through the browser worker it renders deterministic screenshots and DOM/React trees of any component state, with args and globals overrides, and diffs two versions of a state into a verdict a reviewer can act on. A console page shows the explorer (files as on disk, git status, impact chips, live preview, props) and the compare view (line A versus line B, side by side).

No Storybook runtime is needed. Existing .stories.* files and .storybook/preview.* keep working; storybook/test's fn() is shimmed and records an actions log. play functions, MDX and addons are ignored.

Install

iii trigger compose::add worker=stories

Requirements on the host: git, Node 22+ with npm on PATH (the compiler is materialized under data/stories/compiler and installed there once), and the browser worker for screenshots and trees. Each project must have its dependencies installed (react and react-dom 18+); a project with its own vite is built with that version.

Configuration (stories)

Field Default Meaning
data_path data/stories Lines, content-addressed files, renders, history, the compiler.
console_url unset Console origin the browser worker navigates to for renders. Unset: the ADE's address from console::status.
watch true Rebuild the working tree when a story input changes.
keep_lines 12 Built ref/turn lines kept per workspace before pruning.
viewport 1024×768 @1 Default render viewport.
workspaces[] one at . { name, path, projects?, stories?, ignore?, base? }

A workspace is a repository. Projects inside it are discovered from the story files (the nearest package.json), so client-app/ and admin-app/ in one repo each build with their own config and React. Shared packages resolve to their real paths, so a change in packages/ui impacts stories in every project that imports it. base is the ref changes are classified against (default HEAD).

Functions

Function What it does
stories::workspaces / stories::lines Git state, projects, component count and built lines per workspace.
stories::components::list Components of a line with their states; filters by project, group, tag, path, query and change kind against a base line.
stories::components::get One component: states with args, argTypes and inferred controls, inputs with hops, change, related components, history.
stories::tree::fs The explorer tree: folders and story files as on disk with git status and change kinds.
stories::builds::create / stories::builds::get Build a line (worktree, a ref, a sha, turn://) and poll it.
stories::compare Every component that differs between two lines, classified, with the changed files.
stories::diff::file Unified diff of one input between two lines.
stories::screenshot Deterministic PNG of one state (args, globals, viewport, clip), cached by content.
stories::tree DOM tree with layout boxes, computed styles and owning component; React tree; accessibility outline; html; actions.
stories::diff One state across two lines: tree changes, pixel regions mapped to elements, verdict identical / invisible / visual / unexplained.
stories::history Versions of one component seen by working-tree builds.

Triggers: stories:changed fires after a working-tree rebuild with the classified changes ({ workspace?, project?, kinds? }); stories:build fires on every build state transition.

The review workflow for agents lives in skills/.

Lines and data

data/stories/<workspace>/
  lines/worktree/            the live working tree (rebuilt by the watcher)
  lines/worktree.prev/       the build before it
  lines/<sha>/               a commit or a turn tree, immutable
  files/<sha256>             build outputs and story inputs, shared by all lines
  renders/<hash>/            shot.png, tree.json, meta.json of one render
  history/<project>__<id>.jsonl

A ref builds in a detached git worktree; a turn tree comes from the ide worker's private snapshot repository through git archive. node_modules folders are linked from the live checkout. A component's version is the hash of every input's content, so a component unchanged between two lines shares the same render cache.

Story documents are served to the console through stories::ui-file behind the console's /ui-files/stories/// route; the page embeds them in a same-origin iframe.

Development

# terminal 1: rebuild the console page on save
pnpm --dir stories/ui watch
# terminal 2: hot reload the page assets
cd stories && III_STORIES_UI_WATCH=1 cargo run

III_STORIES_NODE / III_STORIES_NPM override the binaries; III_STORIES_IDE_TURNS points at the ide worker's turn store (default data/shell/turns). Tests: cargo test; the pipeline test runs against a real project when III_STORIES_FIXTURE= and III_STORIES_COMPILER_DIR= are set.

Limits

React 18+ only. Projects built by webpack or Next.js need a vite.config with the mocks they rely on (next/link, next/image). Stories that reach application singletons (routers, engine clients) render with errors and show up with an error field. Pixel diffs compare renders from one machine; a different font stack between hosts reads as unexplained.

Skills

API reference (JSON)

agent-api-reference.json
{
  "functions": [
    {
      "description": "Internal: re-read the stories configuration after an operator change.",
      "metadata": {
        "internal": true
      },
      "name": "stories::on-config-change",
      "request_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "description": "Trigger payload for `<worker>::on-config-change`. Advisory only: handlers re-fetch the authoritative value and ignore it, so a direct call can never inject config.",
        "properties": {
          "id": {
            "default": null,
            "description": "Configuration id that changed.",
            "type": [
              "string",
              "null"
            ]
          }
        },
        "title": "OnConfigChangeEvent",
        "type": "object"
      },
      "response_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "description": "Ack returned by the internal `<worker>::on-config-change` handler.",
        "properties": {
          "ok": {
            "type": "boolean"
          }
        },
        "required": [
          "ok"
        ],
        "title": "OnConfigChangeResponse",
        "type": "object"
      }
    }
  ],
  "triggers": [
    {
      "description": "Fires on every build state transition (queued, running, done, failed) of any line. Config: { workspace?, metadata? }.",
      "invocation_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "properties": {
          "metadata": true,
          "workspace": {
            "type": [
              "string",
              "null"
            ]
          }
        },
        "title": "BuildConfig",
        "type": "object"
      },
      "metadata": {},
      "name": "stories:build",
      "return_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "properties": {
          "at": {
            "type": "string"
          },
          "build_id": {
            "type": "string"
          },
          "line": {
            "type": "string"
          },
          "message": {
            "type": [
              "string",
              "null"
            ]
          },
          "status": {
            "description": "queued | running | done | failed",
            "type": "string"
          },
          "workspace": {
            "type": "string"
          }
        },
        "required": [
          "at",
          "build_id",
          "line",
          "status",
          "workspace"
        ],
        "title": "BuildEvent",
        "type": "object"
      }
    },
    {
      "description": "Fires after a working-tree rebuild changed components, with each change classified as direct, indirect, new or removed. Config: { workspace?, project?, kinds?, metadata? }.",
      "invocation_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "properties": {
          "kinds": {
            "description": "Change kinds to fire for: direct, indirect, new, removed. Omit for all.",
            "items": {
              "type": "string"
            },
            "type": [
              "array",
              "null"
            ]
          },
          "metadata": {
            "description": "Metadata attached to every invocation this binding receives."
          },
          "project": {
            "description": "Project name. Omit for every project.",
            "type": [
              "string",
              "null"
            ]
          },
          "workspace": {
            "description": "Workspace slug. Omit for every workspace.",
            "type": [
              "string",
              "null"
            ]
          }
        },
        "title": "ChangedConfig",
        "type": "object"
      },
      "metadata": {},
      "name": "stories:changed",
      "return_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "definitions": {
          "Change": {
            "properties": {
              "a_version": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "b_version": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "file": {
                "type": "string"
              },
              "files": {
                "default": [],
                "items": {
                  "$ref": "#/definitions/ChangedFile"
                },
                "type": "array"
              },
              "id": {
                "type": "string"
              },
              "kind": {
                "$ref": "#/definitions/ChangeKind"
              },
              "project": {
                "type": "string"
              },
              "states": {
                "default": [],
                "items": {
                  "$ref": "#/definitions/StateChange"
                },
                "type": "array"
              },
              "title": {
                "type": "string"
              }
            },
            "required": [
              "file",
              "id",
              "kind",
              "project",
              "title"
            ],
            "type": "object"
          },
          "ChangeKind": {
            "oneOf": [
              {
                "enum": [
                  "unchanged",
                  "new",
                  "removed"
                ],
                "type": "string"
              },
              {
                "description": "The story file or one of its direct imports changed.",
                "enum": [
                  "direct"
                ],
                "type": "string"
              },
              {
                "description": "Only deeper inputs changed.",
                "enum": [
                  "indirect"
                ],
                "type": "string"
              }
            ]
          },
          "ChangeSummary": {
            "properties": {
              "direct": {
                "format": "uint",
                "minimum": 0,
                "type": "integer"
              },
              "indirect": {
                "format": "uint",
                "minimum": 0,
                "type": "integer"
              },
              "new": {
                "format": "uint",
                "minimum": 0,
                "type": "integer"
              },
              "removed": {
                "format": "uint",
                "minimum": 0,
                "type": "integer"
              },
              "unchanged": {
                "format": "uint",
                "minimum": 0,
                "type": "integer"
              }
            },
            "required": [
              "direct",
              "indirect",
              "new",
              "removed",
              "unchanged"
            ],
            "type": "object"
          },
          "ChangedFile": {
            "properties": {
              "hop": {
                "description": "Smallest hop at which the file reaches the component.",
                "format": "uint32",
                "minimum": 0,
                "type": "integer"
              },
              "path": {
                "type": "string"
              },
              "status": {
                "description": "added | removed | modified",
                "type": "string"
              }
            },
            "required": [
              "hop",
              "path",
              "status"
            ],
            "type": "object"
          },
          "LineInfo": {
            "properties": {
              "built_at": {
                "description": "RFC 3339.",
                "type": "string"
              },
              "dirty": {
                "description": "Whether the working tree had uncommitted changes when built.",
                "type": [
                  "boolean",
                  "null"
                ]
              },
              "key": {
                "description": "Stable key: `worktree`, `worktree.prev`, or a git object sha.",
                "type": "string"
              },
              "kind": {
                "$ref": "#/definitions/LineKind"
              },
              "label": {
                "description": "Human label: the ref name, the turn, or `working tree`.",
                "type": "string"
              },
              "sha": {
                "type": [
                  "string",
                  "null"
                ]
              }
            },
            "required": [
              "built_at",
              "key",
              "kind",
              "label"
            ],
            "type": "object"
          },
          "LineKind": {
            "description": "Which kind of source tree a line photographs.",
            "oneOf": [
              {
                "description": "The live working tree, rebuilt by the watcher. Key `worktree`.",
                "enum": [
                  "worktree"
                ],
                "type": "string"
              },
              {
                "description": "The working tree as it was before the last rebuild. Key `worktree.prev`.",
                "enum": [
                  "prev"
                ],
                "type": "string"
              },
              {
                "description": "A commit reached through a ref or a sha. Key: the commit sha.",
                "enum": [
                  "ref"
                ],
                "type": "string"
              },
              {
                "description": "A chat turn's before/after tree from the ide worker. Key: the tree sha.",
                "enum": [
                  "turn"
                ],
                "type": "string"
              }
            ]
          },
          "StateChange": {
            "properties": {
              "id": {
                "type": "string"
              },
              "name": {
                "type": "string"
              },
              "status": {
                "description": "added | removed | kept",
                "type": "string"
              }
            },
            "required": [
              "id",
              "name",
              "status"
            ],
            "type": "object"
          }
        },
        "properties": {
          "at": {
            "type": "string"
          },
          "base": {
            "allOf": [
              {
                "$ref": "#/definitions/LineInfo"
              }
            ],
            "description": "The line the changes were classified against."
          },
          "changes": {
            "description": "Only the changed components (never `unchanged`).",
            "items": {
              "$ref": "#/definitions/Change"
            },
            "type": "array"
          },
          "line": {
            "$ref": "#/definitions/LineInfo"
          },
          "summary": {
            "$ref": "#/definitions/ChangeSummary"
          },
          "workspace": {
            "type": "string"
          }
        },
        "required": [
          "at",
          "base",
          "changes",
          "line",
          "summary",
          "workspace"
        ],
        "title": "ChangedEvent",
        "type": "object"
      }
    }
  ]
}

Search the registry

Find workers, kits and executables, or jump to a page.