packages/cli adds mosaic inbox, decide, tasks, agents and trail over the human-cli transport, and mosaic bus start, stop and status as the trusted host (unit mosaic-bus@<business>, scripts/bus-service.sh). The host boots packages/bus/src/process.mjs, passes config.trackers from the tracker.* variables (lead decision 70), and runs a notifier child. The notifier DMs each open blocking decision once and sends an 08:00 America/Chicago digest, journaled in notify/<business>/sent.jsonl at 0600 with no Discord ids. A torn journal tail is copied aside and truncated; a malformed line, a directory looser than 0700 or a symlinked journal refuses (lead decision 71). packages/discord gains dmRecipient, createDm and notify.mjs. Candidate agents/rocko/work/slice1-s4, baseb9b6cf00, build.patch b52f7d68, manifest e858504e (29 files). Darkwing approved round 2 on #1521 (comment 26855), Filbert approved round 2 (comment 26856). The packet's mutant table lists M28 as killed; it survived, and BUILD-LOG records the correction. Integration gate in a worktree on2557e29dwith the patch applied: bus 67, business 60, cli 49, control-board 124, discord 178, ledger 78, mosaic 69, queue 148, seat 19, tasks 51 and webui 14, all with no failures. Conversation is 149/3, the same K1, K3 and K10 cases that fail on the base; S4 doesn't touch the package. Every scripts/test-*.sh is green, with test-release 14/14 and test-task 98/98 on the existing gate2 compose network. A scratch test, not in this commit, booted the real host with trackers against S3's fake Vikunja: the adapter went ready and a task.close on a missing task answered task-not-found after a Vikunja read. Co-Authored-By: Claude Opus 5.5 <[email protected]>
28 lines
1.6 KiB
JavaScript
28 lines
1.6 KiB
JavaScript
import { test } from "node:test";
|
|
import assert from "node:assert/strict";
|
|
import { formatAgents, formatInbox, formatTasks, formatTrail } from "../src/format.mjs";
|
|
|
|
test("empty views say so", () => {
|
|
assert.equal(formatInbox([]), "inbox: empty\n");
|
|
assert.equal(formatAgents([]), "agents: no role is claimed\n");
|
|
assert.equal(formatTasks([]), "tasks: none\n");
|
|
});
|
|
|
|
test("the trail keeps the broker's order and names a decision's task without its rows", () => {
|
|
const rows = [
|
|
{ at: "2026-10-08T10:00:00Z", table: "decisions", seq: 4, id: "d-1", class: "gated", action: "deploy", route_to: "human", blocking: 1, question: "Ship?", task_ref: "vikunja:1/7" },
|
|
{ at: "2026-10-08T09:00:00Z", table: "events", seq: 2, kind: "decision.raised", actor_role: "coder" },
|
|
{ at: "2026-10-08T11:00:00Z", table: "decision_events", seq: 1, op: "resolved", choice: "yes", by: "jason", via: "cli" },
|
|
];
|
|
const lines = formatTrail("d-1", rows).trimEnd().split("\n");
|
|
assert.deepEqual(lines.slice(1, 4).map((l) => l.split(" ")[1]), ["decisions#4", "events#2", "decision_events#1"]);
|
|
assert.match(lines[1], /gated deploy → human \(blocking\): Ship\?/);
|
|
assert.match(lines[3], /resolved choice yes by jason via cli/);
|
|
assert.deepEqual(lines.slice(-2), ["task: vikunja:1/7", "follow with: mosaic trail vikunja:1/7"]);
|
|
assert.doesNotMatch(formatTrail("vikunja:1/7", rows), /follow with/);
|
|
});
|
|
|
|
test("tasks print the tracker fields the snapshot carries", () => {
|
|
assert.equal(formatTasks([{ task_ref: "vikunja:1/7", fields: { title: "Build S4", done: false } }]), "vikunja:1/7 open Build S4\n");
|
|
});
|