// Fixture for the slice 1 S5 mockup. Rows are shaped like schema v3b // (agents/darkwing/work/slice1-proto/schema-v3b.sql, 179ffe35, lead decision 60), and the rows the // broker writes follow its writers in packages/bus/src/broker.mjs (S2c, d27042fa; S2b refused messages // 101 and 102 and S2c restores them, see SLICE1-VIEWS.md section 10): // human requests are a message plus a human.input naming it, every human answer is a human.input, // every decision.raise has its action.allowed context, session.launched comes from bindLaunch. // checks/slice1-verify.mjs inserts every row into that schema, so its triggers check the fixture too. // `lookups` stands in for what no Q1 read verb returns yet (business file, kanban view, S3 status); // mockups/slice1/q1.js labels each use. Nothing here is live data. window.S1 = (() => { 'use strict'; const NOW = '2026-10-06T15:00:00Z'; const B = 'mosaic-stack'; const id = n => `0d1a${String(n).padStart(4, '0')}-5e7c-4b1a-9c3e-1f2a3b4c5d6e`; const D = { launch: id(1), prd: id(2), prdOld: id(3), conflict: id(4), scope: id(5), merged: id(6) }; const lookups = { business: B, human: 'jason', tracker: { baseUrl: 'http://127.0.0.1:3456', project: 3, view: 9 }, buckets: { 21: 'todo', 22: 'in-progress', 23: 'in-review', 24: 'blocked', 25: 'done' }, bots: { 7: 'bot-mosaic-stack-pm', 8: 'bot-mosaic-stack-cto', 9: 'bot-mosaic-stack-coder', 10: 'bot-mosaic-stack-reviewer' }, instances: [ { instance: 'pm', definition: 'pm', harness: 'pi', model: 'claude-opus-5-5', arbiterFor: 'delivery' }, { instance: 'cto', definition: 'cto', harness: 'claude-code', model: 'claude-opus-5-5', arbiterFor: 'technical' }, { instance: 'coder', definition: 'coder', harness: 'pi', model: 'claude-sonnet-5-5' }, { instance: 'reviewer', definition: 'reviewer', harness: 'pi', model: 'claude-sonnet-5-5' }, ], launch: { by: 'pm', instances: ['coder', 'reviewer'], max: { opus: 4, sonnet: 4 } }, families: { 'claude-opus-5-5': 'opus', 'claude-sonnet-5-5': 'sonnet' }, // Proposed in SLICE1-VIEWS.md Q2. The database can't answer this today. poll: { project: 3, board: '2026-10-06T14:59:41Z', cursor: '2026-10-06T14:59:42Z', error: null, proposed: true }, }; const role_claims = [ { seq: 1, at: '2026-10-06T08:02:10Z', business: B, role: 'pm', op: 'claim', holder_run: 'run-pm-7f31', harness: 'pi', address: null, by: 'run-pm-7f31', reason: null, decision: null }, { seq: 2, at: '2026-10-06T08:05:44Z', business: B, role: 'cto', op: 'claim', holder_run: 'run-cto-2b90', harness: 't3', address: 't3:5c1e9a0d', by: 'run-cto-2b90', reason: null, decision: null }, { seq: 3, at: '2026-10-06T09:46:03Z', business: B, role: 'coder', op: 'claim', holder_run: 'run-coder-a442', harness: 'pi', address: null, by: 'run-coder-a442', reason: null, decision: null }, { seq: 4, at: '2026-10-06T11:20:00Z', business: B, role: 'reviewer', op: 'claim', holder_run: 'run-rev-0c18', harness: 'pi', address: null, by: 'run-rev-0c18', reason: null, decision: null }, { seq: 5, at: '2026-10-06T12:41:27Z', business: B, role: 'reviewer', op: 'release', holder_run: 'run-rev-0c18', harness: 'pi', address: null, by: 'run-rev-0c18', reason: 'verdict posted', decision: null }, ]; // The broker stamps every write with a strictly later millisecond (#now). Rows one call writes are a millisecond apart here. const plus = (at, n) => new Date(Date.parse(at) + n).toISOString(); const opt = (...pairs) => JSON.stringify(pairs.map(([key, text]) => ({ key, text }))); const decisions = [ { seq: 1, id: D.merged, at: '2026-10-06T09:42:00Z', business: B, project: 'stack', raised_by_role: 'pm', raised_by_run: 'run-pm-7f31', class: 'gated', action: 'role.launch', route_to: 'human', question: 'Launch the coder instance for task #41 (bus store)?', options: opt(['yes', 'Launch coder on Sonnet'], ['no', 'Do not launch']), recommendation: 'yes', task_ref: 'vikunja:3/41', requirement_ref: 'REQ-DEC-1', blocking: 1, supersedes: null }, { seq: 2, id: D.prdOld, at: '2026-10-06T10:02:00Z', business: B, project: 'stack', raised_by_role: 'cto', raised_by_run: 'run-cto-2b90', class: 'gated', action: 'prd.approve', route_to: 'human', question: 'Approve the REQ-TASK-2 wording change?', options: opt(['a', 'Approve'], ['b', 'Reject']), recommendation: 'a', task_ref: null, requirement_ref: 'REQ-TASK-2', blocking: 0, supersedes: null }, { seq: 3, id: D.prd, at: '2026-10-06T10:31:00Z', business: B, project: 'stack', raised_by_role: 'cto', raised_by_run: 'run-cto-2b90', class: 'gated', action: 'prd.approve', route_to: 'human', question: 'Approve the REQ-TASK-2 wording change? The draft says **"every 30 seconds"**; probes support it.\nQuoted from the agent, unchanged: ‮exe.txt‬ and a bell \u0007.', options: opt(['a', 'Approve as written'], ['b', 'Approve with "at least every 60 seconds"'], ['c', 'Reject; keep draft 0.3 wording']), recommendation: 'a', task_ref: null, requirement_ref: 'REQ-TASK-2', blocking: 0, supersedes: D.prdOld }, { seq: 4, id: D.launch, at: '2026-10-06T13:52:00Z', business: B, project: 'stack', raised_by_role: 'pm', raised_by_run: 'run-pm-7f31', class: 'gated', action: 'role.launch', route_to: 'human', question: 'Launch a second coder instance so the Vikunja poller (#44) can move while #42 is in progress?', options: opt(['yes', 'Launch coder-2 on Sonnet'], ['no', 'Keep one coder; #44 waits'], ['later', 'Ask again when #42 is in review']), recommendation: 'yes', task_ref: 'vikunja:3/44', requirement_ref: 'REQ-LAUNCH-1', blocking: 1, supersedes: null }, { seq: 5, id: D.conflict, at: '2026-10-06T14:31:20Z', business: B, project: 'stack', raised_by_role: 'coder', raised_by_run: 'run-coder-a442', class: 'cross-role', action: 'task.close', route_to: 'pm', question: 'Task #48 is done in Vikunja but in-review in the queue. Which stands?', options: opt(['queue', 'Keep in-review; ask the person to reopen'], ['vikunja', 'Close in the queue after a verdict']), recommendation: 'queue', task_ref: 'vikunja:3/48', requirement_ref: 'REQ-TASK-4', blocking: 0, supersedes: null }, { seq: 6, id: D.scope, at: '2026-10-06T14:40:05Z', business: B, project: 'stack', raised_by_role: 'coder', raised_by_run: 'run-coder-a442', class: 'cross-role', action: 'task.scope.change', route_to: 'cto', question: 'Task #42 also needs a status read for poll freshness. Add it to the scope?', options: opt(['add', 'Add to #42'], ['split', 'New task for S3']), recommendation: 'split', task_ref: 'vikunja:3/42', requirement_ref: 'REQ-WEB-1', blocking: 0, supersedes: null }, ]; const decision_events = [ { seq: 1, decision: D.merged, at: '2026-10-06T09:44:10Z', op: 'seen', by: 'jason', choice: null, note: null, via: 'cli' }, { seq: 2, decision: D.merged, at: '2026-10-06T09:44:31Z', op: 'resolved', by: 'jason', choice: 'yes', note: null, via: 'cli' }, { seq: 3, decision: D.prdOld, at: '2026-10-06T10:30:59.999Z', op: 'withdrawn', by: 'run-cto-2b90', choice: null, note: null, via: null }, { seq: 4, decision: D.launch, at: '2026-10-06T13:58:02Z', op: 'seen', by: 'jason', choice: null, note: null, via: 'cli' }, ]; // A human request is a message from the CLI plus a human.input naming it (broker message.send). The // broker routes it to the role's holder; the holder's receive and read add deliveries. const PM = { holder_run: 'run-pm-7f31', address: null }; const later = (at, s) => new Date(Date.parse(at) + s * 1000).toISOString().replace('.000Z', 'Z'); const asks = [[3, '2026-10-06T08:29:00Z', 'Write the identity runbook first.'], [5, '2026-10-06T09:38:00Z', 'Build the bus store next.'], [35, '2026-10-06T09:48:10Z', 'Then the read verbs, and the inbox and decide commands on top of them.'], [12, '2026-10-06T09:54:30Z', 'The poller has to catch column moves within one tick.'], [36, '2026-10-06T10:17:40Z', 'Add a task for the task-create path itself.']]; const messages = [ { seq: 1, id: id(101), at: '2026-10-06T13:52:01Z', business: B, from_role: 'pm', from_run: 'run-pm-7f31', to_role: 'human', class: 'DECISION', in_reply_to: null, decision: D.launch, corrects: null, body: 'Blocking: launch a second coder for #44?' }, { seq: 2, id: id(102), at: '2026-10-06T09:42:01Z', business: B, from_role: 'pm', from_run: 'run-pm-7f31', to_role: 'human', class: 'DECISION', in_reply_to: null, decision: D.merged, corrects: null, body: 'Blocking: launch the coder for #41?' }, ...asks.map(([n, at, body]) => ({ seq: 0, id: id(200 + n), at, business: B, from_role: 'human', from_run: 'human-cli', to_role: 'pm', class: 'INFO', in_reply_to: null, decision: null, corrects: null, body })), ]; // The human holds no claim, so a message to the human is routed with no holder. The DM is the Discord connector's delivery. const deliveries = [ { seq: 1, message: id(101), at: plus('2026-10-06T13:52:01Z', 1), op: 'routed', holder_run: null, transport: null, address: null, detail: null }, { seq: 2, message: id(101), at: '2026-10-06T13:52:03Z', op: 'delivered', holder_run: null, transport: 'discord-dm', address: 'dm', detail: null }, { seq: 3, message: id(102), at: plus('2026-10-06T09:42:01Z', 1), op: 'routed', holder_run: null, transport: null, address: null, detail: null }, { seq: 4, message: id(102), at: '2026-10-06T09:42:02Z', op: 'delivered', holder_run: null, transport: 'discord-dm', address: 'dm', detail: null }, ...asks.flatMap(([n, at]) => [ { seq: 0, message: id(200 + n), at: plus(at, 1), op: 'routed', ...PM, transport: 'pi', detail: null }, { seq: 0, message: id(200 + n), at: later(at, 15), op: 'delivered', ...PM, transport: 'broker', detail: null }, { seq: 0, message: id(200 + n), at: later(at, 40), op: 'read', ...PM, transport: 'pi', detail: null }, ]), ]; // A stand-in digest: 64 hex characters from the ref and time (FNV-1a, eight rounds). Not the real field hash. const hex = str => Array.from({ length: 8 }, (_, k) => { let h = 0x811c9dc5 ^ k; for (const c of str) h = Math.imul(h ^ c.charCodeAt(0), 0x01000193); return (h >>> 0).toString(16).padStart(8, '0'); }).join(''); const snap = (seq, at, ref, f, src, extra = {}) => ({ seq, at, business: B, task_ref: ref, updated: extra.updated || at, etag: null, digest: extra.digest || hex(ref + at), fields: JSON.stringify(f), source: src, via: extra.via || null, read_at: extra.read_at || null, role: src === 'self' ? (extra.role || 'pm') : null, run: src === 'self' ? (extra.run || 'run-pm-7f31') : null }); const T = (index, title, bucket, o = {}) => ({ project_id: 3, index, title, description: `…\nRequirement: ${o.req || ''}`, done: bucket === 25 ? 1 : 0, due_date: o.due || null, priority: o.prio ?? 2, percent_done: o.pct ?? 0, bucket, labels: o.labels || [], assignees: o.who ? [o.who] : [] }); const task_snapshots = [ snap(1, '2026-10-06T08:30:00Z', 'vikunja:3/45', T(45, 'Install runbook for slice 1 identities', 21, { req: 'REQ-CRED-1', who: 7 }), 'self'), snap(2, '2026-10-06T09:41:00Z', 'vikunja:3/41', T(41, 'Bus store over node:sqlite', 21, { req: 'REQ-DEC-1', who: 9, prio: 4 }), 'self'), snap(3, '2026-10-06T09:50:00Z', 'vikunja:3/42', T(42, 'Broker read verbs for the CLI and the WebUI', 21, { req: 'REQ-WEB-1', who: 9, prio: 3 }), 'self'), snap(4, '2026-10-06T09:52:00Z', 'vikunja:3/43', T(43, '`mosaic inbox` and `mosaic decide`', 21, { req: 'REQ-CLI-1', who: 9, due: '2026-10-09' }), 'self'), snap(5, '2026-10-06T09:55:00Z', 'vikunja:3/44', T(44, 'Vikunja poller: board read plus cursor read', 21, { req: 'REQ-TASK-2', who: 9, prio: 3 }), 'self'), snap(6, '2026-10-06T10:20:00Z', 'vikunja:3/48', T(48, 'Field table checks in the task verbs', 22, { req: 'REQ-TASK-1', who: 9 }), 'self', { role: 'coder', run: 'run-coder-a442' }), snap(7, '2026-10-06T11:05:00Z', 'vikunja:3/41', T(41, 'Bus store over node:sqlite', 23, { req: 'REQ-DEC-1', who: 9, prio: 4, pct: 90 }), 'self', { role: 'coder', run: 'run-coder-a442' }), // A move between open columns leaves Vikunja's `updated` alone (probe P3), so this self row keeps 09:50. snap(8, '2026-10-06T14:59:40Z', 'vikunja:3/42', T(42, 'Broker read verbs for the CLI and the WebUI', 22, { req: 'REQ-WEB-1', who: 9, prio: 3 }), 'self', { role: 'coder', run: 'run-coder-a442', updated: '2026-10-06T09:50:00Z' }), // A board read sent before that move finished and recorded after it. Its digest differs from the latest // row, so the broker records it (addendum B section 5); task_external_changes ignores it (read_at rule). snap(8.5, '2026-10-06T14:59:41Z', 'vikunja:3/42', T(42, 'Broker read verbs for the CLI and the WebUI', 21, { req: 'REQ-WEB-1', who: 9, prio: 3 }), 'poll', { via: 'board', read_at: '2026-10-06T14:59:39Z', updated: '2026-10-06T09:50:00Z', digest: '0003'.repeat(16) }), snap(9, '2026-10-06T12:15:00Z', 'vikunja:3/45', T(45, 'Install runbook for slice 1 identities', 25, { req: 'REQ-CRED-1', who: 7, pct: 100 }), 'self'), snap(10, '2026-10-06T12:30:11Z', 'vikunja:3/46', { project_id: 3, index: 46, title: 'Check the digest time against Central daylight saving', description: '', done: 0, due_date: null, priority: 1, percent_done: 0, bucket: 21, labels: [], assignees: [] }, 'poll', { via: 'cursor', read_at: '2026-10-06T12:30:10Z' }), snap(11, '2026-10-06T13:50:00Z', 'vikunja:3/44', T(44, 'Vikunja poller: board read plus cursor read', 24, { req: 'REQ-TASK-2', who: 9, prio: 3 }), 'self', { role: 'coder', run: 'run-coder-a442' }), snap(12, '2026-10-06T14:20:00Z', 'vikunja:3/47', { gone: 'not-found' }, 'poll', { via: 'task', read_at: '2026-10-06T14:19:59Z' }), snap(13, '2026-10-06T14:31:12Z', 'vikunja:3/48', T(48, 'Field table checks in the task verbs', 25, { req: 'REQ-TASK-1', who: 9 }), 'poll', { via: 'cursor', read_at: '2026-10-06T14:31:11Z', updated: '2026-10-06T14:31:05Z' }), snap(14, '2026-10-06T14:44:00Z', 'vikunja:3/43', T(43, '`mosaic inbox` and `mosaic decide`', 22, { req: 'REQ-CLI-1', who: 9, due: '2026-10-09' }), 'poll', { via: 'board', read_at: '2026-10-06T14:43:59Z', updated: '2026-10-06T09:52:00Z' }), ]; // 3/47 had an earlier self snapshot; it's gone now. task_snapshots.splice(5, 0, snap(0.5, '2026-10-06T10:00:00Z', 'vikunja:3/47', T(47, 'Spike: webhook receiver (dropped)', 21, { req: 'REQ-TASK-2', who: 7 }), 'self')); // Event shapes. Rows the broker writes follow it exactly: human.input (instruction, answer, admin), // action.allowed (decision.raise context, authorize, agent message.send), action.refused ({code}), // launch.revoked/restored ({via}), session.launched (bindLaunch: the launched run is actor and // subject). Nothing writes task.*, review.*, session.ended or credential.* yet; those keep the shapes // from SLICE1-VIEWS.md, with the task ref in `subject` (schema v3a onward) and a session.ended shaped // like session.launched. const ev = (seq, at, kind, actor_role, actor_run, subject, body) => ({ seq, id: id(500 + seq), at, business: B, kind, actor_role, actor_run, subject, corrects: null, body: JSON.stringify(body) }); const launched = (seq, at, role, run, harness, address = null) => ev(seq, at, 'session.launched', role, run, run, { harness, address, pid: null, startTime: null }); const raise = (seq, d, action, target, approvalChoice) => { const r = decisions.find(x => x.id === d); return ev(seq, plus(r.at, 1), 'action.allowed', r.raised_by_role, r.raised_by_run, r.task_ref || d, { decision: d, action, operation: 'decision.raise', task_ref: r.task_ref, target, approvalChoice, holderRun: null }); }; const answer = (seq, at, decision, op, choice = null) => ev(seq, plus(at, 1), 'human.input', null, null, decision, { kind: 'answer', decision, op, choice }); const events = [ launched(1, '2026-10-06T08:00:00Z', 'pm', 'run-pm-7f31', 'pi'), launched(2, '2026-10-06T08:04:00Z', 'cto', 'run-cto-2b90', 'claude-code', 't3:5c1e9a0d'), ...asks.map(([n, at]) => ev(n, plus(at, 2), 'human.input', null, null, id(200 + n), { kind: 'instruction', message: id(200 + n) })), ev(4, '2026-10-06T08:30:00Z', 'task.created', 'pm', 'run-pm-7f31', 'vikunja:3/45', { request: id(503), requirement: 'REQ-CRED-1', index: 45 }), ev(6, '2026-10-06T09:41:00Z', 'task.created', 'pm', 'run-pm-7f31', 'vikunja:3/41', { request: id(505), requirement: 'REQ-DEC-1', index: 41 }), ev(7, '2026-10-06T09:41:01Z', 'task.assigned', 'pm', 'run-pm-7f31', 'vikunja:3/41', { role: 'coder' }), raise(40, D.merged, 'role.launch', 'coder', 'yes'), // S2c (d27042fa): an agent message.send writes action.allowed first; within-role, so no decision in it. ev(50, plus('2026-10-06T09:42:01Z', -1), 'action.allowed', 'pm', 'run-pm-7f31', 'human', { action: 'message.send', class: 'within-role', target: 'human' }), answer(37, '2026-10-06T09:44:10Z', D.merged, 'seen'), answer(8, '2026-10-06T09:44:31Z', D.merged, 'resolved', 'yes'), // The launcher checks the resolved decision (authorize), then S6 binds the launch record. ev(46, '2026-10-06T09:45:00Z', 'action.allowed', 'pm', 'run-pm-7f31', 'coder', { action: 'role.launch', class: 'gated', decision: D.merged, target: 'coder' }), launched(9, '2026-10-06T09:45:02Z', 'coder', 'run-coder-a442', 'pi'), ev(10, '2026-10-06T09:50:00Z', 'task.created', 'pm', 'run-pm-7f31', 'vikunja:3/42', { request: id(535), requirement: 'REQ-WEB-1', index: 42 }), ev(11, '2026-10-06T09:52:00Z', 'task.created', 'pm', 'run-pm-7f31', 'vikunja:3/43', { request: id(535), requirement: 'REQ-CLI-1', index: 43 }), ev(13, '2026-10-06T09:55:00Z', 'task.created', 'pm', 'run-pm-7f31', 'vikunja:3/44', { request: id(512), requirement: 'REQ-TASK-2', index: 44 }), ev(14, '2026-10-06T09:55:01Z', 'task.assigned', 'pm', 'run-pm-7f31', 'vikunja:3/44', { role: 'coder' }), ev(15, '2026-10-06T10:00:00Z', 'task.created', 'pm', 'run-pm-7f31', 'vikunja:3/47', { request: id(512), requirement: 'REQ-TASK-2', index: 47 }), raise(41, D.prdOld, 'prd.approve', 'REQ-TASK-2', 'a'), ev(16, '2026-10-06T10:19:00Z', 'task.created', 'pm', 'run-pm-7f31', 'vikunja:3/48', { request: id(536), requirement: 'REQ-TASK-1', index: 48 }), ev(17, '2026-10-06T10:20:00Z', 'task.state', 'coder', 'run-coder-a442', 'vikunja:3/48', { from: 'todo', to: 'in-progress' }), raise(42, D.prd, 'prd.approve', 'REQ-TASK-2', 'a'), ev(18, '2026-10-06T11:05:00Z', 'task.state', 'coder', 'run-coder-a442', 'vikunja:3/41', { from: 'todo', to: 'in-review' }), ev(19, '2026-10-06T11:05:02Z', 'review.requested', 'coder', 'run-coder-a442', 'vikunja:3/41', { row: 37, round: 1 }), launched(20, '2026-10-06T11:19:00Z', 'reviewer', 'run-rev-0c18', 'pi'), ev(21, '2026-10-06T14:59:40Z', 'task.state', 'coder', 'run-coder-a442', 'vikunja:3/42', { from: 'todo', to: 'in-progress' }), ev(22, '2026-10-06T12:15:00Z', 'task.closed', 'pm', 'run-pm-7f31', 'vikunja:3/45', { verdict: 'approve' }), ev(23, '2026-10-06T12:30:11Z', 'task.changed.external', null, null, 'vikunja:3/46', { fields: ['created'] }), ev(24, '2026-10-06T12:40:00Z', 'review.verdict', 'reviewer', 'run-rev-0c18', 'vikunja:3/41', { row: 37, round: 1, verdict: 'approve' }), ev(25, '2026-10-06T12:41:30Z', 'session.ended', 'reviewer', 'run-rev-0c18', 'run-rev-0c18', { exit: 0, reason: 'verdict posted' }), ev(26, '2026-10-06T13:10:00Z', 'launch.revoked', null, null, null, { via: 'cli' }), ev(47, plus('2026-10-06T13:10:00Z', 1), 'human.input', null, null, null, { kind: 'admin', event: id(526) }), ev(27, '2026-10-06T13:25:00Z', 'launch.restored', null, null, null, { via: 'cli' }), ev(48, plus('2026-10-06T13:25:00Z', 1), 'human.input', null, null, null, { kind: 'admin', event: id(527) }), ev(28, '2026-10-06T13:50:00Z', 'task.state', 'coder', 'run-coder-a442', 'vikunja:3/44', { from: 'todo', to: 'blocked' }), ev(29, '2026-10-06T13:51:00Z', 'action.refused', 'pm', 'run-pm-7f31', 'vikunja:3/44', { code: 'decision-required' }), raise(43, D.launch, 'role.launch', 'coder', 'yes'), ev(49, plus('2026-10-06T13:52:01Z', -1), 'action.allowed', 'pm', 'run-pm-7f31', 'human', { action: 'message.send', class: 'within-role', target: 'human' }), answer(38, '2026-10-06T13:58:02Z', D.launch, 'seen'), ev(30, '2026-10-06T14:00:00Z', 'credential.expiring', null, null, null, { service: 'vikunja', instance: 'coder', expires: '2026-10-13' }), ev(31, '2026-10-06T14:20:00Z', 'task.missing', null, null, 'vikunja:3/47', { reason: 'not-found' }), ev(32, '2026-10-06T14:31:12Z', 'task.changed.external', null, null, 'vikunja:3/48', { fields: ['done', 'bucket'] }), ev(33, '2026-10-06T14:31:13Z', 'task.conflict', null, null, 'vikunja:3/48', { field: 'state', queue: 'in-review', tracker: 'done', decision: D.conflict }), // Recommended and approving choices differ here: choosing "queue" authorizes nothing. raise(44, D.conflict, 'task.close', 'vikunja:3/48', 'vikunja'), raise(45, D.scope, 'task.scope.change', 'vikunja:3/42', 'add'), ev(34, '2026-10-06T14:44:00Z', 'task.changed.external', null, null, 'vikunja:3/43', { fields: ['bucket'] }), ]; // The broker writes times as toISOString(), with milliseconds; its Store refuses any other form. const ms = t => t && t.length === 20 ? t.replace('Z', '.000Z') : t; for (const t of [role_claims, decisions, decision_events, messages, deliveries, task_snapshots, events]) for (const r of t) { r.at = ms(r.at); if ('read_at' in r) r.read_at = ms(r.read_at); } // seq follows insertion order, which is time order; the verify script checks this against the schema. for (const t of [messages, deliveries, task_snapshots, events]) t.sort((a, b) => a.at.localeCompare(b.at)).forEach((r, i) => { r.seq = i + 1; }); return { NOW, lookups, role_claims, decisions, decision_events, messages, deliveries, task_snapshots, events }; })();