@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 +1 @@
1
- {"version":3,"sources":["../src/scan-context.ts","../src/discovery.ts"],"sourcesContent":["import type {KanbanBoard, KanbanItem, KanbanScanInput, KanbanScanText} from './contracts';\nimport type {KanbanDiscoveryOptions} from './scan-ports';\nimport type {KanbanState} from './ports';\nimport {requireValue} from './rules';\n\n/** A bounded working set, with explicit omitted counts instead of pretending full coverage. */\nexport function scanContext(board: KanbanBoard, state: KanbanState, sessions: KanbanScanText[], options: KanbanDiscoveryOptions, runId: string): KanbanScanInput {\n requireValue(state.board.revision === board.revision, 'SCAN_BOARD_CHANGED');\n const archived = state.archiveIndex ?? [];\n const input: KanbanScanInput = {runId, executorId: options.executor.identity, board: {...board, items: []}, sessions,\n connection: structuredClone(options.policy.connection), projectContext: {totalRecords: state.records?.filter(record => record.status === 'confirmed').length ?? 0, omittedRecords: state.records?.filter(record => record.status === 'confirmed').length ?? 0, excludedRecords: state.records?.filter(record => record.status !== 'confirmed').length ?? 0, records: []}, context: {totalItems: board.items.length, omittedItems: board.items.length,\n archivedTotal: archived.length, archivedItems: []}};\n const bytes = () => new TextEncoder().encode(JSON.stringify(input)).length;\n requireValue(bytes() <= options.policy.maxInputBytes, 'SCAN_INPUT_LIMIT');\n const sessionIds = new Set(sessions.map(s => s.session.id));\n addRecords(input, state, sessionIds, bytes, options.policy.maxInputBytes);\n const content = sessions.flatMap(s => s.messages.map(m => m.text.toLowerCase())).join('\\n');\n const ordered = [...board.items].sort((a, b) => score(a, sessionIds, content) - score(b, sessionIds, content)\n || (a.planning?.priority ?? 2) - (b.planning?.priority ?? 2) || a.id.localeCompare(b.id));\n for (const item of ordered) {\n if (input.board.items.length >= 50) break;\n input.board.items.push(item); input.context!.omittedItems--;\n if (bytes() > options.policy.maxInputBytes) {input.board.items.pop(); input.context!.omittedItems++;}\n }\n const history = [...archived].sort((a, b) => Number(b.sessionIds.some(id => sessionIds.has(id))) - Number(a.sessionIds.some(id => sessionIds.has(id)))\n || (b.closedAt ?? b.updatedAt) - (a.closedAt ?? a.updatedAt));\n for (const item of history.slice(0, 20)) {\n input.context!.archivedItems.push(item);\n if (bytes() > options.policy.maxInputBytes) input.context!.archivedItems.pop();\n }\n return structuredClone(input);\n}\nfunction addRecords(input: KanbanScanInput, state: KanbanState, sessionIds: Set<string>, bytes: () => number, maxBytes: number): void {\n const records = (state.records ?? []).filter(record => record.status === 'confirmed').sort((a, b) => Number(b.kind === 'decision') - Number(a.kind === 'decision')\n || Number(b.sources.some(source => sessionIds.has(source.sessionId))) - Number(a.sources.some(source => sessionIds.has(source.sessionId))) || b.createdAt - a.createdAt);\n for (const record of records.slice(0, 10)) {\n input.projectContext!.records.push(record); input.projectContext!.omittedRecords--;\n if (bytes() > maxBytes) {input.projectContext!.records.pop(); input.projectContext!.omittedRecords++;}\n }\n}\n\nfunction score(item: KanbanItem, sessions: Set<string>, content: string): number {\n if (item.sources?.some(source => sessions.has(source.sessionId)) || content.includes(item.title.toLowerCase())) return 0;\n if (item.status === 'claimed' || item.status === 'review') return 1;\n if (item.status === 'open') return 2;\n return item.status === 'completed' || item.status === 'cancelled' ? 4 : 3;\n}\n","import {discoveryState} from './discovery-state';\nimport {randomUUID} from './utils';\nimport type {KanbanDiscoveryChange, KanbanRequest, KanbanScanInput, KanbanScanSession, KanbanScanText} from './contracts';\nimport {KanbanError, KanbanScanNotStartedError} from './ports';\nimport type {KanbanDiscoveryOptions, KanbanDiscoveryState} from './scan-ports';\nimport {scanContext} from './scan-context';\nimport {identity, positive, requireValue, text as validateText} from './rules';\n\nexport interface KanbanDiscoveryHandle {scan(): Promise<void>; tick(): Promise<void>; reset(runId: string): Promise<void>; quiesce(): Promise<void>; dispose(): Promise<void>}\nexport function createKanbanDiscovery(options: KanbanDiscoveryOptions): KanbanDiscoveryHandle {return new Discovery(options);}\nclass Discovery {\n private readonly now;\n private readonly id;\n private readonly token;\n private active?: Promise<void>;\n private stopping = false;\n private readonly resets = new Set<Promise<void>>();\n private stage = 'prepare';\n constructor(private readonly options: KanbanDiscoveryOptions) {\n this.now = options.now ?? Date.now; this.id = options.id ?? randomUUID; this.token = this.id();\n for (const value of [options.policy.idleMs, options.policy.maxSessions, options.policy.maxInputBytes, options.policy.leaseMs]) positive(value);\n identity(options.policy.connection.connectionId);\n }\n scan = (): Promise<void> => {\n if (this.stopping) return Promise.reject(new KanbanError('SERVICE_STOPPING'));\n if (this.active) return this.active;\n const operationId = this.id(), started = this.now();\n this.active = this.run().catch(async error => {\n if (error instanceof KanbanError && ['SOURCE_CHANGED', 'SCAN_BOARD_CHANGED', 'REVISION_CONFLICT'].includes(error.code)) {\n await this.settleInvalidatedRun();\n await this.update('invalidate', state => {if (state.pending?.token === this.token) {\n state.lastRun = {runId: state.pending.runId, outcome: 'invalidated', at: this.now(), code: error.code}; delete state.pending;\n }});\n }\n throw error;\n }).then(() => this.log(operationId, started, 'success'), error => {\n this.log(operationId, started, 'failure', errorCode(error)); throw error;\n }).finally(() => {this.active = undefined;});\n return this.active;\n };\n tick = (): Promise<void> => this.scan();\n async quiesce(): Promise<void> {\n this.stopping = true;\n await Promise.allSettled([this.active, ...this.resets]);\n }\n dispose = (): Promise<void> => this.quiesce();\n reset = (runId: string): Promise<void> => {\n if (this.stopping) return Promise.reject(new KanbanError('SERVICE_STOPPING'));\n const work = this.resetRun(runId).finally(() => {this.resets.delete(work);});\n this.resets.add(work); return work;\n };\n private async resetRun(runId: string): Promise<void> {\n const pending = discoveryState(await this.options.store.read(this.options.boardId))?.pending;\n requireValue(pending?.runId === runId, 'SCAN_CHANGED');\n await this.options.authorize('commit', pending.input);\n requireValue(pending.token === this.token || pending.leaseUntil <= this.now(), 'SCAN_BUSY');\n if (pending.executionStarted && !pending.executionSettled) requireValue(await this.options.executor.settled?.(pending.input), 'SCAN_EXECUTION_UNSETTLED');\n await this.update('reset', state => {\n requireValue(state.pending?.runId === runId && state.pending.token === pending.token, 'SCAN_CHANGED');\n state.lastRun = {runId, outcome: 'invalidated', at: this.now(), code: 'SCAN_MANUAL_RESET'}; delete state.pending;\n });\n }\n private async run(): Promise<void> {\n await this.options.authorize('read');\n const board = await this.options.service.read(this.options.boardId);\n requireValue(matchesScope(board.scope, this.options.source), 'SOURCE_PROJECT_MISMATCH');\n requireValue(this.options.source.kind === this.options.policy.connection.kind || this.options.policy.connection.allowContentTransfer, 'CONTENT_TRANSFER_NOT_ALLOWED');\n const stored = await this.options.store.read(board.id);\n let pending = discoveryState(stored)?.pending;\n if (pending && pending.token !== this.token && pending.leaseUntil > this.now()) return;\n if (!pending) {\n const sessions = await this.collect(); if (!sessions.length) return;\n const current = await this.options.store.read(board.id); requireValue(current, 'BOARD_NOT_FOUND');\n const input = scanContext(board, current, sessions, this.options, this.id());\n requireValue(new TextEncoder().encode(JSON.stringify(input)).length <= this.options.policy.maxInputBytes, 'SCAN_INPUT_LIMIT');\n pending = {runId: input.runId, autoCreate: this.options.policy.autoCreate && (input.projectContext?.omittedRecords ?? 0) === 0 && input.context!.omittedItems === 0 && input.context!.archivedItems.length === input.context!.archivedTotal, token: this.token, leaseUntil: this.now() + this.options.policy.leaseMs, input};\n }\n const selected = pending;\n await this.update('claim', state => {\n requireValue(!state.pending || state.pending.token === this.token || state.pending.leaseUntil <= this.now(), 'SCAN_BUSY');\n if (state.pending) requireValue(state.pending.runId === selected.runId, 'SCAN_CHANGED');\n const restoring = selected.token !== this.token;\n state.pending = {...selected, ...(restoring ? {request: undefined} : {}), token: this.token, leaseUntil: this.now() + this.options.policy.leaseMs};\n });\n await this.process();\n }\n private async collect(): Promise<KanbanScanText[]> {\n this.stage = 'source.list';\n const sessions = await this.options.source.list(this.options.policy.includeArchived);\n for (const session of sessions) {\n identity(session.id); requireValue(typeof session.revision === 'string' && session.revision.length > 0 && session.revision.length <= 16000, 'INVALID_SOURCE_REVISION');\n }\n requireValue(new Set(sessions.map(s => s.id)).size === sessions.length, 'DUPLICATE_SOURCE_SESSION');\n await this.observe(sessions);\n const state = discoveryState(await this.options.store.read(this.options.boardId))!;\n const result: KanbanScanText[] = []; let bytes = 0;\n for (const session of sessions.sort((a, b) => (a.updatedAt ?? 0) - (b.updatedAt ?? 0) || a.id.localeCompare(b.id))) {\n if (!this.eligible(session, state)) continue;\n const current = await this.options.source.inspect(session.id);\n if (!this.eligible(current, state) || current.revision !== session.revision) continue;\n this.stage = 'source.read';\n const text = await this.readCandidate(current, Math.floor(this.options.policy.maxInputBytes * 0.7) - bytes);\n if (!text) continue;\n requireValue(text.session.id === current.id && text.session.revision === current.revision, 'SOURCE_CHANGED');\n if (!text.complete) continue;\n const size = new TextEncoder().encode(JSON.stringify(text)).length;\n if (bytes + size > this.options.policy.maxInputBytes * 0.7) break;\n bytes += size; result.push(text); if (result.length >= this.options.policy.maxSessions) break;\n }\n return result;\n }\n private async readCandidate(session: KanbanScanSession, maxBytes: number): Promise<KanbanScanText | null> {\n const started = this.now();\n try {\n const result = await this.options.source.read(session, maxBytes);\n if (!result.complete) this.log(this.id(), started, 'failure', 'SOURCE_PROVENANCE_UNKNOWN', session.id);\n return result;\n } catch (error) {\n const code = errorCode(error);\n if (!['SCAN_INPUT_LIMIT', 'SOURCE_CHANGED', 'HARNESS_DIGEST_CHANGED'].includes(code)) throw error;\n this.log(this.id(), started, 'failure', code, session.id); return null;\n }\n }\n private eligible(session: KanbanScanSession, state: KanbanDiscoveryState): boolean {\n const quiet = state.observations?.[session.id];\n return !session.scannerOwned && (this.options.policy.includeArchived || !session.archived)\n && session.activity === 'idle' && session.updatedAt !== null && session.updatedAt <= this.now()\n && quiet?.revision === session.revision && this.now() - quiet.idleSince >= this.options.policy.idleMs\n && state.checkpoints[session.id] !== session.revision;\n }\n private async observe(sessions: KanbanScanSession[]): Promise<void> {\n await this.update('observe', state => {\n state.observations ??= {};\n for (const session of sessions) {\n if (session.activity !== 'idle' || session.scannerOwned) {delete state.observations[session.id]; continue;}\n const old = state.observations[session.id];\n if (old?.revision !== session.revision) state.observations[session.id] = {revision: session.revision, idleSince: this.now()};\n }\n });\n }\n private async process(): Promise<void> {\n let pending = discoveryState(await this.options.store.read(this.options.boardId))!.pending!;\n requireValue(pending.input.executorId === this.options.executor.identity, 'SCANNER_EXECUTOR_CHANGED');\n await this.checkSources(pending.input);\n if (!pending.changes) {\n await this.options.authorize('execute', pending.input);\n this.stage = 'analyzer.execute';\n await this.updateOwned('execute', state => {state.pending!.executionStarted = true;});\n const output = await this.execute(pending.input), result = Array.isArray(output) ? {changes: output} : output;\n await this.updateOwned('settled', state => {state.pending!.executionSettled = true;});\n validateChanges(result.changes, pending.input); validateRecords(result.records ?? [], pending.input, result.changes);\n await this.updateOwned('result', state => {state.pending!.changes = result.changes; state.pending!.records = result.records;});\n }\n pending = discoveryState(await this.options.store.read(this.options.boardId))!.pending!;\n await this.options.authorize('commit', pending.input); await this.checkSources(pending.input);\n if (!pending.request) {\n const board = await this.options.service.read(this.options.boardId);\n requireValue(board.revision === pending.input.board.revision, 'SCAN_BOARD_CHANGED');\n const request: KanbanRequest = {operationId: `discovery:${pending.runId}`, expectedRevision: board.revision,\n command: {type: 'apply-discovery', batch: {runId: pending.runId, leaseToken: this.token, changes: pending.changes!, ...(pending.records ? {records: pending.records} : {}), autoCreate: pending.autoCreate,\n checkpoints: pending.input.sessions.map(s => ({sessionId: s.session.id, revision: s.session.revision}))}}};\n await this.updateOwned('prepare', state => {state.pending!.request = request;});\n pending.request = request;\n }\n this.stage = 'board.commit';\n await this.options.service.execute(this.options.boardId, pending.request);\n }\n private async execute(input: KanbanScanInput): Promise<KanbanDiscoveryChange[] | import('./contracts').KanbanDiscoveryResult> {\n try {return await this.options.executor.run(structuredClone(input));}\n catch (error) {\n if (error instanceof KanbanScanNotStartedError) await this.updateOwned('not-started', state => {state.pending!.executionSettled = true;});\n throw error;\n }\n }\n private async settleInvalidatedRun(): Promise<void> {\n const pending = discoveryState(await this.options.store.read(this.options.boardId))?.pending;\n if (!pending || pending.token !== this.token || !pending.executionStarted || pending.executionSettled) return;\n requireValue(await this.options.executor.settled?.(pending.input), 'SCAN_EXECUTION_UNSETTLED');\n }\n private async checkSources(input: KanbanScanInput): Promise<void> {\n for (const source of input.sessions) {\n const current = await this.options.source.inspect(source.session.id);\n requireValue(current.revision === source.session.revision && current.activity === 'idle' && !current.scannerOwned, 'SOURCE_CHANGED');\n }\n }\n private async updateOwned(stage: string, mutate: (state: KanbanDiscoveryState) => void): Promise<void> {\n await this.update(stage, state => {\n requireValue(state.pending?.token === this.token && state.pending.leaseUntil > this.now(), 'SCAN_LEASE_EXPIRED'); mutate(state);\n });\n }\n private async update(stage: string, mutate: (state: KanbanDiscoveryState) => void): Promise<void> {\n const operation = this.id();\n await this.options.store.update(this.options.boardId, operation, `discovery:${stage}:${operation}`, state => {\n requireValue(state, 'BOARD_NOT_FOUND'); state.discovery ??= {checkpoints: {}}; mutate(state.discovery as KanbanDiscoveryState); return state;\n });\n }\n private log(operationId: string, started: number, result: 'success' | 'failure', code?: string, sessionId?: string): void {\n try {this.options.log({operationId, boardId: this.options.boardId, sessionId, stage: `discovery.${this.stage}`, result, code, durationMs: this.now() - started});}\n catch { /* Diagnostics cannot affect durable execution. */ }\n }\n}\nfunction validateChanges(changes: KanbanDiscoveryChange[], input: KanbanScanInput): void {\n requireValue(Array.isArray(changes) && changes.length <= 50, 'SCAN_CHANGE_LIMIT');\n const ids = new Set<string>();\n for (const change of changes) {\n requireValue(change && ['create', 'update', 'link'].includes(change.action) && !ids.has(change.itemId), 'INVALID_CHANGE'); ids.add(change.itemId); identity(change.itemId);\n if (change.action !== 'create') requireValue(input.board.items.some(i => i.id === change.itemId), 'UNKNOWN_CONTEXT_ITEM');\n validateText(change.title); validateText(change.responsibility); validateText(change.acceptance);\n requireValue(change.eligibleAgentIds === undefined || Array.isArray(change.eligibleAgentIds), 'INVALID_ELIGIBILITY');\n change.eligibleAgentIds?.forEach(identity);\n requireValue(change.eligiblePrincipals === undefined || Array.isArray(change.eligiblePrincipals), 'INVALID_ELIGIBILITY');\n requireValue(Array.isArray(change.sources) && change.sources.length > 0 && change.sources.length <= 100 && change.sources.some(s => s.role === 'user'), 'USER_EVIDENCE_REQUIRED');\n for (const ref of change.sources) {\n const source = input.sessions.find(s => s.session.id === ref.sessionId && s.session.revision === ref.revision);\n requireValue(source?.messages.some(m => m.id === ref.messageId && m.role === ref.role), 'INVALID_SOURCE_REFERENCE');\n }\n }\n}\n\nfunction validateRecords(records: import('./contracts').KanbanRecordInput[], input: KanbanScanInput, changes: KanbanDiscoveryChange[]): void {\n requireValue(Array.isArray(records) && records.length <= 20, 'SCAN_RECORD_LIMIT');\n const ids = new Set<string>();\n for (const record of records) {\n identity(record.id); requireValue(!ids.has(record.id), 'INVALID_RECORD_ID'); ids.add(record.id);\n validateText(record.title); validateText(record.content);\n requireValue(['log', 'decision'].includes(record.kind), 'INVALID_RECORD_KIND');\n if (record.kind === 'decision') validateText(record.rationale ?? '');\n requireValue(Array.isArray(record.itemIds) && record.itemIds.every(id => input.board.items.some(item => item.id === id) || changes.some(change => change.action === 'create' && change.itemId === id)), 'UNKNOWN_CONTEXT_ITEM');\n if (record.supersedes) requireValue(input.projectContext?.records.some(old => old.id === record.supersedes && old.kind === 'decision'), 'UNKNOWN_CONTEXT_RECORD');\n requireValue(Array.isArray(record.sources) && record.sources.length > 0 && record.sources.length <= 100\n && record.sources.some(source => source.role === 'user'), 'USER_EVIDENCE_REQUIRED');\n for (const ref of record.sources) {\n const source = input.sessions.find(s => s.session.id === ref.sessionId && s.session.revision === ref.revision);\n requireValue(source?.messages.some(message => message.id === ref.messageId && message.role === ref.role), 'INVALID_SOURCE_REFERENCE');\n }\n }\n}\n\nfunction errorCode(error: unknown): string {\n const value = error as {code?: unknown; remoteCode?: unknown};\n const code = value?.remoteCode ?? value?.code;\n return typeof code === 'string' && /^[A-Z][A-Z0-9_]{0,79}$/.test(code) ? code : 'SCAN_PORT_FAILURE';\n}\n\nfunction matchesScope(scope: import('./contracts').KanbanBoard['scope'], source: import('./scan-ports').KanbanScanSource): boolean {\n const expected = source.scope ?? (source.projectId ? {kind: 'project' as const, projectId: source.projectId} : undefined);\n if (!expected || scope.kind !== expected.kind) return false;\n if (scope.kind === 'system') return true;\n if (scope.kind === 'project' && expected.kind === 'project') return scope.projectId === expected.projectId;\n return scope.kind === 'collection' && expected.kind === 'collection' && scope.namespace === expected.namespace && scope.id === expected.id;\n}\n"],"mappings":"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","names":["text"]}
1
+ {"version":3,"sources":["../src/scan-context.ts","../src/discovery.ts"],"sourcesContent":["import type {KanbanBoard, KanbanItem, KanbanScanInput, KanbanScanText} from './contracts';\nimport type {KanbanDiscoveryOptions} from './scan-ports';\nimport type {KanbanState} from './ports';\nimport {requireValue} from './rules';\n\n/** A bounded working set, with explicit omitted counts instead of pretending full coverage. */\nexport function scanContext(board: KanbanBoard, state: KanbanState, sessions: KanbanScanText[], options: KanbanDiscoveryOptions, runId: string): KanbanScanInput {\n requireValue(state.board.revision === board.revision, 'SCAN_BOARD_CHANGED');\n const archived = state.archiveIndex ?? [];\n const input: KanbanScanInput = {runId, executorId: options.executor.identity, board: {...board, items: []}, sessions,\n connection: structuredClone(options.policy.connection), projectContext: {totalRecords: state.records?.filter(record => record.status === 'confirmed').length ?? 0, omittedRecords: state.records?.filter(record => record.status === 'confirmed').length ?? 0, excludedRecords: state.records?.filter(record => record.status !== 'confirmed').length ?? 0, records: []}, context: {totalItems: board.items.length, omittedItems: board.items.length,\n archivedTotal: archived.length, archivedItems: []}};\n const bytes = () => new TextEncoder().encode(JSON.stringify(input)).length;\n requireValue(bytes() <= options.policy.maxInputBytes, 'SCAN_INPUT_LIMIT');\n const sessionIds = new Set(sessions.map(s => s.session.id));\n addRecords(input, state, sessionIds, bytes, options.policy.maxInputBytes);\n const content = sessions.flatMap(s => s.messages.map(m => m.text.toLowerCase())).join('\\n');\n const ordered = [...board.items].sort((a, b) => score(a, sessionIds, content) - score(b, sessionIds, content)\n || (a.planning?.priority ?? 2) - (b.planning?.priority ?? 2) || a.id.localeCompare(b.id));\n for (const item of ordered) {\n if (input.board.items.length >= 50) break;\n input.board.items.push(item); input.context!.omittedItems--;\n if (bytes() > options.policy.maxInputBytes) {input.board.items.pop(); input.context!.omittedItems++;}\n }\n const history = [...archived].sort((a, b) => Number(b.sessionIds.some(id => sessionIds.has(id))) - Number(a.sessionIds.some(id => sessionIds.has(id)))\n || (b.closedAt ?? b.updatedAt) - (a.closedAt ?? a.updatedAt));\n for (const item of history.slice(0, 20)) {\n input.context!.archivedItems.push(item);\n if (bytes() > options.policy.maxInputBytes) input.context!.archivedItems.pop();\n }\n return structuredClone(input);\n}\nfunction addRecords(input: KanbanScanInput, state: KanbanState, sessionIds: Set<string>, bytes: () => number, maxBytes: number): void {\n const records = (state.records ?? []).filter(record => record.status === 'confirmed').sort((a, b) => Number(b.kind === 'decision') - Number(a.kind === 'decision')\n || Number(b.sources.some(source => sessionIds.has(source.sessionId))) - Number(a.sources.some(source => sessionIds.has(source.sessionId))) || b.createdAt - a.createdAt);\n for (const record of records.slice(0, 10)) {\n input.projectContext!.records.push(record); input.projectContext!.omittedRecords--;\n if (bytes() > maxBytes) {input.projectContext!.records.pop(); input.projectContext!.omittedRecords++;}\n }\n}\n\nfunction score(item: KanbanItem, sessions: Set<string>, content: string): number {\n if (item.sources?.some(source => sessions.has(source.sessionId)) || content.includes(item.title.toLowerCase())) return 0;\n if (item.status === 'claimed' || item.status === 'review') return 1;\n if (item.status === 'open') return 2;\n return item.status === 'completed' || item.status === 'cancelled' ? 4 : 3;\n}\n","import {discoveryState} from './discovery-state';\nimport {randomUUID} from './utils';\nimport type {KanbanDiscoveryChange, KanbanRequest, KanbanScanInput, KanbanScanSession, KanbanScanText} from './contracts';\nimport {KanbanError, KanbanScanNotStartedError} from './ports';\nimport type {KanbanDiscoveryOptions, KanbanDiscoveryState} from './scan-ports';\nimport {scanContext} from './scan-context';\nimport {identity, positive, requireValue, text as validateText} from './rules';\n\nexport interface KanbanDiscoveryHandle {scan(): Promise<void>; tick(): Promise<void>; reset(runId: string): Promise<void>; quiesce(): Promise<void>; dispose(): Promise<void>}\nexport function createKanbanDiscovery(options: KanbanDiscoveryOptions): KanbanDiscoveryHandle {return new Discovery(options);}\nclass Discovery {\n private readonly now;\n private readonly id;\n private readonly token;\n private active?: Promise<void>;\n private stopping = false;\n private readonly resets = new Set<Promise<void>>();\n private stage = 'prepare';\n constructor(private readonly options: KanbanDiscoveryOptions) {\n this.now = options.now ?? Date.now; this.id = options.id ?? randomUUID; this.token = this.id();\n for (const value of [options.policy.idleMs, options.policy.maxSessions, options.policy.maxInputBytes, options.policy.leaseMs]) positive(value);\n identity(options.policy.connection.connectionId);\n }\n scan = (): Promise<void> => {\n if (this.stopping) return Promise.reject(new KanbanError('SERVICE_STOPPING'));\n if (this.active) return this.active;\n const operationId = this.id(), started = this.now();\n this.active = this.run().catch(async error => {\n if (error instanceof KanbanError && ['SOURCE_CHANGED', 'SCAN_BOARD_CHANGED', 'REVISION_CONFLICT'].includes(error.code)) {\n await this.settleInvalidatedRun();\n await this.update('invalidate', state => {if (state.pending?.token === this.token) {\n state.lastRun = {runId: state.pending.runId, outcome: 'invalidated', at: this.now(), code: error.code}; delete state.pending;\n }});\n }\n throw error;\n }).then(() => this.log(operationId, started, 'success'), error => {\n this.log(operationId, started, 'failure', errorCode(error)); throw error;\n }).finally(() => {this.active = undefined;});\n return this.active;\n };\n tick = (): Promise<void> => this.scan();\n async quiesce(): Promise<void> {\n this.stopping = true;\n await Promise.allSettled([this.active, ...this.resets]);\n }\n dispose = (): Promise<void> => this.quiesce();\n reset = (runId: string): Promise<void> => {\n if (this.stopping) return Promise.reject(new KanbanError('SERVICE_STOPPING'));\n const work = this.resetRun(runId).finally(() => {this.resets.delete(work);});\n this.resets.add(work); return work;\n };\n private async resetRun(runId: string): Promise<void> {\n const pending = discoveryState(await this.options.store.read(this.options.boardId))?.pending;\n requireValue(pending?.runId === runId, 'SCAN_CHANGED');\n await this.options.authorize('commit', pending.input);\n requireValue(pending.token === this.token || pending.leaseUntil <= this.now(), 'SCAN_BUSY');\n if (pending.executionStarted && !pending.executionSettled) requireValue(await this.options.executor.settled?.(pending.input), 'SCAN_EXECUTION_UNSETTLED');\n await this.update('reset', state => {\n requireValue(state.pending?.runId === runId && state.pending.token === pending.token, 'SCAN_CHANGED');\n state.lastRun = {runId, outcome: 'invalidated', at: this.now(), code: 'SCAN_MANUAL_RESET'}; delete state.pending;\n });\n }\n private async run(): Promise<void> {\n await this.options.authorize('read');\n const board = await this.options.service.read(this.options.boardId);\n requireValue(matchesScope(board.scope, this.options.source), 'SOURCE_PROJECT_MISMATCH');\n requireValue(this.options.source.kind === this.options.policy.connection.kind || this.options.policy.connection.allowContentTransfer, 'CONTENT_TRANSFER_NOT_ALLOWED');\n const stored = await this.options.store.read(board.id);\n let pending = discoveryState(stored)?.pending;\n if (pending && pending.token !== this.token && pending.leaseUntil > this.now()) return;\n if (!pending) {\n const sessions = await this.collect(); if (!sessions.length) return;\n const current = await this.options.store.read(board.id); requireValue(current, 'BOARD_NOT_FOUND');\n const input = scanContext(board, current, sessions, this.options, this.id());\n requireValue(new TextEncoder().encode(JSON.stringify(input)).length <= this.options.policy.maxInputBytes, 'SCAN_INPUT_LIMIT');\n pending = {runId: input.runId, autoCreate: this.options.policy.autoCreate && (input.projectContext?.omittedRecords ?? 0) === 0 && input.context!.omittedItems === 0 && input.context!.archivedItems.length === input.context!.archivedTotal, token: this.token, leaseUntil: this.now() + this.options.policy.leaseMs, input};\n }\n const selected = pending;\n await this.update('claim', state => {\n requireValue(!state.pending || state.pending.token === this.token || state.pending.leaseUntil <= this.now(), 'SCAN_BUSY');\n if (state.pending) requireValue(state.pending.runId === selected.runId, 'SCAN_CHANGED');\n const restoring = selected.token !== this.token;\n state.pending = {...selected, ...(restoring ? {request: undefined} : {}), token: this.token, leaseUntil: this.now() + this.options.policy.leaseMs};\n });\n await this.process();\n }\n private async collect(): Promise<KanbanScanText[]> {\n this.stage = 'source.list';\n const sessions = await this.options.source.list(this.options.policy.includeArchived);\n for (const session of sessions) {\n identity(session.id); requireValue(typeof session.revision === 'string' && session.revision.length > 0 && session.revision.length <= 16000, 'INVALID_SOURCE_REVISION');\n }\n requireValue(new Set(sessions.map(s => s.id)).size === sessions.length, 'DUPLICATE_SOURCE_SESSION');\n await this.observe(sessions);\n const state = discoveryState(await this.options.store.read(this.options.boardId))!;\n const result: KanbanScanText[] = []; let bytes = 0;\n for (const session of sessions.sort((a, b) => (a.updatedAt ?? 0) - (b.updatedAt ?? 0) || a.id.localeCompare(b.id))) {\n if (!this.eligible(session, state)) continue;\n const current = await this.options.source.inspect(session.id);\n if (!this.eligible(current, state) || current.revision !== session.revision) continue;\n this.stage = 'source.read';\n const text = await this.readCandidate(current, Math.floor(this.options.policy.maxInputBytes * 0.7) - bytes);\n if (!text) continue;\n requireValue(text.session.id === current.id && text.session.revision === current.revision, 'SOURCE_CHANGED');\n if (!text.complete) continue;\n const size = new TextEncoder().encode(JSON.stringify(text)).length;\n if (bytes + size > this.options.policy.maxInputBytes * 0.7) break;\n bytes += size; result.push(text); if (result.length >= this.options.policy.maxSessions) break;\n }\n return result;\n }\n private async readCandidate(session: KanbanScanSession, maxBytes: number): Promise<KanbanScanText | null> {\n const started = this.now();\n try {\n const result = await this.options.source.read(session, maxBytes);\n if (!result.complete) this.log(this.id(), started, 'failure', 'SOURCE_PROVENANCE_UNKNOWN', session.id);\n return result;\n } catch (error) {\n const code = errorCode(error);\n if (!['SCAN_INPUT_LIMIT', 'SOURCE_CHANGED', 'HARNESS_DIGEST_CHANGED'].includes(code)) throw error;\n this.log(this.id(), started, 'failure', code, session.id); return null;\n }\n }\n private eligible(session: KanbanScanSession, state: KanbanDiscoveryState): boolean {\n const quiet = state.observations?.[session.id];\n return !session.scannerOwned && (this.options.policy.includeArchived || !session.archived)\n && session.activity === 'idle' && session.updatedAt !== null && session.updatedAt <= this.now()\n && quiet?.revision === session.revision && this.now() - quiet.idleSince >= this.options.policy.idleMs\n && state.checkpoints[session.id] !== session.revision;\n }\n private async observe(sessions: KanbanScanSession[]): Promise<void> {\n await this.update('observe', state => {\n state.observations ??= {};\n for (const session of sessions) {\n if (session.activity !== 'idle' || session.scannerOwned) {delete state.observations[session.id]; continue;}\n const old = state.observations[session.id];\n if (old?.revision !== session.revision) state.observations[session.id] = {revision: session.revision, idleSince: this.now()};\n }\n });\n }\n private async process(): Promise<void> {\n let pending = discoveryState(await this.options.store.read(this.options.boardId))!.pending!;\n requireValue(pending.input.executorId === this.options.executor.identity, 'SCANNER_EXECUTOR_CHANGED');\n await this.checkSources(pending.input);\n if (!pending.changes) {\n await this.options.authorize('execute', pending.input);\n this.stage = 'analyzer.execute';\n await this.updateOwned('execute', state => {state.pending!.executionStarted = true;});\n const output = await this.execute(pending.input), result = Array.isArray(output) ? {changes: output} : output;\n await this.updateOwned('settled', state => {state.pending!.executionSettled = true;});\n validateChanges(result.changes, pending.input); validateRecords(result.records ?? [], pending.input, result.changes);\n await this.updateOwned('result', state => {state.pending!.changes = result.changes; state.pending!.records = result.records;});\n }\n pending = discoveryState(await this.options.store.read(this.options.boardId))!.pending!;\n await this.options.authorize('commit', pending.input); await this.checkSources(pending.input);\n if (!pending.request) {\n const board = await this.options.service.read(this.options.boardId);\n requireValue(board.revision === pending.input.board.revision, 'SCAN_BOARD_CHANGED');\n const request: KanbanRequest = {operationId: `discovery:${pending.runId}`, expectedRevision: board.revision,\n command: {type: 'apply-discovery', batch: {runId: pending.runId, leaseToken: this.token, changes: pending.changes!, ...(pending.records ? {records: pending.records} : {}), autoCreate: pending.autoCreate,\n checkpoints: pending.input.sessions.map(s => ({sessionId: s.session.id, revision: s.session.revision}))}}};\n await this.updateOwned('prepare', state => {state.pending!.request = request;});\n pending.request = request;\n }\n this.stage = 'board.commit';\n await this.options.service.execute(this.options.boardId, pending.request);\n }\n private async execute(input: KanbanScanInput): Promise<KanbanDiscoveryChange[] | import('./contracts').KanbanDiscoveryResult> {\n try {return await this.options.executor.run(structuredClone(input));}\n catch (error) {\n if (error instanceof KanbanScanNotStartedError) await this.updateOwned('not-started', state => {state.pending!.executionSettled = true;});\n throw error;\n }\n }\n private async settleInvalidatedRun(): Promise<void> {\n const pending = discoveryState(await this.options.store.read(this.options.boardId))?.pending;\n if (!pending || pending.token !== this.token || !pending.executionStarted || pending.executionSettled) return;\n requireValue(await this.options.executor.settled?.(pending.input), 'SCAN_EXECUTION_UNSETTLED');\n }\n private async checkSources(input: KanbanScanInput): Promise<void> {\n for (const source of input.sessions) {\n const current = await this.options.source.inspect(source.session.id);\n requireValue(current.revision === source.session.revision && current.activity === 'idle' && !current.scannerOwned, 'SOURCE_CHANGED');\n }\n }\n private async updateOwned(stage: string, mutate: (state: KanbanDiscoveryState) => void): Promise<void> {\n await this.update(stage, state => {\n requireValue(state.pending?.token === this.token && state.pending.leaseUntil > this.now(), 'SCAN_LEASE_EXPIRED'); mutate(state);\n });\n }\n private async update(stage: string, mutate: (state: KanbanDiscoveryState) => void): Promise<void> {\n const operation = this.id();\n await this.options.store.update(this.options.boardId, operation, `discovery:${stage}:${operation}`, state => {\n requireValue(state, 'BOARD_NOT_FOUND'); state.discovery ??= {checkpoints: {}}; mutate(state.discovery as KanbanDiscoveryState); return state;\n });\n }\n private log(operationId: string, started: number, result: 'success' | 'failure', code?: string, sessionId?: string): void {\n try {this.options.log({operationId, boardId: this.options.boardId, sessionId, stage: `discovery.${this.stage}`, result, code, durationMs: this.now() - started});}\n catch { /* Diagnostics cannot affect durable execution. */ }\n }\n}\nfunction validateChanges(changes: KanbanDiscoveryChange[], input: KanbanScanInput): void {\n requireValue(Array.isArray(changes) && changes.length <= 50, 'SCAN_CHANGE_LIMIT');\n const ids = new Set<string>();\n for (const change of changes) {\n requireValue(change && ['create', 'update', 'link'].includes(change.action) && !ids.has(change.itemId), 'INVALID_CHANGE'); ids.add(change.itemId); identity(change.itemId);\n if (change.action !== 'create') requireValue(input.board.items.some(i => i.id === change.itemId), 'UNKNOWN_CONTEXT_ITEM');\n validateText(change.title); validateText(change.responsibility); validateText(change.acceptance);\n requireValue(change.eligibleAgentIds === undefined || Array.isArray(change.eligibleAgentIds), 'INVALID_ELIGIBILITY');\n change.eligibleAgentIds?.forEach(identity);\n requireValue(change.eligiblePrincipals === undefined || Array.isArray(change.eligiblePrincipals), 'INVALID_ELIGIBILITY');\n requireValue(Array.isArray(change.sources) && change.sources.length > 0 && change.sources.length <= 100 && change.sources.some(s => s.role === 'user'), 'USER_EVIDENCE_REQUIRED');\n for (const ref of change.sources) {\n const source = input.sessions.find(s => s.session.id === ref.sessionId && s.session.revision === ref.revision);\n requireValue(source?.messages.some(m => m.id === ref.messageId && m.role === ref.role), 'INVALID_SOURCE_REFERENCE');\n }\n }\n}\n\nfunction validateRecords(records: import('./contracts').KanbanRecordInput[], input: KanbanScanInput, changes: KanbanDiscoveryChange[]): void {\n requireValue(Array.isArray(records) && records.length <= 20, 'SCAN_RECORD_LIMIT');\n const ids = new Set<string>();\n for (const record of records) {\n identity(record.id); requireValue(!ids.has(record.id), 'INVALID_RECORD_ID'); ids.add(record.id);\n validateText(record.title); validateText(record.content);\n requireValue(['log', 'decision'].includes(record.kind), 'INVALID_RECORD_KIND');\n if (record.kind === 'decision') validateText(record.rationale ?? '');\n requireValue(Array.isArray(record.itemIds) && record.itemIds.every(id => input.board.items.some(item => item.id === id) || changes.some(change => change.action === 'create' && change.itemId === id)), 'UNKNOWN_CONTEXT_ITEM');\n if (record.supersedes) requireValue(input.projectContext?.records.some(old => old.id === record.supersedes && old.kind === 'decision'), 'UNKNOWN_CONTEXT_RECORD');\n requireValue(Array.isArray(record.sources) && record.sources.length > 0 && record.sources.length <= 100\n && record.sources.some(source => source.role === 'user'), 'USER_EVIDENCE_REQUIRED');\n for (const ref of record.sources) {\n const source = input.sessions.find(s => s.session.id === ref.sessionId && s.session.revision === ref.revision);\n requireValue(source?.messages.some(message => message.id === ref.messageId && message.role === ref.role), 'INVALID_SOURCE_REFERENCE');\n }\n }\n}\n\nfunction errorCode(error: unknown): string {\n const value = error as {code?: unknown; remoteCode?: unknown};\n const code = value?.remoteCode ?? value?.code;\n return typeof code === 'string' && /^[A-Z][A-Z0-9_]{0,79}$/.test(code) ? code : 'SCAN_PORT_FAILURE';\n}\n\nfunction matchesScope(scope: import('./contracts').KanbanBoard['scope'], source: import('./scan-ports').KanbanScanSource): boolean {\n const expected = source.scope ?? (source.projectId ? {kind: 'project' as const, projectId: source.projectId} : undefined);\n if (!expected || scope.kind !== expected.kind) return false;\n if (scope.kind === 'system') return true;\n if (scope.kind === 'project' && expected.kind === 'project') return scope.projectId === expected.projectId;\n return scope.kind === 'collection' && expected.kind === 'collection' && scope.namespace === expected.namespace && scope.id === expected.id;\n}\n"],"mappings":"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","names":["text"]}
@@ -0,0 +1,52 @@
1
+ import { T as KanbanService, I as KanbanPrincipal, r as KanbanDispatch, w as KanbanItem, d as KanbanArtifactRef, u as KanbanExternalReference, s as KanbanEstimate } from './planning-CHrOqQ6b.cjs';
2
+ import { IFileSystem } from '@nestos/vfs-core';
3
+
4
+ interface KanbanExecutionObservation {
5
+ controlCapabilities?: {
6
+ pause: 'boundary' | 'none';
7
+ resume: boolean;
8
+ };
9
+ executionSessionId?: string;
10
+ outcome: 'running' | 'unknown' | 'succeeded' | 'failed' | 'cancelled';
11
+ outputs?: KanbanArtifactRef[];
12
+ code?: string;
13
+ }
14
+ interface KanbanExecutionPort {
15
+ prepareContinuation?(dispatch: KanbanDispatch, item: KanbanItem, previousRequest: unknown): Promise<unknown>;
16
+ /** Persistable request; preparing cannot submit agent work. */
17
+ prepare(dispatch: KanbanDispatch, item: KanbanItem): Promise<unknown>;
18
+ start(request: unknown): Promise<KanbanExecutionObservation>;
19
+ /** Observe the original admission only. Missing receipts mean unknown. */
20
+ inspect(request: unknown): Promise<KanbanExecutionObservation>;
21
+ }
22
+ interface KanbanExecutionBridgeOptions {
23
+ fs: IFileSystem;
24
+ directory: string;
25
+ boardId: string;
26
+ service: KanbanService;
27
+ worker(principal: KanbanPrincipal): Promise<KanbanService>;
28
+ executor: KanbanExecutionPort;
29
+ authorize(stage: 'prepare' | 'start' | 'inspect', dispatch: KanbanDispatch): Promise<void>;
30
+ log(input: {
31
+ operationId: string;
32
+ stage: string;
33
+ result: 'success' | 'failure';
34
+ code?: string;
35
+ retryAfterMs?: number;
36
+ durationMs: number;
37
+ }): void;
38
+ executionReference?(sessionId: string, dispatchId: string): KanbanExternalReference;
39
+ subscribe?(wake: () => void): () => void;
40
+ now?: () => number;
41
+ autoDispatch?: boolean | (() => Promise<boolean>);
42
+ shutdownTimeoutMs?: number;
43
+ estimate?(item: KanbanItem, principal: KanbanPrincipal): Promise<KanbanEstimate | undefined>;
44
+ }
45
+ /** The host owns execution. Prepared/submitting identities survive response loss and host shutdown. */
46
+ declare function createKanbanExecutionBridge(options: KanbanExecutionBridgeOptions): Promise<{
47
+ scan: () => Promise<void>;
48
+ tick: () => Promise<void>;
49
+ quiesce(): Promise<void>;
50
+ }>;
51
+
52
+ export { type KanbanExecutionBridgeOptions as K, type KanbanExecutionObservation as a, type KanbanExecutionPort as b, createKanbanExecutionBridge as c };
@@ -0,0 +1,52 @@
1
+ import { T as KanbanService, I as KanbanPrincipal, r as KanbanDispatch, w as KanbanItem, d as KanbanArtifactRef, u as KanbanExternalReference, s as KanbanEstimate } from './planning-CHrOqQ6b.js';
2
+ import { IFileSystem } from '@nestos/vfs-core';
3
+
4
+ interface KanbanExecutionObservation {
5
+ controlCapabilities?: {
6
+ pause: 'boundary' | 'none';
7
+ resume: boolean;
8
+ };
9
+ executionSessionId?: string;
10
+ outcome: 'running' | 'unknown' | 'succeeded' | 'failed' | 'cancelled';
11
+ outputs?: KanbanArtifactRef[];
12
+ code?: string;
13
+ }
14
+ interface KanbanExecutionPort {
15
+ prepareContinuation?(dispatch: KanbanDispatch, item: KanbanItem, previousRequest: unknown): Promise<unknown>;
16
+ /** Persistable request; preparing cannot submit agent work. */
17
+ prepare(dispatch: KanbanDispatch, item: KanbanItem): Promise<unknown>;
18
+ start(request: unknown): Promise<KanbanExecutionObservation>;
19
+ /** Observe the original admission only. Missing receipts mean unknown. */
20
+ inspect(request: unknown): Promise<KanbanExecutionObservation>;
21
+ }
22
+ interface KanbanExecutionBridgeOptions {
23
+ fs: IFileSystem;
24
+ directory: string;
25
+ boardId: string;
26
+ service: KanbanService;
27
+ worker(principal: KanbanPrincipal): Promise<KanbanService>;
28
+ executor: KanbanExecutionPort;
29
+ authorize(stage: 'prepare' | 'start' | 'inspect', dispatch: KanbanDispatch): Promise<void>;
30
+ log(input: {
31
+ operationId: string;
32
+ stage: string;
33
+ result: 'success' | 'failure';
34
+ code?: string;
35
+ retryAfterMs?: number;
36
+ durationMs: number;
37
+ }): void;
38
+ executionReference?(sessionId: string, dispatchId: string): KanbanExternalReference;
39
+ subscribe?(wake: () => void): () => void;
40
+ now?: () => number;
41
+ autoDispatch?: boolean | (() => Promise<boolean>);
42
+ shutdownTimeoutMs?: number;
43
+ estimate?(item: KanbanItem, principal: KanbanPrincipal): Promise<KanbanEstimate | undefined>;
44
+ }
45
+ /** The host owns execution. Prepared/submitting identities survive response loss and host shutdown. */
46
+ declare function createKanbanExecutionBridge(options: KanbanExecutionBridgeOptions): Promise<{
47
+ scan: () => Promise<void>;
48
+ tick: () => Promise<void>;
49
+ quiesce(): Promise<void>;
50
+ }>;
51
+
52
+ export { type KanbanExecutionBridgeOptions as K, type KanbanExecutionObservation as a, type KanbanExecutionPort as b, createKanbanExecutionBridge as c };