/** * Eddie's Brain — Core Type Definitions * * The type system for the agentic intelligence layer of the Eddie Design System. */ /** Atomic design classification */ export type AtomicLevel = 'atom' | 'molecule' | 'organism' | 'recipe' | 'page'; /** Which Eddie package a component belongs to */ export type EddiePackage = 'eddie-web-components' | 'eddie-recipes' | 'eddie-icons' | 'eddie-pages' | 'eddie-charts'; /** A component property definition */ export interface PropertyEntry { name: string; type: string; default?: string; description: string; /** Allowed values for enum-like props */ options?: string[]; required: boolean; } /** A component slot definition */ export interface SlotEntry { name: string; description: string; } /** A component event definition */ export interface EventEntry { name: string; detail?: string; description: string; /** * Whether the event crosses the shadow boundary (`composed: true`). Events * dispatched through `EdElement.dispatch()` default to composed, so a * consumer can listen for them on the host element from the light DOM. */ composed?: boolean; } /** * A structural anti-pattern: markup that composes Eddie parts in a way that * silently breaks (dropped slot content, a collapsed grid, nested interactive * controls). Authored as `@antipattern — — fix: ` on * the owning component, aggregated into `.eddie-brain/anti-patterns.json`, * and matched on markup by the selector grammar in * `analyze/anti-pattern-selector.ts` (#1888). */ export interface AntiPattern { /** Stable slug derived from the selector (`layout-container-gt-hero`). */ id: string; /** The selector, in the small CSS-like grammar (`ed-grid > *:not(ed-grid-item)`). */ selector: string; /** Why the structure breaks. */ reason: string; /** What to do instead. */ fix: string; /** `error` for silent drops, broken layout, or a11y; `warning` for style. */ severity: 'error' | 'warning'; /** The component whose JSDoc carries the tag. */ source: string; /** Every Eddie tag the selector names, for lookup from either side. */ tags: string[]; } /** Full component registry entry */ export interface ComponentEntry { /** Tag name, e.g. "ed-button" */ tagName: string; /** Human-readable name, e.g. "Button" */ displayName: string; /** CSS class name, e.g. "ed-c-button" */ cssClassName: string; /** Atomic design level */ atomicLevel: AtomicLevel; /** * Functional group from the Storybook title's middle segment — the * grouping axis shared with the component catalog (e.g. "Global", "Cards", * "Blocks", "Forms", "Navigation", "Media", "Tools"). Lets the graph answer * "which card / block recipes exist?" rather than only "which project owns * this?". Undefined when the title has no middle segment. See #1259 sweep. */ category?: string; /** * Owning project for recipes and pages, derived from the directory the asset * lives in (`common`, `we-are-here`, `eddie-slides`, `tools`, …). This is the * ownership axis, orthogonal to `category` (the functional axis): a recipe is * grouped functionally in Storybook but still filterable by product here. * Undefined for core components (they are not project-scoped). */ projectScope?: string; /** Package it belongs to */ package: EddiePackage; /** What this component is FOR (intent, not just what it looks like) */ intent: string; /** Base class: EdElement or EdFormElement */ baseClass: 'EdElement' | 'EdFormElement'; /** All public properties */ properties: PropertyEntry[]; /** Named slots */ slots: SlotEntry[]; /** CSS custom properties it exposes */ cssCustomProperties: string[]; /** Events it dispatches */ events: EventEntry[]; /** * Components it composes with, in both directions — what it contains and what * contains it. Derived, never authored: imports, the `` tags in the * component's own templates, the `` tags across its stories, then * inverted across the whole index. `@related` deliberately does NOT feed it; * that tag names alternatives, and deriving containment from it produced a * graph that told agents `ed-popover` composes with `ed-modal` (#2013). */ composesWith: string[]; /** * For recipes (`ed-r-*`) only: how a consumer or agent should treat the * recipe. Authored via a `@recipe` JSDoc tag on the class. See #1259 and * `docs/RECIPES-COMPOSITION-VS-DECLARATIVE.md`. * * composition — an assembly of `ed-*` components acting as chrome. Adapt the * `canonicalUsage.default` skeleton: swap the content, keep * every slot and wrapper intact. * declarative — a single-purpose, prop-driven component. Configure via props. * exception — a composition intentionally locked (e.g. a living demo); * the JSDoc explains why. */ recipeKind?: 'composition' | 'declarative' | 'exception'; /** * Which parts of a composition are the consumer's to fill, and which are * structure they must not touch (#1685). * * Read from a `composition` export in the component module, so the metadata * and the runtime behaviour have one source and cannot drift. Present on * page templates (`ed-p-*`) and, in time, composition recipes. * * shell — structure the template owns. `mutable: false` is a stated * boundary, not a convention to be inferred; `rationale` says * what a consumer gives up by working around it. * regions — the fillable slots. `expects` narrows the search space for a * consumer choosing what to put in one (advisory, not enforced). */ composition?: { shell: Array<{ component: string; props?: Record; rationale: string; mutable: boolean; }>; regions: Array<{ slot: string; intent: string; expects?: string[]; repeatable?: boolean; required?: boolean; }>; }; /** * Spacing doctrine metadata (docs/SPACING.md, #1647). * * rhythmOwner — this component's CSS gaps its children via a spacing * ROLE token: do not add margins or spacing utilities to * its children; the container owns the rhythm. * rolesConsumed — which of the six spacing roles its stylesheet consumes * (region/section/block/flow/field/inline). * overhang — 'documented' when the class JSDoc carries an * `@overhang` tag: something (a shadow, a decoration) * intentionally renders outside the box. 'none' otherwise. */ spacing?: { rhythmOwner: boolean; rolesConsumed: string[]; overhang: 'documented' | 'none'; }; /** Parent compound component (if child) */ parentComponent?: string; /** Child compound components */ childComponents?: string[]; /** Usage guidelines */ guidelines: { use: string[]; dontUse: string[]; accessibility: string[]; /** Named parts / regions and what belongs in each (`@anatomy`). #1242 */ anatomy?: string[]; /** How to write the text inside — label voice, length, casing (`@content`). #1242 */ content?: string[]; /** Nearest-neighbor components + when to choose them (`@related`). #1242 */ related?: string[]; /** Enumerated states the component can render (`@state`). #1242 */ states?: string[]; /** * The asks this entry should resolve to (`@useWhen`) — the phrases a * composer or `eddie_search` can match ("a dashboard for X", "a P&L"). * Where `use` says what the entry is *for*, this says which requests * should land here. Pages and composition recipes. #1888 */ useWhen?: string[]; /** * Similar-looking asks that belong elsewhere, each naming the alternative * (`@notWhen`). The half no consumer can derive from prose. #1888 */ notWhen?: string[]; }; /** * Structural rules this component owns, authored as `@antipattern` JSDoc * tags on its class and matched by `AntiPatternValidator` (#1888). The * component that carries the tag is the `source`; the selector may name * other tags (`ed-layout-container > ed-hero` lives on `ed-hero`). */ antiPatterns?: AntiPattern[]; /** * Override points intended for consumer customization. * * Distinct from `properties` (which lists everything the class declares) * and `slots` (which lists every slot that exists). This field is the * structured answer to "how do I customize this component?" — populated * from `@overridableSlot` and `@overridableProp` JSDoc tags on the * component's class-level comment. */ overridableSurface?: { slots: Array<{ name: string; purpose: string; exampleMarkup?: string; }>; props: Array<{ name: string; purpose: string; values?: string[]; }>; }; /** * Canonical invocation shape. `default` is the minimal markup a consumer * should paste to "get the default" — extracted from the component's * `Default` Storybook story template. `note` is optional prose from a * `@canonicalUsage` JSDoc tag on the class. */ canonicalUsage?: { default?: string; note?: string; }; /** * Structural answer to "where does this component's content come from?". * Surfaces the prop-vs-slot path so consumers (human or AI) don't reach for * HTML muscle memory and silently drop content. Populated from a * `@contentApi { ... }` JSDoc tag on the class. See #627. * * contentVia — how the user supplies the component's primary content * labelVia — how the user supplies the field label (form components) * labelVisibility — whether that label renders visibly or is a11y-only */ contentApi?: { contentVia?: string; labelVia?: 'prop' | 'slot' | 'none'; labelVisibility?: 'visible' | 'hidden' | 'accessible-only'; }; /** * What a page must load before this component's markup does anything (#1635). * * Derived from `package`, never hand-written per component — the requirement * belongs to the package, and a per-component copy would drift the first time * a component moved between them. * * This exists because the failure it prevents is silent. Eddie ships one * autoloader per package; a page that loads only the components one gets an * `ed-r-*` element that never upgrades, renders its light-DOM children bare, * and reports nothing. That is the root cause of #1634 (a primary nav painted * at 1:1 contrast) and #1616 (a missing banner landmark) — both first read as * component bugs. Anyone reading `canonicalUsage` needs to see this next to * it, or they will write markup that cannot work. */ runtime: { /** npm specifier of the autoloader that registers this component's tag. */ autoloader: string; /** Ready-to-paste script tag, version-pinned by the consumer. */ scriptTag: string; }; /** File paths relative to package root */ paths: { component: string; styles?: string; stories?: string; test?: string; }; } /** Token tier in the 3-tier architecture */ export type TokenTier = 'definition' | 'usage' | 'component'; /** Token category */ export type TokenCategory = 'color' | 'spacing' | 'typography' | 'border' | 'shadow' | 'motion' | 'breakpoint' | 'z-index' | 'opacity'; /** A design token entry */ export interface TokenEntry { /** Full token name, e.g. "--ed-theme-color-background-default" */ name: string; /** Tier */ tier: TokenTier; /** Category */ category: TokenCategory; /** Raw value */ value: string; /** Token it references (for tier 2/3) */ references?: string; /** What this token is for */ intent: string; /** Which components use it */ /** * Components referencing this token, filled in per-query by * `eddie_get_token` (#1805) — never stored on the serialized graph, where it * was a hardcoded `[]` that nothing ever wrote to and every reader believed. */ usedBy?: string[]; /** Theme this token belongs to */ theme: string; /** Whether this token is deprecated */ deprecated: boolean; /** * For color tokens: the subcategory that determines which CSS properties * the token may be applied to. One of "background", "content", "border", * or undefined for non-color tokens. Derived from the token name segment * immediately after `color-` (e.g., `--ed-theme-color-background-default` * → "background"). See #631. */ subcategory?: 'background' | 'content' | 'border'; /** * CSS properties this token is valid to apply to, derived from * category/subcategory. For color tokens only: * background → ["background", "background-color"] * content → ["color", "fill"] * border → ["border-color"] (matches the whole border-*-color family) * Crossing the boundary (e.g., `background: var(--ed-theme-color-content-*)`) * is a `token-category-mismatch` violation in eddie_validate_file. See #631. */ validProperties?: string[]; } /** A component's role in a composition recipe */ export interface RecipeComponent { tagName: string; role: string; variant?: string; required: boolean; slotTarget?: string; } /** A canonical composition recipe */ export interface CompositionRecipe { /** Recipe name, e.g. "destructive-confirmation" */ name: string; /** What this recipe accomplishes */ intent: string; /** Components involved */ components: RecipeComponent[]; /** Design token mappings */ tokens: Record; /** Rules that must hold */ rules: string[]; /** Match confidence when detected in the wild (0-1) */ confidence?: number; /** Whether this recipe is approved as canonical */ status: 'candidate' | 'canonical' | 'deprecated'; } /** Source of a signal */ export type SignalSource = 'figma' | 'git' | 'storybook' | 'ci' | 'usage' | 'manual'; /** Signal severity */ export type SignalSeverity = 'info' | 'warning' | 'error' | 'critical'; /** A signal from any monitored source */ export interface Signal { id: string; timestamp: string; source: SignalSource; severity: SignalSeverity; /** What kind of signal */ type: string; /** Human-readable description */ message: string; /** Affected component/token/file */ target?: string; /** Raw data from the source */ payload?: Record; } /** Health score category */ export type HealthCategory = 'tokens' | 'naming' | 'coverage' | 'documentation' | 'accessibility' | 'consistency'; /** A single health score */ export interface HealthScore { category: HealthCategory; score: number; issues: HealthIssue[]; } /** A specific health issue found */ export interface HealthIssue { id: string; category: HealthCategory; severity: SignalSeverity; message: string; /** File path where the issue was found */ file: string; /** Line number */ line?: number; /** Column */ column?: number; /** The problematic value */ actual?: string; /** What it should be */ expected?: string; /** Can this be auto-fixed? */ autoFixable: boolean; /** Suggested fix */ suggestion?: string; } /** Full health report */ export interface HealthReport { timestamp: string; /** Overall score (weighted average) */ overall: number; /** Scores by category */ categories: HealthScore[]; /** Total issue count */ totalIssues: number; /** Breakdown by severity */ issueCounts: Record; } /** Trust level (L1 = lowest, L5 = highest) */ export type TrustLevel = 'L1_INTERN' | 'L2_JUNIOR' | 'L3_MID' | 'L4_SENIOR' | 'L5_PRINCIPAL'; /** What kind of action the agent can take */ export type ActionType = 'suggest' | 'draft_pr' | 'create_pr' | 'auto_fix' | 'auto_merge' | 'update_docs' | 'notify' | 'escalate'; /** Risk level of a proposed action */ export type RiskLevel = 'low' | 'medium' | 'high' | 'critical'; /** A planned action */ export interface PlannedAction { id: string; timestamp: string; /** What triggered this action */ signal: Signal; /** What the agent wants to do */ actionType: ActionType; /** Risk assessment */ risk: RiskLevel; /** Confidence score (0-1) */ confidence: number; /** Current trust level */ trustLevel: TrustLevel; /** Whether this action is permitted at the current trust level */ permitted: boolean; /** Human-readable description of the planned action */ description: string; /** The actual changes to make */ changes: PlannedChange[]; /** Status */ status: 'pending' | 'approved' | 'rejected' | 'executed' | 'failed'; } /** A specific file change */ export interface PlannedChange { file: string; type: 'create' | 'modify' | 'delete'; description: string; /** For modify: the diff */ diff?: string; } /** Audit log entry */ export interface AuditEntry { id: string; timestamp: string; action: PlannedAction; /** Who approved (or 'auto' if within trust level) */ approvedBy: string; /** Outcome */ outcome: 'success' | 'failure' | 'partial'; /** Any error messages */ errors?: string[]; } /** A learning entry from human feedback */ export interface LearningEntry { id: string; timestamp: string; /** What the agent suggested */ suggestion: { type: string; target: string; proposed: string; }; /** What the human decided */ decision: 'accepted' | 'rejected' | 'modified'; /** If modified, what they changed it to */ modification?: string; /** Why (if provided) */ reason?: string; /** Tags for categorization */ tags: string[]; } /** Trust promotion requirements */ export interface TrustPromotion { from: TrustLevel; to: TrustLevel; requirements: { minCompletions: number; maxRejectionRate: number; minDaysAtLevel: number; }; } /** Eddie Brain configuration */ export interface BrainConfig { /** Path to the Eddie monorepo root */ rootDir: string; /** Current trust level */ trustLevel: TrustLevel; /** Which watchers to enable */ watchers: { figma: boolean; git: boolean; storybook: boolean; usage: boolean; }; /** Health score weights (must sum to 1) */ healthWeights: Record; /** Governance rules file path */ governanceRulesPath?: string; /** MCP server port */ mcpPort?: number; /** Whether to auto-fix within trust level */ autoFix: boolean; } /** Default configuration */ export declare const DEFAULT_CONFIG: BrainConfig; //# sourceMappingURL=types.d.ts.map