@cadraos/sdk 0.3.8 → 0.3.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,4 +1,5 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import { ReactNode } from 'react';
2
3
 
3
4
  /**
4
5
  * Shared kanban types for the @cadraos/sdk/kanban module.
@@ -317,6 +318,13 @@ interface KanbanCard$1 {
317
318
  executionId?: string;
318
319
  /** Summary of the card's current/last run, if any. */
319
320
  run?: KanbanRunSummary;
321
+ /**
322
+ * When this card next becomes DUE on a scheduled lane (ISO string) — maps to
323
+ * `agentic_card.next_due_at` (spec §3a / STORY-003/004). Drives the board's
324
+ * ⧗ "due" chip: `next_due_at ≤ now` → due (the next sweep will pick it up).
325
+ * OPTIONAL — absent → no due chip (today's render).
326
+ */
327
+ nextDueAt?: string;
320
328
  /** Who created the card. */
321
329
  createdBy?: string;
322
330
  /** When the card was created (ISO string). */
@@ -455,6 +463,13 @@ type StageRunStatus = "queued" | "running" | "completed" | "failed";
455
463
  * dispatch or a duplicate completion can't double-fire / double-advance
456
464
  * (design §5/§11 R2), and a `key` rename never collides with a prior run.
457
465
  * Backed by a host `stage_run` table with a UNIQUE constraint on `idemKey`.
466
+ *
467
+ * Stranded-run recovery (design §10): a run left `failed` (a dispatch threw) or
468
+ * stale `queued` (a process died before dispatch) is RE-CLAIMED by the next
469
+ * `beginStageRun` — reset to a fresh `queued` intent (with `attempts` bumped) —
470
+ * so a transient dispatch failure self-heals on the following sweep. v1 is
471
+ * acyclic-only: the idemKey pins `(cardId, stageId, version)`, so a re-claim
472
+ * only ever re-runs a FAILED attempt of the current stage, never a completed one.
458
473
  */
459
474
  interface StageRun {
460
475
  /** Idempotency key (`cardId:stageId:configVersion`; stageId = immutable id). UNIQUE. */
@@ -471,6 +486,19 @@ interface StageRun {
471
486
  executionUuid?: string;
472
487
  /** Error message (when failed). */
473
488
  error?: string;
489
+ /**
490
+ * Dispatch attempt count (design §10 — stranded-run recovery). `1` on first
491
+ * claim; incremented each time `beginStageRun` RE-CLAIMS a stranded
492
+ * (failed/stale-queued) run. Optional/back-compat — a host that doesn't track
493
+ * re-claims leaves it undefined; surfaced for the sweep digest + observability.
494
+ *
495
+ * v1 RECORDS attempts but does NOT cap them: self-heal is bounded by the
496
+ * CADENCE (one re-dispatch per sweep tick), not a max-attempts ceiling, so a
497
+ * transient outage recovers while a genuinely poison card simply re-fails once
498
+ * per tick (visible via this counter). A retry ceiling / backoff is a
499
+ * deliberate follow-on, not v1.
500
+ */
501
+ attempts?: number;
474
502
  /** When the run intent was created (ISO string). */
475
503
  createdAt: string;
476
504
  }
@@ -664,6 +692,13 @@ interface WireHandler {
664
692
  type?: string;
665
693
  agent_uuid?: string;
666
694
  team_uuid?: string;
695
+ /**
696
+ * remote_agent → the `remote_agents.uuid` for a `remote_agent` lane (§5.3).
697
+ * Snake-cased on the wire (published JSONB); mapped to `remoteAgentUuid`.
698
+ * WITHOUT this the binding is silently dropped at parse and every published
699
+ * remote_agent lane fails dispatch with "missing remoteAgentUuid" (FINDING-6).
700
+ */
701
+ remote_agent_uuid?: string;
667
702
  /** Legacy p28 slug ref. */
668
703
  agent_ref?: string;
669
704
  webhook?: {
@@ -1605,6 +1640,11 @@ interface KanbanAdapter {
1605
1640
  * @returns The updated card.
1606
1641
  */
1607
1642
  transitionCard(input: TransitionCardInput): Promise<KanbanCard$1>;
1643
+ /**
1644
+ * Delete a card permanently. OPTIONAL — when absent, the board offers no
1645
+ * delete affordance (back-compat). Server-authorized + org-scoped.
1646
+ */
1647
+ deleteCard?(cardId: string): Promise<void>;
1608
1648
  /**
1609
1649
  * Create an `ExecutionStreamController` for a card's run, keyed on the
1610
1650
  * card's `executionId`.
@@ -1623,6 +1663,77 @@ interface KanbanAdapter {
1623
1663
  createRunStream?(executionId: string): ExecutionStreamController;
1624
1664
  }
1625
1665
 
1666
+ interface ScheduleStatusProps {
1667
+ /** Next scheduled sweep (ISO string) → the "next run in {t}" countdown. */
1668
+ nextTickAt?: string;
1669
+ /** Per-lane due counts (lane key → # of due cards) — powers "· {n} due". */
1670
+ dueByLane?: Record<string, number>;
1671
+ /**
1672
+ * Override the leading status label. Defaults to "On schedule" (`on-schedule`
1673
+ * tone), "Paused" (`paused`), or "Running now" (`due`).
1674
+ */
1675
+ label?: string;
1676
+ /** Status tone → drives the dot colour + default label. Default `on-schedule`. */
1677
+ tone?: "on-schedule" | "due" | "paused";
1678
+ /** Additional CSS class. */
1679
+ className?: string;
1680
+ }
1681
+ /**
1682
+ * Render the calm schedule-status line.
1683
+ */
1684
+ declare function ScheduleStatus({ nextTickAt, dueByLane, label, tone, className, }: ScheduleStatusProps): react_jsx_runtime.JSX.Element;
1685
+
1686
+ /** The kind of agent bound to a lane — drives the picker's leading icon. */
1687
+ type LaneAgentKind = "cadra" | "remote" | "human";
1688
+ /** A selectable agent option for a lane. */
1689
+ interface LaneAgentOption {
1690
+ /** Stable agent id (Cadra agent/team uuid, remote runner id, or "human"). */
1691
+ id: string;
1692
+ /** Display name, e.g. "Cadra · Lead Sourcer" or "My desktop agent". */
1693
+ label: string;
1694
+ /** Agent kind — icon + semantics. Default `cadra`. */
1695
+ kind?: LaneAgentKind;
1696
+ /**
1697
+ * Connection state for a `remote` agent (desktop/ACP). `true` → online dot,
1698
+ * `false` → offline dot + the lane will pause. Omit for always-on agents.
1699
+ */
1700
+ online?: boolean;
1701
+ }
1702
+ interface LaneAgentPickerProps {
1703
+ /** The currently-assigned agent (display). */
1704
+ selected?: LaneAgentOption;
1705
+ /** The agents the owner may pick from. */
1706
+ options?: LaneAgentOption[];
1707
+ /** Fired when the owner assigns an agent to this lane. */
1708
+ onSelect?: (agentId: string) => void;
1709
+ /**
1710
+ * Owner gate (M10 / §14). When `false`, the picker is LOCKED (read-only + a
1711
+ * visible reason). Defaults to `true`; an editable picker still needs
1712
+ * `onSelect` + `options` to actually open.
1713
+ */
1714
+ canEdit?: boolean;
1715
+ /**
1716
+ * The visible reason shown under a locked picker (IS-6), e.g.
1717
+ * "Only Ava (owner) can change agents". Rendered as text, never a tooltip.
1718
+ */
1719
+ lockedReason?: string;
1720
+ /**
1721
+ * MV3 (optional, additive). When provided AND an agent is `selected`, the
1722
+ * open menu shows an "Edit this agent" footer action; clicking it calls
1723
+ * `onEditAgent(selected.id)` and closes the menu. The HOST owns the edit
1724
+ * surface (e.g. an in-shell sheet) — the picker only fires the intent.
1725
+ * Omit → today's render, unchanged.
1726
+ */
1727
+ onEditAgent?: (agentId: string) => void;
1728
+ /** Additional CSS class. */
1729
+ className?: string;
1730
+ }
1731
+ /**
1732
+ * Render the lane agent picker — interactive for owners, locked-with-reason for
1733
+ * members.
1734
+ */
1735
+ declare function LaneAgentPicker({ selected, options, onSelect, canEdit, lockedReason, onEditAgent, className, }: LaneAgentPickerProps): react_jsx_runtime.JSX.Element;
1736
+
1626
1737
  interface KanbanBoardProps {
1627
1738
  /** The adapter driving the board (default HTTP or a custom direct adapter). */
1628
1739
  adapter: KanbanAdapter;
@@ -1663,13 +1774,108 @@ interface KanbanBoardProps {
1663
1774
  * Default off (undefined).
1664
1775
  */
1665
1776
  pollIntervalMs?: number;
1777
+ /**
1778
+ * Server-aggregate card counts per lane KEY (spec §3a, `countsByLane`). Drives
1779
+ * each lane header's count pill + cap meter (the rendered array may be a
1780
+ * page). Absent → headers show the rendered `cards.length`.
1781
+ */
1782
+ laneCounts?: Record<string, number>;
1783
+ /**
1784
+ * Owner gate for per-lane agent pickers (M10 / §14). When `false`, every
1785
+ * lane's picker renders LOCKED (read-only + a visible reason). Passed to each
1786
+ * column as the default `canEdit`. Default `true`.
1787
+ */
1788
+ canEditHandlers?: boolean;
1789
+ /**
1790
+ * Resolve the agent-picker slot for a lane — "Cadra agent | My desktop agent"
1791
+ * (§12 wireframe). Return `undefined` for lanes with no picker. Owner-gated by
1792
+ * `canEditHandlers` unless the slot sets its own `canEdit`. Absent → no picker.
1793
+ */
1794
+ laneAgentPicker?: (lane: KanbanLane) => LaneAgentPickerProps | undefined;
1795
+ /**
1796
+ * The calm schedule-status strip rendered ABOVE the board — "On schedule ·
1797
+ * next run in 10m" (spec §6 ScheduleStatus; carries `nextTickAt` + `dueByLane`).
1798
+ * NEVER the word "Conductor" (M2). Absent → no strip.
1799
+ */
1800
+ scheduleStatus?: ScheduleStatusProps;
1801
+ /**
1802
+ * Host-driven card focus (p45 STORY-011): when set (or changed), the board
1803
+ * OPENS this card's drawer — powering a deep-link (the cockpit `Review`
1804
+ * ?card= target) and the activity-rail `Review` primary. `openCard` fetches
1805
+ * the card via `adapter.getCard`, so the target need NOT be in the loaded
1806
+ * page. Absent → no auto-open (today's behavior, M10). Re-set the SAME id (or
1807
+ * change it) to re-open after the user closes the drawer.
1808
+ */
1809
+ focusCardId?: string;
1810
+ /**
1811
+ * Replace the built-in `CardDrawer` with a HOST-owned drawer (work-shell v2,
1812
+ * Screen C). When provided, the board renders `renderCardDrawer(ctx)` instead
1813
+ * of its internal drawer whenever a card is selected — the host owns the
1814
+ * tabbed Run/Context/Activity/Steer presentation while the board still owns
1815
+ * selection state (`focusCardId`, click-to-open, close). Absent → today's
1816
+ * internal drawer (M10 back-compat; yobo + the test harness are unchanged).
1817
+ *
1818
+ * The context carries everything the internal drawer receives, so the host
1819
+ * drawer can advance/transition/retry/edit the card and react to run-terminal
1820
+ * exactly as the built-in one does.
1821
+ */
1822
+ renderCardDrawer?: (ctx: CardDrawerRenderContext) => ReactNode;
1823
+ /**
1824
+ * Enable HTML5 drag-to-move cards between lanes (work-shell v2 / MV4). A card
1825
+ * can only be dropped on a lane reachable by a `manualAllowed` transition from
1826
+ * its current lane — allowed lanes highlight, others refuse the drop. The move
1827
+ * commits through the same `transitionCard` path as the drawer (server
1828
+ * re-validates). OPTIONAL — absent → no drag (today's render, M10). Requires
1829
+ * `enableAdvance` (the transitions that gate drops come from the same source).
1830
+ */
1831
+ enableDrag?: boolean;
1666
1832
  /** Additional CSS class for the board container. */
1667
1833
  className?: string;
1668
1834
  }
1835
+ /**
1836
+ * Everything a HOST-owned card drawer needs — mirrors the props the built-in
1837
+ * `CardDrawer` receives (work-shell v2 Screen C seam). The board owns selection;
1838
+ * the host owns rendering.
1839
+ */
1840
+ interface CardDrawerRenderContext {
1841
+ /** The currently open card. */
1842
+ card: KanbanCard$1;
1843
+ /** The adapter (provides `getCard`, the run-stream factory, etc.). */
1844
+ adapter: KanbanAdapter;
1845
+ /** All lanes (for resolving transition target labels + human-gate detection). */
1846
+ lanes: KanbanLane[];
1847
+ /**
1848
+ * The lifecycle's declared edges — filter to `fromLane === card.lane &&
1849
+ * manualAllowed` for the "Move to a step" targets. Undefined when advance is
1850
+ * disabled.
1851
+ */
1852
+ transitions?: KanbanTransition[];
1853
+ /** Whether the card can be advanced (false in a terminal lane). */
1854
+ canAdvance: boolean;
1855
+ /** Close the drawer (clears the board's selection). */
1856
+ onClose: () => void;
1857
+ /** Re-dispatch a failed card. Absent when the host supplied no `onRetryCard`. */
1858
+ onRetry?: (card: KanbanCard$1) => void;
1859
+ /** Persist an inline title/description edit. Absent when `enableEdit` is false. */
1860
+ onUpdate?: (cardId: string, patch: {
1861
+ title?: string;
1862
+ description?: string;
1863
+ }) => void | Promise<void | unknown>;
1864
+ /** Auto-advance a single-edge lane. Absent when `enableAdvance` is false. */
1865
+ onAdvance?: (cardId: string) => void | Promise<void | unknown>;
1866
+ /** Take a specific manual transition. Absent when `enableAdvance` is false. */
1867
+ onTransition?: (cardId: string, toLane: string) => void | Promise<void | unknown>;
1868
+ /** Delete this card permanently. Absent when the adapter has no `deleteCard`. */
1869
+ onDelete?: (cardId: string) => Promise<boolean>;
1870
+ /** Fired when the card's run reaches a terminal state (host should refetch). */
1871
+ onRunTerminal?: (status: "completed" | "failed") => void;
1872
+ /** Fired on a non-throwing run/board error. */
1873
+ onError?: (message: string) => void;
1874
+ }
1669
1875
  /**
1670
1876
  * Drop-in agent-first kanban board.
1671
1877
  */
1672
- declare function KanbanBoard({ adapter, onCreateCard, onRetryCard, onError, onCardCreated, enableCreate, enableEdit, enableAdvance, pollIntervalMs, className, }: KanbanBoardProps): react_jsx_runtime.JSX.Element;
1878
+ declare function KanbanBoard({ adapter, onCreateCard, onRetryCard, onError, onCardCreated, enableCreate, enableEdit, enableAdvance, pollIntervalMs, laneCounts, canEditHandlers, laneAgentPicker, scheduleStatus, focusCardId, renderCardDrawer, enableDrag, className, }: KanbanBoardProps): react_jsx_runtime.JSX.Element;
1673
1879
 
1674
1880
  /**
1675
1881
  * The host's BROWSER-SIDE card source. The host implements these against its
@@ -1691,6 +1897,8 @@ interface AgentBoardCardSource {
1691
1897
  }): Promise<KanbanCard$1>;
1692
1898
  /** Request a manual transition → host engine validates + advances. */
1693
1899
  transitionCard(input: TransitionCardInput): Promise<KanbanCard$1>;
1900
+ /** Delete a card permanently. OPTIONAL — absent → no delete affordance. */
1901
+ deleteCard?(cardId: string): Promise<void>;
1694
1902
  /**
1695
1903
  * Create an `ExecutionStreamController` for a card's run (the SAME controller
1696
1904
  * the chat module uses). Optional — when absent, the drawer falls back to the
@@ -1748,14 +1956,54 @@ interface KanbanColumnProps {
1748
1956
  onCreateCard?: (lane: KanbanLane) => void;
1749
1957
  /** Whether to show the "+ New card" affordance on this column. */
1750
1958
  showCreate?: boolean;
1959
+ /**
1960
+ * SERVER-aggregate card count for the header pill + cap meter (spec §3a,
1961
+ * `countsByLane`). The rendered array may be a page; this is the true total.
1962
+ * OPTIONAL — absent → the header shows `cards.length` (today's render).
1963
+ */
1964
+ count?: number;
1965
+ /**
1966
+ * The per-lane agent-picker slot (spec §6). Rendered in the lane header when
1967
+ * supplied; owner-gating comes from the slot's own `canEdit` or the
1968
+ * board-level `canEditHandlers`. OPTIONAL — absent → no picker.
1969
+ */
1970
+ agentPicker?: LaneAgentPickerProps;
1971
+ /**
1972
+ * Board-level owner gate for lane pickers (M10). Used as the default
1973
+ * `canEdit` for `agentPicker` when the slot doesn't set its own.
1974
+ */
1975
+ canEditHandlers?: boolean;
1976
+ /** Make this column's cards draggable sources. */
1977
+ draggableCards?: boolean;
1978
+ /** A drag is in progress somewhere on the board (drives the dimming of
1979
+ * non-target lanes). */
1980
+ dragActive?: boolean;
1981
+ /** This lane is a VALID drop target for the card currently being dragged
1982
+ * (`manualAllowed` from its lane) — highlight + accept the drop. */
1983
+ dropAllowed?: boolean;
1984
+ /** Fired when a card in this column starts being dragged. */
1985
+ onCardDragStart?: (card: KanbanCard$1) => void;
1986
+ /** Fired when a drag ends (drop or cancel). */
1987
+ onCardDragEnd?: () => void;
1988
+ /** Fired when a card is dropped onto this lane (only when `dropAllowed`). */
1989
+ onLaneDrop?: (lane: KanbanLane) => void;
1751
1990
  /** Additional CSS class. */
1752
1991
  className?: string;
1753
1992
  }
1754
1993
  /**
1755
1994
  * Render a single lane column with its cards.
1756
1995
  */
1757
- declare function KanbanColumn({ lane, cards, onCardClick, selectedCardId, onCreateCard, showCreate, className, }: KanbanColumnProps): react_jsx_runtime.JSX.Element;
1996
+ declare function KanbanColumn({ lane, cards, onCardClick, selectedCardId, onCreateCard, showCreate, count, agentPicker, canEditHandlers, draggableCards, dragActive, dropAllowed, onCardDragStart, onCardDragEnd, onLaneDrop, className, }: KanbanColumnProps): react_jsx_runtime.JSX.Element;
1758
1997
 
1998
+ /**
1999
+ * A card's lane-relative schedule state (spec §3a), computed by the column from
2000
+ * the card + its lane and passed down. OPTIONAL — absent → today's render.
2001
+ *
2002
+ * - `ready` — run completed and awaiting the next lane (success chip).
2003
+ * - `due` — `next_due_at ≤ now`; the next sweep will pick it up (neutral).
2004
+ * - `waiting` — sitting in a scheduled lane, not yet due (neutral).
2005
+ */
2006
+ type DueState = "due" | "ready" | "waiting";
1759
2007
  interface KanbanCardProps {
1760
2008
  /** The card to render. */
1761
2009
  card: KanbanCard$1;
@@ -1763,13 +2011,30 @@ interface KanbanCardProps {
1763
2011
  onClick?: (card: KanbanCard$1) => void;
1764
2012
  /** Whether this card is currently selected/open. */
1765
2013
  selected?: boolean;
2014
+ /**
2015
+ * The card's lane-relative schedule state (spec §3a), supplied by the column.
2016
+ * Drives the due/ready/waiting chips. OPTIONAL — absent → run-status chip only
2017
+ * (today's render).
2018
+ */
2019
+ dueState?: DueState;
2020
+ /**
2021
+ * Make this card an HTML5 drag SOURCE (work-shell v2 / MV4). OPTIONAL —
2022
+ * absent → not draggable (today's render, M10). The board decides where a
2023
+ * drop is allowed (only `manualAllowed` targets); the card just reports the
2024
+ * drag start/end.
2025
+ */
2026
+ draggable?: boolean;
2027
+ /** Fired on drag start (draggable cards only). */
2028
+ onDragStart?: (card: KanbanCard$1) => void;
2029
+ /** Fired on drag end (draggable cards only). */
2030
+ onDragEnd?: () => void;
1766
2031
  /** Additional CSS class. */
1767
2032
  className?: string;
1768
2033
  }
1769
2034
  /**
1770
2035
  * Render a single kanban card tile.
1771
2036
  */
1772
- declare function KanbanCard({ card, onClick, selected, className }: KanbanCardProps): react_jsx_runtime.JSX.Element;
2037
+ declare function KanbanCard({ card, onClick, selected, dueState, draggable, onDragStart, onDragEnd, className, }: KanbanCardProps): react_jsx_runtime.JSX.Element;
1773
2038
 
1774
2039
  interface CardDrawerProps {
1775
2040
  /** The card to display, or null when the drawer is closed. */
@@ -1833,6 +2098,69 @@ interface CardDrawerProps {
1833
2098
  */
1834
2099
  declare function CardDrawer({ card, adapter, onClose, onRetry, onUpdate, onAdvance, canAdvance, transitions, lanes, onTransition, onRunTerminal, onError, className, }: CardDrawerProps): react_jsx_runtime.JSX.Element | null;
1835
2100
 
2101
+ interface CapMeterProps {
2102
+ /** Current card count in the lane (server aggregate). */
2103
+ count: number;
2104
+ /** The lane's per-sweep cap. */
2105
+ cap: number;
2106
+ /** Additional CSS class. */
2107
+ className?: string;
2108
+ }
2109
+ /**
2110
+ * Render a lane capacity meter (`count/cap` + a filling track).
2111
+ */
2112
+ declare function CapMeter({ count, cap, className }: CapMeterProps): react_jsx_runtime.JSX.Element;
2113
+
2114
+ /**
2115
+ * Internal styling helpers (inline — no clsx dependency).
2116
+ *
2117
+ * All board surfaces speak in SEMANTIC design tokens (p45 STORY-010): the
2118
+ * board is a shared SDK component, so it must adopt the CONSUMER's active
2119
+ * theme, never bake in a fixed palette. Tokens resolve per-theme via CSS
2120
+ * variables (light/dark flip without `dark:` variants), and the status
2121
+ * vocabulary is one-color-one-meaning (spec §3 crit F3):
2122
+ *
2123
+ * - `info` → running / working
2124
+ * - `success` → ready / done / sent / on-schedule
2125
+ * - `warning` → needs-you / human gate (RESERVED — never for "due")
2126
+ * - `destructive` → failed
2127
+ * - `neutral` → scheduled / due / waiting (machine will handle it)
2128
+ *
2129
+ * @module @cadraos/sdk/kanban
2130
+ */
2131
+ /**
2132
+ * Join truthy class names with a space.
2133
+ */
2134
+ declare function cn(...classes: (string | undefined | false | null)[]): string;
2135
+ /**
2136
+ * The badge/chip variants the board renders. Mirrors the shadcn `Badge` enum
2137
+ * every consumer ships (`default | secondary | destructive | outline | success
2138
+ * | warning`) PLUS the one new variant this story adds — `info` — for the
2139
+ * load-bearing "running/working" chip (spec §6). `neutral` is NOT a new Badge
2140
+ * variant: it composes the REAL `secondary` + `muted-foreground` tokens so the
2141
+ * calm scheduled/due/waiting chips stay distinct from amber without inventing a
2142
+ * `badge-muted` class (judge polish note 1).
2143
+ */
2144
+ type BadgeVariant = "default" | "secondary" | "outline" | "destructive" | "success" | "warning" | "info" | "neutral";
2145
+ /**
2146
+ * Token classes for a badge/chip variant. Colour only — the caller supplies the
2147
+ * chip layout (rounded-full, padding, text size). Soft-tinted for the status
2148
+ * variants (matching the mock's `hsl(var(--token)/.15)` fills) so they read as
2149
+ * calm siblings, solid for `default`.
2150
+ */
2151
+ declare function badgeVariantClass(variant: BadgeVariant): string;
2152
+ /**
2153
+ * Visual tone for a run status badge (reworked to semantic tokens — STORY-010).
2154
+ * Labels are unchanged; only the classes move from hardcoded palette to tokens.
2155
+ * `pending`/`cancelled` map to the CALM `neutral` tone — amber is reserved for
2156
+ * needs-you (F3), so a queued run never borrows the human-action colour.
2157
+ */
2158
+ declare function runStatusTone(status: string | undefined): {
2159
+ label: string;
2160
+ variant: BadgeVariant;
2161
+ className: string;
2162
+ };
2163
+
1836
2164
  interface UseKanbanBoardOptions {
1837
2165
  /** The adapter driving all board/card operations. */
1838
2166
  adapter: KanbanAdapter;
@@ -1893,6 +2221,8 @@ interface UseKanbanBoardResult {
1893
2221
  }) => Promise<KanbanCard$1 | null>;
1894
2222
  /** Manually transition a card; refetches the board afterwards. */
1895
2223
  transitionCard: (input: TransitionCardInput) => Promise<KanbanCard$1 | null>;
2224
+ /** Delete a card permanently (optimistic + rollback). Returns success. */
2225
+ deleteCard: (cardId: string) => Promise<boolean>;
1896
2226
  /**
1897
2227
  * Called by the drawer when the open card's run reaches a terminal state.
1898
2228
  * Refetches the board (Spec-1 propagation contract).
@@ -4331,7 +4661,12 @@ declare function createDefaultKanbanAdapter(config: DefaultKanbanAdapterConfig):
4331
4661
  *
4332
4662
  * 2. `beginStageRun` is a CAS insert keyed on `idemKey` (UNIQUE). It returns
4333
4663
  * `{ created: true, run }` on first insert and `{ created: false, run }`
4334
- * when the key already exists — so a retried dispatch is a no-op.
4664
+ * when a NON-reclaimable run already exists — so a retried dispatch is a
4665
+ * no-op. A `failed` or STALE `queued` run is RE-CLAIMED (reset to `queued`
4666
+ * → `{ created: true }`) so a transient dispatch failure self-heals on the
4667
+ * next sweep (design §10 — stranded-run recovery); a `running`/`completed`
4668
+ * run is never re-claimed. v1 is acyclic-only: the idemKey pins
4669
+ * `(cardId, stageId, version)`, so a re-claim only re-runs a FAILED attempt.
4335
4670
  *
4336
4671
  * 3. `completeStageRun` runs in ONE transaction: mark the stage run completed
4337
4672
  * AND `applyTransition` to `toLane` together; it returns `{ applied: false }`
@@ -4445,11 +4780,42 @@ interface KanbanStorageAdapter {
4445
4780
  */
4446
4781
  capabilities?(): AdapterCapabilities;
4447
4782
  /**
4448
- * CAS-insert a stage-run intent keyed on `idemKey` (UNIQUE).
4449
- *
4450
- * @returns `{ created: true, run }` on first insert; `{ created: false, run }`
4451
- * when the key already exists (the engine treats this as "already
4452
- * dispatched" and does NOT re-invoke the handler).
4783
+ * CAS-insert a stage-run intent keyed on `idemKey` (UNIQUE), with
4784
+ * FAILED/STALE-QUEUED RE-CLAIM (design §10 — stranded-run recovery).
4785
+ *
4786
+ * @returns `{ created: true, run }` when this call OWNS the dispatch — either
4787
+ * a first insert, OR a RE-CLAIM of an existing run left `failed` (a prior
4788
+ * dispatch threw → `failStageRun`) or stale `queued` (a process died between
4789
+ * begin and dispatch): the adapter resets that run to a fresh `queued` intent
4790
+ * (clearing the stale error/executionUuid, bumping `attempts`) and the engine
4791
+ * dispatches again. `{ created: false, run }` when a NON-reclaimable run
4792
+ * already exists (`running` — mid-flight; or `completed`) — the engine treats
4793
+ * this as "already dispatched"/terminal and does NOT re-invoke the handler.
4794
+ *
4795
+ * A host implements the re-claim as a single conditional write (e.g. an
4796
+ * `UPDATE stage_run SET status='queued', attempts=attempts+1, created_at=now(),
4797
+ * error=NULL, execution_uuid=NULL WHERE idem_key=$1 AND (status='failed' OR
4798
+ * (status='queued' AND created_at < $stale)) RETURNING …`, alongside the plain
4799
+ * CAS insert).
4800
+ *
4801
+ * ⚠ CRITICAL for the "two overlapping ticks never both win" guarantee: the SET
4802
+ * MUST move the row OUT of its own WHERE range — reset the `created_at`
4803
+ * discriminator (as shown). Under READ COMMITTED, a concurrent tick's UPDATE
4804
+ * blocks on the row lock, then RE-CHECKS its WHERE against the just-committed
4805
+ * row: with `created_at=now()` the stale-queued predicate no longer matches, so
4806
+ * it updates 0 rows (RETURNING empty → `created:false`). A naive
4807
+ * `SET status='queued'` that leaves `created_at` UNCHANGED is the trap — both
4808
+ * ticks still match the same stale `queued` row and BOTH re-dispatch. (The
4809
+ * in-memory reference adapter resets `createdAt` for exactly this reason.)
4810
+ * Equivalently, claim via a `SELECT … FOR UPDATE SKIP LOCKED` subselect. The
4811
+ * staleness horizon MUST comfortably exceed the host's dispatch timeout so a
4812
+ * genuinely in-flight `queued` row (mid dispatch call) is never re-claimed out
4813
+ * from under it.
4814
+ *
4815
+ * v1 scope is ACYCLIC flows only: because the idemKey embeds
4816
+ * `(cardId, stageId, version)`, a card can never legitimately re-enter the same
4817
+ * stage at the same version, so a re-claim only ever re-runs a FAILED attempt
4818
+ * of the current stage — it never re-executes a completed one.
4453
4819
  */
4454
4820
  beginStageRun(cardId: string, r: {
4455
4821
  stageId: string;
@@ -4476,7 +4842,12 @@ interface KanbanStorageAdapter {
4476
4842
  applied: boolean;
4477
4843
  }>;
4478
4844
  /**
4479
- * Mark a stage run failed (surfaced + retryable). Does NOT advance the card.
4845
+ * Mark a stage run failed (surfaced). Does NOT advance the card — it stays in
4846
+ * its lane. The engine calls this when a stage DISPATCH throws (design §10 —
4847
+ * stranded-run recovery), so the run is left `failed` (NOT a stranded `queued`
4848
+ * row the CAS would skip forever) and is RE-CLAIMED by the next
4849
+ * `beginStageRun` on the following sweep. A `completed` run is never clobbered
4850
+ * (a late failure after a success is a no-op — idempotent).
4480
4851
  */
4481
4852
  failStageRun(idemKey: string, f: {
4482
4853
  error: string;
@@ -4496,4 +4867,4 @@ interface KanbanStorageAdapter {
4496
4867
  getStageRun(idemKey: string): Promise<StageRun | null>;
4497
4868
  }
4498
4869
 
4499
- export { type Actor, type AdapterCapabilities, AgentBoard, type AgentBoardCardSource, type AgentBoardProps, type ApplyTransitionInput, BoardConfigClient, type BoardConfigClientOptions, type BoardConfigHttp, CadraLifecycleSource, CardDrawer, type CardDrawerProps, type CardList, type CreateCardInput, type CreateCardServerInput, type DefaultKanbanAdapterConfig, type DueRule, type KanbanActorType, type KanbanAdapter, KanbanBoard, type KanbanBoard$1 as KanbanBoardData, type KanbanBoardProps, type KanbanCard$1 as KanbanCard, type KanbanCardProps, KanbanCard as KanbanCardTile, KanbanColumn, type KanbanColumnProps, type KanbanHandlerType, type KanbanLane, type KanbanLaneDispatch, type KanbanLaneHandler, type KanbanLaneTrigger, type KanbanRunStatus, type KanbanRunSummary, type KanbanStorageAdapter, type KanbanTransition, type KanbanWebhookBinding, type LifecycleRef, type LifecycleSource, type LifecycleSourceKind, type ListCardsInput, type ResolveLifecycleRef, type ResolvedBoardConfig, type ResolvedLifecycle, type Stage, type StageRun, type StageRunStatus, type Transition, type TransitionCardInput, type TransitionLogEntry, type UseCardRunOptions, type UseCardRunResult, type UseKanbanBoardOptions, type UseKanbanBoardResult, createDefaultKanbanAdapter, mapDefinitionToBoard, toResolvedLifecycle, useCardRun, useKanbanBoard };
4870
+ export { type Actor, type AdapterCapabilities, AgentBoard, type AgentBoardCardSource, type AgentBoardProps, type ApplyTransitionInput, type BadgeVariant, BoardConfigClient, type BoardConfigClientOptions, type BoardConfigHttp, CadraLifecycleSource, CapMeter, type CapMeterProps, CardDrawer, type CardDrawerProps, type CardDrawerRenderContext, type CardList, type CreateCardInput, type CreateCardServerInput, type DefaultKanbanAdapterConfig, type DueRule, type DueState, type KanbanActorType, type KanbanAdapter, KanbanBoard, type KanbanBoard$1 as KanbanBoardData, type KanbanBoardProps, type KanbanCard$1 as KanbanCard, type KanbanCardProps, KanbanCard as KanbanCardTile, KanbanColumn, type KanbanColumnProps, type KanbanHandlerType, type KanbanLane, type KanbanLaneDispatch, type KanbanLaneHandler, type KanbanLaneTrigger, type KanbanRunStatus, type KanbanRunSummary, type KanbanStorageAdapter, type KanbanTransition, type KanbanWebhookBinding, type LaneAgentKind, type LaneAgentOption, LaneAgentPicker, type LaneAgentPickerProps, type LifecycleRef, type LifecycleSource, type LifecycleSourceKind, type ListCardsInput, type ResolveLifecycleRef, type ResolvedBoardConfig, type ResolvedLifecycle, ScheduleStatus, type ScheduleStatusProps, type Stage, type StageRun, type StageRunStatus, type Transition, type TransitionCardInput, type TransitionLogEntry, type UseCardRunOptions, type UseCardRunResult, type UseKanbanBoardOptions, type UseKanbanBoardResult, badgeVariantClass, cn, createDefaultKanbanAdapter, mapDefinitionToBoard, runStatusTone, toResolvedLifecycle, useCardRun, useKanbanBoard };