Initial Commit

This commit is contained in:
2026-05-27 23:29:53 +01:00
commit fbc70f96e6
10000 changed files with 1102152 additions and 0 deletions

View File

@@ -0,0 +1,186 @@
# OpenSpec Change: enterprise-browser-react-kanban
- **Change ID**: enterprise-browser-react-kanban
- **Package**: `@suaveplan/react-kanban`
- **Category**: browser
- **Target path**: `browser/react/react-kanban`
- **Status**: IN PROGRESS
- **Date**: 2026-04-24
- **Reference spec**: `/mnt/s/web/suaveplan/enterprise/react-kanban.md`
---
## Summary
Deliver `@suaveplan/react-kanban` on the genesis branch at enterprise quality. The package provides a headless `useKanban` hook with pure helpers (`findCardColumn`, `isColumnAtWipLimit`, `moveCardInBoard`, `moveColumnInBoard`, `validateMove`), plus reference `Board`, `Column`, `Card` components with Storybook stories. Genesis fixes the broken export map (missing `./core/column/column` and `./core/card/card` source files), splits the over-cap `board.ts` (320 lines) and `board.test.ts` (541 lines), adds Zod schemas for board state, OTel metrics, keyboard-drag support, and axe-core clean stories. No `react-dom` in runtime deps.
---
## Current-state audit (genesis)
Fresh build — does not exist on genesis yet.
---
## Available workspace deps on genesis
| Package | Role |
|---|---|
| `@suaveplan/error` (`workspace:^`) | `WIP_LIMIT_EXCEEDED`, `INVALID_MOVE`, `CARD_NOT_FOUND` |
| `@suaveplan/logger` (`workspace:^`) | Structured logging |
| `@suaveplan/schema-registry` (`workspace:^`) | Board / card schema registration |
| `zod` (`catalog:forms`) | Board, column, card Zod schemas |
| `@opentelemetry/api` (`catalog:observability`) | Move / WIP-violation metrics |
| `tslib` (`catalog:build`) | Runtime helpers |
Dev: `vitest`, `@vitest/coverage-istanbul`, `happy-dom`, `@testing-library/react`, `@suaveplan/testing`, `@suaveplan/testing-dom`.
No `react-dom` in runtime `dependencies` — peer only.
---
## Preserved public API surface
```ts
// Hook
function useKanban(config: KanbanConfig): [KanbanState, KanbanActions]
// Pure helpers
function findCardColumn(board: KanbanBoard, cardId: string): KanbanColumn | null
function isColumnAtWipLimit(column: KanbanColumn): boolean
function moveCardInBoard(board: KanbanBoard, source: DragSource, dest: DropTarget): KanbanBoard
function moveColumnInBoard(board: KanbanBoard, fromIdx: number, toIdx: number): KanbanBoard
function validateMove(board: KanbanBoard, source: DragSource, dest: DropTarget): MoveResult
// Reference components (new)
function Board(props: BoardProps): JSX.Element
function Column(props: ColumnProps): JSX.Element
function Card(props: CardProps): JSX.Element
// Types
interface KanbanBoard { columns: KanbanColumn[] }
interface KanbanColumn { id: string; title: string; wipLimit?: number; cards: KanbanCard[] }
interface KanbanCard<T = unknown> { id: string; title: string; data?: T }
interface KanbanConfig { board: KanbanBoard; onMove?(src: DragSource, dst: DropTarget): void; enforceWipLimits?: boolean }
interface KanbanState { board: KanbanBoard; dragging: DragSource | null }
interface KanbanActions { moveCard(src: DragSource, dst: DropTarget): MoveResult; moveColumn(from: number, to: number): void }
```
Subpath exports: `"."`, `"./core/board/board"`, `"./core/column/column"`, `"./core/card/card"`, `"./utils/drag-drop"`, `"./components/board"`.
---
## New additions for genesis
### 1. Implement the missing `Column` and `Card` modules
Main-branch exported `./core/column/column` and `./core/card/card` from `package.json` but had no source files. Genesis ships `src/core/column/column.ts` and `src/core/card/card.ts` with typed interfaces, helpers, and tests.
### 2. Split `board.ts` and `board.test.ts`
`board.ts` (320 lines) → `board.ts` + `moves.ts` + `wip.ts` + `swimlanes.ts`, each ≤ 300 lines. `board.test.ts` (541 lines) → split by responsibility: `board.test.ts`, `moves.test.ts`, `wip.test.ts`, each ≤ 500 lines.
### 3. Reference `Board`, `Column`, `Card` components + stories
`src/components/` — reference visual components built on `useKanban`. Stories: default, playground, WIP-limit enforcement, swimlane grouping.
### 4. Keyboard-drag support
Arrow-key move (card left/right/up/down) wired in `useKanban`; reference components respect it. Axe-core clean.
### 5. Zod schemas
`KanbanBoardSchema`, `KanbanColumnSchema`, `KanbanCardSchema` registered in `@suaveplan/schema-registry`.
### 6. OTel metrics
Counter `kanban.move_total{success}`, counter `kanban.wip_violation_total`, histogram `kanban.move_latency_ms`.
### 7. Error codes
`WIP_LIMIT_EXCEEDED`, `INVALID_MOVE`, `CARD_NOT_FOUND`.
---
## File layout
```text
browser/react/react-kanban/
package.json
tsconfig.json
vitest.config.ts
src/
index.ts
error-codes.ts
core/
board/
board.ts (useKanban hook, ≤300 lines)
board.test.ts
board.md
moves.ts (move helpers)
moves.test.ts
wip.ts (WIP-limit enforcement)
wip.test.ts
swimlanes.ts (swimlane grouping)
swimlanes.test.ts
index.ts
column/
column.ts
column.test.ts
column.md
column.types.ts
index.ts
card/
card.ts
card.test.ts
card.md
card.types.ts
index.ts
components/
board/
board.tsx
board.stories.tsx
board.test.tsx
board.md
index.ts
column/
column.tsx
column.stories.tsx
column.test.tsx
column.md
index.ts
card/
card.tsx
card.stories.tsx
card.test.tsx
card.md
index.ts
utils/
drag-drop/
drag-drop.ts
drag-drop.test.ts
drag-drop.md
index.ts
schema/
board-schema/
board-schema.ts
board-schema.md
index.ts
types/
common.ts
common.md
constants/
index.ts
tests/
integration.test.ts
```
---
## Impact
**Downstream**: any Suaveplan browser app with a kanban board.
**Breaking changes**: None — new components and modules are additive. `board.ts` split is internal.
**Prerequisites**: `@suaveplan/error`, `@suaveplan/schema-registry` on Verdaccio.

View File

@@ -0,0 +1,138 @@
# Tasks: enterprise-browser-react-kanban
**Change ID**: enterprise-browser-react-kanban
**Package**: `@suaveplan/react-kanban`
**Target path**: `browser/react/react-kanban`
**Prerequisite packages on Verdaccio**: `@suaveplan/error`, `@suaveplan/schema-registry`
---
## 0. Pre-flight (non-negotiable)
- [ ] 0.1 Confirm clean working tree (`git status` reports nothing) and no in-flight worktrees with uncommitted work (`git worktree list` then `git status` in each)
- [ ] 0.2 If any worktrees exist under `.claude/worktrees/`, account for each one: still-active agent, work-pending-merge, or stale-and-prune. Stale worktrees MUST be removed (`git worktree remove`) before opening a new one — code that has been lost twice on main is the failure mode this guards against.
- [ ] 0.3 Confirm the package set in `MANIFEST.md` matches `find packages -maxdepth 3 -name package.json` count. Drift here means a prior change archived without regenerating the manifest.
## 1. Scaffold
- [ ] Create directory tree per `proposal.md`.
- [ ] `package.json` v0.0.0, `"sideEffects": false`.
- `dependencies`: `@suaveplan/error workspace:^`, `@suaveplan/logger workspace:^`, `@suaveplan/schema-registry workspace:^`, `zod catalog:forms`, `@opentelemetry/api catalog:observability`, `tslib catalog:build`, `react catalog:react`
- `peerDependencies`: `react`, `react-dom`
- `devDependencies`: `vitest catalog:testing`, `@vitest/coverage-istanbul catalog:testing`, `happy-dom catalog:testing`, `@testing-library/react catalog:testing`, `@suaveplan/testing workspace:^`, `@suaveplan/testing-dom workspace:^`
- Subpath exports: `"."`, `"./core/board/board"`, `"./core/column/column"`, `"./core/card/card"`, `"./utils/drag-drop"`, `"./components/board"`
- [ ] `tsconfig.json`, `vitest.config.ts` (pool: forks, happy-dom environment).
---
## 2. Error codes
- [ ] `WIP_LIMIT_EXCEEDED`, `INVALID_MOVE`, `CARD_NOT_FOUND`.
- [ ] `registerKanbanErrorCodes()` — call eagerly.
---
## 3. Types (`src/types/common.ts`)
- [ ] `KanbanBoard`, `KanbanColumn`, `KanbanCard`, `KanbanConfig`, `KanbanState`, `KanbanActions`, `DragSource`, `DropTarget`, `MoveResult`, `KanbanSwimlane`.
- [ ] Write `common.md`.
---
## 4. WIP sub-module (`src/core/board/wip.ts`)
- [ ] `isColumnAtWipLimit(column)`, `enforceWipLimit(board, dest)` — throw `WIP_LIMIT_EXCEEDED`.
- [ ] Write `wip.test.ts`. Write docs inline (part of `board.md`).
---
## 5. Moves sub-module (`src/core/board/moves.ts`)
- [ ] `moveCardInBoard(board, src, dst)`, `moveColumnInBoard(board, from, to)`, `validateMove(board, src, dst)`.
- [ ] `findCardColumn(board, cardId)` — throw `CARD_NOT_FOUND` when absent.
- [ ] Write `moves.test.ts`.
---
## 6. Swimlanes sub-module (`src/core/board/swimlanes.ts`)
- [ ] `groupBySwimlane(board, keyFn)` returning `Map<string, KanbanSwimlane>`.
- [ ] Write `swimlanes.test.ts`.
---
## 7. `useKanban` hook (`src/core/board/board.ts`)
- [ ] Hook composing moves + wip + swimlanes; keyboard-drag support (ArrowLeft/Right/Up/Down).
- [ ] Wire OTel counters + histogram.
- [ ] Write `board.test.ts` (≤500 lines). Write `board.md`.
---
## 8. Column module (`src/core/column/`)
- [ ] `column.ts` — column type helpers: `addCard`, `removeCard`, `reorderCards`.
- [ ] Write `column.test.ts`, `column.md`.
---
## 9. Card module (`src/core/card/`)
- [ ] `card.ts` — card type helpers: `updateCard`, `cardMatches`.
- [ ] Write `card.test.ts`, `card.md`.
---
## 10. Drag-drop utilities (`src/utils/drag-drop/`)
- [ ] `drag-drop.ts` — `DragSource`, `DropTarget` helpers; drag-handle type utilities.
- [ ] Write `drag-drop.test.ts`, `drag-drop.md`.
---
## 11. Board schemas (`src/schema/board-schema/`)
- [ ] `board-schema.ts` — `KanbanBoardSchema`, `KanbanColumnSchema`, `KanbanCardSchema`; `registerKanbanSchemas()`.
- [ ] Write `board-schema.md`.
---
## 12. Reference `Card` component (`src/components/card/`)
- [ ] `card.tsx` — draggable card with keyboard support; `card.stories.tsx` (default, variants). Axe-core clean.
- [ ] Write `card.test.tsx`, `card.md`.
---
## 13. Reference `Column` component (`src/components/column/`)
- [ ] `column.tsx` — drop-target column showing WIP badge; `column.stories.tsx`. Axe-core clean.
- [ ] Write `column.test.tsx`, `column.md`.
---
## 14. Reference `Board` component (`src/components/board/`)
- [ ] `board.tsx` — full board composing `Column` + `Card`; `board.stories.tsx` (default, playground, WIP-enforcement, swimlanes).
- [ ] Write `board.test.tsx`, `board.md`.
---
## 15. Integration test (`tests/integration.test.ts`)
- [ ] Full round-trip: render board, drag card, assert state, verify WIP rejection.
- [ ] Keyboard-drag round-trip (arrow keys).
---
## 16. Docs + verify
- [ ] Write `.md` per module (≥200 words, API reference, examples).
- [ ] Write `README.md` with subpath table, hook API, WIP-limit guide, a11y notes.
- [ ] `bunx turbo lint typecheck test --filter=@suaveplan/react-kanban` — green.
- [ ] `bunx turbo test:coverage` — 100%.
- [ ] No `@ts-expect-error` in `src/` or tests. No `react-dom` in runtime deps.
- [ ] All subpath exports resolve to real source files.
- [ ] Reference components pass axe-core.
- [ ] `bun scripts/publish.ts --package @suaveplan/react-kanban`.
- [ ] Archive to `openspec/archive/2026-04-24-enterprise-browser-react-kanban/`.