@nestos/kanban 0.0.0-stage → 0.5.1

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 (99) hide show
  1. package/README.md +121 -2
  2. package/dist/activity-C6xDM-cg.d.cts +232 -0
  3. package/dist/activity-Dw8DOw0L.d.ts +232 -0
  4. package/dist/activity.cjs +288 -0
  5. package/dist/activity.cjs.map +1 -0
  6. package/dist/activity.d.cts +34 -0
  7. package/dist/activity.d.ts +34 -0
  8. package/dist/activity.js +288 -0
  9. package/dist/activity.js.map +1 -0
  10. package/dist/chunk-2YFPV5GF.cjs +16 -0
  11. package/dist/chunk-2YFPV5GF.cjs.map +1 -0
  12. package/dist/chunk-4KF7KUAI.cjs +221 -0
  13. package/dist/chunk-4KF7KUAI.cjs.map +1 -0
  14. package/dist/chunk-4XF6LQSU.cjs +9 -0
  15. package/dist/chunk-4XF6LQSU.cjs.map +1 -0
  16. package/dist/chunk-5DLZWW5Y.js +148 -0
  17. package/dist/chunk-5DLZWW5Y.js.map +1 -0
  18. package/dist/chunk-5XKOCKY2.js +58 -0
  19. package/dist/chunk-5XKOCKY2.js.map +1 -0
  20. package/dist/chunk-6MN3H3YF.cjs +183 -0
  21. package/dist/chunk-6MN3H3YF.cjs.map +1 -0
  22. package/dist/chunk-ATUFFNLV.js +221 -0
  23. package/dist/chunk-ATUFFNLV.js.map +1 -0
  24. package/dist/chunk-DFTOTBGF.js +439 -0
  25. package/dist/chunk-DFTOTBGF.js.map +1 -0
  26. package/dist/chunk-G6FCDLDF.js +16 -0
  27. package/dist/chunk-G6FCDLDF.js.map +1 -0
  28. package/dist/chunk-IUI5CI2S.cjs +439 -0
  29. package/dist/chunk-IUI5CI2S.cjs.map +1 -0
  30. package/dist/chunk-KYNYPKKH.cjs +71 -0
  31. package/dist/chunk-KYNYPKKH.cjs.map +1 -0
  32. package/dist/chunk-L44TM4EE.cjs +1498 -0
  33. package/dist/chunk-L44TM4EE.cjs.map +1 -0
  34. package/dist/chunk-LSE6JG5I.cjs +20 -0
  35. package/dist/chunk-LSE6JG5I.cjs.map +1 -0
  36. package/dist/chunk-O4OGAAV4.js +71 -0
  37. package/dist/chunk-O4OGAAV4.js.map +1 -0
  38. package/dist/chunk-OHQTKGQN.js +1498 -0
  39. package/dist/chunk-OHQTKGQN.js.map +1 -0
  40. package/dist/chunk-PSKQAFDF.cjs +58 -0
  41. package/dist/chunk-PSKQAFDF.cjs.map +1 -0
  42. package/dist/chunk-QTMTBEBA.js +20 -0
  43. package/dist/chunk-QTMTBEBA.js.map +1 -0
  44. package/dist/chunk-RQP77ZTY.js +9 -0
  45. package/dist/chunk-RQP77ZTY.js.map +1 -0
  46. package/dist/chunk-T7P5EIC4.cjs +148 -0
  47. package/dist/chunk-T7P5EIC4.cjs.map +1 -0
  48. package/dist/chunk-TB2RZGDV.js +183 -0
  49. package/dist/chunk-TB2RZGDV.js.map +1 -0
  50. package/dist/content-CAIp1njn.d.cts +35 -0
  51. package/dist/content-CGMJdyH_.d.ts +35 -0
  52. package/dist/contracts/index.cjs +21 -0
  53. package/dist/contracts/index.cjs.map +1 -0
  54. package/dist/contracts/index.d.cts +4 -0
  55. package/dist/contracts/index.d.ts +4 -0
  56. package/dist/contracts/index.js +21 -0
  57. package/dist/contracts/index.js.map +1 -0
  58. package/dist/discovery-DxpirOz3.d.cts +61 -0
  59. package/dist/discovery-gSqWpuRm.d.ts +61 -0
  60. package/dist/discovery.cjs +379 -0
  61. package/dist/discovery.cjs.map +1 -0
  62. package/dist/discovery.d.cts +22 -0
  63. package/dist/discovery.d.ts +22 -0
  64. package/dist/discovery.js +379 -0
  65. package/dist/discovery.js.map +1 -0
  66. package/dist/execution.cjs +368 -0
  67. package/dist/execution.cjs.map +1 -0
  68. package/dist/execution.d.cts +89 -0
  69. package/dist/execution.d.ts +89 -0
  70. package/dist/execution.js +368 -0
  71. package/dist/execution.js.map +1 -0
  72. package/dist/index.cjs +76 -0
  73. package/dist/index.cjs.map +1 -0
  74. package/dist/index.d.cts +61 -0
  75. package/dist/index.d.ts +61 -0
  76. package/dist/index.js +76 -0
  77. package/dist/index.js.map +1 -0
  78. package/dist/module-BbvwdCDF.d.cts +11 -0
  79. package/dist/module-CWNK2BMj.d.ts +11 -0
  80. package/dist/planning-CcFZYdIL.d.cts +844 -0
  81. package/dist/planning-CcFZYdIL.d.ts +844 -0
  82. package/dist/ports-Bn6ro2FB.d.ts +142 -0
  83. package/dist/ports-CaL6geqR.d.cts +142 -0
  84. package/dist/storage.cjs +289 -0
  85. package/dist/storage.cjs.map +1 -0
  86. package/dist/storage.d.cts +71 -0
  87. package/dist/storage.d.ts +71 -0
  88. package/dist/storage.js +289 -0
  89. package/dist/storage.js.map +1 -0
  90. package/dist/store-Ct0gOAAg.d.ts +28 -0
  91. package/dist/store-q63eSMoT.d.cts +28 -0
  92. package/dist/ui/index.cjs +1961 -0
  93. package/dist/ui/index.cjs.map +1 -0
  94. package/dist/ui/index.d.cts +209 -0
  95. package/dist/ui/index.d.ts +209 -0
  96. package/dist/ui/index.js +1961 -0
  97. package/dist/ui/index.js.map +1 -0
  98. package/dist/ui/kanban.css +194 -0
  99. package/package.json +113 -4
@@ -0,0 +1,379 @@
1
+ import {
2
+ discoveryState,
3
+ identity,
4
+ positive,
5
+ requireValue,
6
+ text
7
+ } from "./chunk-ATUFFNLV.js";
8
+ import {
9
+ KanbanError,
10
+ KanbanScanNotStartedError
11
+ } from "./chunk-G6FCDLDF.js";
12
+ import "./chunk-5XKOCKY2.js";
13
+ import {
14
+ randomUUID
15
+ } from "./chunk-TB2RZGDV.js";
16
+ import "./chunk-QTMTBEBA.js";
17
+
18
+ // src/scan-context.ts
19
+ function scanContext(board, state, sessions, options, runId) {
20
+ requireValue(state.board.revision === board.revision, "SCAN_BOARD_CHANGED");
21
+ const archived = state.archiveIndex ?? [];
22
+ const input = {
23
+ runId,
24
+ executorId: options.executor.identity,
25
+ board: { ...board, items: [] },
26
+ sessions,
27
+ connection: structuredClone(options.policy.connection),
28
+ 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: [] },
29
+ context: {
30
+ totalItems: board.items.length,
31
+ omittedItems: board.items.length,
32
+ archivedTotal: archived.length,
33
+ archivedItems: []
34
+ }
35
+ };
36
+ const bytes = () => new TextEncoder().encode(JSON.stringify(input)).length;
37
+ requireValue(bytes() <= options.policy.maxInputBytes, "SCAN_INPUT_LIMIT");
38
+ const sessionIds = new Set(sessions.map((s) => s.session.id));
39
+ addRecords(input, state, sessionIds, bytes, options.policy.maxInputBytes);
40
+ const content = sessions.flatMap((s) => s.messages.map((m) => m.text.toLowerCase())).join("\n");
41
+ const ordered = [...board.items].sort((a, b) => score(a, sessionIds, content) - score(b, sessionIds, content) || (a.planning?.priority ?? 2) - (b.planning?.priority ?? 2) || a.id.localeCompare(b.id));
42
+ for (const item of ordered) {
43
+ if (input.board.items.length >= 50) break;
44
+ input.board.items.push(item);
45
+ input.context.omittedItems--;
46
+ if (bytes() > options.policy.maxInputBytes) {
47
+ input.board.items.pop();
48
+ input.context.omittedItems++;
49
+ }
50
+ }
51
+ const history = [...archived].sort((a, b) => Number(b.sessionIds.some((id) => sessionIds.has(id))) - Number(a.sessionIds.some((id) => sessionIds.has(id))) || (b.closedAt ?? b.updatedAt) - (a.closedAt ?? a.updatedAt));
52
+ for (const item of history.slice(0, 20)) {
53
+ input.context.archivedItems.push(item);
54
+ if (bytes() > options.policy.maxInputBytes) input.context.archivedItems.pop();
55
+ }
56
+ return structuredClone(input);
57
+ }
58
+ function addRecords(input, state, sessionIds, bytes, maxBytes) {
59
+ const records = (state.records ?? []).filter((record) => record.status === "confirmed").sort((a, b) => Number(b.kind === "decision") - Number(a.kind === "decision") || Number(b.sources.some((source) => sessionIds.has(source.sessionId))) - Number(a.sources.some((source) => sessionIds.has(source.sessionId))) || b.createdAt - a.createdAt);
60
+ for (const record of records.slice(0, 10)) {
61
+ input.projectContext.records.push(record);
62
+ input.projectContext.omittedRecords--;
63
+ if (bytes() > maxBytes) {
64
+ input.projectContext.records.pop();
65
+ input.projectContext.omittedRecords++;
66
+ }
67
+ }
68
+ }
69
+ function score(item, sessions, content) {
70
+ if (item.sources?.some((source) => sessions.has(source.sessionId)) || content.includes(item.title.toLowerCase())) return 0;
71
+ if (item.status === "claimed" || item.status === "review") return 1;
72
+ if (item.status === "open") return 2;
73
+ return item.status === "completed" || item.status === "cancelled" ? 4 : 3;
74
+ }
75
+
76
+ // src/discovery.ts
77
+ function createKanbanDiscovery(options) {
78
+ return new Discovery(options);
79
+ }
80
+ var Discovery = class {
81
+ constructor(options) {
82
+ this.options = options;
83
+ this.now = options.now ?? Date.now;
84
+ this.id = options.id ?? randomUUID;
85
+ this.token = this.id();
86
+ for (const value of [options.policy.idleMs, options.policy.maxSessions, options.policy.maxInputBytes, options.policy.leaseMs]) positive(value);
87
+ identity(options.policy.connection.connectionId);
88
+ }
89
+ now;
90
+ id;
91
+ token;
92
+ active;
93
+ stopping = false;
94
+ resets = /* @__PURE__ */ new Set();
95
+ stage = "prepare";
96
+ scan = () => {
97
+ if (this.stopping) return Promise.reject(new KanbanError("SERVICE_STOPPING"));
98
+ if (this.active) return this.active;
99
+ const operationId = this.id(), started = this.now();
100
+ this.active = this.run().catch(async (error) => {
101
+ if (error instanceof KanbanError && ["SOURCE_CHANGED", "SCAN_BOARD_CHANGED", "REVISION_CONFLICT"].includes(error.code)) {
102
+ await this.settleInvalidatedRun();
103
+ await this.update("invalidate", (state) => {
104
+ if (state.pending?.token === this.token) {
105
+ state.lastRun = { runId: state.pending.runId, outcome: "invalidated", at: this.now(), code: error.code };
106
+ delete state.pending;
107
+ }
108
+ });
109
+ }
110
+ throw error;
111
+ }).then(() => this.log(operationId, started, "success"), (error) => {
112
+ this.log(operationId, started, "failure", errorCode(error));
113
+ throw error;
114
+ }).finally(() => {
115
+ this.active = void 0;
116
+ });
117
+ return this.active;
118
+ };
119
+ tick = () => this.scan();
120
+ async quiesce() {
121
+ this.stopping = true;
122
+ await Promise.allSettled([this.active, ...this.resets]);
123
+ }
124
+ dispose = () => this.quiesce();
125
+ reset = (runId) => {
126
+ if (this.stopping) return Promise.reject(new KanbanError("SERVICE_STOPPING"));
127
+ const work = this.resetRun(runId).finally(() => {
128
+ this.resets.delete(work);
129
+ });
130
+ this.resets.add(work);
131
+ return work;
132
+ };
133
+ async resetRun(runId) {
134
+ const pending = discoveryState(await this.options.store.read(this.options.boardId))?.pending;
135
+ requireValue(pending?.runId === runId, "SCAN_CHANGED");
136
+ await this.options.authorize("commit", pending.input);
137
+ requireValue(pending.token === this.token || pending.leaseUntil <= this.now(), "SCAN_BUSY");
138
+ if (pending.executionStarted && !pending.executionSettled) requireValue(await this.options.executor.settled?.(pending.input), "SCAN_EXECUTION_UNSETTLED");
139
+ await this.update("reset", (state) => {
140
+ requireValue(state.pending?.runId === runId && state.pending.token === pending.token, "SCAN_CHANGED");
141
+ state.lastRun = { runId, outcome: "invalidated", at: this.now(), code: "SCAN_MANUAL_RESET" };
142
+ delete state.pending;
143
+ });
144
+ }
145
+ async run() {
146
+ await this.options.authorize("read");
147
+ const board = await this.options.service.read(this.options.boardId);
148
+ requireValue(matchesScope(board.scope, this.options.source), "SOURCE_PROJECT_MISMATCH");
149
+ requireValue(this.options.source.kind === this.options.policy.connection.kind || this.options.policy.connection.allowContentTransfer, "CONTENT_TRANSFER_NOT_ALLOWED");
150
+ const stored = await this.options.store.read(board.id);
151
+ let pending = discoveryState(stored)?.pending;
152
+ if (pending && pending.token !== this.token && pending.leaseUntil > this.now()) return;
153
+ if (!pending) {
154
+ const sessions = await this.collect();
155
+ if (!sessions.length) return;
156
+ const current = await this.options.store.read(board.id);
157
+ requireValue(current, "BOARD_NOT_FOUND");
158
+ const input = scanContext(board, current, sessions, this.options, this.id());
159
+ requireValue(new TextEncoder().encode(JSON.stringify(input)).length <= this.options.policy.maxInputBytes, "SCAN_INPUT_LIMIT");
160
+ 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 };
161
+ }
162
+ const selected = pending;
163
+ await this.update("claim", (state) => {
164
+ requireValue(!state.pending || state.pending.token === this.token || state.pending.leaseUntil <= this.now(), "SCAN_BUSY");
165
+ if (state.pending) requireValue(state.pending.runId === selected.runId, "SCAN_CHANGED");
166
+ const restoring = selected.token !== this.token;
167
+ state.pending = { ...selected, ...restoring ? { request: void 0 } : {}, token: this.token, leaseUntil: this.now() + this.options.policy.leaseMs };
168
+ });
169
+ await this.process();
170
+ }
171
+ async collect() {
172
+ this.stage = "source.list";
173
+ const sessions = await this.options.source.list(this.options.policy.includeArchived);
174
+ for (const session of sessions) {
175
+ identity(session.id);
176
+ requireValue(typeof session.revision === "string" && session.revision.length > 0 && session.revision.length <= 16e3, "INVALID_SOURCE_REVISION");
177
+ }
178
+ requireValue(new Set(sessions.map((s) => s.id)).size === sessions.length, "DUPLICATE_SOURCE_SESSION");
179
+ await this.observe(sessions);
180
+ const state = discoveryState(await this.options.store.read(this.options.boardId));
181
+ const result = [];
182
+ let bytes = 0;
183
+ for (const session of sessions.sort((a, b) => (a.updatedAt ?? 0) - (b.updatedAt ?? 0) || a.id.localeCompare(b.id))) {
184
+ if (!this.eligible(session, state)) continue;
185
+ const current = await this.options.source.inspect(session.id);
186
+ if (!this.eligible(current, state) || current.revision !== session.revision) continue;
187
+ this.stage = "source.read";
188
+ const text2 = await this.readCandidate(current, Math.floor(this.options.policy.maxInputBytes * 0.7) - bytes);
189
+ if (!text2) continue;
190
+ requireValue(text2.session.id === current.id && text2.session.revision === current.revision, "SOURCE_CHANGED");
191
+ if (!text2.complete) continue;
192
+ const size = new TextEncoder().encode(JSON.stringify(text2)).length;
193
+ if (bytes + size > this.options.policy.maxInputBytes * 0.7) break;
194
+ bytes += size;
195
+ result.push(text2);
196
+ if (result.length >= this.options.policy.maxSessions) break;
197
+ }
198
+ return result;
199
+ }
200
+ async readCandidate(session, maxBytes) {
201
+ const started = this.now();
202
+ try {
203
+ const result = await this.options.source.read(session, maxBytes);
204
+ if (!result.complete) this.log(this.id(), started, "failure", "SOURCE_PROVENANCE_UNKNOWN", session.id);
205
+ return result;
206
+ } catch (error) {
207
+ const code = errorCode(error);
208
+ if (!["SCAN_INPUT_LIMIT", "SOURCE_CHANGED", "HARNESS_DIGEST_CHANGED"].includes(code)) throw error;
209
+ this.log(this.id(), started, "failure", code, session.id);
210
+ return null;
211
+ }
212
+ }
213
+ eligible(session, state) {
214
+ const quiet = state.observations?.[session.id];
215
+ return !session.scannerOwned && (this.options.policy.includeArchived || !session.archived) && session.activity === "idle" && session.updatedAt !== null && session.updatedAt <= this.now() && quiet?.revision === session.revision && this.now() - quiet.idleSince >= this.options.policy.idleMs && state.checkpoints[session.id] !== session.revision;
216
+ }
217
+ async observe(sessions) {
218
+ await this.update("observe", (state) => {
219
+ state.observations ??= {};
220
+ for (const session of sessions) {
221
+ if (session.activity !== "idle" || session.scannerOwned) {
222
+ delete state.observations[session.id];
223
+ continue;
224
+ }
225
+ const old = state.observations[session.id];
226
+ if (old?.revision !== session.revision) state.observations[session.id] = { revision: session.revision, idleSince: this.now() };
227
+ }
228
+ });
229
+ }
230
+ async process() {
231
+ let pending = discoveryState(await this.options.store.read(this.options.boardId)).pending;
232
+ requireValue(pending.input.executorId === this.options.executor.identity, "SCANNER_EXECUTOR_CHANGED");
233
+ await this.checkSources(pending.input);
234
+ if (!pending.changes) {
235
+ await this.options.authorize("execute", pending.input);
236
+ this.stage = "analyzer.execute";
237
+ await this.updateOwned("execute", (state) => {
238
+ state.pending.executionStarted = true;
239
+ });
240
+ const output = await this.execute(pending.input), result = Array.isArray(output) ? { changes: output } : output;
241
+ await this.updateOwned("settled", (state) => {
242
+ state.pending.executionSettled = true;
243
+ });
244
+ validateChanges(result.changes, pending.input);
245
+ validateRecords(result.records ?? [], pending.input, result.changes);
246
+ await this.updateOwned("result", (state) => {
247
+ state.pending.changes = result.changes;
248
+ state.pending.records = result.records;
249
+ });
250
+ }
251
+ pending = discoveryState(await this.options.store.read(this.options.boardId)).pending;
252
+ await this.options.authorize("commit", pending.input);
253
+ await this.checkSources(pending.input);
254
+ if (!pending.request) {
255
+ const board = await this.options.service.read(this.options.boardId);
256
+ requireValue(board.revision === pending.input.board.revision, "SCAN_BOARD_CHANGED");
257
+ const request = {
258
+ operationId: `discovery:${pending.runId}`,
259
+ expectedRevision: board.revision,
260
+ command: { type: "apply-discovery", batch: {
261
+ runId: pending.runId,
262
+ leaseToken: this.token,
263
+ changes: pending.changes,
264
+ ...pending.records ? { records: pending.records } : {},
265
+ autoCreate: pending.autoCreate,
266
+ checkpoints: pending.input.sessions.map((s) => ({ sessionId: s.session.id, revision: s.session.revision }))
267
+ } }
268
+ };
269
+ await this.updateOwned("prepare", (state) => {
270
+ state.pending.request = request;
271
+ });
272
+ pending.request = request;
273
+ }
274
+ this.stage = "board.commit";
275
+ await this.options.service.execute(this.options.boardId, pending.request);
276
+ }
277
+ async execute(input) {
278
+ try {
279
+ return await this.options.executor.run(structuredClone(input));
280
+ } catch (error) {
281
+ if (error instanceof KanbanScanNotStartedError) await this.updateOwned("not-started", (state) => {
282
+ state.pending.executionSettled = true;
283
+ });
284
+ throw error;
285
+ }
286
+ }
287
+ async settleInvalidatedRun() {
288
+ const pending = discoveryState(await this.options.store.read(this.options.boardId))?.pending;
289
+ if (!pending || pending.token !== this.token || !pending.executionStarted || pending.executionSettled) return;
290
+ requireValue(await this.options.executor.settled?.(pending.input), "SCAN_EXECUTION_UNSETTLED");
291
+ }
292
+ async checkSources(input) {
293
+ for (const source of input.sessions) {
294
+ const current = await this.options.source.inspect(source.session.id);
295
+ requireValue(current.revision === source.session.revision && current.activity === "idle" && !current.scannerOwned, "SOURCE_CHANGED");
296
+ }
297
+ }
298
+ async updateOwned(stage, mutate) {
299
+ await this.update(stage, (state) => {
300
+ requireValue(state.pending?.token === this.token && state.pending.leaseUntil > this.now(), "SCAN_LEASE_EXPIRED");
301
+ mutate(state);
302
+ });
303
+ }
304
+ async update(stage, mutate) {
305
+ const operation = this.id();
306
+ await this.options.store.update(this.options.boardId, operation, `discovery:${stage}:${operation}`, (state) => {
307
+ requireValue(state, "BOARD_NOT_FOUND");
308
+ state.discovery ??= { checkpoints: {} };
309
+ mutate(state.discovery);
310
+ return state;
311
+ });
312
+ }
313
+ log(operationId, started, result, code, sessionId) {
314
+ try {
315
+ this.options.log({ operationId, boardId: this.options.boardId, sessionId, stage: `discovery.${this.stage}`, result, code, durationMs: this.now() - started });
316
+ } catch {
317
+ }
318
+ }
319
+ };
320
+ function validateChanges(changes, input) {
321
+ requireValue(Array.isArray(changes) && changes.length <= 50, "SCAN_CHANGE_LIMIT");
322
+ const ids = /* @__PURE__ */ new Set();
323
+ for (const change of changes) {
324
+ requireValue(change && ["create", "update", "link"].includes(change.action) && !ids.has(change.itemId), "INVALID_CHANGE");
325
+ ids.add(change.itemId);
326
+ identity(change.itemId);
327
+ if (change.action !== "create") requireValue(input.board.items.some((i) => i.id === change.itemId), "UNKNOWN_CONTEXT_ITEM");
328
+ text(change.title);
329
+ text(change.responsibility);
330
+ text(change.acceptance);
331
+ requireValue(change.eligibleAgentIds === void 0 || Array.isArray(change.eligibleAgentIds), "INVALID_ELIGIBILITY");
332
+ change.eligibleAgentIds?.forEach(identity);
333
+ requireValue(change.eligiblePrincipals === void 0 || Array.isArray(change.eligiblePrincipals), "INVALID_ELIGIBILITY");
334
+ requireValue(Array.isArray(change.sources) && change.sources.length > 0 && change.sources.length <= 100 && change.sources.some((s) => s.role === "user"), "USER_EVIDENCE_REQUIRED");
335
+ for (const ref of change.sources) {
336
+ const source = input.sessions.find((s) => s.session.id === ref.sessionId && s.session.revision === ref.revision);
337
+ requireValue(source?.messages.some((m) => m.id === ref.messageId && m.role === ref.role), "INVALID_SOURCE_REFERENCE");
338
+ }
339
+ }
340
+ }
341
+ function validateRecords(records, input, changes) {
342
+ requireValue(Array.isArray(records) && records.length <= 20, "SCAN_RECORD_LIMIT");
343
+ const ids = /* @__PURE__ */ new Set();
344
+ for (const record of records) {
345
+ identity(record.id);
346
+ requireValue(!ids.has(record.id), "INVALID_RECORD_ID");
347
+ ids.add(record.id);
348
+ text(record.title);
349
+ text(record.content);
350
+ requireValue(["log", "decision"].includes(record.kind), "INVALID_RECORD_KIND");
351
+ if (record.kind === "decision") text(record.rationale ?? "");
352
+ 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");
353
+ if (record.supersedes) requireValue(input.projectContext?.records.some((old) => old.id === record.supersedes && old.kind === "decision"), "UNKNOWN_CONTEXT_RECORD");
354
+ requireValue(Array.isArray(record.sources) && record.sources.length > 0 && record.sources.length <= 100 && record.sources.some((source) => source.role === "user"), "USER_EVIDENCE_REQUIRED");
355
+ for (const ref of record.sources) {
356
+ const source = input.sessions.find((s) => s.session.id === ref.sessionId && s.session.revision === ref.revision);
357
+ requireValue(source?.messages.some((message) => message.id === ref.messageId && message.role === ref.role), "INVALID_SOURCE_REFERENCE");
358
+ }
359
+ }
360
+ }
361
+ function errorCode(error) {
362
+ const value = error;
363
+ const code = value?.remoteCode ?? value?.code;
364
+ return typeof code === "string" && /^[A-Z][A-Z0-9_]{0,79}$/.test(code) ? code : "SCAN_PORT_FAILURE";
365
+ }
366
+ function matchesScope(scope, source) {
367
+ const expected = source.scope ?? (source.projectId ? { kind: "project", projectId: source.projectId } : void 0);
368
+ if (!expected || scope.kind !== expected.kind) return false;
369
+ if (scope.kind === "system") return true;
370
+ if (scope.kind === "project" && expected.kind === "project") return scope.projectId === expected.projectId;
371
+ return scope.kind === "collection" && expected.kind === "collection" && scope.namespace === expected.namespace && scope.id === expected.id;
372
+ }
373
+ export {
374
+ KanbanScanNotStartedError,
375
+ scanContext as buildDiscoveryContext,
376
+ createKanbanDiscovery,
377
+ discoveryState
378
+ };
379
+ //# sourceMappingURL=discovery.js.map
@@ -0,0 +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"]}