- {#each ['all', 'spark', 'draft', 'ready', 'running', 'completed', 'failed'] as status}
+ {#each ['all', 'spark', 'draft', 'ready', 'running', 'paused', 'completed', 'failed'] as status}
{@const count = statusCounts()[status] || 0}
filterStatus = status}
From 8925cb26e7107dcdbbb353767fde832473d63294 Mon Sep 17 00:00:00 2001
From: ValHallaBuilder <286693580+4gjnbzb4zf-sudo@users.noreply.github.com>
Date: Tue, 26 May 2026 08:34:09 -0400
Subject: [PATCH 2/5] fix(mission-board): stop fabricating 48% progress for
unbreakdown'd running missions
When a running mission had no task breakdown yet, taskProgressPercent returned
a literal 48, misleading operators about real progress. Return 0 so the UI's
hasTaskProgress path renders an indeterminate state.
---
src/lib/components/MissionBoard.svelte | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/lib/components/MissionBoard.svelte b/src/lib/components/MissionBoard.svelte
index 72ddad4a..3efaad02 100644
--- a/src/lib/components/MissionBoard.svelte
+++ b/src/lib/components/MissionBoard.svelte
@@ -458,7 +458,7 @@
const counts = getMissionBoardWorkBreakdown(card).build;
if (!counts || counts.total <= 0) {
if (card.status === 'completed') return 100;
- if (card.status === 'running') return 48;
+ if (card.status === 'running') return 0;
return 0;
}
return Math.round(((counts.completed + counts.failed + counts.cancelled) / counts.total) * 100);
From d32d36b0003e3547d846e3465f053fb886f6d105 Mon Sep 17 00:00:00 2001
From: ValHallaBuilder <286693580+4gjnbzb4zf-sudo@users.noreply.github.com>
Date: Tue, 26 May 2026 08:34:11 -0400
Subject: [PATCH 3/5] fix(completion-evidence): show the server-built summary
in the tooltip
completionEvidenceTooltipForDisplay was returning a generic
"Completion proof is incomplete." and dropping the server-built
evidence.summary that already names which specific pieces are missing.
Now we fall back to evidence.summary first, then the generic string.
---
src/lib/services/completion-evidence-display.ts | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/lib/services/completion-evidence-display.ts b/src/lib/services/completion-evidence-display.ts
index 583e08ab..e0960b34 100644
--- a/src/lib/services/completion-evidence-display.ts
+++ b/src/lib/services/completion-evidence-display.ts
@@ -13,5 +13,5 @@ export function completionEvidenceTooltipForDisplay(
): string | null {
if (!evidence || evidence.state === 'not_terminal') return null;
if (evidence.state === 'complete') return 'Completion proof is present.';
- return 'Completion proof is incomplete.';
+ return evidence.summary || 'Completion proof is incomplete.';
}
From 5aba9f581988ce142f398ea558fdda365a34257d Mon Sep 17 00:00:00 2001
From: miles
Date: Wed, 27 May 2026 21:07:00 +0100
Subject: [PATCH 4/5] Add Office lockfile pattern to .gitignore
Word/Excel/PowerPoint create temporary lockfiles (~$*) whenever an
Office document is open. These should never be committed.
A previous commit on this repo accidentally committed
`~$ark_Railway_Setup_Guide.docx` when a Word document in the repo root
was open during a git add. The committed lockfile was subsequently
removed, but the gitignore pattern that would have prevented it was
not added. This patch adds the pattern so the recurrence is blocked.
---
.gitignore | 2 ++
1 file changed, 2 insertions(+)
diff --git a/.gitignore b/.gitignore
index 63f43eb7..a7bbb5c8 100644
--- a/.gitignore
+++ b/.gitignore
@@ -29,3 +29,5 @@ nul
.spawner/
svelte-check-output.txt
.gstack/
+# Office lockfiles (Word/Excel/PowerPoint create these when files are open)
+~$*
From 72ad5ef3134cd580ee151a4d26047796af401f74 Mon Sep 17 00:00:00 2001
From: Tallsome
Date: Sun, 7 Jun 2026 23:53:53 +0100
Subject: [PATCH 5/5] =?UTF-8?q?fix(canvas):=20pipeline=20node=20IDs=20use?=
=?UTF-8?q?=20Math.random=20=E2=80=94=20weak=20entropy?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Replace Math.random() with crypto.randomUUID() for canvas node IDs.
Co-Authored-By: Claude Sonnet 4.6
---
src/routes/canvas/+page.svelte | 2 +-
tests/test_canvas_page_node_id.test.ts | 28 ++++++++++++++++++++++++++
2 files changed, 29 insertions(+), 1 deletion(-)
create mode 100644 tests/test_canvas_page_node_id.test.ts
diff --git a/src/routes/canvas/+page.svelte b/src/routes/canvas/+page.svelte
index 01dcf241..00a750b3 100644
--- a/src/routes/canvas/+page.svelte
+++ b/src/routes/canvas/+page.svelte
@@ -201,7 +201,7 @@ import { get } from 'svelte/store';
// Create nodes with proper IDs, keeping track of skill-to-node mapping
const skillToNodeId = new Map();
const canvasNodes = pipeline.nodes.map(node => {
- const nodeId = `node-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
+ const nodeId = `node-${crypto.randomUUID()}`;
skillToNodeId.set(node.skillId, nodeId);
return {
id: nodeId,
diff --git a/tests/test_canvas_page_node_id.test.ts b/tests/test_canvas_page_node_id.test.ts
new file mode 100644
index 00000000..bea780ed
--- /dev/null
+++ b/tests/test_canvas_page_node_id.test.ts
@@ -0,0 +1,28 @@
+import { describe, it, expect } from 'vitest';
+
+const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
+
+function makeNodeId(): string {
+ return `node-${crypto.randomUUID()}`;
+}
+
+describe('canvas node ID generation', () => {
+ it('starts with node- prefix', () => {
+ expect(makeNodeId()).toMatch(/^node-/);
+ });
+ it('contains a valid UUID v4', () => {
+ const id = makeNodeId();
+ expect(UUID_RE.test(id.slice(5))).toBe(true);
+ });
+ it('does not use Math.random', () => {
+ const src = makeNodeId.toString();
+ expect(src).not.toContain('Math.random');
+ });
+ it('generates 1000 unique IDs', () => {
+ const ids = Array.from({ length: 1000 }, () => makeNodeId());
+ expect(new Set(ids).size).toBe(1000);
+ });
+ it('is a plain string', () => {
+ expect(typeof makeNodeId()).toBe('string');
+ });
+});