@nestos/kanban 0.5.1 → 0.5.2

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.
Files changed (147) hide show
  1. package/LICENSE +674 -0
  2. package/README.md +73 -29
  3. package/dist/{activity-C6xDM-cg.d.cts → activity-BbC4sgKL.d.cts} +28 -2
  4. package/dist/{activity-Dw8DOw0L.d.ts → activity-Czlan5xW.d.ts} +28 -2
  5. package/dist/activity-module-DaO1Rry0.d.cts +22 -0
  6. package/dist/activity-module-T22wliZu.d.ts +22 -0
  7. package/dist/activity-ports-Dqxa86zT.d.cts +12 -0
  8. package/dist/activity-ports-cxfrelaL.d.ts +12 -0
  9. package/dist/activity.cjs +10 -282
  10. package/dist/activity.cjs.map +1 -1
  11. package/dist/activity.d.cts +8 -30
  12. package/dist/activity.d.ts +8 -30
  13. package/dist/activity.js +9 -281
  14. package/dist/activity.js.map +1 -1
  15. package/dist/{chunk-O4OGAAV4.js → chunk-3JYLJPE5.js} +29 -5
  16. package/dist/chunk-3JYLJPE5.js.map +1 -0
  17. package/dist/{chunk-ATUFFNLV.js → chunk-3LEIHB7Y.js} +5 -5
  18. package/dist/chunk-4XF6LQSU.cjs.map +1 -1
  19. package/dist/chunk-5Q7WB63A.cjs +282 -0
  20. package/dist/chunk-5Q7WB63A.cjs.map +1 -0
  21. package/dist/{chunk-PSKQAFDF.cjs → chunk-7KDOTBRX.cjs} +1 -1
  22. package/dist/chunk-7KDOTBRX.cjs.map +1 -0
  23. package/dist/{chunk-5DLZWW5Y.js → chunk-AHHEGTUA.js} +3 -3
  24. package/dist/chunk-AOHQCX7K.cjs +95 -0
  25. package/dist/chunk-AOHQCX7K.cjs.map +1 -0
  26. package/dist/{chunk-IUI5CI2S.cjs → chunk-DINLR6E6.cjs} +96 -92
  27. package/dist/chunk-DINLR6E6.cjs.map +1 -0
  28. package/dist/{chunk-L44TM4EE.cjs → chunk-EYTVC2H5.cjs} +568 -454
  29. package/dist/chunk-EYTVC2H5.cjs.map +1 -0
  30. package/dist/chunk-FEE2YBZ7.js +1972 -0
  31. package/dist/chunk-FEE2YBZ7.js.map +1 -0
  32. package/dist/chunk-FNO5K3A7.js +282 -0
  33. package/dist/chunk-FNO5K3A7.js.map +1 -0
  34. package/dist/chunk-IHHDXZPZ.js +382 -0
  35. package/dist/chunk-IHHDXZPZ.js.map +1 -0
  36. package/dist/chunk-J3EAIVA3.js +280 -0
  37. package/dist/chunk-J3EAIVA3.js.map +1 -0
  38. package/dist/{chunk-DFTOTBGF.js → chunk-KH3WF3K4.js} +20 -16
  39. package/dist/chunk-KH3WF3K4.js.map +1 -0
  40. package/dist/{chunk-TB2RZGDV.js → chunk-M35X5PHO.js} +14 -1
  41. package/dist/chunk-M35X5PHO.js.map +1 -0
  42. package/dist/chunk-QRGZD2JU.cjs +1972 -0
  43. package/dist/chunk-QRGZD2JU.cjs.map +1 -0
  44. package/dist/{chunk-4KF7KUAI.cjs → chunk-RBOER6EW.cjs} +6 -6
  45. package/dist/chunk-RBOER6EW.cjs.map +1 -0
  46. package/dist/{chunk-6MN3H3YF.cjs → chunk-RSH6QSEC.cjs} +16 -3
  47. package/dist/chunk-RSH6QSEC.cjs.map +1 -0
  48. package/dist/{chunk-T7P5EIC4.cjs → chunk-VMRDXOLX.cjs} +33 -33
  49. package/dist/chunk-VMRDXOLX.cjs.map +1 -0
  50. package/dist/{chunk-5XKOCKY2.js → chunk-X4UHZX6F.js} +1 -1
  51. package/dist/chunk-XBSGUVCY.cjs +382 -0
  52. package/dist/chunk-XBSGUVCY.cjs.map +1 -0
  53. package/dist/{chunk-OHQTKGQN.js → chunk-XKDZHAUE.js} +242 -128
  54. package/dist/chunk-XKDZHAUE.js.map +1 -0
  55. package/dist/chunk-ZUZGAO6G.cjs +280 -0
  56. package/dist/chunk-ZUZGAO6G.cjs.map +1 -0
  57. package/dist/{content-CAIp1njn.d.cts → content-C5aJZFlw.d.cts} +1 -1
  58. package/dist/{content-CGMJdyH_.d.ts → content-hLH5BNud.d.ts} +1 -1
  59. package/dist/contracts/index.cjs +13 -3
  60. package/dist/contracts/index.cjs.map +1 -1
  61. package/dist/contracts/index.d.cts +5 -4
  62. package/dist/contracts/index.d.ts +5 -4
  63. package/dist/contracts/index.js +14 -4
  64. package/dist/{discovery-DxpirOz3.d.cts → discovery-9E-LfwIw.d.cts} +1 -1
  65. package/dist/{discovery-gSqWpuRm.d.ts → discovery-D60yrjej.d.ts} +1 -1
  66. package/dist/discovery.cjs +63 -65
  67. package/dist/discovery.cjs.map +1 -1
  68. package/dist/discovery.d.cts +4 -4
  69. package/dist/discovery.d.ts +4 -4
  70. package/dist/discovery.js +5 -7
  71. package/dist/discovery.js.map +1 -1
  72. package/dist/execution-bridge-fpCnH6z5.d.cts +52 -0
  73. package/dist/execution-bridge-rk0uVy_n.d.ts +52 -0
  74. package/dist/execution.cjs +6 -356
  75. package/dist/execution.cjs.map +1 -1
  76. package/dist/execution.d.cts +4 -51
  77. package/dist/execution.d.ts +4 -51
  78. package/dist/execution.js +9 -359
  79. package/dist/execution.js.map +1 -1
  80. package/dist/index.cjs +10 -12
  81. package/dist/index.cjs.map +1 -1
  82. package/dist/index.d.cts +9 -16
  83. package/dist/index.d.ts +9 -16
  84. package/dist/index.js +10 -12
  85. package/dist/index.js.map +1 -1
  86. package/dist/{module-CWNK2BMj.d.ts → module-CYfE7YhM.d.ts} +2 -2
  87. package/dist/{module-BbvwdCDF.d.cts → module-Ca9L3qCn.d.cts} +2 -2
  88. package/dist/{planning-CcFZYdIL.d.cts → planning-CHrOqQ6b.d.cts} +45 -10
  89. package/dist/{planning-CcFZYdIL.d.ts → planning-CHrOqQ6b.d.ts} +45 -10
  90. package/dist/{ports-Bn6ro2FB.d.ts → ports-B0McVg7B.d.ts} +4 -3
  91. package/dist/ports-BeGO1VRI.d.cts +49 -0
  92. package/dist/{ports-CaL6geqR.d.cts → ports-GQy0x-RI.d.cts} +4 -3
  93. package/dist/ports-ykGiNUSx.d.ts +49 -0
  94. package/dist/runtime/index.cjs +205 -0
  95. package/dist/runtime/index.cjs.map +1 -0
  96. package/dist/runtime/index.d.cts +75 -0
  97. package/dist/runtime/index.d.ts +75 -0
  98. package/dist/runtime/index.js +205 -0
  99. package/dist/runtime/index.js.map +1 -0
  100. package/dist/runtime/integration.cjs +423 -0
  101. package/dist/runtime/integration.cjs.map +1 -0
  102. package/dist/runtime/integration.d.cts +658 -0
  103. package/dist/runtime/integration.d.ts +658 -0
  104. package/dist/runtime/integration.js +423 -0
  105. package/dist/runtime/integration.js.map +1 -0
  106. package/dist/storage.cjs +9 -277
  107. package/dist/storage.cjs.map +1 -1
  108. package/dist/storage.d.cts +6 -6
  109. package/dist/storage.d.ts +6 -6
  110. package/dist/storage.js +11 -279
  111. package/dist/storage.js.map +1 -1
  112. package/dist/{store-q63eSMoT.d.cts → store-DPwHzGiz.d.cts} +2 -2
  113. package/dist/{store-Ct0gOAAg.d.ts → store-DgdrPCV9.d.ts} +2 -2
  114. package/dist/task-projection-CSorlMws.d.ts +11 -0
  115. package/dist/task-projection-DKKB_ueb.d.cts +11 -0
  116. package/examples/standalone.ts +22 -0
  117. package/package.json +117 -87
  118. package/dist/chunk-2YFPV5GF.cjs +0 -16
  119. package/dist/chunk-2YFPV5GF.cjs.map +0 -1
  120. package/dist/chunk-4KF7KUAI.cjs.map +0 -1
  121. package/dist/chunk-6MN3H3YF.cjs.map +0 -1
  122. package/dist/chunk-DFTOTBGF.js.map +0 -1
  123. package/dist/chunk-G6FCDLDF.js +0 -16
  124. package/dist/chunk-G6FCDLDF.js.map +0 -1
  125. package/dist/chunk-IUI5CI2S.cjs.map +0 -1
  126. package/dist/chunk-KYNYPKKH.cjs +0 -71
  127. package/dist/chunk-KYNYPKKH.cjs.map +0 -1
  128. package/dist/chunk-L44TM4EE.cjs.map +0 -1
  129. package/dist/chunk-LSE6JG5I.cjs +0 -20
  130. package/dist/chunk-LSE6JG5I.cjs.map +0 -1
  131. package/dist/chunk-O4OGAAV4.js.map +0 -1
  132. package/dist/chunk-OHQTKGQN.js.map +0 -1
  133. package/dist/chunk-PSKQAFDF.cjs.map +0 -1
  134. package/dist/chunk-QTMTBEBA.js +0 -20
  135. package/dist/chunk-QTMTBEBA.js.map +0 -1
  136. package/dist/chunk-T7P5EIC4.cjs.map +0 -1
  137. package/dist/chunk-TB2RZGDV.js.map +0 -1
  138. package/dist/ui/index.cjs +0 -1961
  139. package/dist/ui/index.cjs.map +0 -1
  140. package/dist/ui/index.d.cts +0 -209
  141. package/dist/ui/index.d.ts +0 -209
  142. package/dist/ui/index.js +0 -1961
  143. package/dist/ui/index.js.map +0 -1
  144. package/dist/ui/kanban.css +0 -194
  145. /package/dist/{chunk-ATUFFNLV.js.map → chunk-3LEIHB7Y.js.map} +0 -0
  146. /package/dist/{chunk-5DLZWW5Y.js.map → chunk-AHHEGTUA.js.map} +0 -0
  147. /package/dist/{chunk-5XKOCKY2.js.map → chunk-X4UHZX6F.js.map} +0 -0
@@ -1 +0,0 @@
1
- {"version":3,"sources":["/home/li/share/prj/x1/release/local-libraries/work/1ca933a0-6fe7-4e9c-b13b-76d7f318e0fb/source/libraries/kanban/dist/ui/index.cjs","../../src/ui/html.ts","../../src/ui/task-state.ts","../../src/ui/session-links.ts","../../src/ui/references.ts","../../src/ui/suggestion-field.ts","../../src/ui/session-view.ts","../../src/ui/view.ts","../../src/ui/children.ts","../../src/ui/panel-header.ts","../../src/ui/context-view.ts","../../src/ui/event-history.ts","../../src/ui/forms.ts","../../src/ui/automation.ts","../../src/ui/suggestion-picker.ts","../../src/ui/suggestions.ts","../../src/ui/index.ts","../../src/ui/drawer.ts","../../src/ui/diagnostics.ts","../../src/ui/operations.ts","../../src/ui/work-resources.ts","../../src/ui/renderer.ts","../../src/ui/commands.ts"],"names":["value"],"mappings":"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","file":"/home/li/share/prj/x1/release/local-libraries/work/1ca933a0-6fe7-4e9c-b13b-76d7f318e0fb/source/libraries/kanban/dist/ui/index.cjs","sourcesContent":[null,"export function escapeHTML(value: unknown): string {\n return String(value ?? '').replace(/[&<>\"']/g, character => ({'&': '&amp;', '<': '&lt;', '>': '&gt;', '\"': '&quot;', \"'\": '&#39;'}[character]!));\n}\nexport const escapeAttr = escapeHTML;\n","import type {KanbanTaskProjection} from '../contracts';\nimport type {KanbanPresentation} from './ports';\nimport {escapeHTML, escapeAttr} from './html';\n\nexport function taskStatus(ui: KanbanPresentation, task?: KanbanTaskProjection): string {\n if (!task) return '';\n return `<span class=\"kanban__task-state\" data-task-phase=\"${escapeAttr(task.phase)}\">${escapeHTML(ui.text('taskPhase.' + task.phase))}${task.reason ? `<small>${escapeHTML(taskReason(ui, task.reason))}</small>` : ''}${task.phase === 'waiting-start' && task.reason !== 'MANUAL_START_REQUIRED' && task.startAt !== undefined ? `<time data-task-start-at=\"${task.startAt}\" datetime=\"${new Date(task.startAt).toISOString()}\">${escapeHTML(startTimeText(ui,task.startAt))}</time>` : ''}</span>`;\n}\nexport function taskControls(ui: KanbanPresentation, task: KanbanTaskProjection, fixAvailable = false): string {\n return task.actions.filter(action => ['run', 'pause', 'resume', 'edit', 'cancel'].includes(action) || action === 'fix' && fixAvailable).map(action => {\n const label = ui.text('taskAction.' + action), icon = action==='cancel'?ui.icons.close:ui.icons[action as 'run' | 'pause' | 'resume' | 'fix' | 'edit'];\n return `<button type=\"button\" data-action=\"task-${action}\" title=\"${escapeAttr(label)}\" aria-label=\"${escapeAttr(label)}\">${icon ?? escapeHTML(label)}</button>`;\n }).join('');\n}\nexport function phaseFilter(ui: KanbanPresentation, selected?: string): string {\n const phases = ['waiting-start','analyzing','queued','waiting-dependency','preparing','running','pausing','paused','resuming','waiting-input','error','review','completed','cancelled','unknown'];\n return `<label>${escapeHTML(ui.text('taskState'))}<select name=\"phase\"><option value=\"\">${escapeHTML(ui.text('all'))}</option>${phases.map(phase => `<option value=\"${phase}\" ${phase === selected ? 'selected' : ''}>${escapeHTML(ui.text('taskPhase.'+phase))}</option>`).join('')}</select></label>`;\n}\n\nfunction startTimeText(ui:KanbanPresentation,startAt:number):string {\n return ui.text('taskStartAt')+' '+new Date(startAt).toLocaleString()+' · '+Math.max(0,Math.ceil((startAt-Date.now())/1000))+'s';\n}\nexport function updateTaskCountdowns(editor:HTMLElement,ui:KanbanPresentation):void {\n for(const node of editor.querySelectorAll<HTMLElement>('[data-task-start-at]')) {\n const at=Number(node.dataset.taskStartAt);if(Number.isFinite(at))node.textContent=startTimeText(ui,at);\n }\n}\n\nfunction taskReason(ui: KanbanPresentation, reason: string): string {\n const key = 'taskReason.' + reason, value = ui.text(key);\n return value === key || value === 'kanban.' + key ? reason : value;\n}\n","import type {KanbanPresentation} from './ports';\nimport {escapeHTML, escapeAttr} from './html';\n\nexport function sessionLink(ui: KanbanPresentation, id: string, label = id, url?: string): string {\n if (!url) return `<button type=\"button\" data-session=\"${escapeAttr(id)}\">${escapeHTML(label)}</button>`;\n return linkPair(ui, url, label, id);\n}\nexport function linkPair(ui: KanbanPresentation, href: string, label: string, sessionId?: string): string {\n return `<span class=\"kanban__session-links\"><a href=\"${escapeAttr(href)}\"${sessionId ? ` data-session=\"${escapeAttr(sessionId)}\" title=\"${escapeAttr(ui.text('openSessionInTab'))}\"` : ''}>${escapeHTML(label)}</a></span>`;\n}\n","import type {KanbanPresentation} from './ports';\nimport {escapeHTML, escapeAttr} from './html';\nimport {kanbanReference, type KanbanBoardSummary} from '../contracts';\n\nexport function referenceView(ui: KanbanPresentation, board: KanbanBoardSummary, entity?: {id: string; number?: number; kind?: 'log' | 'decision'}): string {\n return `<div class=\"kanban__reference\"><code>${escapeHTML(kanbanReference(board, entity))}</code>${referenceMenu(ui, board, entity)}</div>`;\n}\nexport function referenceMenu(ui: KanbanPresentation, board: KanbanBoardSummary, entity?: {id: string; number?: number; kind?: 'log' | 'decision'}, actions = ''): string {\n const reference = kanbanReference(board, entity);\n const path = `/board/${encodeURIComponent(board.id)}${entity ? entity.kind ? '/@record/' + encodeURIComponent(entity.id) : '/' + encodeURIComponent(entity.id) : ''}`;\n return `<details class=\"kanban__menu\" data-popover><summary aria-label=\"${escapeAttr(ui.text('referenceActions'))}\" title=\"${escapeAttr(ui.text('referenceActions'))}\">${ui.icons.more}</summary>\n <div class=\"kanban__menu-content\"><code class=\"kanban__menu-reference\">${escapeHTML(reference)}</code>${actions}<button type=\"button\" data-action=\"copy-reference\" data-copy=\"${escapeAttr(reference)}\">${escapeHTML(ui.text('copyReference'))}</button>\n <button type=\"button\" data-action=\"copy-link\" data-copy=\"${escapeAttr(path)}\">${escapeHTML(ui.text('copyLink'))}</button></div></details>`;\n}\nexport async function copyReference(ui: KanbanPresentation, button: HTMLElement): Promise<void> {\n const value = button.dataset.copy ?? '';\n const text = button.dataset.action === 'copy-link' ? ui.referenceUrl?.(value) ?? value : value;\n try {\n await navigator.clipboard.writeText(text);\n if (button.dataset.action === 'copy-resource') {button.title = ui.text('copied'); button.setAttribute('aria-label', ui.text('copied'));}\n else button.textContent = ui.text('copied');\n }\n catch {throw Object.assign(new Error(ui.text('copyFailed')), {code:'COPY_FAILED'});}\n}\n","import {escapeAttr} from './html';\nimport type {KanbanBoardSummary, KanbanSuggestion, KanbanSuggestionKind} from '../contracts';\nexport interface SuggestionField {\n name: string; label: string; kind: KanbanSuggestionKind; selected?: KanbanSuggestion[];\n multiple?: boolean; free?: boolean; manual?: boolean; required?: boolean; excludeItemId?: string;\n}\nexport function suggestionField(options: SuggestionField): string {\n return `<div class=\"kanban__suggest\" data-kanban-suggest=\"${escapeAttr(JSON.stringify(options))}\"></div>`;\n}\nexport function selectedItems(board: KanbanBoardSummary, ids: string[]): KanbanSuggestion[] {\n return ids.map(id => ({id, label: board.items.find(item => item.id === id)?.title ?? id}));\n}\n","import type {KanbanPresentation} from './ports';\nimport {escapeHTML, escapeAttr} from './html';\nimport type {KanbanExecutionSession,KanbanExecutionSessionDetail, KanbanRelatedSession, KanbanSessionExecution} from '../contracts';\nimport {sessionLink} from './session-links';\nimport {suggestionField} from './suggestion-field';\nexport function executionBadge(ui: KanbanPresentation, session?:KanbanExecutionSession,sessionUrl?: (id:string)=>string):string {\n if(!session) return '';\n const resource=session.resourceId??session.sessionId;\n return `<span class=\"kanban__execution\" data-execution-state=\"${escapeAttr(session.state)}\" data-stale=\"${session.stale}\"><span>${sessionUrl?sessionLink(ui,resource,ui.text('executionSession'),sessionUrl(resource)):escapeHTML(ui.text('executionSession'))} · ${escapeHTML(ui.text('sessionState.'+session.state))}${session.stale?' · '+escapeHTML(ui.text('activityStale')):''}</span><small>${escapeHTML(activityTime(ui, session))}</small></span>`;\n}\nfunction activityTime(ui: KanbanPresentation, session:KanbanExecutionSession):string {\n if(session.lastActivityAt===undefined) return ui.text('activityUnknown');\n const minutes=Math.max(0,Math.floor((Date.now()-session.lastActivityAt)/60000));\n return ui.text('lastActivity')+' · '+(minutes<1?ui.text('justNow'):minutes<60?ui.text('minutesAgo').replace('{count}',String(minutes)):new Date(session.lastActivityAt).toLocaleString());\n}\nexport function executionSessionView(ui: KanbanPresentation, detail:KanbanExecutionSessionDetail,editable:boolean, sessionUrl?: (id: string) => string):string {\n const session=detail.current,resource=session?.resourceId??session?.sessionId;\n return `<section class=\"kanban__execution-detail\"><h4>${escapeHTML(ui.text('executionSession'))}</h4><p>${escapeHTML(ui.text('executionSessionHelp'))}</p>\n ${detail.continuation ? `<button type=\"button\" data-action=\"continue-execution\" data-dispatch=\"${escapeAttr(detail.continuation.previousDispatchId)}\">${escapeHTML(ui.text('continueExecution'))}</button>` : ''}\n ${session?`${executionBadge(ui, session)}${sessionLink(ui, resource!, session.title ?? session.sessionId, sessionUrl?.(resource!))}<small>${escapeHTML(session.sessionId)}</small>${session.observedAt?`<small>${escapeHTML(ui.text('observedAt'))} · ${escapeHTML(new Date(session.observedAt).toLocaleString())}</small>`:''}${session.code?`<p>${escapeHTML(ui.text('activityReadFailed'))} (${escapeHTML(session.code)})</p>`:''}`:`<p>${escapeHTML(ui.text('noExecutionSession'))}</p>`}\n ${editable?`<details><summary>${escapeHTML(ui.text(session?'changeExecutionSession':'bindExecutionSession'))}</summary><form data-form=\"execution-session\">${suggestionField({name:'executionSession',label:'executionSession',kind:'session',manual:true,required:true})}<button>${escapeHTML(ui.text('bindExecutionSession'))}</button><p data-execution-error role=\"alert\" hidden></p></form></details>`:''}\n </section>`;\n}\nexport function relatedSessionsView(ui: KanbanPresentation, detail: KanbanExecutionSessionDetail, sessionUrl?: (id: string) => string): string {\n const sessions = detail.sessions ?? legacySessions(detail);\n return `${detail.sessionsCode ? `<p role=\"alert\">${escapeHTML(ui.text('sessionRelationsIncomplete'))} (${escapeHTML(detail.sessionsCode)})</p>` : ''}\n ${sessions.length ? `<ul class=\"kanban__session-list\">${sessions.map(session => relatedSession(ui, session, sessionUrl)).join('')}</ul>` : `<p>${escapeHTML(ui.text('noRelatedSessions'))}</p>`}`;\n}\nfunction relatedSession(ui: KanbanPresentation, session: KanbanRelatedSession, sessionUrl?: (id: string) => string): string {\n const resource = session.resourceId ?? session.sessionId, title = session.title ?? session.sessionId;\n return `<li class=\"kanban__related-session\" data-related-session=\"${escapeAttr(session.sessionId)}\"><div class=\"kanban__session-heading\" title=\"${escapeAttr(title)}\">${sessionLink(ui, resource, title, sessionUrl?.(resource))}</div>\n <small>${escapeHTML(session.sessionId)}</small>${session.createdAt !== undefined ? `<small>${escapeHTML(ui.text('sessionCreatedAt'))} · ${escapeHTML(date(session.createdAt))}</small>` : ''}<p>${session.current ? `<strong>${escapeHTML(ui.text('currentExecution'))}</strong> · ` : ''}${session.purposes.map(purpose => escapeHTML(ui.text('sessionPurpose.' + purpose))).join(' · ')} · ${escapeHTML(ui.text('sessionState.' + session.state))}${session.stale ? ' · ' + escapeHTML(ui.text('activityStale')) : ''}</p>\n ${session.code ? `<p role=\"alert\">${escapeHTML(ui.text('activityReadFailed'))} (${escapeHTML(session.code)})</p>` : ''}\n ${session.executions.length ? `<details><summary>${escapeHTML(ui.text('sessionExecutions'))} (${session.executions.length})</summary><ol>${session.executions.map(run => executionView(ui, run)).join('')}</ol></details>` : ''}</li>`;\n}\nfunction executionView(ui: KanbanPresentation, run: KanbanSessionExecution): string {\n const label = run.previousDispatchId ? ui.text('sessionContinuation') : ui.text('sessionPurpose.' + run.purpose);\n const showState = run.state && run.state !== run.outcome && !(run.state === 'completed' && ['succeeded', 'accepted', 'rejected'].includes(run.outcome ?? ''));\n const fields: Array<[string, string | undefined]> = [[ui.text('sessionExecutionId'), run.id], [ui.text('sessionRoundId'), run.roundId], [ui.text('sessionTurnId'), run.turnId],\n [ui.text('sessionPreviousExecution'), run.previousDispatchId], [ui.text(run.roundId ? 'sessionExecutionStarted' : 'sessionExecutionPrepared'), date(run.createdAt)], [ui.text('sessionExecutionEnded'), date(run.completedAt)],\n ...run.resources?.map(resource => [resource.label, resource.value] as [string, string]) ?? []];\n return `<li class=\"kanban__session-run\"><p>${escapeHTML(label)}${run.attempt ? ' · #' + run.attempt : ''}${run.outcome ? ' · ' + escapeHTML(ui.text('sessionRunOutcome.' + run.outcome)) : ''}${showState ? ' · ' + escapeHTML(ui.text('sessionState.' + run.state)) : ''}</p>\n <dl>${fields.filter((field): field is [string, string] => field[1] !== undefined).map(([key, value]) => `<dt>${escapeHTML(key)}</dt><dd>${escapeHTML(value)}</dd>`).join('')}</dl>${run.code ? `<p role=\"alert\">${escapeHTML(run.code)}</p>` : ''}</li>`;\n}\nfunction date(value?: number): string | undefined {return value === undefined ? undefined : new Date(value).toLocaleString();}\nfunction legacySessions(detail: KanbanExecutionSessionDetail): KanbanRelatedSession[] {\n return [...new Map(detail.history.map(row => [row.sessionId, row])).values()].map(row => ({\n ...(detail.current?.sessionId === row.sessionId ? detail.current : {sessionId: row.sessionId, state: 'unknown' as const, stale: true, recent: false}),\n current: detail.current?.sessionId === row.sessionId, purposes: ['execution'], executions: [],\n }));\n}\nexport function executionFilter(ui: KanbanPresentation, value='all'):string {\n return `<select name=\"execution\" aria-label=\"${escapeAttr(ui.text('executionFilter'))}\">${['all','running','recent','active'].map(filter=>`<option value=\"${filter}\" ${filter===value?'selected':''}>${escapeHTML(ui.text('executionFilter.'+filter))}</option>`).join('')}</select>`;\n}\n","import {taskStatus, phaseFilter} from './task-state';\nimport type {KanbanPresentation} from './ports';\nimport {referenceMenu} from './references';\nimport {kanbanReference} from '../contracts';\nimport {suggestionField} from './suggestion-field';\nimport {escapeHTML, escapeAttr} from './html';\nimport type {KanbanBoardSummary, KanbanItem, KanbanSessionItemPage, KanbanSessionActivityQuery, KanbanPrincipal} from '../contracts';\nimport {executionBadge,executionFilter} from './session-view';\n\nexport const states: KanbanItem['status'][] = ['proposed', 'backlog', 'open', 'claimed', 'review', 'completed', 'cancelled'];\nexport const button = (ui: KanbanPresentation, action: string, key: string, primary = false) => `<button type=\"button\"${primary ? ' class=\"kanban__primary\"' : ''} data-action=\"${escapeAttr(action)}\">${escapeHTML(ui.text(key))}</button>`;\nexport function contextTabs(ui: KanbanPresentation, active = 'board'): string {\n return `<nav class=\"kanban__context-tabs\" aria-label=\"${escapeAttr(ui.text('recordContext'))}\">${[['board', 'tabBoard'], ['log', 'tabLog'], ['decision', 'tabDecision']].map(([value, key]) =>\n `<button type=\"button\" data-context=\"${value}\" ${active === value ? 'aria-current=\"page\"' : ''}>${escapeHTML(ui.text(key))}</button>`).join('')}</nav>`;\n}\nexport function field(ui: KanbanPresentation, name: string, key: string, value = '', required = true, type = 'text'): string {\n return `<label>${escapeHTML(ui.text(key))}<input name=\"${name}\" type=\"${type}\" value=\"${escapeAttr(value)}\" ${required ? 'required' : ''}></label>`;\n}\nexport function textarea(ui: KanbanPresentation, name: string, key: string, value = ''): string {\n return `<label>${escapeHTML(ui.text(key))}<textarea name=\"${name}\" required>${escapeHTML(value)}</textarea></label>`;\n}\nexport function owns(item: KanbanItem, actor?: KanbanPrincipal): boolean {\n return !!actor && !!item.owner && (item.owner.kind ?? 'agent') === actor.kind && (item.owner.id ?? item.owner.agentId) === actor.id;\n}\nexport function navigation(ui: KanbanPresentation, boards: KanbanBoardSummary[], selected: string): string {\n return `<header class=\"kanban__sidebar-header\"><h2>${escapeHTML(ui.text('title'))}</h2>${button(ui, 'new-board', 'createShort')}</header>\n <section class=\"kanban__board-list\"><p class=\"kanban__caption\">${escapeHTML(ui.text('scope'))}</p><nav aria-label=\"${escapeAttr(ui.text('title'))}\">${boards.map(board =>\n `<button type=\"button\" data-board=\"${escapeAttr(board.id)}\" ${board.id === selected ? 'aria-current=\"page\"' : ''}><small>${escapeHTML(kanbanReference(board))}</small><span>${escapeHTML(board.title)}</span></button>`).join('')}</nav></section>\n <footer class=\"kanban__sidebar-tools\"><details class=\"kanban__reference-lookup\"><summary>${escapeHTML(ui.text('openReference'))}</summary>${referenceLookup(ui)}</details>\n <details class=\"kanban__scope-note\"><summary>${escapeHTML(ui.text('scopeInfo'))}</summary><p>${escapeHTML(ui.text('scopeHelp'))}</p></details></footer>`;\n}\nexport function referenceLookup(ui: KanbanPresentation): string {\n return `<form data-form=\"reference\"><input name=\"reference\" aria-label=\"${escapeAttr(ui.text('reference'))}\" placeholder=\"${escapeAttr(ui.text('referencePlaceholder'))}\" required><button>${escapeHTML(ui.text('openShort'))}</button></form>`;\n}\nexport function boardHeader(ui: KanbanPresentation, board: KanbanBoardSummary, active = 'board', automation = false): string {\n const [action, key] = active === 'log' ? ['new-log', 'newLog'] : active === 'decision' ? ['new-decision', 'newDecision'] : ['new-item', 'newItem'];\n const actions = `<div class=\"kanban__mobile-create\">${button(ui, action, key)}</div>${button(ui, 'refresh', 'refresh')}${automation ? button(ui, 'automation', 'automationTitle') : ''}`;\n return `<header class=\"kanban__page-header\" data-titlebar><span class=\"kanban__navigation\" data-navigation></span><div class=\"kanban__board-heading\"><h2 data-titlebar-title title=\"${escapeAttr(board.title)}\">${escapeHTML(board.title)}</h2></div>\n <div class=\"kanban__header-actions\"><span class=\"kanban__desktop-create\">${button(ui, action, key, true)}</span><span class=\"kanban__window-navigation\" data-window-navigation></span>${referenceMenu(ui, board, undefined, actions)}</div></header>`;\n}\nexport function catalogView(ui: KanbanPresentation, boards: KanbanBoardSummary[]): string {\n return `<header class=\"kanban__page-header\" data-titlebar><span class=\"kanban__navigation\" data-navigation></span><div class=\"kanban__board-heading\"><h2 data-titlebar-title>${escapeHTML(ui.text('title'))}</h2></div><span class=\"kanban__window-navigation\" data-window-navigation></span>${button(ui, 'new-board', 'createShort')}</header>\n <div class=\"kanban__record-list\">${boards.map(board => `<button type=\"button\" class=\"kanban__card\" data-board=\"${escapeAttr(board.id)}\"><strong>${escapeHTML(board.title)}</strong><small>${escapeHTML(kanbanReference(board))}</small></button>`).join('') || `<p>${escapeHTML(ui.text('emptyBoards'))}</p>`}</div>`;\n}\nexport function boardView(ui: KanbanPresentation, board: KanbanBoardSummary, page: KanbanSessionItemPage, query: KanbanSessionActivityQuery, activity = false, automation = false, sessionUrl?: (id:string)=>string): string {\n const columns = query.view === 'archive' ? [{id: 'archive', label: ui.text('archive'), states}] : board.columns ?? states.map(state => ({id: state, label: ui.text(state), states: [state]}));\n return `${boardHeader(ui, board, 'board', automation)}${contextTabs(ui)}<form data-form=\"filter\" class=\"kanban__filters\"><input name=\"search\" aria-label=\"${escapeAttr(ui.text('search'))}\" placeholder=\"${escapeAttr(ui.text('search'))}\" value=\"${escapeAttr(query.search ?? '')}\">\n <details class=\"kanban__filter-more\" data-popover><summary>${escapeHTML(ui.text('moreFilters'))}${query.area || query.milestone || query.view !== 'all' || query.execution && query.execution !== 'all' ? '<span class=\"kanban__filter-dot\" aria-hidden=\"true\"></span>' : ''}</summary><div class=\"kanban__filter-fields\"><label>${escapeHTML(ui.text('filter'))}<select name=\"view\" aria-label=\"${escapeAttr(ui.text('filter'))}\">${['all', 'backlog', 'ready', 'in-progress', 'review', 'closed', 'proposed', 'archive'].map(view => `<option value=\"${view}\" ${view === query.view ? 'selected' : ''}>${escapeHTML(ui.text(view === 'ready' ? 'open' : view === 'in-progress' ? 'claimed' : view === 'closed' ? 'completed' : view))}</option>`).join('')}</select></label>${phaseFilter(ui,query.phase)}${activity ? executionFilter(ui, query.execution) : ''}${suggestionField({name: 'area', label: 'area', kind: 'area', free: true, selected: query.area ? [{id: query.area, label: query.area}] : []})}\n ${suggestionField({name: 'milestone', label: 'milestone', kind: 'milestone', free: true, selected: query.milestone ? [{id: query.milestone, label: query.milestone}] : []})}</div></details><button>${escapeHTML(ui.text('filter'))}</button></form>\n <p class=\"kanban__count\" role=\"status\">${escapeHTML(ui.text('total', {count: page.total}))}</p><div class=\"kanban__columns\" data-has-items=\"${page.items.length > 0}\">${columns.map(column => {\n const items = page.items.filter(item => column.states.includes(item.status));\n return `<section class=\"kanban__column\" data-empty=\"${items.length === 0}\"><h3>${escapeHTML(column.label)} <small>${items.length}</small></h3><div class=\"kanban__column-items\" data-column=\"${escapeAttr(column.id)}\" tabindex=\"0\" aria-label=\"${escapeAttr(column.label + ' · ' + ui.text('columnItems'))}\">${items.map(item=>itemCard(ui,board,item,sessionUrl)).join('') || `<p>${escapeHTML(ui.text('emptyItems'))}</p>`}</div></section>`;\n }).join('')}</div><footer class=\"kanban__toolbar\"><small>${escapeHTML(ui.text('scrollHint'))}</small>${button(ui, 'scroll-left', 'previousColumns')}${button(ui, 'scroll-right', 'nextColumns')}${query.cursor ? button(ui, 'first', 'first') : ''}${page.nextCursor ? button(ui, 'next', 'next') : ''}</footer>`;\n}\n\nfunction itemCard(ui:KanbanPresentation,board:KanbanBoardSummary,item:KanbanSessionItemPage['items'][number],sessionUrl?: (id:string)=>string):string {\n const content=`<strong>${escapeHTML(item.title)}</strong><small>${escapeHTML(kanbanReference(board, item))}</small><small>P${item.planning.priority} · ${escapeHTML(item.planning.area ?? '')} · ${escapeHTML(item.planning.milestone ?? '')}</small>${taskStatus(ui,item.task)}${item.pendingAmendments ? `<small class=\"kanban__badge\">${escapeHTML(ui.text('pendingAmendments'))} (${item.pendingAmendments})</small>` : ''}${item.progress ? `<small>${item.progress.completed}/${item.progress.total}</small>` : ''}`;\n const identity=item.task?.sessionId?`data-session=\"${escapeAttr(item.task.sessionId)}\"`:`data-item=\"${escapeAttr(item.id)}\"`;\n return `<article class=\"kanban__card\"><button type=\"button\" class=\"kanban__item-open\" ${identity}>${content}</button>${item.task?.sessionId?`<button type=\"button\" data-item=\"${escapeAttr(item.id)}\" title=\"${escapeAttr(ui.text('details'))}\" aria-label=\"${escapeAttr(ui.text('details'))}\">${escapeHTML(ui.text('details'))}</button>`:''}<span data-item-execution=\"${escapeAttr(item.id)}\">${executionBadge(ui,item.execution,sessionUrl)}</span></article>`;\n}\n","import {taskStatus} from './task-state';\nimport {DEFAULT_KANBAN_WORK_TYPES, type KanbanBoardSummary, type KanbanItem, type KanbanSessionItemPage} from '../contracts';\nimport type {KanbanPresentation} from './ports';\nimport {escapeAttr, escapeHTML} from './html';\nimport {sessionLink} from './session-links';\nimport {button} from './view';\n\nexport function childType(board: KanbanBoardSummary, parent: KanbanItem): string | undefined {\n const types = board.workTypes ?? DEFAULT_KANBAN_WORK_TYPES;\n const rank = types.find(type => type.id === (parent.planning?.kind ?? board.defaultWorkType ?? 'task'))?.rank;\n const candidates = types.filter(type => rank !== undefined && type.rank > rank);\n return (candidates.find(type => type.id === (board.defaultWorkType ?? 'task')) ?? candidates.sort((a, b) => a.rank - b.rank)[0])?.id;\n}\nexport function childrenView(ui: KanbanPresentation, page: KanbanSessionItemPage, cursor: string | undefined, canCreate: boolean, sessionUrl?: (id: string) => string): string {\n return `<h4>${escapeHTML(ui.text('children'))} (${page.total})</h4><div class=\"kanban__actions\">${canCreate ? button(ui, 'new-child', 'newChild') : ''}</div>\n <div class=\"kanban__record-list\">${page.items.map(item => `<article class=\"kanban__card\"><button type=\"button\" data-item=\"${escapeAttr(item.id)}\"><strong>${escapeHTML(item.title)}</strong></button>${taskStatus(ui,item.task)}${item.task?.sessionId?sessionLink(ui,item.task.sessionId,ui.text('executionSession'),sessionUrl?.(item.task.sessionId)):''}${item.execution ? sessionLink(ui, item.execution.resourceId??item.execution.sessionId, ui.text('executionSession'), sessionUrl?.(item.execution.resourceId??item.execution.sessionId)) : ''}</article>`).join('')}</div>\n <div class=\"kanban__actions\">${cursor ? button(ui, 'children-first', 'first') : ''}${page.nextCursor ? `<button type=\"button\" data-action=\"children-next\" data-cursor=\"${escapeAttr(page.nextCursor)}\">${escapeHTML(ui.text('next'))}</button>` : ''}</div>`;\n}\n","import type {KanbanPresentation} from './ports';\nimport type {KanbanBoardSummary} from '../contracts';\nimport {referenceMenu} from './references';\nimport {escapeHTML, escapeAttr} from './html';\n\n/** Detail and creation panels use the same compact navigation row. */\nexport function panelHeader(ui: KanbanPresentation, title: string, board?: KanbanBoardSummary,\n entity?: {id: string; number?: number; kind?: 'log' | 'decision'}): string {\n const label = escapeAttr(ui.text('close'));\n return `<header class=\"kanban__panel-header\" data-titlebar><button type=\"button\" class=\"kanban__panel-back\" data-titlebar-back data-action=\"close-panel\" aria-label=\"${label}\" title=\"${label}\">${ui.icons.back ?? ui.icons.close}</button>\n <h3 data-titlebar-title title=\"${escapeAttr(title)}\">${escapeHTML(title)}</h3><span class=\"kanban__window-navigation\" data-window-navigation></span>${board ? referenceMenu(ui, board, entity, `<p class=\"kanban__menu-title\">${escapeHTML(title)}</p>`) : ''}</header>`;\n}\n","import type {KanbanPresentation} from './ports';\nimport {panelHeader} from './panel-header';\nimport {kanbanReference} from '../contracts';\nimport {suggestionField, selectedItems} from './suggestion-field';\nimport {escapeHTML, escapeAttr} from './html';\nimport type {KanbanBoardSummary, KanbanItem, KanbanProjectRecord, KanbanRecordPage, KanbanSourceRef} from '../contracts';\nimport {button, field, textarea, contextTabs, boardHeader} from './view';\nexport function recordsView(ui: KanbanPresentation, page: KanbanRecordPage, kind: KanbanProjectRecord['kind'], hasPrevious: boolean, board?: KanbanBoardSummary): string {\n return `${board ? boardHeader(ui, board, kind) : `<header class=\"kanban__toolbar\"><h2>${escapeHTML(ui.text(kind))}</h2></header>`}\n ${contextTabs(ui, kind)}<p>${escapeHTML(ui.text('recordHelp'))}</p><div class=\"kanban__record-list\" tabindex=\"0\" aria-label=\"${escapeAttr(ui.text(kind))}\">${recordLinks(ui, page, board)}${!page.total ? `<p>${escapeHTML(ui.text('emptyRecords'))}</p>` : ''}</div>\n <footer class=\"kanban__toolbar\">${hasPrevious ? button(ui, 'records-first', 'first') : ''}${page.nextCursor ? button(ui, 'records-next', 'next') : ''}</footer>`;\n}\nexport function recordLinks(ui: KanbanPresentation, page: KanbanRecordPage, board?: KanbanBoardSummary): string {\n return page.items.map(record => `<button type=\"button\" class=\"kanban__card\" data-record=\"${escapeAttr(record.id)}\"><strong>${escapeHTML(record.title)}</strong>${board && !record.activity ? `<small>${escapeHTML(kanbanReference(board, record))}</small>` : ''}<small>${escapeHTML(ui.text(record.activity ? 'automaticLog' : record.kind))} · ${escapeHTML(ui.text(record.status))} · ${escapeHTML(new Date(record.createdAt).toLocaleString())}</small></button>`).join('');\n}\nexport function sourcesView(ui: KanbanPresentation, sources: KanbanSourceRef[]): string {\n return sources.length ? `<ul>${sources.map(source => `<li><button type=\"button\" data-session=\"${escapeAttr(source.sessionId)}\">${escapeHTML(source.sessionId)}</button><small>${escapeHTML(source.messageId)} · ${escapeHTML(source.revision)} · ${escapeHTML(source.role)}</small></li>`).join('')}</ul>` : `<p>${escapeHTML(ui.text('noSources'))}</p>`;\n}\nexport function recordForm(ui: KanbanPresentation, kind: KanbanProjectRecord['kind'], board: KanbanBoardSummary, itemId = '', resources: import('../contracts').KanbanRecordResource[] = []): string {\n return `${panelHeader(ui, ui.text(kind === 'log' ? 'newLog' : 'newDecision'))}<form data-form=\"record\">\n <input type=\"hidden\" name=\"kind\" value=\"${kind}\">${field(ui, 'title', 'name')}${textarea(ui, 'content', 'content')}${kind === 'decision' ? textarea(ui, 'rationale', 'rationale') + suggestionField({name: 'supersedes', label: 'supersedes', kind: 'decision'}) : ''}\n ${kind === 'decision' && resources.length ? `<label>${escapeHTML(ui.text('recordResources'))}<select name=\"recordResources\" multiple>${resources.map(resource => `<option value=\"${escapeAttr(JSON.stringify(resource.reference))}\">${escapeHTML(resource.label)}</option>`).join('')}</select></label>` : ''}\n ${itemChoices(board, itemId)}<details><summary>${escapeHTML(ui.text('sources'))}</summary>${suggestionField({name: 'sourceSession', label: 'sourceSession', kind: 'session', manual: true})}${field(ui, 'sourceMessage', 'sourceMessage', '', false)}${field(ui, 'sourceRevision', 'sourceRevision', '', false)}</details><button>${escapeHTML(ui.text('save'))}</button></form>`;\n}\nexport function recordDetail(ui: KanbanPresentation, record: KanbanProjectRecord, board: KanbanBoardSummary, executions?: import('../contracts').KanbanRecordExecution[]): string {\n return `${panelHeader(ui, record.title, board, record)}\n <p>${escapeHTML(ui.text(record.kind))} · ${escapeHTML(ui.text(record.status))} · ${escapeHTML(ui.text(record.origin))}</p>${record.activity ? '' : `<small>${escapeHTML(ui.text('recordId'))}: ${escapeHTML(record.id)}</small>`}<p>${escapeHTML(ui.text('author'))}: ${escapeHTML(record.author.id)} · ${escapeHTML(new Date(record.createdAt).toLocaleString())}</p>\n <section data-reference-links></section><section class=\"kanban__prose\"><h4>${escapeHTML(ui.text('content'))}</h4><p>${escapeHTML(record.content)}</p>${record.rationale ? `<h4>${escapeHTML(ui.text('rationale'))}</h4><p>${escapeHTML(record.rationale)}</p>` : ''}</section>\n ${record.status === 'draft' ? `<div class=\"kanban__actions\">${button(ui, 'confirm-record', 'confirmRecord')}${button(ui, 'reject-record', 'rejectRecord')}</div>` : ''}\n ${record.kind === 'decision' && record.status === 'confirmed' && executions ? recordExecutionView(ui, board, executions) : ''}\n ${(record.externalRefs ?? []).map(ref => `<p>${escapeHTML(ref.system)}: ${escapeHTML(ref.id)}</p>`).join('')}\n ${record.supersedes ? recordLink(ui, record.supersedes, 'supersedes') : ''}${record.supersededBy ? recordLink(ui, record.supersededBy, 'superseded') : ''}\n <h4>${escapeHTML(ui.text('itemIds'))}</h4>${record.itemIds.map(id => `<button type=\"button\" data-item=\"${escapeAttr(id)}\">${escapeHTML(board.items.find(item => item.id === id)?.title ?? id)}</button>`).join('')}\n ${record.activity ? '' : `<form data-form=\"record-link\">${suggestionField({name: 'itemId', label: 'itemIds', kind: 'item', required: true})}<button>${escapeHTML(ui.text('linkRecord'))}</button></form>`}<h4>${escapeHTML(ui.text('sources'))}</h4>${sourcesView(ui, record.sources)}`;\n}\nfunction recordExecutionView(ui: KanbanPresentation, board: KanbanBoardSummary, executions: import('../contracts').KanbanRecordExecution[]): string {\n const completed = executions.some(run => run.state === 'completed');\n const actions = completed ? `<p>${escapeHTML(ui.text('completedFollowup'))}</p>`\n : `${button(ui, 'run-record', 'runRecord')}<details><summary>${escapeHTML(ui.text('referenceActions'))}</summary>${button(ui, 'run-record-separate', 'runRecordSeparate')}</details>`;\n return `<div class=\"kanban__actions\">${actions}</div>${completed ? '' : `<p>${escapeHTML(ui.text('runRecordHelp'))}</p>`}\n ${executions.map(run => `<p><button type=\"button\" ${run.rootItemId && run.boardId === board.id ? `data-item=\"${escapeAttr(run.rootItemId)}\"` : `data-board=\"${escapeAttr(run.boardId)}\"`}>${escapeHTML(ui.text(run.rootItemId && run.boardId === board.id ? 'executionWork' : 'executionBoard'))}</button> · ${escapeHTML(ui.text(run.state))}${run.code ? ` · ${escapeHTML(run.code)}` : ''}</p>`).join('')}`;\n}\nfunction recordLink(ui: KanbanPresentation, id: string, key: string): string {return `<p>${escapeHTML(ui.text(key))}: <button type=\"button\" data-record=\"${escapeAttr(id)}\">${escapeHTML(id)}</button></p>`;}\nexport function amendmentsView(ui: KanbanPresentation, item: KanbanItem): string {\n const pending = item.amendments?.filter(proposal => proposal.status === 'pending') ?? [];\n if (!pending.length) return '';\n return `<section class=\"kanban__amendments\"><h4>${escapeHTML(ui.text('amendments'))}</h4><p>${escapeHTML(ui.text('amendmentHint'))}</p>${pending.map(proposal => `<article><strong>${escapeHTML(proposal.proposedBy.id)}</strong><small> v${proposal.baseRevision}</small>\n <details><summary>${escapeHTML(ui.text('currentText'))}</summary><p>${escapeHTML(item.title)}</p><p>${escapeHTML(item.responsibility)}</p><p>${escapeHTML(item.acceptance)}</p></details>\n <h5>${escapeHTML(ui.text('proposedText'))}</h5><p>${escapeHTML(proposal.proposed.title)}</p><p>${escapeHTML(proposal.proposed.responsibility)}</p><p>${escapeHTML(proposal.proposed.acceptance)}</p>\n ${sourcesView(ui, proposal.sources)}<form data-form=\"amendment\" data-amendment=\"${escapeAttr(proposal.id)}\"><select name=\"decision\" aria-label=\"${escapeAttr(ui.text('amendments'))}\"><option value=\"accept\">${escapeHTML(ui.text('applyAmendment'))}</option><option value=\"reject\">${escapeHTML(ui.text('rejectAmendment'))}</option></select>${field(ui, 'reason', 'reason', '', false)}<button>${escapeHTML(ui.text('save'))}</button></form></article>`).join('')}</section>`;\n}\n\nfunction itemChoices( board: KanbanBoardSummary, itemId: string): string {\n return suggestionField({name: 'itemIds', label: 'itemIds', kind: 'item', multiple: true, selected: selectedItems(board, itemId ? [itemId] : [])});\n}\n","import type {KanbanEvent} from '../contracts';\n\n/** Old persisted heartbeat events remain available to audit readers, without cluttering task history. */\nexport function meaningfulItemEvents(events: KanbanEvent[]): KanbanEvent[] {\n const last = new Map<string, string>(), hidden = new Set<KanbanEvent>();\n for (const event of [...events].sort((a, b) => a.sequence - b.sequence)) {\n if (event.kind === 'renew') {hidden.add(event); continue;}\n if (event.kind !== 'observe-execution' || !event.data.dispatchId || !event.data.reason) continue;\n const key = JSON.stringify([event.boardId, event.itemId, event.data.dispatchId]);\n const state = JSON.stringify([event.data.reason, event.data.code, event.data.status]);\n if (last.get(key) === state) hidden.add(event);\n last.set(key, state);\n }\n return events.filter(event => !hidden.has(event));\n}\n","import {projectKanbanTask} from '../contracts/task-projection';\nimport {taskControls, taskStatus} from './task-state';\nimport {executableWorkItem, DEFAULT_KANBAN_WORK_TYPES} from '../contracts';\nimport type {KanbanPresentation, KanbanReviewGuidance} from './ports';\nimport {childType} from './children';\nimport {panelHeader} from './panel-header';\nimport {suggestionField, selectedItems} from './suggestion-field';\nimport {amendmentsView, sourcesView} from './context-view';\nimport {escapeHTML, escapeAttr} from './html';\nimport type {KanbanBoardSummary, KanbanEvent, KanbanItem, KanbanPrincipal, KanbanPlanning, KanbanItemResourceCatalog} from '../contracts';\nimport {button, field, textarea, owns} from './view';\nimport {meaningfulItemEvents} from './event-history';\n\nexport function newBoardForm(ui: KanbanPresentation): string {\n return `${panelHeader(ui, ui.text('newBoard'))}<form data-form=\"board\">${field(ui, 'title', 'boardName')}\n <label><input name=\"requireEstimate\" type=\"checkbox\">${escapeHTML(ui.text('requireEstimate'))}</label>\n ${save(ui)}</form>`;\n}\nexport function newItemForm(ui: KanbanPresentation, board: KanbanBoardSummary, parent?: KanbanItem, resources?:KanbanItemResourceCatalog): string {\n const preset: KanbanPlanning | undefined = parent ? {kind: childType(board, parent)!, parentId: parent.id, priority: parent.planning?.priority ?? 2, dependencies: [], references: []} : undefined;\n return `${panelHeader(ui, ui.text('newItem'))}<form data-form=\"item\">${field(ui, 'title', 'name')}\n ${textarea(ui, 'responsibility', 'responsibility')}${textarea(ui, 'acceptance', 'acceptance')}${launchModeField(ui)}${planningFields(ui, board, undefined, preset)}${itemResourceField(ui,resources,(board.workTypes??DEFAULT_KANBAN_WORK_TYPES).some(type=>type.id===(preset?.kind??board.defaultWorkType??'task')&&type.executable))}${save(ui)}</form>`;\n}\nexport function planningFields(ui: KanbanPresentation, board: KanbanBoardSummary, item?: KanbanItem, preset?: KanbanPlanning): string {\n const plan = preset ?? item?.planning, types = board.workTypes ?? DEFAULT_KANBAN_WORK_TYPES;\n return `<label>${escapeHTML(ui.text('type'))}<select name=\"kind\">${types.map(type => `<option data-executable=\"${type.executable}\" value=\"${escapeAttr(type.id)}\" ${type.id === (plan?.kind ?? board.defaultWorkType ?? 'task') ? 'selected' : ''}>${escapeHTML(type.label)}</option>`).join('')}</select></label>\n ${field(ui, 'priority', 'priority', String(plan?.priority ?? 2), true, 'number')}\n ${suggestionField({name: 'area', label: 'area', kind: 'area', free: true, selected: plan?.area ? [{id: plan.area, label: plan.area}] : []})}${suggestionField({name: 'milestone', label: 'milestone', kind: 'milestone', free: true, selected: plan?.milestone ? [{id: plan.milestone, label: plan.milestone}] : []})}\n ${suggestionField({name: 'parentId', label: 'parent', kind: 'parent', excludeItemId: item?.id, selected: selectedItems(board, plan?.parentId ? [plan.parentId] : [])})}${suggestionField({name: 'dependencies', label: 'dependencies', kind: 'dependency', multiple: true, excludeItemId: item?.id, selected: selectedItems(board, plan?.dependencies ?? [])})}\n ${field(ui, 'dueAt', 'dueAt', dateInput(plan?.dueAt), false, 'datetime-local')}`;\n}\nfunction dateInput(at?: number): string {\n if (at === undefined) return '';\n const date = new Date(at); return new Date(at - date.getTimezoneOffset() * 60000).toISOString().slice(0, 16);\n}\nconst save = (ui: KanbanPresentation) => `<div class=\"kanban__actions\"><button>${escapeHTML(ui.text('save'))}</button>${button(ui, 'close-panel', 'close')}</div>`;\nexport function detailView(ui: KanbanPresentation, board: KanbanBoardSummary, item: KanbanItem, actor: KanbanPrincipal | undefined, archived: boolean, events: KanbanEvent[], relatedSessions = false, guidance?: KanbanReviewGuidance, interactive = false, task = projectKanbanTask(item), fixAvailable = false): string {\n return `${panelHeader(ui, item.title, board, item)}${taskStatus(ui, task)}${archived ? '' : `<div class=\"kanban__actions\">${taskControls(ui,task,fixAvailable)}</div>`}\n <p>${escapeHTML(ui.text('origin'))}: ${escapeHTML(item.origin ? ui.text(item.origin.kind === 'discovery' ? 'flow' : 'manual') + ' · ' + item.origin.principal.id : ui.text('unknownOrigin'))}</p>\n <dl><dt>${escapeHTML(ui.text('itemId'))}</dt><dd>${escapeHTML(item.id)}</dd><dt>${escapeHTML(ui.text('responsibility'))}</dt><dd>${escapeHTML(item.responsibility)}</dd>\n <dt>${escapeHTML(ui.text('acceptance'))}</dt><dd>${escapeHTML(item.acceptance)}</dd><dt>${escapeHTML(ui.text('owner'))}</dt><dd>${escapeHTML(item.owner?.id ?? item.owner?.agentId ?? ui.text('none'))}</dd></dl>\n <p>${escapeHTML(ui.text(item.status))}${item.blocked ? ' · ' + escapeHTML(ui.text('blocked') + ': ' + item.blocked.reason) : ''}</p>\n ${interactive ? '<section data-item-operations></section>' : ''}\n ${item.status === 'review' ? interactive ? `<details><summary>${escapeHTML(ui.text('reviewFlow'))}</summary>${reviewGuidance(ui, guidance)}</details>` : reviewGuidance(ui, guidance) : ''}\n <div class=\"kanban__actions\">${controls(ui, board, item, actor, archived, interactive)}</div>\n ${item.issues?.filter(issue=>!issue.resolvedBy).map(issue=>`<form data-form=\"task-reply\"><input type=\"hidden\" name=\"attemptId\" value=\"${escapeAttr(issue.attemptId)}\"><input type=\"hidden\" name=\"replyTo\" value=\"${escapeAttr(issue.id)}\">${textarea(ui,'content','message')}<button>${escapeHTML(ui.text('send'))}</button></form>`).join('') ?? ''}\n ${amendmentsView(ui, item)}\n ${archived ? '' : detailForms(ui, board, item, actor)}\n ${(item.planning?.dependencies ?? []).length ? `<p>${escapeHTML(ui.text('dependencies'))}: ${(item.planning?.dependencies ?? []).map(id => `<button type=\"button\" data-item=\"${escapeAttr(id)}\">${escapeHTML(board.items.find(i => i.id === id)?.title ?? id)}</button>`).join(' ')}</p>` : ''}\n ${item.planning?.parentId ? `<p>${escapeHTML(ui.text('parent'))}: <button type=\"button\" data-item=\"${escapeAttr(item.planning.parentId)}\">${escapeHTML(board.items.find(parent => parent.id === item.planning?.parentId)?.title ?? item.planning.parentId)}</button></p>` : ''}\n <section data-children></section><section data-related-records></section><h4>${escapeHTML(ui.text('sources'))}</h4>${sourcesView(ui, item.sources ?? [])}\n <div data-analysis></div><div data-execution-session></div><section data-related-sessions-section></section><section data-reference-links></section><section data-work-resource-section></section>\n ${relatedSessions ? '' : `<h4>${escapeHTML(ui.text('sessions'))}</h4>${item.participants.map(p => `<button type=\"button\" data-session=\"${escapeAttr(p.sessionId)}\">${escapeHTML(p.sessionId)} · ${escapeHTML(p.roles.join(', '))}</button>`).join('') || `<p>${escapeHTML(ui.text('none'))}</p>`}`}\n ${archived ? '' : `<details><summary>${escapeHTML(ui.text('registerSession'))}</summary><p>${escapeHTML(ui.text('registerSessionHelp'))}</p><form data-form=\"session\">${suggestionField({name: 'sessionId', label: 'session', kind: 'session', manual: true, required: true})}<button>${escapeHTML(ui.text('registerSession'))}</button></form></details>`}\n <section data-item-events>${itemEventsView(ui, events)}</section>`;\n}\nfunction reviewGuidance(ui: KanbanPresentation, guidance?: KanbanReviewGuidance): string {\n const [summary, ...steps] = guidance?.paragraphs ?? [ui.text('reviewHelp')];\n return `<section class=\"kanban__review-guide\"><strong>${escapeHTML(guidance?.title ?? ui.text('reviewHelpTitle'))}</strong><p>${escapeHTML(summary)}</p>\n ${steps.length ? `<details><summary>${escapeHTML(ui.text('reviewFlow'))}</summary><ol>${steps.map(text => `<li>${escapeHTML(text)}</li>`).join('')}</ol></details>` : ''}</section>`;\n}\nexport function itemEventsView(ui: KanbanPresentation, events: KanbanEvent[]): string {\n return `<details><summary>${escapeHTML(ui.text('eventsHelp'))}</summary><ol>${meaningfulItemEvents(events).map(event => `<li>${escapeHTML(event.kind)} · ${escapeHTML(event.actor.id ?? event.actor.agentId ?? '')} · ${escapeHTML(new Date(event.at).toLocaleString())}${event.data.message ? `<p>${escapeHTML(event.data.message)}</p>` : ''}</li>`).join('')}</ol></details>`;\n}\nfunction controls(ui: KanbanPresentation, board: KanbanBoardSummary, item: KanbanItem, actor: KanbanPrincipal | undefined, archived: boolean, interactive = false): string {\n if (archived) return button(ui, 'restore', 'restore');\n if (['completed', 'cancelled'].includes(item.status)) return button(ui, 'archive', 'archiveItem');\n const executable = executableWorkItem(board,item);\n let result = item.status === 'proposed' ? button(ui, 'publish', 'publish') : item.status === 'backlog' && executable ? button(ui, 'ready', 'ready') : item.status === 'open' ? button(ui, 'defer', 'defer') : '';\n if (owns(item, actor) && item.status === 'claimed') result += button(ui, 'renew', 'renew');\n if (item.status === 'review' && !owns(item, actor)) result += interactive ? '' : button(ui, 'accept', 'accept') + button(ui, 'reject', 'reject');\n if (item.blocked) result += button(ui, 'unblock', 'unblock');\n if (item.status === 'review' && owns(item, actor) && board.policy.review !== 'assignee') result += `<p>${escapeHTML(ui.text('noReview'))}</p>`;\n return result;\n}\nfunction detailForms(ui: KanbanPresentation, board: KanbanBoardSummary, item: KanbanItem, actor?: KanbanPrincipal): string {\n if (['completed', 'cancelled'].includes(item.status)) return '';\n let forms = '';\n const executable = executableWorkItem(board,item);\n if (!executable && ['backlog', 'proposed', 'open'].includes(item.status)) forms += `<form data-form=\"close-plan\">${field(ui, 'resultRef', 'result')}<button>${escapeHTML(ui.text('closePlan'))}</button></form>`;\n if (!board.automation && executable && item.status === 'open' && !item.blocked) forms += `<form data-form=\"claim\">${board.policy.requireEstimate !== false ? estimates(ui) : ''}<button>${escapeHTML(ui.text('claim'))}</button></form>`;\n if (item.status === 'claimed' && owns(item, actor)) forms += `<form data-form=\"submit\">${field(ui, 'resultRef', 'result')}<button>${escapeHTML(ui.text('submit'))}</button></form>`;\n const automatic = item.eligiblePrincipals?.some(principal => principal.kind === board.automation?.worker.kind && principal.id === board.automation.worker.id);\n let secondary = '';\n if (['proposed', 'backlog', 'open'].includes(item.status) && !automatic && !board.automation) secondary += `<details><summary>${escapeHTML(ui.text('editItem'))}</summary><form data-form=\"edit-item\">${field(ui, 'title', 'name', item.title)}${textarea(ui, 'responsibility', 'responsibility', item.responsibility)}${textarea(ui, 'acceptance', 'acceptance', item.acceptance)}<button>${escapeHTML(ui.text('save'))}</button></form></details>`;\n if (!board.automation && item.launch?.admittedAt === undefined && ['proposed', 'backlog', 'open'].includes(item.status)) secondary += `<details><summary>${escapeHTML(ui.text('plan'))}</summary><form data-form=\"plan\">${planningFields(ui, board, item)}<button>${escapeHTML(ui.text('save'))}</button></form></details><form data-form=\"cancel\">${field(ui, 'reason', 'reason')}<button>${escapeHTML(ui.text('cancel'))}</button></form>`;\n if (owns(item, actor)) secondary += `<form data-form=\"release\">${field(ui, 'reason', 'reason')}<button>${escapeHTML(ui.text('release'))}</button></form>`;\n if (!item.blocked && (!item.owner || owns(item, actor))) secondary += `<form data-form=\"block\">${field(ui, 'reason', 'reason')}<button>${escapeHTML(ui.text('block'))}</button></form>`;\n return forms + `<details><summary>${escapeHTML(ui.text('moreActions'))}</summary>` + secondary + `<form data-form=\"message\">${textarea(ui, 'message', 'message')}<button>${escapeHTML(ui.text('send'))}</button></form></details>`;\n}\nfunction estimates(ui: KanbanPresentation): string {\n return `${field(ui, 'complexity', 'complexity', '1', true, 'number')}${field(ui, 'workUnits', 'workUnits', '1', true, 'number')}\n ${field(ui, 'duration', 'duration', '30', true, 'number')}<label>${escapeHTML(ui.text('confidence'))}<select name=\"confidence\">${['low', 'medium', 'high'].map(value => `<option value=\"${value}\">${escapeHTML(ui.text(value))}</option>`).join('')}</select></label>`;\n}\n\nexport function itemResourceField(ui:KanbanPresentation,catalog:KanbanItemResourceCatalog|undefined,executable:boolean):string {\n if(!catalog||!catalog.resources.length&&!catalog.selected)return '';\n const options=[...catalog.resources],selected=catalog.selected?JSON.stringify(catalog.selected):'';\n if(catalog.selected&&!options.some(option=>option.reference.system===catalog.selected!.system&&option.reference.id===catalog.selected!.id))options.push({reference:catalog.selected,label:catalog.selected.id});\n return `<label>${escapeHTML(ui.text('itemResource'))}<select name=\"itemResource\" data-resource-required=\"${catalog.required===true}\" ${catalog.required&&executable?'required':''} ${catalog.readonly?'disabled':''}>\n <option value=\"\">${escapeHTML(ui.text('none'))}</option>${options.map(option=>`<option value=\"${escapeAttr(JSON.stringify(option.reference))}\" ${JSON.stringify(option.reference)===selected?'selected':''}>${escapeHTML(option.label)}</option>`).join('')}</select>\n ${catalog.readonly?`<input type=\"hidden\" name=\"itemResource\" value=\"${escapeAttr(selected)}\">`:''}</label>`;\n}\n\nexport function editTaskForm(ui: KanbanPresentation, board: KanbanBoardSummary, item: KanbanItem, editId: string, resources?: KanbanItemResourceCatalog): string {\n return `${panelHeader(ui, item.title, board, item)}<p>${escapeHTML(ui.text('editLeaseHelp'))}</p><form data-form=\"save-edit\"><input type=\"hidden\" name=\"editId\" value=\"${escapeAttr(editId)}\">${field(ui,'title','name',item.title)}${textarea(ui,'responsibility','responsibility',item.responsibility)}${textarea(ui,'acceptance','acceptance',item.acceptance)}${launchModeField(ui,item.launch?.mode)}${planningFields(ui,board,item)}${itemResourceField(ui,resources,true)}${save(ui)}</form>`;\n}\n\nfunction launchModeField(ui: KanbanPresentation, mode = 'automatic'): string {\n return `<label>${escapeHTML(ui.text('taskStartMode'))}<select name=\"launchMode\">${['automatic', 'manual'].map(value => `<option value=\"${value}\" ${mode === value ? 'selected' : ''}>${escapeHTML(ui.text('taskMode.' + value))}</option>`).join('')}</select></label>`;\n}\n","import type {KanbanAutomationCatalog, KanbanBoardSummary, KanbanAnalysisStatus} from '../contracts';\nimport type {KanbanPresentation} from './ports';\nimport {panelHeader} from './panel-header';\nimport {escapeHTML, escapeAttr} from './html';\nimport {button} from './view';\n\nexport function automationForm(ui: KanbanPresentation, board: KanbanBoardSummary, catalog: KanbanAutomationCatalog): string {\n const settings = board.automation;\n const select = (name: 'planner' | 'executor' | 'preparation', label: string) => {\n const selected = settings?.[name], targets = [...(name === 'preparation' ? catalog.preparations ?? [] : catalog.targets)];\n if (settings?.[name] && !targets.some(entry => sameReference(entry.reference, selected)))\n targets.push({reference: settings[name], label: ui.text('automationTargetUnavailable')});\n return `<label>${escapeHTML(ui.text(label))}<select name=\"${name}\" ${name === 'preparation' ? '' : 'required'}><option value=\"\">${escapeHTML(ui.text('automationChoose'))}</option>${targets.map(entry => {\n const value = JSON.stringify(entry.reference);\n return `<option value=\"${escapeAttr(value)}\" ${sameReference(entry.reference, selected) ? 'selected' : ''}>${escapeHTML(entry.label)}</option>`;\n }).join('')}</select></label>`;\n };\n return `${panelHeader(ui, ui.text('automationTitle'))}<form data-form=\"automation\">\n <p>${escapeHTML(ui.text('automationHelp'))}</p>\n <label><input type=\"checkbox\" name=\"enabled\" ${settings?.enabled ? 'checked' : ''}>${escapeHTML(ui.text('automationEnabled'))}</label>\n ${select('planner', 'automationPlanner')}${select('executor', 'automationExecutor')}${select('preparation', 'automationPreparation')}<div data-preparation-options>${preparationFields(board, catalog)}</div>\n <label><input type=\"checkbox\" name=\"autoAccept\" ${settings?.autoAccept ? 'checked' : ''}>${escapeHTML(ui.text('automationAutoAccept'))}</label>\n <p>${escapeHTML(ui.text('automationReviewHelp'))}</p>\n <div class=\"kanban__actions\"><button>${escapeHTML(ui.text('save'))}</button>${button(ui, 'close-panel', 'close')}</div></form>`;\n}\nexport function analysisView(ui: KanbanPresentation, status: KanbanAnalysisStatus | null): string {\n if (!status) return `<p>${escapeHTML(ui.text('automationPending'))}</p>`;\n return `<section><h4>${escapeHTML(ui.text('automationAnalysis'))}</h4><p>${escapeHTML(ui.text('automationState.' + status.state))}</p>\n ${status.reason ? `<p>${escapeHTML(status.reason)}</p>` : ''}${status.code ? `<p>${escapeHTML(ui.text('errors.' + status.code))}</p>` : ''}\n ${['failed', 'clarification'].includes(status.state) ? button(ui, 'reanalyze', 'automationReanalyze') : ''}</section>`;\n}\n\nexport function preparationFields(board: KanbanBoardSummary, catalog: KanbanAutomationCatalog, reference: unknown = board.automation?.preparation): string {\n const selected = reference;\n const entries = catalog.preparations?.filter(entry => sameReference(entry.reference, selected)) ?? [];\n return entries.flatMap(entry => (entry.options ?? []).map(option => {\n const value = board.automation?.preparationOptions?.[option.name] ?? option.defaultValue ?? '';\n const name = 'preparation-option-' + option.name;\n const control = option.choices ? `<select name=\"${escapeAttr(name)}\">${option.choices.map(choice =>\n `<option value=\"${escapeAttr(choice.value)}\" ${choice.value === value ? 'selected' : ''}>${escapeHTML(choice.label)}</option>`).join('')}</select>`\n : `<input name=\"${escapeAttr(name)}\" value=\"${escapeAttr(value)}\" maxlength=\"4096\">`;\n return `<label>${escapeHTML(option.label)}${control}</label>`;\n })).join('');\n}\n\nfunction sameReference(left: unknown, right: unknown): boolean {\n const a = left as {system?: string; id?: string; revision?: string} | null;\n const b = right as typeof a;\n return !!a && !!b && a.system === b.system && a.id === b.id && a.revision === b.revision;\n}\n","import {randomUUID} from '../utils';\nimport type {KanbanPresentation} from './ports';\nimport type {KanbanSuggestion, KanbanSuggestionPage, KanbanSuggestionQuery} from '../contracts';\nimport type {SuggestionField} from './suggestion-field';\nexport class KanbanSuggestionPicker {\n private input = document.createElement('input');\n private list = document.createElement('div');\n private chips = document.createElement('div');\n private values = document.createElement('div');\n private hint = document.createElement('small');\n private selected: KanbanSuggestion[];\n private rows: KanbanSuggestion[] = [];\n private active = -1;\n private loading = false;\n private generation = 0;\n private timer?: ReturnType<typeof setTimeout>;\n private abort = new AbortController();\n constructor(readonly root: HTMLElement, private readonly config: SuggestionField,\n private readonly fetch: (query: KanbanSuggestionQuery) => Promise<KanbanSuggestionPage>, private readonly presentation: KanbanPresentation) {\n this.selected = [...(config.selected ?? [])]; this.mount(); this.bind(); this.renderValues(); this.close();\n }\n private mount(): void {\n const caption = document.createElement('label'), id = 'kanban-suggest-' + randomUUID();\n caption.textContent = this.presentation.text(this.config.label); caption.htmlFor = this.input.id = id;\n this.input.type = 'text'; this.input.autocomplete = 'off'; this.input.maxLength = 256; this.input.required = !!this.config.required;\n this.input.setAttribute('role', 'combobox'); this.input.setAttribute('aria-autocomplete', 'list'); this.input.setAttribute('aria-controls', this.list.id = id + '-list');\n this.input.setAttribute('aria-describedby', this.hint.id = id + '-hint');\n this.input.placeholder = this.config.free ? this.presentation.text(this.config.label) + ' · ' + this.presentation.text('suggestFreePlaceholder') : this.presentation.text('suggestPlaceholder');\n if (this.config.free) this.input.name = this.config.name;\n this.input.value = this.config.multiple ? '' : this.selected[0]?.label ?? '';\n this.list.className = 'kanban__suggest-list'; this.list.setAttribute('role', 'listbox'); this.list.setAttribute('aria-label', caption.textContent);\n this.chips.className = 'kanban__suggest-chips'; this.hint.setAttribute('aria-live', 'polite');\n this.hint.textContent = this.presentation.text(this.config.free ? 'suggestFreeHint' : 'suggestHint');\n this.root.replaceChildren(caption, this.chips, this.input, this.values, this.list, this.hint);\n }\n private bind(): void {\n const signal = this.abort.signal;\n this.input.addEventListener('focus', () => {if (!this.config.multiple && this.selected.length) this.input.select(); if (!this.loading) void this.search();}, {signal});\n this.input.addEventListener('click', () => {if (this.list.hidden && !this.loading) void this.search();}, {signal});\n this.input.addEventListener('input', () => {\n if (!this.config.multiple && !this.config.free) {this.selected = []; this.renderValues();}\n this.validate(); clearTimeout(this.timer); this.generation++;\n this.rows = []; this.active = -1; this.list.replaceChildren(); this.hint.textContent = this.presentation.text('loading'); this.open();\n this.timer = setTimeout(() => {void this.search();}, 150);\n }, {signal});\n this.input.addEventListener('keydown', event => this.keydown(event), {signal});\n this.list.addEventListener('pointerdown', event => event.preventDefault(), {signal});\n this.root.addEventListener('click', event => this.click(event), {signal});\n document.addEventListener('scroll', () => this.position(), {signal, capture: true});\n window.addEventListener('resize', () => this.position(), {signal});\n this.root.addEventListener('focusout', event => {if (!this.root.contains(event.relatedTarget as Node | null)) this.close();}, {signal});\n }\n private async search(): Promise<void> {\n const generation = ++this.generation, operationId = randomUUID(), started = Date.now();\n const search = !this.config.multiple && this.selected[0]?.label === this.input.value ? '' : this.input.value.trim();\n const kind = this.root.closest('form')?.querySelector<HTMLSelectElement>('[name=kind]')?.value;\n this.revealInput(); this.loading = true; this.hint.textContent = this.presentation.text('loading');\n try {\n const page = await this.fetch({kind: this.config.kind, search, limit: 12, ...(this.config.multiple ? {excludeIds: this.selected.map(row => row.id)} : {}), ...(this.config.excludeItemId ? {excludeItemId: this.config.excludeItemId} : {}),\n ...(this.config.kind === 'parent' && kind ? {childKind: kind} : {})});\n if (generation !== this.generation || this.abort.signal.aborted || !this.root.isConnected) return;\n this.rows = page.items.filter(row => !this.config.multiple || !this.selected.some(value => value.id === row.id));\n if (this.config.manual && search && !this.rows.some(row => row.id === search)) this.rows.push({id: search, label: search, detail: this.presentation.text('suggestManualId')});\n this.active = this.rows.length ? 0 : -1; this.renderOptions();\n this.hint.textContent = this.presentation.text(page.hasMore ? 'suggestRefine' : this.rows.length ? this.config.free ? 'suggestFreeHint' : 'suggestHint' : 'suggestEmpty');\n this.log( {operationId, stage: this.config.kind, result: 'success', count: page.items.length, durationMs: Date.now() - started});\n } catch (error) {\n if (generation !== this.generation || this.abort.signal.aborted) return;\n this.rows = []; this.list.replaceChildren(); this.hint.textContent = this.presentation.text('suggestFailed');\n const retry = document.createElement('button'); retry.type = 'button'; retry.textContent = this.presentation.text('retry'); retry.dataset.suggestRetry = '';\n this.list.append(retry); this.open();\n const raw = (error as {code?: unknown})?.code, code = typeof raw === 'string' && /^[A-Z_0-9]{1,80}$/.test(raw) ? raw : 'KANBAN_SUGGEST_FAILED';\n this.log( {operationId, stage: this.config.kind, result: 'failure', code, durationMs: Date.now() - started});\n } finally {if (generation === this.generation) this.loading = false;}\n }\n private log(event: Parameters<NonNullable<KanbanPresentation['log']>>[0]): void {\n try {this.presentation.log?.(event);} catch { /* Diagnostics cannot change selection state. */ }\n }\n private renderOptions(): void {\n this.list.replaceChildren();\n this.rows.forEach((row, index) => {\n const button = document.createElement('button'); button.type = 'button'; button.tabIndex = -1; button.dataset.suggestIndex = String(index);\n button.id = this.list.id + '-' + index; button.setAttribute('role', 'option');\n const title = document.createElement('strong'), detail = document.createElement('small'); title.textContent = row.label;\n detail.textContent = [row.status ? this.presentation.text(row.status) : '', row.archived ? this.presentation.text('archive') : '', row.detail, this.config.free ? '' : row.id].filter(Boolean).join(' · ');\n button.append(title, detail); this.list.append(button);\n });\n this.open(); this.markActive();\n }\n private click(event: Event): void {\n const button = (event.target as HTMLElement).closest<HTMLButtonElement>('button'); if (!button) return;\n if (button.dataset.suggestIndex !== undefined) this.choose(this.rows[Number(button.dataset.suggestIndex)]);\n else if (button.dataset.suggestRemove !== undefined) {this.selected = this.selected.filter(row => row.id !== button.dataset.suggestRemove); this.renderValues(); this.changed(); this.input.focus();}\n else if (button.hasAttribute('data-suggest-retry')) {void this.search(); this.input.focus();}\n }\n private choose(row: KanbanSuggestion | undefined): void {\n if (!row) return;\n this.selected = this.config.multiple ? [...this.selected, row] : [row]; this.input.value = this.config.multiple ? '' : row.label;\n this.renderValues(); this.input.setCustomValidity(''); this.hint.textContent = this.presentation.text(this.config.free ? 'suggestFreeHint' : 'suggestHint'); this.changed(); this.close(); this.input.focus();\n }\n private renderValues(): void {\n this.values.replaceChildren(); this.chips.replaceChildren();\n if (!this.config.free) for (const row of this.selected.length ? this.selected : this.config.multiple ? [] : [{id: '', label: ''}]) {\n const input = document.createElement('input'); input.type = 'hidden'; input.name = this.config.name; input.value = row.id; this.values.append(input);\n }\n if (this.config.multiple) for (const row of this.selected) {\n const chip = document.createElement('button'); chip.type = 'button'; chip.dataset.suggestRemove = row.id;\n const name = document.createElement('span'), remove = document.createElement('span'); name.textContent = row.label; remove.innerHTML = this.presentation.icons.close; remove.setAttribute('aria-hidden', 'true'); chip.append(name, remove); chip.setAttribute('aria-label', this.presentation.text('suggestRemove') + ': ' + row.label); this.chips.append(chip);\n }\n }\n private validate(): void {\n const unselected = !this.config.free && !!this.input.value.trim() && (this.config.multiple || !this.selected.length);\n this.input.setCustomValidity(unselected ? this.presentation.text('suggestPickRequired') : '');\n }\n private changed(): void {this.validate(); this.root.dispatchEvent(new Event('input', {bubbles: true}));}\n private keydown(event: KeyboardEvent): void {\n if (event.key === 'Escape' && !this.list.hidden) {event.preventDefault(); event.stopPropagation(); this.close(); return;}\n if (event.key === 'ArrowDown' || event.key === 'ArrowUp') {\n event.preventDefault(); event.stopPropagation();\n if (this.list.hidden) {void this.search(); return;}\n this.active = this.rows.length ? (this.active + (event.key === 'ArrowDown' ? 1 : -1) + this.rows.length) % this.rows.length : -1; this.markActive();\n } else if (event.key === 'Enter' && !this.list.hidden) {event.preventDefault(); event.stopPropagation(); this.choose(this.rows[this.active]);}\n else if (event.key === 'Tab' && !this.list.querySelector('[data-suggest-retry]')) this.close();\n else if (event.key === 'Backspace' && this.config.multiple && !this.input.value && this.selected.length) {this.selected.pop(); this.renderValues(); this.changed();}\n }\n private markActive(): void {\n [...this.list.children].forEach((node, index) => node.setAttribute('aria-selected', String(index === this.active)));\n if (this.active >= 0) {this.input.setAttribute('aria-activedescendant', this.list.id + '-' + this.active); const row = this.list.children[this.active] as HTMLElement | undefined;\n if (row && row.offsetTop < this.list.scrollTop) this.list.scrollTop = row.offsetTop;\n else if (row && row.offsetTop + row.offsetHeight > this.list.scrollTop + this.list.clientHeight) this.list.scrollTop = row.offsetTop + row.offsetHeight - this.list.clientHeight;}\n else this.input.removeAttribute('aria-activedescendant');\n }\n private open(): void {this.list.hidden = false; this.input.setAttribute('aria-expanded', 'true'); this.position();}\n private revealInput(): void {\n const panel = this.root.closest<HTMLElement>('[data-panel]'); if (!panel) return;\n const input = this.input.getBoundingClientRect(), bounds = panel.getBoundingClientRect();\n const top = (panel.querySelector('.kanban__panel-header')?.getBoundingClientRect().bottom ?? bounds.top) + 8, bottom = bounds.bottom - 12;\n if (input.bottom > bottom) panel.scrollTop += input.bottom - bottom;\n else if (input.top < top) panel.scrollTop -= top - input.top;\n }\n private position(): void {\n if (this.list.hidden || !this.root.isConnected) return;\n const input = this.input.getBoundingClientRect(), scope = (this.root.closest('[data-panel]') ?? this.root.closest('.kanban') ?? document.documentElement).getBoundingClientRect();\n const below = Math.max(0, scope.bottom - input.bottom - 8), above = Math.max(0, input.top - scope.top - 8), up = above > below && below < 160;\n const height = Math.min(210, up ? above : below), width = Math.min(Math.max(input.width, 220), Math.max(0, scope.width - 16));\n const left = Math.max(scope.left + 8, Math.min(input.left, scope.right - width - 8));\n Object.assign(this.list.style, {position: 'fixed', zIndex: '30', left: `${left}px`, width: `${width}px`, maxHeight: `${height}px`,\n top: up ? 'auto' : `${input.bottom + 4}px`, bottom: up ? `${innerHeight - input.top + 4}px` : 'auto'});\n }\n private close(): void {this.loading = false; if (this.hint.textContent === this.presentation.text('loading')) this.hint.textContent = this.presentation.text(this.config.free ? 'suggestFreeHint' : 'suggestHint'); clearTimeout(this.timer); this.generation++; this.list.hidden = true; this.input.setAttribute('aria-expanded', 'false'); this.input.removeAttribute('aria-activedescendant');}\n destroy(): void {clearTimeout(this.timer); this.generation++; this.abort.abort();}\n}\n","import type {KanbanPresentation} from './ports';\nimport type {KanbanService, KanbanSuggestionQuery} from '../contracts';\nimport {KanbanSuggestionPicker} from './suggestion-picker';\nimport type {SuggestionField} from './suggestion-field';\n\n/** Pickers follow rendered forms; replaced views cannot receive stale query results. */\nexport class KanbanSuggestions {\n private observer: MutationObserver;\n private fields = new Map<HTMLElement, KanbanSuggestionPicker>();\n constructor(private readonly root: HTMLElement, private readonly service: KanbanService | undefined, private readonly boardId: () => string, private readonly presentation: KanbanPresentation) {\n this.observer = new MutationObserver(() => this.sync());\n this.observer.observe(root, {childList: true, subtree: true}); this.sync();\n }\n private sync(): void {\n for (const [node, field] of this.fields) if (!this.root.contains(node)) {field.destroy(); this.fields.delete(node);}\n if (!this.service) return;\n for (const node of this.root.querySelectorAll<HTMLElement>('[data-kanban-suggest]')) {\n if (this.fields.has(node)) continue;\n const options = JSON.parse(node.dataset.kanbanSuggest!) as SuggestionField;\n const picker = new KanbanSuggestionPicker(node, options, (query: KanbanSuggestionQuery) => this.service!.suggest(this.boardId(), query), this.presentation);\n this.fields.set(node, picker);\n }\n }\n destroy(): void {this.observer.disconnect(); for (const field of this.fields.values()) field.destroy(); this.fields.clear();}\n}\n","import {updateTaskCountdowns} from './task-state';\nimport {projectKanbanTask} from '../contracts/task-projection';\nimport {editTaskForm} from './forms';\nimport {automationForm, analysisView, preparationFields} from './automation';\nimport {KanbanSuggestions} from './suggestions';\nimport {mountTitleBar as mountSharedTitleBar} from '@nestos/ui-common';\nimport {KanbanDrawer, drawerMarkup, preserveBoardScroll} from './drawer';\nimport {escapeHTML} from './html';\nimport {childrenView, childType} from './children';\nimport {linkPair} from './session-links';\nimport {failureDetails} from './diagnostics';\nimport {itemOperationsView} from './operations';\nimport type {KanbanItemOperations} from '../contracts';\nimport {workResourcesView} from './work-resources';\nimport {relatedSessionsView} from './session-view';\nimport {randomUUID} from '../utils';\nimport type {KanbanUIOptions} from './ports';\nimport {createRenderer} from './renderer';\nimport type {KanbanBoardSummary, KanbanCommand, KanbanItem, KanbanSessionItemPage, KanbanSessionActivityQuery, KanbanPrincipal, KanbanRequest} from '../contracts';\nimport {actionCommand, boardInput, formCommand, value} from './commands';\n\nexport class KanbanWorkbench {\n private board?: KanbanBoardSummary;\n private page?: KanbanSessionItemPage;\n private actor?: KanbanPrincipal;\n private automationCatalog?: import('../contracts').KanbanAutomationCatalog;\n private item?: KanbanItem;\n private editing?: {itemId:string; editId:string};\n private itemArchived = false;\n private operations?: KanbanItemOperations;\n private boardId = '';\n private recordKind?: 'log' | 'decision';\n private recordId = '';\n private recordPage?: import('../contracts').KanbanRecordPage;\n private recordCursor?: string;\n private suggestions!: KanbanSuggestions;\n private drawer!: KanbanDrawer;\n private itemId = '';\n private query: KanbanSessionActivityQuery = {view: 'all', limit: 50};\n private activityTimer?: ReturnType<typeof setInterval>;\n private generation = 0;\n private eventGeneration = 0;\n private abort = new AbortController();\n private busy = false;\n private background?: Promise<void>;\n private operationStage = 'ui.operation';\n private dirty = false;\n private visible = true;\n private invalid = false;\n private pending?: {boardId: string; fingerprint: string; request: KanbanRequest};\n private readonly ui;\n private releaseNavigation?: () => void;\n private readonly windowMounts = new Map<HTMLElement, () => void>();\n constructor(private readonly options: KanbanUIOptions, private readonly container?: HTMLElement) {this.ui = createRenderer(options.presentation);}\n async start(): Promise<void> {\n const {sidebar, editor} = this.options, signal = this.abort.signal;\n sidebar?.classList.add('kanban__sidebar'); editor.classList.add('kanban');\n editor.innerHTML = `<p data-status role=\"status\"></p><p data-error role=\"alert\"></p><div data-retry hidden>${this.ui.button('refresh', 'retry')}</div><div data-content class=\"kanban__content\"></div>${drawerMarkup(this.options.presentation)}`;\n this.drawer = new KanbanDrawer(editor, sidebar);\n this.suggestions = new KanbanSuggestions(editor, this.options.service, () => this.boardId, this.options.presentation);\n document.addEventListener('pointerdown', event => {\n for (const menu of editor.querySelectorAll<HTMLDetailsElement>('[data-popover][open]')) if (!menu.contains(event.target as Node)) menu.open = false;\n }, {signal});\n editor.addEventListener('toggle', event => {\n const opened = event.target;\n if (opened instanceof HTMLDetailsElement && opened.open && opened.hasAttribute('data-related-sessions')) {void this.loadRelatedSessions(); return;}\n if (opened instanceof HTMLDetailsElement && opened.open && opened.hasAttribute('data-work-resources')) {void this.loadWorkResources(); return;}\n if (!(opened instanceof HTMLDetailsElement) || !opened.open || !opened.hasAttribute('data-popover')) return;\n for (const menu of editor.querySelectorAll<HTMLDetailsElement>('[data-popover][open]')) if (menu !== opened) menu.open = false;\n }, {capture: true, signal});\n editor.addEventListener('change',event=>{\n const target=event.target;if(!(target instanceof HTMLSelectElement)||target.name!=='kind')return;\n const resource=target.closest('form')?.querySelector<HTMLSelectElement>('select[name=\"itemResource\"]');\n if(resource)resource.required=resource.dataset.resourceRequired==='true'&&target.selectedOptions[0]?.dataset.executable==='true';\n },{signal});\n editor.addEventListener('keydown', event => {\n const menu = editor.querySelector<HTMLDetailsElement>('[data-popover][open]');\n if (event.key === 'Escape' && menu) {event.preventDefault(); event.stopPropagation(); menu.open = false; menu.querySelector<HTMLElement>('summary')?.focus(); return;}\n\n this.drawer.keydown(event);\n if (event.key === 'Escape' && this.drawer.open) {event.preventDefault(); void this.run(() => this.requestClose(false));}\n }, {signal});\n for (const element of sidebar ? [sidebar, editor] : [editor]) element.addEventListener('click', event => {\n if (event instanceof MouseEvent && (event.ctrlKey || event.metaKey || event.shiftKey || event.altKey || event.button !== 0)) return;\n if (!(event.target as HTMLElement).closest('[data-action], [data-board], [data-item], [data-session], [data-record], [data-context]')) return;\n void this.run(async () => {\n await this.click(event);\n if (element === sidebar) this.options.activated?.();\n });\n }, {signal});\n sidebar?.addEventListener('submit', event => {event.preventDefault(); void this.run(() => this.submit(event));}, {signal});\n editor.addEventListener('submit', event => {event.preventDefault(); void this.run(() => this.submit(event));}, {signal});\n editor.addEventListener('change', event => {\n const target = event.target as HTMLSelectElement;\n if (target.name === 'preparation' && this.board && this.automationCatalog) {\n const container = editor.querySelector('[data-preparation-options]');\n if (container) container.innerHTML = preparationFields(this.board, this.automationCatalog, target.value ? JSON.parse(target.value) : null);\n }\n }, {signal});\n editor.addEventListener('input', event => {\n const target = event.target as HTMLElement;\n if (target.closest('[data-form=reference]')) return;\n if (target.closest('form')) this.generation++;\n if (target.closest('[data-panel]')) {this.dirty = true; this.options.changed?.();}\n }, {signal});\n this.activityTimer = setInterval(() => {\n if (!this.visible || document.hidden) return;\n updateTaskCountdowns(this.options.editor, this.options.presentation);\n if (this.options.service?.queryActivity && (this.board?.automation?.enabled || this.page?.items.some(item => item.execution)\n || this.query.execution && this.query.execution !== 'all')) this.refreshInBackground(true);\n }, 15000);\n if (this.options.initialResource !== undefined) await this.openResource(this.options.initialResource);\n else await this.run(() => this.refresh());\n }\n open(target: {boardId?: string; itemId?: string; recordId?: string}): Promise<void> {\n const path = target.boardId ? `/board/${encodeURIComponent(target.boardId)}${target.recordId ? '/@record/' + encodeURIComponent(target.recordId) : target.itemId ? '/' + encodeURIComponent(target.itemId) : ''}` : '/';\n return this.openResource(path);\n }\n dispose(): Promise<void> {return this.destroy();}\n async openResource(path: string): Promise<void> {\n if (this.busy || this.dirty) throw new Error(this.ui.label('unsaved'));\n this.closePanel();\n const parts = path.split('/'); this.boardId = decodeURIComponent(parts[2] ?? ''); this.itemId = decodeURIComponent(parts[3] ?? '');\n this.recordId = parts[3] === '@record' ? decodeURIComponent(parts[4] ?? '') : '';\n if (this.recordId) this.itemId = '';\n this.recordKind = ['@log', '@decision'].includes(parts[3] ?? '') ? parts[3].slice(1) as 'log' | 'decision' : undefined;\n if (this.recordKind) this.itemId = '';\n this.recordCursor = undefined;\n this.query = {view: 'all', limit: 50}; await this.run(() => this.refresh());\n }\n createBoard(): Promise<void> {return this.run(async () => {\n if (this.dirty) throw new Error(this.ui.label('unsaved'));\n this.showBoardForm();\n });}\n private showBoardForm(): void {this.itemId = ''; this.drawer.show(this.ui.newBoardForm(), 'new-board'); this.dirty = true;}\n getActiveResourceId(): string {return this.boardId ? `/board/${encodeURIComponent(this.boardId)}${this.recordId ? '/@record/' + encodeURIComponent(this.recordId) : this.itemId ? '/' + encodeURIComponent(this.itemId) : this.recordKind ? '/@' + this.recordKind : ''}` : '/';}\n getViewState() {\n return {title: this.drawer?.open ? this.panel().querySelector('h3')?.textContent ?? this.board?.title : this.board?.title ?? this.ui.label('title'),\n context: this.board?.title, dirty: this.dirty, busy: this.busy};\n }\n captureRecovery(): unknown {if (this.busy || this.dirty) throw new Error(this.ui.label('unsaved')); return null;}\n restoreRecovery(): void {}\n suspendForRecovery(): void {this.generation++; this.abort.abort(); this.suggestions?.destroy();if(this.activityTimer) clearInterval(this.activityTimer);}\n async destroy(): Promise<void> {\n for (const release of this.windowMounts.values()) release(); this.windowMounts.clear();\n this.suspendForRecovery(); this.releaseNavigation?.(); this.releaseNavigation = undefined; this.suggestions?.destroy(); this.drawer?.destroy(); this.options.sidebar?.replaceChildren(); this.options.editor.replaceChildren(); this.options.editor.inert = false;\n this.options.editor.classList.remove('kanban'); this.options.sidebar?.classList.remove('kanban__sidebar');\n this.container?.replaceChildren(); this.container?.classList.remove('kanban-layout');\n }\n invalidateFromEvents(): void {\n this.invalid = true; this.eventGeneration++;\n this.refreshInBackground();\n }\n setVisible(visible: boolean): void {this.visible = visible; if (visible && this.invalid) this.invalidateFromEvents();}\n private refreshInBackground(activityOnly = false): void {\n if (this.background || this.busy || this.dirty || !this.visible || this.abort.signal.aborted) return;\n if (document.activeElement?.closest('form') && this.options.editor.contains(document.activeElement)) return;\n const started = Date.now(), operationId = randomUUID();\n const reading = activityOnly ? this.refreshActivity(this.generation) : this.refresh(), generation = this.generation;\n this.background = reading.catch(error => {\n if (!this.alive(generation)) return;\n this.invalid = false;\n if (!this.abort.signal.aborted) {this.reportRead('ui.background', error, operationId, started); this.error(errorText(error, this.ui.label, 'ui.background'));}\n }).finally(() => {\n this.background = undefined;\n if (this.invalid && !this.busy && !this.dirty && this.visible && !this.abort.signal.aborted) this.refreshInBackground();\n });\n }\n private queryPage(): Promise<KanbanSessionItemPage> {\n const service = this.options.service!;\n return this.query.execution && this.query.execution !== 'all' && service.queryActivity\n ? service.queryActivity(this.boardId, this.query) : service.query(this.boardId, this.query);\n }\n private async refreshActivity(generation: number): Promise<void> {\n if (this.item && this.itemId) {await Promise.all([this.loadExecutionSections(generation, this.item, this.itemArchived), this.operations?.operations.some(operation => [\"running\", \"unknown\"].includes(operation.state)) ? this.loadOperations(generation) : undefined]); return;}\n if (!this.board || this.recordId || this.recordKind || !this.options.service?.queryActivity) return;\n await this.supplement('ui.activity', generation, () => this.options.service!.queryActivity!(this.boardId, this.query), page => {\n if (page.revision !== this.page?.revision) {this.invalid = true; this.refreshInBackground(); return;}\n if (JSON.stringify(page) === JSON.stringify(this.page)) return;\n const sameItems = page.total === this.page.total && page.nextCursor === this.page.nextCursor\n && JSON.stringify(page.items.map(item => item.id)) === JSON.stringify(this.page.items.map(item => item.id));\n this.page = page;\n if (sameItems) {\n const sessions = new Map(page.items.map(item => [item.id, item.execution]));\n for (const slot of this.content().querySelectorAll<HTMLElement>('[data-item-execution]')) {\n const html = this.ui.executionBadge(sessions.get(slot.dataset.itemExecution!), this.options.sessionUrl);\n if (slot.innerHTML !== html) slot.innerHTML = html;\n }\n return;\n }\n preserveBoardScroll(this.content(), () => {this.content().innerHTML = this.ui.boardView(this.board!, page, this.query, true, !!this.options.service?.automationCatalog, this.options.sessionUrl);});\n this.mountHeaderActions();\n });\n }\n private async refresh(): Promise<void> {\n const service = this.options.service, generation = ++this.generation, events = this.eventGeneration;\n if (!service) {this.content().innerHTML = `<p>${escapeHTML(this.ui.label('unavailable'))}</p>`; return;}\n this.operationStage = 'ui.snapshot';\n const pageRequest = this.boardId ? this.queryPage() : undefined;\n const itemRequest = this.boardId && this.itemId ? service.item(this.boardId, this.itemId) : undefined;\n const [boards, actor, initialPage, initialItem] = await Promise.all([service.catalog?.() ?? service.list(), service.currentPrincipal(), pageRequest, itemRequest]);\n if (!this.alive(generation)) return;\n this.actor = actor;\n if (!this.boardId && this.options.initialResource === undefined) this.boardId = boards[0]?.id ?? '';\n this.board = boards.find(board => board.id === this.boardId);\n if (this.boardId && !this.board) throw Object.assign(new Error('Board unavailable'), {code: 'BOARD_NOT_FOUND'});\n if(this.query.execution&&this.query.execution!=='all'&&!service.queryActivity) throw Object.assign(new Error('Execution Session projection unavailable'),{code:'EXECUTION_SESSION_UNAVAILABLE'});\n const page = this.board ? initialPage ?? await this.queryPage() : undefined;\n if (!this.alive(generation)) return;\n this.page = page; this.invalid = events !== this.eventGeneration;\n if (this.options.sidebar) this.options.sidebar.innerHTML = this.ui.navigation(boards, this.boardId);\n this.operationStage = 'ui.render';\n preserveBoardScroll(this.content(), () => {this.content().innerHTML = this.board && page ? this.ui.boardView(this.board, page, this.query, !!service.queryActivity, !!service.automationCatalog, this.options.sessionUrl) : this.ui.catalogView(boards);});\n if (this.recordKind) await this.showRecords(generation);\n if (!this.alive(generation)) return;\n this.releaseNavigation?.(); this.releaseNavigation = undefined;\n const navigation = this.content().querySelector<HTMLElement>('[data-navigation]');\n if (navigation && !this.options.mountTitleBar) this.releaseNavigation = this.options.mountNavigation?.(navigation);\n if (this.recordId) await this.showRecord(this.recordId, generation);\n else if (this.itemId) await this.showItem(generation, initialItem);\n else if (!this.recordKind) void this.refreshActivity(generation);\n this.mountHeaderActions();\n this.operationStage = 'ui.selection'; this.options.selected(this.getActiveResourceId());\n }\n private async showItem(generation = this.generation, loaded?: Awaited<ReturnType<NonNullable<KanbanUIOptions['service']>['item']>>): Promise<void> {\n const service = this.options.service;\n if (!service || !this.board || !this.itemId) return;\n this.operationStage='ui.item.load';\n const {item, archive, task} = loaded ?? await service.item(this.boardId, this.itemId);\n if (!this.alive(generation)) return;\n this.operationStage='ui.item.render';\n const referencesChanged = this.item?.id === item.id && JSON.stringify(this.item.externalRefs) !== JSON.stringify(item.externalRefs);\n if (!this.drawer.open || this.itemArchived !== !!archive || JSON.stringify(this.item) !== JSON.stringify(item)) this.drawer.show(this.ui.detailView(this.board, item, this.actor, !!archive, [], !!service.executionSession, this.options.reviewGuidance?.(this.board, item), !!service.itemOperations && !archive && (this.options.itemOperationsAvailable?.(this.board, item) ?? true), task ?? projectKanbanTask(item), !!service.fixTask), 'item:' + item.id);\n this.item = item; this.itemArchived = !!archive;\n this.loadItemSections(generation, item, !!archive);\n const resources = this.panel().querySelector<HTMLDetailsElement>('[data-work-resources]');\n if (referencesChanged && resources) {\n resources.dataset.associationRevision = String(Number(resources.dataset.associationRevision ?? 0) + 1);\n delete resources.dataset.loaded; if (resources.open) void this.loadWorkResources(false, true);\n }\n this.options.selected(this.getActiveResourceId());\n }\n private loadItemSections(generation: number, item: KanbanItem, archived: boolean): void {\n const service = this.options.service!, boardId = this.boardId;\n if (service.executionSession && !this.panel().querySelector('[data-related-sessions]'))\n this.setSection('[data-related-sessions-section]', `<details data-related-sessions><summary>${escapeHTML(this.ui.label('relatedSessions'))}</summary><button type=\"button\" data-action=\"refresh-related-sessions\">${escapeHTML(this.ui.label('refresh'))}</button><div data-related-sessions-body></div></details>`);\n if (service.workResources) {\n const resources = this.panel().querySelector<HTMLDetailsElement>('[data-work-resources]');\n if (!resources) this.setSection('[data-work-resource-section]', `<details data-work-resources><summary>${escapeHTML(this.ui.label('workResourceTitle'))}</summary><p>${escapeHTML(this.ui.label('workResourceHelp'))}</p><div class=\"kanban__actions\"><button type=\"button\" data-action=\"refresh-work-resources\">${escapeHTML(this.ui.label('refresh'))}</button><button type=\"button\" data-action=\"inspect-work-resources\">${escapeHTML(this.ui.label('inspectWorkResources'))}</button></div><div data-work-resource-body></div></details>`);\n }\n void this.supplement('ui.item.events', generation, () => service.events(boardId, 0, 200), events => {\n this.setSection('[data-item-events]', this.ui.itemEventsView(events.filter(event => event.itemId === item.id).reverse()));\n }, '[data-item-events]');\n void this.supplement('ui.item.records', generation, () => service.records(boardId, {itemId: item.id, limit: 10}), records => {\n this.setSection('[data-related-records]', `<h4>${escapeHTML(this.ui.label('relatedRecords'))} (${records.total})</h4>${this.ui.recordLinks(records, this.board!)}<div class=\"kanban__actions\">${this.ui.button('new-log', 'newLog')}${this.ui.button('new-decision', 'newDecision')}</div>`);\n }, '[data-related-records]');\n void this.loadOperations(generation);\n void this.loadExecutionSections(generation, item, archived, true);\n if (!service.executionSession) this.loadReferenceSection(generation, item);\n void this.supplement('ui.item.children', generation, () => this.showChildren(generation), () => {}, '[data-children]');\n }\n private async loadExecutionSections(generation: number, item: KanbanItem, archived: boolean, references = false): Promise<void> {\n const service = this.options.service!, boardId = this.boardId, reads: Promise<void>[] = [];\n if (service.executionSession) reads.push(this.supplement('ui.item.session', generation, () => service.executionSession!(boardId, item.id), execution => {\n this.setSection('[data-execution-session]', this.ui.executionSessionView(execution, !!this.options.executionReference && !archived && !['completed', 'cancelled'].includes(item.status) && item.launch?.admittedAt === undefined, this.options.sessionUrl));\n if (references) this.loadReferenceSection(generation, item, new Set(execution.history.map(binding => binding.sessionId)));\n }, '[data-execution-session]'));\n if (this.board?.automation && service.analysisStatus) reads.push(this.supplement('ui.item.analysis', generation, () => service.analysisStatus!(boardId, item.id), analysis => {\n this.setSection('[data-analysis]', analysisView(this.options.presentation, analysis ?? null));\n }, '[data-analysis]'));\n await Promise.all(reads);\n }\n private async loadOperations(generation: number): Promise<void> {\n const service = this.options.service, item = this.item;\n if (!service?.itemOperations || !item || !this.panel().querySelector('[data-item-operations]')) return;\n await this.supplement('ui.item.operations', generation, () => service.itemOperations!(this.boardId, item.id), view => {\n this.operations = view;\n this.setSection('[data-item-operations]', itemOperationsView(this.options.presentation, view, this.options.sessionUrl));\n }, '[data-item-operations]');\n }\n private async submitOperation(form: HTMLFormElement, data: FormData, binding: boolean): Promise<void> {\n const service = this.options.service!, action = form.dataset.operation!, itemId = this.item!.id;\n const request = {operationId: form.dataset.resourceId!, itemId, action, expectedRevision: form.dataset.revision!};\n const entry = this.operations?.operations.find(operation => operation.id === action);\n this.operationStage = binding ? 'ui.operation.bind' : 'ui.operation.start';\n const view = binding ? await service.bindItemOperation!(this.boardId, {...request, target: JSON.parse(value(data, 'target'))})\n : await service.runItemOperation!(this.boardId, {...request, confirmed: data.has('confirmed'),\n selections: Object.fromEntries((entry?.fields ?? []).map(field => [field.id, value(data, 'choice-' + field.id)]))});\n this.dirty = false; this.operations = view; this.options.committed?.();\n this.setSection('[data-item-operations]', itemOperationsView(this.options.presentation, view, this.options.sessionUrl));\n this.options.changed?.();\n const result = view.operations.find(operation => operation.id === action);\n if (!binding && result?.state === 'succeeded') await this.refresh();\n if (!binding && entry?.openSessionOnRun && result?.sessionId) await this.options.openSession(result.sessionId);\n }\n private loadReferenceSection(generation: number, item: KanbanItem, skip?: Set<string>): void {\n void this.supplement('ui.item.references', generation, () => this.showReferenceLinks(item.externalRefs ?? [], generation, skip), () => {}, '[data-reference-links]');\n }\n private async supplement<T>(stage: string, generation: number, read: () => Promise<T>, apply: (value: T) => void, selector?: string): Promise<void> {\n const started = Date.now(), operationId = randomUUID();\n try {\n const value = await read();\n if (!this.alive(generation) || this.dirty) return;\n apply(value);\n try {this.options.presentation.log?.({operationId, stage, result: 'success', durationMs: Date.now() - started});} catch { /* Diagnostics cannot change a read result. */ }\n } catch (error) {\n if (!this.alive(generation)) return;\n this.reportRead(stage, error, operationId, started);\n const slot = selector ? this.panel().querySelector(selector) : undefined;\n if (slot) {\n const notice = slot.querySelector('[data-read-error]') ?? document.createElement('p'); notice.setAttribute('role', 'alert'); notice.setAttribute('data-read-error', '');\n notice.textContent = errorText(error, this.ui.label, stage); if (!notice.parentElement) slot.append(notice);\n }\n else this.error(errorText(error, this.ui.label, stage));\n }\n }\n private setSection(selector: string, html: string): void {\n const slot = this.panel().querySelector(selector);\n if (slot && slot.innerHTML !== html) slot.innerHTML = html;\n }\n private reportRead(stage: string, error: unknown, operationId = randomUUID(), started = Date.now()): void {\n const raw = (error as {code?: unknown})?.code;\n try {this.options.presentation.log?.({operationId, stage, result: 'failure', ...failureDetails(error), code: typeof raw === 'string' && /^[A-Z][A-Z0-9_]{0,79}$/.test(raw) ? raw : 'KANBAN_OPERATION_FAILED', durationMs: Date.now() - started});} catch { /* Diagnostics cannot change a read result. */ }\n }\n private async showChildren(generation: number, cursor?: string): Promise<void> {\n const {service} = this.options, item = this.item, board = this.board;\n if (!service || !item || !board || !childType(board, item)) return;\n const query = {parentId: item.id, view: 'all' as const, limit: 50, ...(cursor ? {cursor} : {})};\n const page = await (service.queryActivity?.(board.id, query) ?? service.query(board.id, query));\n if (!this.alive(generation)) return;\n this.panel().querySelector('[data-children]')!.innerHTML = childrenView(this.options.presentation, page, cursor, !['completed', 'cancelled'].includes(item.status) && !item.closedAt, this.options.sessionUrl);\n }\n private async showRecords(generation: number): Promise<void> {\n if (!this.recordKind || !this.options.service) return;\n let page: import('../contracts').KanbanRecordPage;\n try {page = await this.options.service.records(this.boardId, {kind: this.recordKind, limit: 50, ...(this.recordCursor ? {cursor: this.recordCursor} : {})});}\n catch (error) {\n if ((error as {code?: string})?.code !== 'CURSOR_STALE' || !this.recordCursor) throw error;\n this.recordCursor = undefined;\n page = await this.options.service.records(this.boardId, {kind: this.recordKind, limit: 50});\n }\n if (!this.alive(generation)) return;\n this.recordPage = page; this.content().innerHTML = this.ui.recordsView(page, this.recordKind, !!this.recordCursor, this.board);\n }\n private async showRecord(id: string, generation = this.generation): Promise<void> {\n if (!this.options.service) return;\n const record = await this.options.service.record(this.boardId, id);\n const executions = record.kind === 'decision' ? await this.options.service.recordExecutions?.(this.boardId, id) : undefined;\n if (!this.alive(generation)) return;\n this.recordId = id; this.drawer.show(this.ui.recordDetail(record, this.board!, executions), 'record:' + id); await this.showReferenceLinks(record.externalRefs ?? [], generation); this.options.selected(this.getActiveResourceId());\n }\n private async showReferenceLinks(references: import('../contracts').KanbanExternalReference[], generation: number, skip = new Set<string>()): Promise<void> {\n if (!this.options.referenceLink) return;\n const started = Date.now();\n const results = await Promise.allSettled(references.filter(ref => !skip.has(ref.id)).map(ref => this.options.referenceLink!(ref)));\n if (!this.alive(generation)) return;\n const resolved = results.flatMap(result => result.status === 'fulfilled' && result.value ? [result.value] : []);\n const links = [...new Map(resolved.map(link => [link.href, link])).values()];\n const slot = this.panel().querySelector('[data-reference-links]');\n if (slot) slot.innerHTML = links.map(link => linkPair(this.options.presentation, link.href, link.label, link.sessionId)).join('');\n const errors = results.filter((result): result is PromiseRejectedResult => result.status === 'rejected');\n if (errors.length) {\n const raw = (errors[0].reason as {code?: unknown})?.code, code = typeof raw === 'string' && /^[A-Z][A-Z0-9_]{0,79}$/.test(raw) ? raw : 'SESSION_LINK_UNAVAILABLE';\n if (slot) {const error = document.createElement('p'); error.setAttribute('role', 'alert'); error.textContent = this.ui.label('sessionLinkUnavailable') + ' (' + code + ')'; slot.append(error);}\n this.options.presentation.log?.({operationId: randomUUID(), stage: 'ui.reference-links', result: 'failure', code, ...failureDetails(errors[0].reason), durationMs: Date.now() - started});\n }\n }\n private async loadWorkResources(inspect = false, refresh = false): Promise<void> {\n const service = this.options.service, boardId = this.boardId, itemId = this.item?.id;\n const section = this.panel().querySelector<HTMLDetailsElement>('[data-work-resources]');\n if (!service?.workResources || !itemId || !section || section.dataset.loading || !refresh && section.dataset.loaded) return;\n const operationId = randomUUID(), started = Date.now(), revision = section.dataset.associationRevision; section.dataset.loading = 'true';\n if (!section.dataset.loaded) this.setSection('[data-work-resource-body]', `<p role=\"status\">${escapeHTML(this.ui.label('loading'))}</p>`);\n try {\n const resources = await service.workResources(boardId, itemId, {inspect});\n if (this.abort.signal.aborted || !section.isConnected || this.item?.id !== itemId || this.boardId !== boardId || revision !== section.dataset.associationRevision) return;\n this.setSection('[data-work-resource-body]', workResourcesView(this.options.presentation, resources)); section.dataset.loaded = 'true';\n } catch (error) {\n if (!section.isConnected || this.abort.signal.aborted) return;\n this.reportRead('ui.item.work-resources', error, operationId, started);\n const body = section.querySelector('[data-work-resource-body]'), notice = document.createElement('p');\n notice.setAttribute('role', 'alert'); notice.textContent = errorText(error, this.ui.label, 'ui.item.work-resources'); body?.append(notice);\n } finally {\n delete section.dataset.loading;\n if (!this.abort.signal.aborted && section.isConnected && section.open && revision !== section.dataset.associationRevision) void this.loadWorkResources(false, true);\n }\n }\n private async loadRelatedSessions(refresh = false): Promise<void> {\n const service = this.options.service, boardId = this.boardId, itemId = this.item?.id;\n const details = this.panel().querySelector<HTMLDetailsElement>('[data-related-sessions]');\n if (!service?.executionSession || !itemId || !details || details.dataset.loading || !refresh && details.dataset.loaded) return;\n details.dataset.loading = 'true';\n this.setSection('[data-related-sessions-body]', `<p role=\"status\">${escapeHTML(this.ui.label('loading'))}</p>`);\n try {\n await this.supplement('ui.item.related-sessions', this.generation, () => service.executionSession!(boardId, itemId, {includeRelated: true}), detail => {\n if (this.item?.id !== itemId || this.boardId !== boardId) return;\n this.setSection('[data-related-sessions-body]', relatedSessionsView(this.options.presentation, detail, this.options.sessionUrl));\n details.dataset.loaded = 'true';\n }, '[data-related-sessions-body]');\n } finally {delete details.dataset.loading;}\n }\n private async click(event: Event): Promise<void> {\n const target = (event.target as HTMLElement).closest<HTMLElement>('button, a'); if (!target || target.matches(':disabled')) return;\n if (target.dataset.action === 'refresh-related-sessions') {await this.loadRelatedSessions(true); return;}\n for (const menu of this.options.editor.querySelectorAll<HTMLDetailsElement>('[data-popover][open]')) if (!menu.contains(target)) menu.open = false;\n if (target.dataset.session) {event.preventDefault(); await this.options.openSession(target.dataset.session); return;}\n if (this.dirty && !['close-panel', 'copy-reference', 'copy-link', 'copy-resource'].includes(target.dataset.action ?? '')) throw new Error(this.ui.label('unsaved'));\n const path = this.navigationTarget(target);\n if (path && this.options.navigate) {await this.options.navigate(path); return;}\n if (target.dataset.board) {await this.navigate(target.dataset.board); return;}\n if (target.dataset.context) {this.recordKind = target.dataset.context === 'board' ? undefined : target.dataset.context as 'log' | 'decision'; this.recordCursor = undefined; this.closePanel(); await this.refresh(); return;}\n if (target.dataset.record) {this.itemId = ''; this.item = undefined; await this.showRecord(target.dataset.record); return;}\n if (target.dataset.item) {this.recordId = ''; this.itemId = target.dataset.item; await this.showItem(); return;}\n const action = target.dataset.action;\n if (action === 'continue-execution' && this.item && target.dataset.dispatch) {\n await this.execute({type: 'continue-execution', previousDispatchId: target.dataset.dispatch, dispatchId: randomUUID()}); return;\n }\n if (action === 'refresh-work-resources' || action === 'inspect-work-resources') {await this.loadWorkResources(action === 'inspect-work-resources', true); return;}\n if (action === 'cleanup-work-resource' && this.item && this.options.service?.cleanupWorkResource) {\n if (!globalThis.confirm(this.ui.label('cleanupWorkResourceConfirm'))) return;\n try {await this.options.service.cleanupWorkResource(this.boardId, {operationId: randomUUID(), itemId: this.item.id, resourceId: target.dataset.resource!, version: target.dataset.version!});}\n finally {await this.loadWorkResources(false, true);} return;\n }\n if (action === 'close-panel' || action === 'dismiss-panel') {await this.requestClose(action === 'close-panel'); return;}\n if (action === 'copy-reference' || action === 'copy-link' || action === 'copy-resource') {await this.ui.copyReference(target.closest<HTMLElement>('[data-action]')!); return;}\n if (action === 'automation' && this.board && this.options.service?.automationCatalog) {\n this.automationCatalog = await this.options.service.automationCatalog();\n this.itemId = ''; this.drawer.show(automationForm(this.options.presentation, this.board, this.automationCatalog), 'automation'); this.dirty = true; return;\n }\n if (action === 'task-edit' && this.item && this.board) {\n const editId=randomUUID(),itemId=this.item.id;\n await this.execute({type:'begin-edit',itemId,editId,leaseMs:300000});\n this.editing={itemId,editId};\n const resources=await this.options.service?.itemResources?.(this.boardId);\n if(resources)resources.selected=this.item.externalRefs?.find(ref=>resources.resources.some(r=>r.reference.system===ref.system&&r.reference.id===ref.id));\n this.drawer.show(editTaskForm(this.options.presentation,this.board,this.item,editId,resources),'edit:'+itemId);this.dirty=true;return;\n }\n if (action === 'task-fix' && this.item && this.options.service?.fixTask) {\n target.dataset.operationId ??= randomUUID();\n const fix=await this.options.service.fixTask(this.boardId,{itemId:this.item.id,operationId:target.dataset.operationId});\n await this.options.openSession(fix.sessionId);return;\n }\n if (action?.startsWith('task-') && this.item) {\n if(action==='task-cancel')await this.execute({type:'cancel-item',itemId:this.item.id,reason:this.ui.label('taskAction.cancel')});\n else if(action==='task-run')await this.execute({type:'request-run',itemId:this.item.id});\n else if(action==='task-pause'||action==='task-resume')await this.execute({type:'request-control',itemId:this.item.id,controlId:randomUUID(),action:action==='task-pause'?'pause':'resume'});\n return;\n }\n if (action === 'reanalyze' && this.item?.planning) {await this.execute({type:'retry-analysis',itemId:this.item.id,retryId:randomUUID()}); return;}\n if (action === 'new-board') {this.showBoardForm(); return;}\n if (action === 'children-next' || action === 'children-first') {await this.showChildren(this.generation, action === 'children-next' ? target.dataset.cursor : undefined); return;}\n if (action === 'new-child' && this.board && this.item && childType(this.board, this.item)) {\n await this.showItemForm(this.item);return;\n }\n if (action === 'new-item' && this.board) {await this.showItemForm();return;}\n if (action === 'new-log' || action === 'new-decision') {\n const resources = action === 'new-decision' ? await this.options.service?.recordResources?.() : [];\n this.recordId = ''; this.drawer.show(this.ui.recordForm(action === 'new-log' ? 'log' : 'decision', this.board!, this.itemId, resources), action); this.dirty = true; return;\n }\n if ((action === 'run-record' || action === 'run-record-separate') && this.options.service?.runRecord) {\n const record = await this.options.service.record(this.boardId, this.recordId);\n target.dataset.operationId ??= randomUUID();\n await this.options.service.runRecord(this.boardId, {operationId: target.dataset.operationId, recordId: record.id, expectedRevision: record.revision, placement: action === 'run-record-separate' ? 'new-board' : 'current-board'});\n this.options.committed?.(); await this.refresh(); return;\n }\n if (action === 'scroll-left' || action === 'scroll-right') {const board = this.content().querySelector<HTMLElement>('.kanban__columns'); if (board) board.scrollLeft += board.clientWidth * (action === 'scroll-left' ? -0.8 : 0.8); return;}\n if (action === 'records-next' || action === 'records-first') {this.recordCursor = action === 'records-next' ? this.recordPage?.nextCursor : undefined; await this.refresh(); return;}\n if ((action === 'confirm-record' || action === 'reject-record') && this.recordId) {await this.execute({type: 'review-record', recordId: this.recordId, accept: action === 'confirm-record'}); return;}\n if (action === 'next' || action === 'first') {this.query.cursor = action === 'next' ? this.page?.nextCursor : undefined; await this.refresh(); return;}\n if (action === 'refresh-item-operations') {this.dirty = false; await this.loadOperations(this.generation); this.options.changed?.(); return;}\n if (action === 'refresh') {await this.refresh(); return;}\n if (action && this.item) await this.execute(actionCommand(action, this.item));\n }\n private async openReference(reference: string): Promise<void> {\n if (this.dirty) throw new Error(this.ui.label('unsaved'));\n const target = await this.options.service!.resolve(reference);\n if (this.options.navigate) {\n const leaf = target.recordId ? '/@record/' + encodeURIComponent(target.recordId) : target.itemId ? '/' + encodeURIComponent(target.itemId) : '';\n await this.options.navigate(`/board/${encodeURIComponent(target.boardId)}${leaf}`); return;\n }\n this.closePanel(); this.boardId = target.boardId; this.itemId = target.itemId ?? ''; this.recordId = target.recordId ?? '';\n this.recordKind = undefined; this.recordCursor = undefined; this.query = {view: 'all', limit: 50};\n await this.refresh();\n this.options.activated?.();\n }\n private async navigate(id: string): Promise<void> {\n this.boardId = id; this.itemId = ''; this.recordKind = undefined; this.recordCursor = undefined; this.query = {view: 'all', limit: 50}; this.closePanel(); await this.refresh();\n }\n private async submit(event: Event): Promise<void> {\n const form = event.target as HTMLFormElement, kind = form.dataset.form, data = new FormData(form);\n form.dataset.resourceId ??= randomUUID();\n if (!form.reportValidity()) return;\n if (kind === 'item-operation' || kind === 'operation-binding') {await this.submitOperation(form, data, kind === 'operation-binding'); return;}\n if (kind === 'reference') {await this.openReference(value(data, 'reference')); return;}\n if (kind === 'filter') {\n if (this.dirty) throw new Error(this.ui.label('unsaved'));\n this.query = {phase:(value(data,'phase')||undefined) as KanbanSessionActivityQuery['phase'], view: value(data, 'view') as KanbanSessionActivityQuery['view'],execution:(value(data,'execution')||'all') as KanbanSessionActivityQuery['execution'], search: value(data, 'search'), area: value(data, 'area') || undefined, milestone: value(data, 'milestone') || undefined, limit: 50};\n await this.refresh(); return;\n }\n if (kind === 'board' && this.actor && this.options.service) {\n const created = await this.options.service.create(boardInput(data, this.actor, form.dataset.resourceId));\n this.options.committed?.(); this.dirty = false; await this.navigate(created.id); return;\n }\n if (kind === 'automation' && this.automationCatalog) {\n await this.execute({type: 'configure-automation', settings: {enabled: data.has('enabled'), autoAccept: data.has('autoAccept'),\n ...(value(data, 'preparation') ? {preparation: JSON.parse(value(data, 'preparation')),\n preparationOptions: Object.fromEntries([...data.entries()].filter(([key]) => key.startsWith('preparation-option-'))\n .map(([key, value]) => [key.slice('preparation-option-'.length), String(value)]))} : {}),\n planner: JSON.parse(value(data, 'planner')), executor: JSON.parse(value(data, 'executor')),\n coordinator: this.automationCatalog.coordinator, worker: this.automationCatalog.worker}}); return;\n }\n if(kind==='task-reply'&&this.item&&this.options.service?.replyTask) {\n await this.options.service.replyTask(this.boardId,{itemId:this.item.id,attemptId:value(data,'attemptId'),replyTo:value(data,'replyTo'),operationId:form.dataset.resourceId,content:value(data,'content')});\n this.dirty=false;await this.refresh();return;\n }\n if(kind==='save-edit'&&this.item) {\n const planning=formCommand('plan',data,this.board!,this.item);\n if(planning.type!=='plan-item')return;\n await this.execute({type:'save-edit',launchMode:value(data,'launchMode') as 'automatic'|'manual',itemId:this.item.id,editId:value(data,'editId'),title:value(data,'title'),responsibility:value(data,'responsibility'),acceptance:value(data,'acceptance'),planning:planning.planning,externalRefs:[...(this.item.externalRefs??[]).filter(ref=>!value(data,'itemResource')||ref.system!==JSON.parse(value(data,'itemResource')).system),...(value(data,'itemResource')?[JSON.parse(value(data,'itemResource'))]:[])]});\n this.editing=undefined;return;\n }\n if (kind === 'amendment') data.set('amendmentId', form.dataset.amendment ?? '');\n if (kind === 'record-link' && this.recordId) {await this.execute({type: 'link-record', recordId: this.recordId, itemId: value(data, 'itemId')}); return;}\n if (kind && this.board) {\n if (kind === 'execution-session' && this.item && this.options.executionReference) {\n await this.execute({type: 'link-reference', itemId: this.item.id, reference: this.options.executionReference(value(data, 'executionSession'), form.dataset.resourceId)}); return;\n }\n await this.execute(formCommand(kind, data, this.board, this.item, form.dataset.resourceId));\n }\n }\n private async showItemForm(parent?:KanbanItem):Promise<void> {\n const board=this.board,generation=this.generation;if(!board)return;\n const resources=await this.options.service?.itemResources?.(board.id,parent?.id);\n if(this.abort.signal.aborted||generation!==this.generation)return;\n this.itemId='';this.drawer.show(this.ui.newItemForm(board,parent,resources),parent?'new-child':'new-item');this.dirty=true;\n }\n private async execute(command: KanbanCommand): Promise<void> {\n if (!this.board || !this.options.service) return;\n if (command.type === 'review') {\n const workflow = await this.options.service.workflow(this.board.id);\n const dispatch = Object.values(workflow.dispatches).find(dispatch => dispatch.itemId === ('itemId' in command ? command.itemId : '') && dispatch.outcome === 'succeeded');\n if (dispatch) command = {type: 'accept-execution', dispatchId: dispatch.id, accept: command.accept};\n }\n const fingerprint = JSON.stringify(command);\n if (this.pending && (this.pending.boardId !== this.board.id || this.pending.fingerprint !== fingerprint)) throw Object.assign(new Error('Pending write'), {code: 'WRITE_OUTCOME_UNKNOWN'});\n this.pending ??= {boardId: this.board.id, fingerprint, request: {operationId: randomUUID(), expectedRevision: this.board.revision, command}};\n try {await this.options.service.execute(this.board.id, this.pending.request);}\n catch (error) {\n if ((error as Error)?.name === 'KanbanError' || (error as {outcome?: string})?.outcome === 'not-committed') this.pending = undefined;\n throw error;\n }\n this.pending = undefined; this.options.committed?.();\n if (command.type === 'configure-automation') this.closePanel();\n this.dirty = false; this.query.cursor = undefined; this.recordCursor = undefined;\n if (command.type === 'create-item') this.itemId = command.item.id;\n if (command.type === 'append-record') {this.recordId = command.record.id; this.itemId = ''; this.item = undefined;}\n await this.refresh();\n }\n private async run(action: () => Promise<void>): Promise<void> {\n if (this.busy || this.abort.signal.aborted) return;\n const operationId = randomUUID(), started = Date.now();\n this.operationStage = 'ui.operation';\n this.generation++; this.busy = true; this.status(this.ui.label('loading')); this.error(''); this.options.editor.setAttribute('aria-busy', 'true');\n let succeeded = false;\n try {await action(); succeeded = true;}\n catch (error) {\n const raw = (error as {code?: unknown})?.code;\n try {this.options.presentation.log?.({operationId, stage: this.operationStage, result: 'failure', ...failureDetails(error), code: typeof raw === 'string' && /^[A-Z][A-Z0-9_]{0,79}$/.test(raw) ? raw : 'KANBAN_OPERATION_FAILED', durationMs: Date.now() - started});} catch { /* Reporting cannot alter drafts. */ }\n if (!this.abort.signal.aborted) this.error(errorText(error, this.ui.label, this.operationStage));\n }\n finally {\n this.busy = false;\n if (!this.abort.signal.aborted) {\n this.status(''); this.options.editor.removeAttribute('aria-busy'); this.options.editor.inert = false; this.mountHeaderActions(); this.drawer.flushFocus(); this.options.changed?.();\n if (succeeded && this.invalid && this.visible && !this.dirty) this.refreshInBackground();\n }\n }\n }\n private navigationTarget(target: HTMLElement): string | undefined {\n if (target.dataset.board) return `/board/${encodeURIComponent(target.dataset.board)}`;\n const board = `/board/${encodeURIComponent(this.boardId)}`;\n if (target.dataset.item) return `${board}/${encodeURIComponent(target.dataset.item)}`;\n if (target.dataset.record) return `${board}/@record/${encodeURIComponent(target.dataset.record)}`;\n if (target.dataset.context) return board + (target.dataset.context === 'board' ? '' : '/@' + target.dataset.context);\n }\n private mountHeaderActions(): void {\n for (const [slot, release] of this.windowMounts) if (!this.options.editor.contains(slot)) {release(); this.windowMounts.delete(slot);}\n if (this.options.mountTitleBar) {\n for (const header of this.options.editor.querySelectorAll<HTMLElement>('[data-titlebar]')) if (!this.windowMounts.has(header)) this.mountTitleBar(header);\n return;\n }\n for (const slot of this.options.editor.querySelectorAll<HTMLElement>('[data-window-navigation]')) {\n if (!this.windowMounts.has(slot) && this.options.mountWindowList) this.windowMounts.set(slot, this.options.mountWindowList(slot));\n }\n for (const header of this.options.editor.querySelectorAll<HTMLElement>('[data-titlebar]')) if (!this.windowMounts.has(header)) this.mountDefaultTitleBar(header);\n }\n private mountDefaultTitleBar(header: HTMLElement): void {\n const back = header.querySelector<HTMLElement>('[data-titlebar-back]') ?? undefined;\n const title = header.querySelector<HTMLElement>('[data-titlebar-title]') ?? undefined;\n back?.classList.add('ui-nav-button'); header.querySelector('.kanban__menu > summary')?.classList.add('ui-nav-button');\n const view = mountSharedTitleBar(header, {\n title, back,\n leading: [...header.querySelectorAll<HTMLElement>('[data-navigation] > button')],\n trailing: [...header.querySelectorAll<HTMLElement>('[data-window-navigation]')],\n actions: [...header.querySelectorAll<HTMLElement>('.kanban__desktop-create, :scope > [data-action=new-board]')],\n menu: header.querySelector<HTMLElement>('.kanban__menu') ?? undefined,\n });\n view.element.dataset.titlebarSlot = ''; header.dataset.titlebarHost = 'true';\n view.update({title: title?.textContent ?? ''});\n this.windowMounts.set(header, () => {view.destroy(); delete header.dataset.titlebarHost;});\n }\n private mountTitleBar(header: HTMLElement): void {\n const slot = document.createElement('div'); slot.dataset.titlebarSlot = ''; header.prepend(slot); header.dataset.titlebarHost = 'true';\n const release = this.options.mountTitleBar!(slot, {\n title: header.querySelector<HTMLElement>('[data-titlebar-title]') ?? undefined,\n back: header.querySelector<HTMLElement>('[data-titlebar-back]') ?? undefined,\n menu: header.querySelector<HTMLElement>('.kanban__menu') ?? undefined,\n actions: [...header.querySelectorAll<HTMLElement>('.kanban__desktop-create, :scope > [data-action=new-board]')],\n });\n this.windowMounts.set(header, () => {release(); slot.remove(); delete header.dataset.titlebarHost;});\n }\n private closePanel(): void {this.dirty = false; this.itemId = ''; this.recordId = ''; this.item = undefined; this.drawer.close();}\n private async requestClose(explicit: boolean): Promise<void> {\n if (this.editing && explicit) {await this.execute({type:'end-edit',...this.editing});this.editing=undefined;}\n if (this.dirty && !explicit) throw new Error(this.ui.label('unsaved'));\n if (this.options.navigate && this.boardId) {\n if (this.dirty) this.closePanel();\n await this.options.navigate(`/board/${encodeURIComponent(this.boardId)}`); return;\n }\n this.closePanel(); void this.refreshActivity(this.generation); this.options.selected(this.getActiveResourceId());\n }\n private alive(generation: number): boolean {return generation === this.generation && !this.abort.signal.aborted;}\n private content(): HTMLElement {return this.options.editor.querySelector('[data-content]')!;}\n private panel(): HTMLElement {return this.options.editor.querySelector('[data-panel]')!;}\n private status(message: string): void {this.options.editor.querySelector('[data-status]')!.textContent = message;}\n private error(message: string): void {\n const error = this.options.editor.querySelector<HTMLElement>('[data-error]')!; error.textContent = message; error.hidden = this.drawer?.open ?? false;\n const panelError = this.options.editor.querySelector('[data-panel-error]'); if (panelError) panelError.textContent = message;\n const executionError=this.options.editor.querySelector<HTMLElement>('[data-execution-error]');\n if(executionError) {\n executionError.textContent=message;executionError.hidden=!message;\n if(message) {const panel=this.panel(),bottom=executionError.getBoundingClientRect().bottom,edge=panel.getBoundingClientRect().bottom;if(bottom>edge) panel.scrollTop+=bottom-edge+12;}\n }\n this.options.editor.querySelector<HTMLElement>('[data-retry]')!.hidden = !message || (this.drawer?.open ?? false);\n }\n}\nfunction errorText(error: unknown, label: (key: string) => string, stage: string): string {\n const code = (error as {outcome?: string})?.outcome === 'unknown' ? 'WRITE_OUTCOME_UNKNOWN' : (error as {code?: unknown})?.code;\n if (code === 'REVISION_CONFLICT' || code === 'CURSOR_STALE') return label('conflict');\n if (error instanceof Error && error.message === label('unsaved')) return error.message;\n const safe = typeof code === 'string' && /^[A-Z_]{1,80}$/.test(code) ? code : 'KANBAN_OPERATION_FAILED';\n const translated = label('errors.' + safe);\n const details = safe === 'KANBAN_OPERATION_FAILED' ? failureDetails(error) : {};\n const diagnostic = details.errorName ? `\\n${label('diagnostic')}: ${stage} · ${details.errorName}${details.source ? ' · ' + details.source : ''}` : '';\n return (translated !== 'kanban.errors.' + safe && translated !== 'errors.' + safe ? `${translated} (${safe})` : `${label('failed')}: ${safe}`) + diagnostic;\n}\n\nexport async function mountKanban(container: HTMLElement, options: Omit<KanbanUIOptions, 'sidebar' | 'editor'>) {\n const sidebar = document.createElement('aside'), editor = document.createElement('main');\n container.classList.add('kanban-layout'); container.replaceChildren(sidebar, editor);\n const view = new KanbanWorkbench({...options, sidebar, editor}, container);\n await view.start();\n return view;\n}\nexport {KanbanSuggestionPicker} from './suggestion-picker';\nexport type {SuggestionField} from './suggestion-field';\nexport type {KanbanUIOptions, KanbanPresentation, KanbanReviewGuidance} from './ports';\n","import {escapeAttr} from './html';\nimport type {KanbanPresentation} from './ports';\n\n/** A bounded modal inspector preserves the board's independent scroll positions. */\nexport class KanbanDrawer {\n private key = '';\n private focusRequested = false;\n private restoreTarget?: HTMLElement;\n private restoreItem?: string;\n private restoreRecord?: string;\n private restoreRequested = false;\n constructor(private readonly editor: HTMLElement, private readonly sidebar?: HTMLElement) {}\n get element(): HTMLElement {return this.editor.querySelector('[data-panel]')!;}\n get open(): boolean {return !this.element.hidden;}\n show(html: string, key: string): void {\n const panel = this.element, same = this.open && this.key === key, top = same ? panel.scrollTop : 0;\n if (!this.open) {\n const active = document.activeElement as HTMLElement | null;\n this.restoreTarget = active ?? undefined; this.restoreItem = active?.dataset.item; this.restoreRecord = active?.dataset.record;\n }\n this.focusRequested ||= same && panel.contains(document.activeElement);\n const retained = same ? ['[data-work-resource-section]', '[data-related-sessions-section]'].map(selector => ({selector, node: panel.querySelector(selector)})) : [];\n panel.innerHTML = html;\n for (const {selector, node} of retained) if (node?.hasChildNodes()) panel.querySelector(selector)?.replaceWith(node);\n const error = document.createElement('p'); error.dataset.panelError = ''; error.setAttribute('role', 'alert');\n const header = panel.querySelector('.kanban__panel-header'); if (header) header.after(error); else panel.prepend(error);\n panel.hidden = false; panel.scrollTop = top;\n this.key = key; this.focusRequested ||= !same;\n this.editor.querySelector<HTMLElement>('[data-content]')!.inert = true; if (this.sidebar) this.sidebar.inert = true;\n this.editor.querySelector<HTMLElement>('[data-backdrop]')!.hidden = false;\n this.editor.classList.add('kanban--detail');\n }\n close(): void {\n this.element.hidden = true; this.element.replaceChildren(); this.key = ''; this.focusRequested = false; this.restoreRequested = true;\n this.editor.querySelector<HTMLElement>('[data-content]')!.inert = false; if (this.sidebar) this.sidebar.inert = false;\n this.editor.querySelector<HTMLElement>('[data-backdrop]')!.hidden = true; this.editor.classList.remove('kanban--detail');\n }\n flushFocus(): void {\n if (this.focusRequested && this.open) {this.focusRequested = false; this.element.focus({preventScroll: true});}\n else if (this.restoreRequested) {\n this.restoreRequested = false;\n const matches = [...this.editor.querySelectorAll<HTMLElement>('[data-item], [data-record]')];\n const target = this.restoreTarget?.isConnected ? this.restoreTarget : matches.find(node =>\n this.restoreItem ? node.dataset.item === this.restoreItem : this.restoreRecord ? node.dataset.record === this.restoreRecord : false);\n (target ?? this.editor.querySelector<HTMLElement>('button'))?.focus({preventScroll: true});\n }\n }\n keydown(event: KeyboardEvent): void {\n if (!this.open || event.key !== 'Tab') return;\n const nodes = [...this.element.querySelectorAll<HTMLElement>('button, input:not([type=hidden]), select, textarea, a[href], summary, [tabindex=\"0\"]')]\n .filter(node => !node.matches(':disabled') && node.tabIndex >= 0 && !node.closest('[hidden], [inert]') && !node.hidden && !node.closest('details:not([open]) :not(summary)'));\n const first = nodes[0], last = nodes[nodes.length - 1], active = document.activeElement;\n if (!first) {event.preventDefault(); this.element.focus(); return;}\n if (event.shiftKey && (active === first || active === this.element)) {event.preventDefault(); last.focus();}\n else if (!event.shiftKey && (active === last || !this.element.contains(active))) {event.preventDefault(); first.focus();}\n }\n destroy(): void {if (this.sidebar) this.sidebar.inert = false; this.editor.classList.remove('kanban--detail');}\n}\nexport function drawerMarkup(ui: KanbanPresentation): string {\n return `<button type=\"button\" class=\"kanban__backdrop\" data-backdrop data-action=\"dismiss-panel\" aria-label=\"${escapeAttr(ui.text('closeDetail'))}\" tabindex=\"-1\" hidden></button>\n <section data-panel class=\"kanban__panel\" role=\"dialog\" aria-modal=\"true\" aria-label=\"${escapeAttr(ui.text('details'))}\" tabindex=\"-1\" hidden></section>`;\n}\nexport function preserveBoardScroll(container: HTMLElement, render: () => void): void {\n const columns = [...container.querySelectorAll<HTMLElement>('[data-column]')].map(node => ({id: node.dataset.column, top: node.scrollTop}));\n const left = container.querySelector<HTMLElement>('.kanban__columns')?.scrollLeft ?? 0;\n render();\n for (const node of container.querySelectorAll<HTMLElement>('[data-column]')) node.scrollTop = columns.find(column => column.id === node.dataset.column)?.top ?? 0;\n const board = container.querySelector<HTMLElement>('.kanban__columns'); if (board) board.scrollLeft = left;\n}\n","/** Keep browser failure diagnostics useful without logging messages or user content. */\nexport function failureDetails(error: unknown): {errorName?: string; source?: string} {\n if (!error || typeof error !== 'object') return {};\n const value = error as {name?: unknown; stack?: unknown};\n const known = /^(?:Error|TypeError|ReferenceError|SyntaxError|RangeError|SecurityError|QuotaExceededError|NotSupportedError|InvalidStateError|DataCloneError|AbortError|NetworkError)$/;\n const errorName = typeof value.name === 'string' && known.test(value.name) ? value.name : undefined;\n const frame = typeof value.stack === 'string' ? value.stack.match(/(?:^|\\/)([A-Za-z0-9_.-]+\\.m?js):(\\d+):(\\d+)/m) : null;\n return {...(errorName ? {errorName} : {}), ...(frame ? {source: `${frame[1]}:${frame[2]}:${frame[3]}`} : {})};\n}\n","import type {KanbanItemOperation, KanbanItemOperations} from '../contracts';\nimport type {KanbanPresentation} from './ports';\nimport {escapeAttr, escapeHTML} from './html';\nimport {sessionLink} from './session-links';\nexport function itemOperationsView(ui: KanbanPresentation, view: KanbanItemOperations, sessionUrl?: (id: string) => string): string {\n return `<button type=\"button\" data-action=\"refresh-item-operations\">${escapeHTML(ui.text('operationRefresh'))}</button><h4>${escapeHTML(ui.text('operationsTitle'))}</h4>\n <ol class=\"kanban__operations\">${view.operations.map(operation => operationCard(ui, view, operation, sessionUrl)).join('')}</ol>`;\n}\nfunction operationCard(ui: KanbanPresentation, view: KanbanItemOperations, operation: KanbanItemOperation, url?: (id: string) => string): string {\n const reason = operation.code ? ui.text('errors.' + operation.code) : '';\n return `<li data-operation-id=\"${escapeAttr(operation.id)}\"><strong>${escapeHTML(operation.label)}</strong> · ${escapeHTML(ui.text('operationState.' + operation.state))}\n ${operation.reason ? `<p>${escapeHTML(operation.reason)}</p>` : ''}${operation.code ? `<p role=\"alert\">${escapeHTML(reason.endsWith('errors.' + operation.code) ? operation.code : reason)}</p>` : ''}\n ${operation.summary ? `<pre class=\"kanban__operation-summary\">${escapeHTML(operation.summary)}</pre>` : ''}\n ${operation.sessionId ? sessionLink(ui, operation.sessionId, ui.text('operationSession'), url?.(operation.sessionId)) : ''}\n <form data-form=\"item-operation\" data-operation=\"${escapeAttr(operation.id)}\" data-revision=\"${escapeAttr(view.revision)}\">\n ${choices(ui, operation)}${operation.confirmation ? `<label><input type=\"checkbox\" name=\"confirmed\" required>${escapeHTML(operation.confirmation)}</label>` : ''}\n <button type=\"submit\"${operation.enabled ? '' : ' disabled'}>${escapeHTML(operation.label)}</button></form>${configuration(ui, view, operation)}</li>`;\n}\nfunction choices(ui: KanbanPresentation, operation: KanbanItemOperation): string {\n return (operation.fields ?? []).map(field => `<label>${escapeHTML(field.label)}<select name=\"choice-${escapeAttr(field.id)}\" required>\n <option value=\"\">${escapeHTML(ui.text('operationChoose'))}</option>${field.choices.map(choice => `<option value=\"${escapeAttr(choice.id)}\"${field.selected === choice.id ? ' selected' : ''}>${escapeHTML(choice.label + (choice.detail ? ' · ' + choice.detail : ''))}</option>`).join('')}</select></label>`).join('');\n}\nfunction configuration(ui: KanbanPresentation, view: KanbanItemOperations, operation: KanbanItemOperation): string {\n if (!operation.configurable) return '';\n return `<details><summary>${escapeHTML(ui.text('operationConfigure'))}</summary><form data-form=\"operation-binding\" data-operation=\"${escapeAttr(operation.id)}\" data-revision=\"${escapeAttr(view.revision)}\">\n <label>${escapeHTML(ui.text('operationFlow'))}<select name=\"target\" required><option value=\"\">${escapeHTML(ui.text('operationChoose'))}</option>\n ${(view.targets ?? []).map(target => `<option value=\"${escapeAttr(JSON.stringify(target.reference))}\"${JSON.stringify(target.reference) === JSON.stringify(operation.binding) ? ' selected' : ''}>${escapeHTML(target.label)}</option>`).join('')}</select></label>\n <button type=\"submit\">${escapeHTML(ui.text('save'))}</button>${view.targets?.length ? '' : `<p>${escapeHTML(ui.text('operationNoFlows'))}</p>`}</form></details>`;\n}\n","import type {KanbanWorkResource} from '../contracts';\nimport type {KanbanPresentation} from './ports';\nimport {escapeHTML, escapeAttr} from './html';\n\nexport function workResourcesView(ui: KanbanPresentation, resources: KanbanWorkResource[]): string {\n if (!resources.length) return `<p>${escapeHTML(ui.text('none'))}</p>`;\n return resources.map(resource => `<article class=\"kanban__work-resource\" data-work-resource=\"${escapeAttr(resource.id)}\">\n <strong>${escapeHTML(resource.title)}</strong>${copy(ui, resource.title, ui.text('copyWorkResourceName'))} · ${escapeHTML(ui.text('workResourceState.' + resource.state))}\n <p><code>${escapeHTML(resource.location)}</code>${copy(ui, resource.location, ui.text('copyWorkResourcePath'))}</p>\n <dl>${resource.properties.map(row => `<dt>${escapeHTML(row.label)}</dt><dd>${escapeHTML(row.value)}${copy(ui, row.value, ui.text('copyValue') + ' · ' + row.label)}</dd>`).join('')}</dl>\n ${resource.reasons.map(reason => `<p>${escapeHTML(reasonLabel(ui, reason))}</p>`).join('')}\n <button type=\"button\" data-action=\"cleanup-work-resource\" data-resource=\"${escapeAttr(resource.id)}\" data-version=\"${escapeAttr(resource.version)}\" ${resource.canCleanup ? '' : 'disabled'}>${escapeHTML(ui.text('cleanupWorkResource'))}</button>\n </article>`).join('');\n}\nfunction copy(ui: KanbanPresentation, value: string, label: string): string {\n return `<button type=\"button\" class=\"kanban__copy-button\" data-action=\"copy-resource\" data-copy=\"${escapeAttr(value)}\" title=\"${escapeAttr(label)}\" aria-label=\"${escapeAttr(label)}\">${ui.icons.copy ?? escapeHTML(ui.text('copyValue'))}</button>`;\n}\nfunction reasonLabel(ui: KanbanPresentation, reason: string): string {\n const key = 'workResourceReason.' + reason, label = ui.text(key); return label.includes(key) ? reason : label;\n}\n","import type {KanbanPresentation} from './ports';\nimport {boardView, catalogView, navigation, button} from './view';\nimport {detailView, itemEventsView, newBoardForm, newItemForm} from './forms';\nimport {recordLinks, recordsView, recordDetail, recordForm} from './context-view';\nimport {executionSessionView, executionBadge} from './session-view';\nimport {copyReference} from './references';\n\nexport function createRenderer(ui: KanbanPresentation) {\n const bind = <Args extends unknown[], Result>(fn: (ui: KanbanPresentation, ...args: Args) => Result) => (...args: Args): Result => fn(ui, ...args);\n return {label: ui.text.bind(ui),\n boardView: bind(boardView),\n catalogView: bind(catalogView),\n navigation: bind(navigation),\n button: bind(button),\n detailView: bind(detailView),\n itemEventsView: bind(itemEventsView),\n newBoardForm: bind(newBoardForm),\n newItemForm: bind(newItemForm),\n recordLinks: bind(recordLinks),\n recordsView: bind(recordsView),\n recordDetail: bind(recordDetail),\n recordForm: bind(recordForm),\n executionSessionView: bind(executionSessionView),\n executionBadge: bind(executionBadge),\n copyReference: bind(copyReference)\n };\n}\n","import type {KanbanBoardSummary, KanbanCommand, KanbanEstimate, KanbanItem, KanbanPlanning, KanbanPrincipal} from '../contracts';\nimport {randomUUID} from '../utils';\nexport const value = (data: FormData, key: string) => String(data.get(key) ?? '').trim();\nexport function readPlan(data: FormData, previous?: KanbanPlanning): KanbanPlanning {\n const area = value(data, 'area'), milestone = value(data, 'milestone'), parentId = value(data, 'parentId'), dueAt = value(data, 'dueAt');\n return {kind: value(data, 'kind'), ...(previous?.execution ? {execution:previous.execution} : {}), priority: Number(value(data, 'priority')), references: previous?.references ?? [],\n dependencies: [...new Set(data.getAll('dependencies').flatMap(value => String(value).split(',')).map(id => id.trim()).filter(Boolean))],\n ...(area ? {area} : {}), ...(milestone ? {milestone} : {}), ...(parentId ? {parentId} : {}), ...(dueAt ? {dueAt: new Date(dueAt).getTime()} : {})};\n}\nexport function boardInput(data: FormData, actor: KanbanPrincipal, id: string) {\n return {id, title: value(data, 'title'), scope: {kind: 'system' as const},\n members: [{...actor, responsibility: 'Manage personal work', roles: ['worker', 'reviewer', 'coordinator'] as Array<'worker' | 'reviewer' | 'coordinator'>}],\n policy: {requireEstimate: data.has('requireEstimate'), review: 'assignee' as const}};\n}\nexport function formCommand(kind: string, data: FormData, board: KanbanBoardSummary, item?: KanbanItem, id = randomUUID()): KanbanCommand {\n if (kind === 'item') return {type: 'create-item', ...(value(data, 'launchMode') ? {launch: {mode: value(data, 'launchMode') as 'automatic' | 'manual'}} : {}), item: {id, title: value(data, 'title'), responsibility: value(data, 'responsibility'), acceptance: value(data, 'acceptance'),...(value(data,'itemResource')?{externalRefs:[JSON.parse(value(data,'itemResource'))]}:{})}, planning: readPlan(data)};\n if (kind === 'record') {\n const sessionId = value(data, 'sourceSession'), messageId = value(data, 'sourceMessage'), revision = value(data, 'sourceRevision');\n if ([sessionId, messageId, revision].some(Boolean) && ![sessionId, messageId, revision].every(Boolean)) throw Object.assign(new Error('Missing source fields'), {code: 'SOURCE_FIELDS_REQUIRED'});\n return {type: 'append-record', record: {id, kind: value(data, 'kind') as 'log' | 'decision', title: value(data, 'title'), content: value(data, 'content'),\n externalRefs: data.getAll('recordResources').map(value => JSON.parse(String(value))),\n itemIds: [...new Set(data.getAll('itemIds').flatMap(value => String(value).split(',')).map(id => id.trim()).filter(Boolean))],\n sources: sessionId ? [{sessionId, messageId, revision, role: 'user'}] : [],\n ...(value(data, 'rationale') ? {rationale: value(data, 'rationale')} : {}), ...(value(data, 'supersedes') ? {supersedes: value(data, 'supersedes')} : {})}};\n }\n if (!item) throw new Error('ITEM_NOT_FOUND');\n const itemId = item.id, token = item.owner?.token ?? '';\n if (kind === 'edit-item') return {type: 'edit-item', itemId, title: value(data, 'title'), responsibility: value(data, 'responsibility'), acceptance: value(data, 'acceptance')};\n if (kind === 'amendment') return {type: 'review-amendment', itemId, amendmentId: value(data, 'amendmentId'), accept: value(data, 'decision') === 'accept', reason: value(data, 'reason') || undefined};\n if (kind === 'claim') return {type: 'claim', itemId, ...(board.policy.requireEstimate !== false ? {estimate: estimate(data)} : {})};\n if (kind === 'close-plan') return {type: 'close-plan', itemId, resultRef: value(data, 'resultRef')};\n if (kind === 'submit') return {type: 'submit', itemId, token, resultRef: value(data, 'resultRef')};\n if (kind === 'plan') return {type: 'plan-item', itemId, planning: readPlan(data, item.planning)};\n if (kind === 'cancel') return {type: 'cancel-item', itemId, reason: value(data, 'reason')};\n if (kind === 'release') return {type: 'release-item', itemId, token, next: 'backlog', reason: value(data, 'reason')};\n if (kind === 'block') return {type: 'block-item', itemId, token, reason: value(data, 'reason')};\n if (kind === 'session') return {type: 'register-session', itemId, sessionId: value(data, 'sessionId'), role: 'observer', subscribed: true};\n if (kind === 'message') return {type: 'message', itemId, message: value(data, 'message'), recipientSessionIds: item.participants.filter(p => p.subscribed).map(p => p.sessionId)};\n throw new Error('UNKNOWN_COMMAND');\n}\nfunction estimate(data: FormData): KanbanEstimate {\n return {complexity: Number(value(data, 'complexity')), workUnits: Number(value(data, 'workUnits')), durationMs: Number(value(data, 'duration')) * 60000, confidence: value(data, 'confidence') as KanbanEstimate['confidence']};\n}\nexport function actionCommand(action: string, item: KanbanItem): KanbanCommand {\n const itemId = item.id, token = item.owner?.token ?? '';\n if (action === 'ready' || action === 'defer') return {type: 'set-stage', itemId, stage: action === 'ready' ? 'open' : 'backlog'};\n if (action === 'publish') return {type: 'publish-proposal', itemId};\n if (action === 'renew') return {type: 'renew', itemId, token};\n if (action === 'accept' || action === 'reject') return {type: 'review', itemId, accept: action === 'accept'};\n if (action === 'archive') return {type: 'archive-items', itemIds: [itemId], reason: 'manual'};\n if (action === 'restore') return {type: 'restore-item', itemId};\n if (action === 'unblock') return {type: 'unblock-item', itemId, token};\n throw new Error('UNKNOWN_COMMAND');\n}\n"]}