Skip to content

Callil Consolidation — Project Blueprint

  1. Each run is independently verifiable — build, lint, tsc, tests, and VRT (where applicable) must pass at the end of every run.
  2. Expose risks early — visual changes (theme migration) and complex new code (auto-publish) are scheduled before mechanical integration work.
  3. Clear entry/exit criteria — each run has prerequisites, deliverables, and a verification gate. A run is not complete until its gate passes.
  4. Incremental commits — each run produces one or more commits on the target branch. No run depends on uncommitted work from a prior run.

Steps marked with [Playwright MCP] can use the Playwright MCP server to automate visual verification against the running Storybook dev server. This replaces manual spot-checks with reproducible browser-driven inspection:

  • Navigate to specific story URLs
  • Take screenshots for visual comparison
  • Interact with components (click, type, hover) to verify behavior
  • Compare against dev-witness stories side-by-side

Start Storybook (npm run dev in ux-prototype/) before running Playwright MCP checks.

jmpicnic/component-consolidation created off HEAD of jmpicnic/list-view-management-620 in the main clone (ux-prototype/).

All design decisions are recorded in analysis/:

DocumentGoverns
analysis/index.mdMaster decisions and dispatch
analysis/primitives.mdComponent organization, organism import mapping
analysis/styles.mdCSS tokens, globals, sonner/next-themes
analysis/dependencies.mdpackage.json changes, library build
analysis/grid-integration.mdEntity-data-grid evolution
analysis/test-coverage.mdStories, tests, VRT, play function convention
analysis/canary-refactor.mdFollow-on adaptation

Risk: None Effort: Trivial

  • Main clone on jmpicnic/list-view-management-620 with clean working tree
  • Worktree at callil-consolidation-worktree on jmpicnic/callil-consolidation available for reference
  • Branch jmpicnic/component-consolidation created off HEAD of jmpicnic/list-view-management-620
  • Verified: npm ci && npm run lint && npx tsc --noEmit && npm run test pass on the new branch (baseline)
  • Green baseline on the new branch — all existing tests pass, no regressions from branch creation

Risk: High blast radius (many file moves and import rewrites), low per-file risk Effort: Medium Governs: analysis/primitives.md, analysis/styles.md

Intermediate gates are blocking. If an intermediate gate fails, stop and fix before proceeding to the next task. This isolates failures to the task that caused them. The final verification gate re-runs everything as a comprehensive check before committing.

This run establishes the new directory structure, design tokens, and import conventions that everything else depends on.

  • Run 0 complete (branch exists, baseline green)
  • Update src/styles/tokens.css to Arda orange palette (worktree values), add new tokens (control heights, state ramp, font vars, destructive-foreground, dark mode)
  • Refactor src/styles/globals.css to @import './tokens.css' instead of redeclaring values
  • Replace remaining globals.css content with worktree version (Arda orange, Geist Mono font, touch media queries, sidebar-inset dark override)
  • Drop legacy sidebar CSS (.sidebar-menu-button-hover, [data-active='true'], .add-company-button)
  • Drop duplicate :root block

Intermediate gate (1a): npx tsc --noEmit + npm run test — token changes could break any component referencing CSS vars.

  • Create src/components/canary/primitives/
  • Copy 13 stock shadcn files from worktree src/components/ui/ (collapsible, dropdown-menu, input, label, separator, sheet, sidebar, skeleton, table, tabs, textarea, toggle, tooltip)
  • Rewrite cn imports in primitives from @/utils to @/types/canary/utils
  • Do NOT copy sonner.tsx (dropped per styles.md decision)

Intermediate gate (1b): npx tsc --noEmit — verifies imports resolve. Tests won’t exercise primitives directly (no consumers yet).

  • Copy custom components from worktree src/components/ui/ to src/components/canary/atoms/:
    • avatar.tsxatoms/avatar/
    • badge.tsxatoms/badge/ (merge with existing callil ArdaBadge wrapper)
    • button.tsxatoms/button/ (merge with existing callil ArdaButton wrapper)
    • card.tsxatoms/card/
    • dialog.tsxatoms/dialog/
    • input-group.tsxatoms/input-group/
  • Copy existing callil canary atoms from worktree: brand-logo, drawer, icon-button, icon-label, read-only-field, search-input (with their stories and tests)
  • Rewrite cn imports from @/utils to @/types/canary/utils
  • Rewrite @/components/ui/* imports to @/components/canary/primitives/*

Intermediate gate (1c): npx tsc --noEmit + npm run test — atoms have unit tests from the worktree; verifies they pass in the new location.

  • Copy src/hooks/use-mobile.ts from worktree to src/types/canary/hooks/use-mobile.ts
  • Update any imports referencing @/hooks/use-mobile to @/types/canary/hooks/use-mobile

Intermediate gate (1d): npx tsc --noEmit — small change, verify the import resolves.

  • Rewrite 38+ files from import { cn } from '@/utils' to import { cn } from '@/types/canary/utils'
  • Verify src/utils.ts is not needed (if no other exports, do not copy it)

Intermediate gate (1e): npx tsc --noEmit + npm run lint + npm run testfull gate. This is the highest-risk task in Run 1 (broadest file change set). Any missed or malformed import shows up here. Lint catches import ordering. Run this gate before 1f so cn issues are debugged in isolation.

  • Add src/components/canary/primitives/primitives.stories.tsx — one integration story importing from several primitives and rendering a composed example

Intermediate gate (1f): npm run build-storybook — verifies Storybook can index and build with the new structure. [Playwright MCP] Navigate to the primitives import check story and verify components render.

  • New directory structure: canary/primitives/, updated canary/atoms/, types/canary/hooks/
  • Updated tokens.css and globals.css
  • All imports rewritten to canonical paths
  • Primitives import check story
  • npm run lint — pass
  • npx tsc --noEmit — pass
  • npm run test — pass (all existing tests, no regressions)
  • npm run build-storybook — pass
  • VRT: generate new baselines for all new atom stories (badge, button, drawer, etc.)
  • [Playwright MCP] Spot-check new atom stories (badge, button, drawer, etc.) render correctly in Storybook
  • All files in canonical locations with canonical import paths
  • No references to @/components/ui/* or @/utils remain (except in vendored code)
  • Build green, Storybook builds

Run 2: Visual Foundation (Theme Migration)

Section titled “Run 2: Visual Foundation (Theme Migration)”

Risk: Highest visual risk — diffs every grid story Effort: Small (code change is small, validation is thorough) Governs: analysis/grid-integration.md (decision #5)

  • Run 1 complete (styles, primitives, atoms in place)
  • Retrofit DataGrid molecule (src/components/canary/molecules/data-grid/data-grid.tsx) from 'legacy' theme string to themeQuartz with design-system token mapping
  • Apply item-grid’s theme configuration: row height (48px), header height (36px), font (Geist Sans 14px, 13px headers, 600 weight), cell padding (12px), color token mapping, column border styling, wrapper border radius
  • Add ThemedGrid visual story to DataGrid molecule
  • DataGrid molecule using themeQuartz with Arda design tokens
  • ThemedGrid story
  • npm run test — pass (existing DataGrid tests)
  • VRT: before/after comparison is the critical gate. Every grid story will diff — this includes DataGrid molecule stories, EntityDataGrid factory stories, EntityDataGridShim stories, and any other stories rendering an AG Grid. Expected variances: row heights, fonts, colors, borders all change from browser defaults to Arda tokens. Regenerate all affected baselines after review confirms visual correctness.
  • [Playwright MCP] Navigate to DataGrid and EntityDataGrid stories; screenshot before and after the theme change. Visually verify: row heights, header styling, font rendering, cell padding, color tokens applied correctly. Compare ThemedGrid story against item-grid’s existing appearance in the worktree Storybook for reference.
  • All grid stories render with Arda-themed appearance
  • VRT baselines regenerated and committed
  • No regressions in non-grid stories

Risk: Medium (many files, but mechanical) Effort: Medium-Large Governs: analysis/primitives.md (organism import mapping section)

Move callil organisms and their supporting molecules into the main clone as-is (with import rewrites only, no behavioral changes). This validates that the foundation from Run 1 supports the organisms correctly.

  • Run 2 complete (themed DataGrid in place)
  • Copy from worktree to main clone src/components/canary/molecules/:
    • sidebar/ (sidebar-header, sidebar-nav, sidebar-nav-item, sidebar-nav-group, sidebar-user-menu) — with stories and tests
    • item-grid/ (item-grid-columns, item-grid-fixtures, typeahead-cell-editor, select-cell-editor) — with stories and tests
    • item-details/ (item-details-header, item-details-card-preview, action-toolbar, field-list) — with stories and tests
    • overflow-toolbar/ — with stories
  • Rewrite all @/components/ui/* imports per the mapping in primitives.md
  • Rewrite @/utils@/types/canary/utils
  • Copy from worktree to main clone src/components/canary/organisms/:
    • sidebar/ (sidebar.tsx, sidebar-context.tsx, sidebar.stories.tsx, sidebar.test.tsx, sidebar.mdx, index.ts)
    • app-header/ (all files)
    • item-details/ (all files)
    • item-grid/ (all files)
  • Rewrite all imports per mapping
  • Copy any assets from the worktree (callil-consolidation-worktree/public/canary/) into the main clone (ux-prototype/public/canary/) that the organisms reference (brand logos, images), if not already present in the main clone
  • Note: all copy operations in Run 3 follow this same direction — source: worktree (callil branch, read-only reference) → target: main clone (component-consolidation branch, where edits happen)
  • All 4 callil organisms and their supporting molecules in the main clone
  • All imports rewritten to canonical canary paths
  • All organism stories render in Storybook
  • npm run lint — pass
  • npx tsc --noEmit — pass
  • npm run test — pass (existing + new organism tests)
  • Storybook build succeeds (npm run build-storybook)
  • [Playwright MCP] Navigate to each organism’s stories in Storybook and verify rendering:
    • Sidebar: Default, WithBadges, Composition (check nav items, dark theme, collapse/expand)
    • AppHeader: Default, WithSearch (check action buttons, search input)
    • ItemGrid: Default, WithSearch, Editable (check grid renders with data, search filters)
    • ItemDetails: Default (check drawer opens, fields display, tabs work)
  • All organisms functional in Storybook with correct imports
  • No references to @/components/ui/* in any canary code

Risk: Medium (replaces existing atom, could break grid stories) Effort: Small-Medium Governs: analysis/grid-integration.md (decision #2)

  • Run 3 complete (organisms ported, including callil’s select-cell-editor molecule)
  • Promote callil’s SelectCellEditor from molecules/item-grid/ to atoms/grid/select/ as a first-class canary atom
  • Generalize options format to accept both SelectOption[] and Record<string, string>
  • Update the atom to follow canary conventions (StaticConfig/RuntimeConfig split, factory function createSelectCellEditor)
  • Add stories: Default, WithManyOptions, KeyboardNavigation, BothFormats
  • Add unit tests: highlight cycling, Enter/Escape, ARIA roles, checkmark rendering
  • Add play function with step DSL: open → Arrow Down 3x → Enter → verify
  • Update EnumCellEditor to delegate to SelectCellEditor (or replace entirely and update all references)
  • Delete deprecated EnumCellEditor if fully replaced
  • src/components/canary/atoms/grid/select/ with full story/test/play coverage
  • EnumCellEditor replaced or deprecated
  • All grid stories using enum editors still work
  • npm run test — pass
  • New SelectCellEditor stories render correctly
  • Existing grid stories with enum columns render correctly (regression check)
  • VRT: capture SelectCellEditor popup baseline
  • [Playwright MCP] Navigate to SelectCellEditor stories; verify popup rendering, keyboard navigation (Arrow Down cycling, Enter to select, Escape to cancel), checkmark on selected item. Also check an existing enum grid story to confirm no regression.
  • Single canonical select/enum cell editor in canary atoms
  • All consumers updated

Risk: Highest complexity — core of the project Effort: Large Governs: analysis/grid-integration.md (decisions #1, #3, #4), analysis/test-coverage.md

Promote general-purpose capabilities from item-grid into entity-data-grid. Each sub-run adds one capability with its own stories and tests. Sub-runs are ordered by risk (highest first).

  • Run 4 complete (SelectCellEditor in place)

Risk: Highest — new editing paradigm replacing cell-granular mode

  • Implement row-auto-publish as the entity-data-grid editing mode
  • Remove cell-granular onEntityUpdated callback
  • Add onRowPublish(rowId, changes, entity?) async callback
  • Implement pending changes accumulation per row
  • Implement 50ms debounce on row blur before publish
  • Add row visual states: idle → saving (.ag-row-saving) → success/error (.ag-row-error)
  • Implement imperative ref API: saveAll(), discardAll(), getDirtyRowIds()
  • Add stories: RowAutoPublish, RowAutoPublishError, SaveAllDrafts, DiscardAllDrafts (all with step DSL play functions)
  • Add unit tests for pending changes, publish lifecycle, ref API, CSS class injection
  • Gate: stories + tests + play functions pass; VRT for row visual states
  • [Playwright MCP] Navigate to RowAutoPublish story; edit a cell, edit another cell in same row, click a different row. Verify: saving row background appears briefly, then clears. Navigate to RowAutoPublishError; verify error row background persists. Check SaveAllDrafts/DiscardAllDrafts via toolbar button interactions.

Risk: Medium — new InitConfig property

  • Add actionsColumn as InitConfig property (mount-time ColDef with optional actionCount)
  • Inject as pinned-right column with auto-width calculation
  • Add story: WithActionsColumn (with step DSL play function)
  • Add unit tests: column injection, width calc, action callbacks
  • Gate: story + test + play function pass
  • [Playwright MCP] Navigate to WithActionsColumn story; click action buttons in rows; verify visual pinned-right column appearance.

Risk: Medium — new UI elements in entity-data-grid

  • Add searchConfig to factory config (field list, placeholder)
  • Implement search bar with 150ms debounce
  • Implement filtered count display (“3 of 12 items” / “3 of 12 selected”)
  • Add stories: WithSearch, WithSearchAndSelection (with step DSL play functions)
  • Add unit tests: debounce timing, filter predicate, count updates
  • Gate: stories + tests + play functions pass
  • [Playwright MCP] Navigate to WithSearch story; type a search term; verify row count updates after debounce; verify count display text. Check WithSearchAndSelection: select rows, verify count switches to “X of Y selected”.

Risk: Low-Medium — StaticConfig property, both modes already implemented elsewhere

  • Add pagination mode as StaticConfig (design-time) property
  • Support server-driven (PaginationData + callbacks) — existing behavior
  • Support client-side (AG Grid built-in pageSize) — from item-grid
  • Modes are mutually exclusive
  • Add story: ClientPagination; verify existing ServerPagination still works
  • Add unit tests: both modes produce correct grid config
  • Gate: stories + tests pass

Sub-run 5e: Toolbar, Auto-Height, Drag-to-Scroll

Section titled “Sub-run 5e: Toolbar, Auto-Height, Drag-to-Scroll”

Risk: Low — additive features

  • Add toolbar prop (ReactNode slot above grid)
  • Add autoHeight StaticConfig property (domLayout: 'autoHeight')
  • Add enableDragToScroll opt-in with useDragToScroll hook (~80 lines pointer-event logic)
  • Add stories: WithToolbar, AutoHeight, DragToScroll
  • Add unit tests: toolbar render, autoHeight pass-through
  • Gate: stories + tests pass
  • Entity-data-grid with all promoted capabilities
  • 10+ new stories with step DSL play functions
  • Comprehensive unit test coverage for each capability
  • VRT baselines for row visual states
  • Entity-data-grid API supports all capabilities needed for item-grid to use it as a base
  • All new stories render and pass
  • All existing entity-data-grid stories still pass (no regressions)

Risk: Integration risk — the core consolidation goal Effort: Medium-Large Governs: analysis/grid-integration.md (overall goal)

Refactor item-grid to use entity-data-grid as its base instead of building directly on the DataGrid molecule.

  • Run 5 complete (entity-data-grid has all needed capabilities)
  • Evaluate whether item-grid can be a pure factory config call (createEntityDataGrid(itemGridConfig)) or needs a thin wrapper
  • Refactor item-grid implementation:
    • Replace useItemGridEditing with entity-data-grid’s built-in auto-publish
    • Replace inline search with entity-data-grid’s search/filter
    • Replace inline drag-to-scroll with entity-data-grid’s built-in
    • Replace inline toolbar with entity-data-grid’s toolbar slot
    • Replace inline actions column with entity-data-grid’s InitConfig actions
    • Keep item-domain-specific code: curated columns, typeahead lookups, nested field value getters/setters, custom renderers
  • Verify item-grid stories render identically to pre-refactor
  • Update item-grid tests
  • Refactored item-grid using entity-data-grid as base
  • Reduced item-grid code (domain config only, no framework reimplementation)
  • item-grid stories visually equivalent to pre-refactor
  • npm run test — pass
  • Item-grid stories render correctly (VRT comparison against Run 3 baselines)
  • Entity-data-grid stories still pass (no regressions from refactoring)
  • Play functions in item-grid stories pass
  • [Playwright MCP] Navigate to all item-grid stories (Default, Empty, Loading, WithSelection, WithSearch, Editable, Paginated, Composition). Compare visually against the same stories rendered from the worktree Storybook to verify equivalence. Key checks: search filtering, editable cells with save lifecycle, selection count, pagination, actions column, drag-to-scroll.
  • Item-grid delegates to entity-data-grid for all general-purpose capabilities
  • No duplicated framework code between item-grid and entity-data-grid

Risk: Low (mechanical, but affects published package) Effort: Small Governs: analysis/dependencies.md, analysis/index.md (barrel exports)

  • Run 6 complete (all components in final state)
  • Update src/canary.ts: union of both branches minus ArdaDetailField, plus all worktree additions (sidebar, app-header, item-grid, new atoms, new molecules)
  • Note: ArdaBadge is exported from both canary.ts and extras.ts — this collision is acceptable (different import paths). The canary version is canonical long-term; the extras version must not be used outside **/extras/** paths.
  • Verify src/extras.ts and src/index.ts unchanged (identical in both branches)
  • Add radix-ui to production dependencies
  • Remove @radix-ui/react-tooltip (superseded)
  • Add to devDependencies: @reduxjs/toolkit, react-redux, redux-persist, sonner
  • Remove from devDependencies: next-themes, react-icons, shadcn, pako, qr-scanner, react-dropzone
  • Resolve @storybook/addon-links (drop if no linkTo usage confirmed)
  • Delete src/components/canary/atoms/detail-field/ (replaced by ReadOnlyField)
  • Delete src/components/ui/ directory if still present (all contents moved)
  • Delete src/utils.ts if no remaining references
  • Delete src/hooks/use-mobile.ts if moved to types/canary/hooks/
  • Updated barrel exports
  • Updated package.json
  • No orphaned files
  • npm run lint — pass
  • npx tsc --noEmit — pass
  • npm run test — pass
  • npm run build:lib — pass (library build produces dist/)
  • Verify dist/ contains expected entry points
  • Published package API reflects consolidated component library
  • Library builds successfully
  • No unused dependencies

Risk: Low (verification, not implementation) Effort: Medium Governs: analysis/test-coverage.md (composition stories section)

  • Run 7 complete (package API finalized)
  • Add items browse/view/edit stories in src/use-cases/reference/items/ (REF::ITM::0001, 0002, 0004)
  • Add suppliers browse/view stories in src/use-cases/reference/business-affiliates/ as canary variants (REF::BA::0001, 0002)
  • Add list view general behavior stories in src/use-cases/general-behaviors/list-views/ (GEN::LST::0002, 0003, 0004, 0006)
  • Add KitchenSink developer reference in entity-data-grid/
  • All play functions use step DSL
  • Generate VRT baselines for all new composition pages
  • Generate VRT baselines for any components not yet captured
  • [Playwright MCP] Visual comparison of composition stories against dev-witness equivalents: navigate to each composition story and the corresponding dev-witness story side-by-side; screenshot both; verify layout, spacing, colors, and interaction patterns are recognizably similar
  • Ensure MDX docs exist for all components at every level:
    • Primitives: one primitives.mdx describing the purpose of the primitives layer, what’s included, shadcn/ui origin, import convention, and regeneration instructions. No individual primitive descriptions needed.
    • Atoms: MDX for each new atom (badge, button, avatar, card, dialog, drawer, input-group, icon-button, icon-label, read-only-field, search-input, SelectCellEditor)
    • Molecules: MDX for new molecules (sidebar sub-components, action-toolbar, field-list, overflow-toolbar)
    • Organisms: MDX for all organisms (sidebar, app-header, item-details — currently lacks MDX, item-grid, entity-data-grid, entity-data-grid-shim)
  • Update any stale MDX docs from the worktree that reference old import paths
  • 6+ composition stories mapped to product use cases
  • Complete VRT baseline set
  • MDX documentation for all organisms
  • npm run test — pass
  • npm run build-storybook — pass
  • npm run test:storybook — all play functions pass
  • VRT baselines committed
  • [Playwright MCP] Final walkthrough of all composition stories: navigate through items page (sidebar → grid → search → row click → drawer), suppliers page (grid → search → row click), KitchenSink (exercise all capabilities). Verify end-to-end interaction flow works.
  • Consolidated component library is proven to reproduce dev-witness page layouts using only canary components
  • Full test/story/VRT coverage per test-coverage.md metrics

Run 9: canary-refactor Adaptation (Follow-on)

Section titled “Run 9: canary-refactor Adaptation (Follow-on)”

Risk: Medium (may surface component gaps) Effort: Medium Governs: analysis/canary-refactor.md

  • Run 8 complete (acceptance passed)
  • Adapt existing src/canary-refactor/ stories to use consolidated canary components
  • Determine if ItemDetailsPanel.tsx was absorbed into item-details organism or needs recreation
  • Determine if ItemTableAGGrid.tsx and columnPresets.tsx are still needed
  • Use divergences found during adaptation to drive fixes back into the component library
  • canary-refactor stories using consolidated components
  • List of component fixes/enhancements discovered during adaptation
  • canary-refactor stories render and pass
  • Visual comparison against dev-witness originals
  • canary-refactor stories demonstrate visual/behavioral equivalence with vendored pages
  • All discovered issues resolved or tracked

Before any push to origin, the following must pass locally:

  • npm run lint
  • npx tsc --noEmit
  • npm run test
  • VRT if snapshots changed (npx playwright test --project=vrt)

RiskRunMitigation
Theme migration diffs every grid storyRun 2Scheduled early; VRT before/after is explicit gate
Auto-publish is complex new codeRun 5aFirst sub-run of capability promotion; isolated with its own tests
Import rewrite blast radius (38+ files)Run 1Mechanical with grep verification; build gate catches misses
SelectCellEditor replaces existing atomRun 4Existing enum stories serve as regression tests
Item-grid refactoring may need wrapperRun 6Entity-data-grid capabilities proven in Run 5 before refactoring begins
Barrel export changes affect published APIRun 7Library build gate; deferred until all components finalized
canary-refactor may surface gapsRun 9Explicitly last; gaps drive fixes back into library

Run 0: Branch Setup
└─► Run 1: Foundation (styles, primitives, atoms, imports)
└─► Run 2: Visual Foundation (theme migration)
└─► Run 3: Organism Port (sidebar, app-header, item-details, item-grid as-is)
└─► Run 4: Cell Editor Upgrade (SelectCellEditor)
└─► Run 5: Entity-data-grid Evolution
│ ├─ 5a: Auto-publish lifecycle
│ ├─ 5b: Actions column
│ ├─ 5c: Search/filter UI
│ ├─ 5d: Pagination modes
│ └─ 5e: Toolbar, auto-height, drag-to-scroll
└─► Run 6: Item-grid Integration
└─► Run 7: Package API
└─► Run 8: Acceptance (composition stories)
└─► Run 9: canary-refactor (follow-on)

All runs are sequential — each depends on the prior run’s exit criteria being met. Sub-runs within Run 5 are also sequential (5a → 5b → 5c → 5d → 5e) to allow incremental verification.