From 30d6ae6ba1230c5ab41937a5e05e8c3790ad2860 Mon Sep 17 00:00:00 2001 From: Khaliq Date: Wed, 4 Mar 2026 13:17:13 -0800 Subject: [PATCH 1/2] more deterministic tests --- .trajectories/active/traj_ygwd7ep8ab1e.json | 19 - .../completed/2026-03/traj_fxq5ufnzc3k3.json | 53 ++ .../completed/2026-03/traj_fxq5ufnzc3k3.md | 31 ++ .../completed/2026-03/traj_ygwd7ep8ab1e.json | 78 +++ .../completed/2026-03/traj_ygwd7ep8ab1e.md | 38 ++ .../2026-03/traj_ygwd7ep8ab1e.trace.json | 488 ++++++++++++++++++ .trajectories/index.json | 14 +- src/cli/commands/messaging.test.ts | 33 ++ src/cli/index.test.ts | 115 +---- src/cli/lib/agent-management-listing.test.ts | 105 ++++ 10 files changed, 851 insertions(+), 123 deletions(-) delete mode 100644 .trajectories/active/traj_ygwd7ep8ab1e.json create mode 100644 .trajectories/completed/2026-03/traj_fxq5ufnzc3k3.json create mode 100644 .trajectories/completed/2026-03/traj_fxq5ufnzc3k3.md create mode 100644 .trajectories/completed/2026-03/traj_ygwd7ep8ab1e.json create mode 100644 .trajectories/completed/2026-03/traj_ygwd7ep8ab1e.md create mode 100644 .trajectories/completed/2026-03/traj_ygwd7ep8ab1e.trace.json create mode 100644 src/cli/lib/agent-management-listing.test.ts diff --git a/.trajectories/active/traj_ygwd7ep8ab1e.json b/.trajectories/active/traj_ygwd7ep8ab1e.json deleted file mode 100644 index cdef7ae20..000000000 --- a/.trajectories/active/traj_ygwd7ep8ab1e.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "id": "traj_ygwd7ep8ab1e", - "version": 1, - "task": { - "title": "Stabilize flaky CLI agents --json test timeout" - }, - "status": "active", - "startedAt": "2026-03-04T21:01:07.410Z", - "agents": [], - "chapters": [], - "commits": [], - "filesChanged": [], - "projectId": "/Users/khaliqgant/Projects/agent-workforce/relay", - "tags": [], - "_trace": { - "startRef": "2c332bd4bbfdf971c3b424515d62493b0582a53f", - "endRef": "2c332bd4bbfdf971c3b424515d62493b0582a53f" - } -} \ No newline at end of file diff --git a/.trajectories/completed/2026-03/traj_fxq5ufnzc3k3.json b/.trajectories/completed/2026-03/traj_fxq5ufnzc3k3.json new file mode 100644 index 000000000..548e78f8c --- /dev/null +++ b/.trajectories/completed/2026-03/traj_fxq5ufnzc3k3.json @@ -0,0 +1,53 @@ +{ + "id": "traj_fxq5ufnzc3k3", + "version": 1, + "task": { + "title": "Reduce process-level CLI flake surface by keeping only smoke coverage in index.test" + }, + "status": "completed", + "startedAt": "2026-03-04T21:15:49.943Z", + "agents": [ + { + "name": "default", + "role": "lead", + "joinedAt": "2026-03-04T21:15:50.135Z" + } + ], + "chapters": [ + { + "id": "chap_bsx5rxzuy2le", + "title": "Work", + "agentName": "default", + "startedAt": "2026-03-04T21:15:50.135Z", + "events": [ + { + "ts": 1772658950135, + "type": "decision", + "content": "Removed broker-dependent subprocess assertions from index.test and relied on command/lib unit tests: Removed broker-dependent subprocess assertions from index.test and relied on command/lib unit tests", + "raw": { + "question": "Removed broker-dependent subprocess assertions from index.test and relied on command/lib unit tests", + "chosen": "Removed broker-dependent subprocess assertions from index.test and relied on command/lib unit tests", + "alternatives": [], + "reasoning": "unit tests already cover status/agents/who/history/read behavior deterministically; subprocess tests were flaky due broker startup timing" + }, + "significance": "high" + } + ], + "endedAt": "2026-03-04T21:15:50.309Z" + } + ], + "commits": [], + "filesChanged": [], + "projectId": "/Users/khaliqgant/Projects/agent-workforce/relay", + "tags": [], + "_trace": { + "startRef": "be51324e0be71d973c950aab4a61b7bf96a0c151", + "endRef": "be51324e0be71d973c950aab4a61b7bf96a0c151" + }, + "completedAt": "2026-03-04T21:15:50.309Z", + "retrospective": { + "summary": "Trimmed index.test to version/help plus agents --json smoke; hardened smoke JSON detection against broker log prefixes", + "approach": "Standard approach", + "confidence": 0.94 + } +} \ No newline at end of file diff --git a/.trajectories/completed/2026-03/traj_fxq5ufnzc3k3.md b/.trajectories/completed/2026-03/traj_fxq5ufnzc3k3.md new file mode 100644 index 000000000..75d86a7b1 --- /dev/null +++ b/.trajectories/completed/2026-03/traj_fxq5ufnzc3k3.md @@ -0,0 +1,31 @@ +# Trajectory: Reduce process-level CLI flake surface by keeping only smoke coverage in index.test + +> **Status:** ✅ Completed +> **Confidence:** 94% +> **Started:** March 4, 2026 at 01:15 PM +> **Completed:** March 4, 2026 at 01:15 PM + +--- + +## Summary + +Trimmed index.test to version/help plus agents --json smoke; hardened smoke JSON detection against broker log prefixes + +**Approach:** Standard approach + +--- + +## Key Decisions + +### Removed broker-dependent subprocess assertions from index.test and relied on command/lib unit tests +- **Chose:** Removed broker-dependent subprocess assertions from index.test and relied on command/lib unit tests +- **Reasoning:** unit tests already cover status/agents/who/history/read behavior deterministically; subprocess tests were flaky due broker startup timing + +--- + +## Chapters + +### 1. Work +*Agent: default* + +- Removed broker-dependent subprocess assertions from index.test and relied on command/lib unit tests: Removed broker-dependent subprocess assertions from index.test and relied on command/lib unit tests diff --git a/.trajectories/completed/2026-03/traj_ygwd7ep8ab1e.json b/.trajectories/completed/2026-03/traj_ygwd7ep8ab1e.json new file mode 100644 index 000000000..a87d3bded --- /dev/null +++ b/.trajectories/completed/2026-03/traj_ygwd7ep8ab1e.json @@ -0,0 +1,78 @@ +{ + "id": "traj_ygwd7ep8ab1e", + "version": 1, + "task": { + "title": "Stabilize flaky CLI agents --json test timeout" + }, + "status": "completed", + "startedAt": "2026-03-04T21:01:07.410Z", + "agents": [ + { + "name": "default", + "role": "lead", + "joinedAt": "2026-03-04T21:12:10.243Z" + } + ], + "chapters": [ + { + "id": "chap_8fpb7wtgaj1m", + "title": "Work", + "agentName": "default", + "startedAt": "2026-03-04T21:12:10.243Z", + "events": [ + { + "ts": 1772658730244, + "type": "decision", + "content": "Moved agents/who/history JSON assertions from process-level CLI test to deterministic unit tests: Moved agents/who/history JSON assertions from process-level CLI test to deterministic unit tests", + "raw": { + "question": "Moved agents/who/history JSON assertions from process-level CLI test to deterministic unit tests", + "chosen": "Moved agents/who/history JSON assertions from process-level CLI test to deterministic unit tests", + "alternatives": [], + "reasoning": "direct handler tests avoid broker startup/network timing and remove CI timeout flake while keeping one end-to-end smoke check" + }, + "significance": "high" + } + ], + "endedAt": "2026-03-04T21:12:10.460Z" + } + ], + "commits": [ + "be51324e", + "4e2a8934" + ], + "filesChanged": [ + ".github/workflows/build-broker-binary.yml", + ".trajectories/active/traj_ygwd7ep8ab1e.json", + ".trajectories/index.json", + "CHANGELOG.md", + "package-lock.json", + "package.json", + "packages/acp-bridge/package.json", + "packages/config/package.json", + "packages/hooks/package.json", + "packages/memory/package.json", + "packages/openclaw/package.json", + "packages/policy/package.json", + "packages/sdk-py/pyproject.toml", + "packages/sdk/package.json", + "packages/telemetry/package.json", + "packages/trajectory/package.json", + "packages/user-directory/package.json", + "packages/utils/package.json", + "src/cli/commands/agent-management.ts", + "src/cli/index.test.ts" + ], + "projectId": "/Users/khaliqgant/Projects/agent-workforce/relay", + "tags": [], + "_trace": { + "startRef": "2c332bd4bbfdf971c3b424515d62493b0582a53f", + "endRef": "be51324e0be71d973c950aab4a61b7bf96a0c151", + "traceId": "trace_nbs8xyxldw67" + }, + "completedAt": "2026-03-04T21:12:10.460Z", + "retrospective": { + "summary": "Added deterministic unit tests for agent-management-listing and messaging history JSON; reduced CLI JSON integration surface to a single agents --json smoke test", + "approach": "Standard approach", + "confidence": 0.93 + } +} \ No newline at end of file diff --git a/.trajectories/completed/2026-03/traj_ygwd7ep8ab1e.md b/.trajectories/completed/2026-03/traj_ygwd7ep8ab1e.md new file mode 100644 index 000000000..24ecad2ad --- /dev/null +++ b/.trajectories/completed/2026-03/traj_ygwd7ep8ab1e.md @@ -0,0 +1,38 @@ +# Trajectory: Stabilize flaky CLI agents --json test timeout + +> **Status:** ✅ Completed +> **Confidence:** 93% +> **Started:** March 4, 2026 at 01:01 PM +> **Completed:** March 4, 2026 at 01:12 PM + +--- + +## Summary + +Added deterministic unit tests for agent-management-listing and messaging history JSON; reduced CLI JSON integration surface to a single agents --json smoke test + +**Approach:** Standard approach + +--- + +## Key Decisions + +### Moved agents/who/history JSON assertions from process-level CLI test to deterministic unit tests +- **Chose:** Moved agents/who/history JSON assertions from process-level CLI test to deterministic unit tests +- **Reasoning:** direct handler tests avoid broker startup/network timing and remove CI timeout flake while keeping one end-to-end smoke check + +--- + +## Chapters + +### 1. Work +*Agent: default* + +- Moved agents/who/history JSON assertions from process-level CLI test to deterministic unit tests: Moved agents/who/history JSON assertions from process-level CLI test to deterministic unit tests + +--- + +## Artifacts + +**Commits:** be51324e, 4e2a8934 +**Files changed:** 20 diff --git a/.trajectories/completed/2026-03/traj_ygwd7ep8ab1e.trace.json b/.trajectories/completed/2026-03/traj_ygwd7ep8ab1e.trace.json new file mode 100644 index 000000000..2a1c9d4d3 --- /dev/null +++ b/.trajectories/completed/2026-03/traj_ygwd7ep8ab1e.trace.json @@ -0,0 +1,488 @@ +{ + "version": 1, + "id": "trace_nbs8xyxldw67", + "timestamp": "2026-03-04T21:12:10.524Z", + "trajectory": "traj_ygwd7ep8ab1e", + "files": [ + { + "path": ".github/workflows/build-broker-binary.yml", + "conversations": [ + { + "contributor": { + "type": "agent", + "model": "unknown" + }, + "ranges": [ + { + "start_line": 48, + "end_line": 56, + "revision": "be51324e0be71d973c950aab4a61b7bf96a0c151" + }, + { + "start_line": 59, + "end_line": 69, + "revision": "be51324e0be71d973c950aab4a61b7bf96a0c151" + } + ] + } + ] + }, + { + "path": ".trajectories/active/traj_ygwd7ep8ab1e.json", + "conversations": [ + { + "contributor": { + "type": "agent", + "model": "unknown" + }, + "ranges": [ + { + "start_line": 1, + "end_line": 19, + "revision": "be51324e0be71d973c950aab4a61b7bf96a0c151" + } + ] + } + ] + }, + { + "path": ".trajectories/index.json", + "conversations": [ + { + "contributor": { + "type": "agent", + "model": "unknown" + }, + "ranges": [ + { + "start_line": 1, + "end_line": 6, + "revision": "be51324e0be71d973c950aab4a61b7bf96a0c151" + }, + { + "start_line": 554, + "end_line": 565, + "revision": "be51324e0be71d973c950aab4a61b7bf96a0c151" + } + ] + } + ] + }, + { + "path": "CHANGELOG.md", + "conversations": [ + { + "contributor": { + "type": "agent", + "model": "unknown" + }, + "ranges": [ + { + "start_line": 29, + "end_line": 42, + "revision": "be51324e0be71d973c950aab4a61b7bf96a0c151" + } + ] + } + ] + }, + { + "path": "package-lock.json", + "conversations": [ + { + "contributor": { + "type": "agent", + "model": "unknown" + }, + "ranges": [ + { + "start_line": 1, + "end_line": 12, + "revision": "be51324e0be71d973c950aab4a61b7bf96a0c151" + }, + { + "start_line": 22, + "end_line": 34, + "revision": "be51324e0be71d973c950aab4a61b7bf96a0c151" + }, + { + "start_line": 8791, + "end_line": 8800, + "revision": "be51324e0be71d973c950aab4a61b7bf96a0c151" + }, + { + "start_line": 8811, + "end_line": 8817, + "revision": "be51324e0be71d973c950aab4a61b7bf96a0c151" + }, + { + "start_line": 8824, + "end_line": 8834, + "revision": "be51324e0be71d973c950aab4a61b7bf96a0c151" + }, + { + "start_line": 8838, + "end_line": 8846, + "revision": "be51324e0be71d973c950aab4a61b7bf96a0c151" + }, + { + "start_line": 8850, + "end_line": 8860, + "revision": "be51324e0be71d973c950aab4a61b7bf96a0c151" + }, + { + "start_line": 9652, + "end_line": 9660, + "revision": "be51324e0be71d973c950aab4a61b7bf96a0c151" + }, + { + "start_line": 9664, + "end_line": 9672, + "revision": "be51324e0be71d973c950aab4a61b7bf96a0c151" + }, + { + "start_line": 9677, + "end_line": 9683, + "revision": "be51324e0be71d973c950aab4a61b7bf96a0c151" + }, + { + "start_line": 9689, + "end_line": 9697, + "revision": "be51324e0be71d973c950aab4a61b7bf96a0c151" + }, + { + "start_line": 9701, + "end_line": 9709, + "revision": "be51324e0be71d973c950aab4a61b7bf96a0c151" + }, + { + "start_line": 9713, + "end_line": 9721, + "revision": "be51324e0be71d973c950aab4a61b7bf96a0c151" + } + ] + } + ] + }, + { + "path": "package.json", + "conversations": [ + { + "contributor": { + "type": "agent", + "model": "unknown" + }, + "ranges": [ + { + "start_line": 1, + "end_line": 6, + "revision": "be51324e0be71d973c950aab4a61b7bf96a0c151" + }, + { + "start_line": 174, + "end_line": 186, + "revision": "be51324e0be71d973c950aab4a61b7bf96a0c151" + } + ] + } + ] + }, + { + "path": "packages/acp-bridge/package.json", + "conversations": [ + { + "contributor": { + "type": "agent", + "model": "unknown" + }, + "ranges": [ + { + "start_line": 1, + "end_line": 6, + "revision": "be51324e0be71d973c950aab4a61b7bf96a0c151" + }, + { + "start_line": 46, + "end_line": 52, + "revision": "be51324e0be71d973c950aab4a61b7bf96a0c151" + } + ] + } + ] + }, + { + "path": "packages/config/package.json", + "conversations": [ + { + "contributor": { + "type": "agent", + "model": "unknown" + }, + "ranges": [ + { + "start_line": 1, + "end_line": 6, + "revision": "be51324e0be71d973c950aab4a61b7bf96a0c151" + } + ] + } + ] + }, + { + "path": "packages/hooks/package.json", + "conversations": [ + { + "contributor": { + "type": "agent", + "model": "unknown" + }, + "ranges": [ + { + "start_line": 1, + "end_line": 6, + "revision": "be51324e0be71d973c950aab4a61b7bf96a0c151" + }, + { + "start_line": 37, + "end_line": 45, + "revision": "be51324e0be71d973c950aab4a61b7bf96a0c151" + } + ] + } + ] + }, + { + "path": "packages/memory/package.json", + "conversations": [ + { + "contributor": { + "type": "agent", + "model": "unknown" + }, + "ranges": [ + { + "start_line": 1, + "end_line": 6, + "revision": "be51324e0be71d973c950aab4a61b7bf96a0c151" + }, + { + "start_line": 22, + "end_line": 28, + "revision": "be51324e0be71d973c950aab4a61b7bf96a0c151" + } + ] + } + ] + }, + { + "path": "packages/openclaw/package.json", + "conversations": [ + { + "contributor": { + "type": "agent", + "model": "unknown" + }, + "ranges": [ + { + "start_line": 1, + "end_line": 6, + "revision": "be51324e0be71d973c950aab4a61b7bf96a0c151" + }, + { + "start_line": 29, + "end_line": 35, + "revision": "be51324e0be71d973c950aab4a61b7bf96a0c151" + } + ] + } + ] + }, + { + "path": "packages/policy/package.json", + "conversations": [ + { + "contributor": { + "type": "agent", + "model": "unknown" + }, + "ranges": [ + { + "start_line": 1, + "end_line": 6, + "revision": "be51324e0be71d973c950aab4a61b7bf96a0c151" + }, + { + "start_line": 22, + "end_line": 28, + "revision": "be51324e0be71d973c950aab4a61b7bf96a0c151" + } + ] + } + ] + }, + { + "path": "packages/sdk-py/pyproject.toml", + "conversations": [ + { + "contributor": { + "type": "agent", + "model": "unknown" + }, + "ranges": [ + { + "start_line": 4, + "end_line": 10, + "revision": "be51324e0be71d973c950aab4a61b7bf96a0c151" + } + ] + } + ] + }, + { + "path": "packages/sdk/package.json", + "conversations": [ + { + "contributor": { + "type": "agent", + "model": "unknown" + }, + "ranges": [ + { + "start_line": 1, + "end_line": 6, + "revision": "be51324e0be71d973c950aab4a61b7bf96a0c151" + }, + { + "start_line": 81, + "end_line": 87, + "revision": "be51324e0be71d973c950aab4a61b7bf96a0c151" + } + ] + } + ] + }, + { + "path": "packages/telemetry/package.json", + "conversations": [ + { + "contributor": { + "type": "agent", + "model": "unknown" + }, + "ranges": [ + { + "start_line": 1, + "end_line": 6, + "revision": "be51324e0be71d973c950aab4a61b7bf96a0c151" + } + ] + } + ] + }, + { + "path": "packages/trajectory/package.json", + "conversations": [ + { + "contributor": { + "type": "agent", + "model": "unknown" + }, + "ranges": [ + { + "start_line": 1, + "end_line": 6, + "revision": "be51324e0be71d973c950aab4a61b7bf96a0c151" + }, + { + "start_line": 22, + "end_line": 28, + "revision": "be51324e0be71d973c950aab4a61b7bf96a0c151" + } + ] + } + ] + }, + { + "path": "packages/user-directory/package.json", + "conversations": [ + { + "contributor": { + "type": "agent", + "model": "unknown" + }, + "ranges": [ + { + "start_line": 1, + "end_line": 6, + "revision": "be51324e0be71d973c950aab4a61b7bf96a0c151" + }, + { + "start_line": 22, + "end_line": 28, + "revision": "be51324e0be71d973c950aab4a61b7bf96a0c151" + } + ] + } + ] + }, + { + "path": "packages/utils/package.json", + "conversations": [ + { + "contributor": { + "type": "agent", + "model": "unknown" + }, + "ranges": [ + { + "start_line": 1, + "end_line": 6, + "revision": "be51324e0be71d973c950aab4a61b7bf96a0c151" + }, + { + "start_line": 112, + "end_line": 118, + "revision": "be51324e0be71d973c950aab4a61b7bf96a0c151" + } + ] + } + ] + }, + { + "path": "src/cli/commands/agent-management.ts", + "conversations": [ + { + "contributor": { + "type": "agent", + "model": "unknown" + }, + "ranges": [ + { + "start_line": 86, + "end_line": 105, + "revision": "be51324e0be71d973c950aab4a61b7bf96a0c151" + } + ] + } + ] + }, + { + "path": "src/cli/index.test.ts", + "conversations": [ + { + "contributor": { + "type": "agent", + "model": "unknown" + }, + "ranges": [ + { + "start_line": 32, + "end_line": 38, + "revision": "be51324e0be71d973c950aab4a61b7bf96a0c151" + } + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/.trajectories/index.json b/.trajectories/index.json index b17074469..b6705f97e 100644 --- a/.trajectories/index.json +++ b/.trajectories/index.json @@ -1,6 +1,6 @@ { "version": 1, - "lastUpdated": "2026-03-04T21:01:07.425Z", + "lastUpdated": "2026-03-04T21:15:50.404Z", "trajectories": { "traj_1b1dj40sl6jl": { "title": "Revert aggressive retry logic in relay-pty-orchestrator", @@ -557,9 +557,17 @@ }, "traj_ygwd7ep8ab1e": { "title": "Stabilize flaky CLI agents --json test timeout", - "status": "active", + "status": "completed", "startedAt": "2026-03-04T21:01:07.410Z", - "path": "/Users/khaliqgant/Projects/agent-workforce/relay/.trajectories/active/traj_ygwd7ep8ab1e.json" + "completedAt": "2026-03-04T21:12:10.460Z", + "path": "/Users/khaliqgant/Projects/agent-workforce/relay/.trajectories/completed/2026-03/traj_ygwd7ep8ab1e.json" + }, + "traj_fxq5ufnzc3k3": { + "title": "Reduce process-level CLI flake surface by keeping only smoke coverage in index.test", + "status": "completed", + "startedAt": "2026-03-04T21:15:49.943Z", + "completedAt": "2026-03-04T21:15:50.309Z", + "path": "/Users/khaliqgant/Projects/agent-workforce/relay/.trajectories/completed/2026-03/traj_fxq5ufnzc3k3.json" } } } \ No newline at end of file diff --git a/src/cli/commands/messaging.test.ts b/src/cli/commands/messaging.test.ts index ca2f1ba9e..8bd0d007a 100644 --- a/src/cli/commands/messaging.test.ts +++ b/src/cli/commands/messaging.test.ts @@ -187,6 +187,39 @@ describe('registerMessagingCommands', () => { expect(deps.log).not.toHaveBeenCalledWith(expect.stringContaining('One -> #general')); }); + it('shows message history as JSON with stable payload fields', async () => { + const relaycastClient = createRelaycastClientMock({ + messages: vi.fn(async () => [ + { + id: 'm1', + agent_name: 'One', + text: 'first', + created_at: '2026-02-20T11:00:01.000Z', + }, + ]), + }); + const { program, deps } = createHarness({ relaycastClient }); + + const exitCode = await runCommand(program, ['history', '--json', '--limit', '1']); + + expect(exitCode).toBeUndefined(); + expect(deps.createRelaycastClient).toHaveBeenCalledWith({ agentName: '__cli_history__' }); + expect(relaycastClient.messages).toHaveBeenCalledWith('general', { limit: 100 }); + expect(deps.log).toHaveBeenCalledTimes(1); + expect(JSON.parse((deps.log as ReturnType).mock.calls[0][0] as string)).toEqual([ + { + id: 'm1', + ts: Date.parse('2026-02-20T11:00:01.000Z'), + timestamp: '2026-02-20T11:00:01.000Z', + from: 'One', + to: '#general', + thread: null, + kind: 'message', + body: 'first', + }, + ]); + }); + it('shows unread inbox summary', async () => { const relaycastClient = createRelaycastClientMock({ inbox: vi.fn(async () => ({ diff --git a/src/cli/index.test.ts b/src/cli/index.test.ts index 308cad9d2..244ccf49f 100644 --- a/src/cli/index.test.ts +++ b/src/cli/index.test.ts @@ -28,6 +28,7 @@ async function runCli(args: string): Promise<{ stdout: string; stderr: string; c try { const { stdout, stderr } = await execAsync(`node ${CLI_PATH} ${args}`, { cwd: testProjectRoot, // Run in isolated temp directory + timeout: 12000, env: { ...process.env, DOTENV_CONFIG_QUIET: 'true', @@ -78,114 +79,26 @@ describeCli('CLI', () => { }); }); - describe('status', () => { - it('should show status when broker not running', async () => { - // This test assumes broker isn't running on a test socket - const { stdout } = await runCli('status'); - expect(stdout).toMatch(/Status:/i); - }); - }); - describe('agents', () => { - it('should handle no agents file gracefully', async () => { - const { stdout, stderr } = await runCli('agents'); - const output = stdout + stderr; - // Either shows agents, "No agents" message, or broker-not-running error - expect(output).toMatch(/(No agents|NAME.*STATUS|broker|relaycast|Failed)/i); - }, 15000); - - it('should support --json flag', async () => { + it('supports --json output (smoke)', async () => { const { stdout, stderr } = await runCli('agents --json'); - // When broker is running: valid JSON; when not: may output error text - if (stdout.trim()) { - try { - JSON.parse(stdout); - } catch { - // Broker not running — error message in stdout is acceptable - expect(stdout + stderr).toMatch(/(broker|relaycast|Failed)/i); - } - } - }, 15000); - }); - - describe('who', () => { - it('should handle no active agents gracefully', async () => { - const { stdout, stderr } = await runCli('who'); - const output = stdout + stderr; - // Either shows agents, "No active agents", or broker-not-running error - expect(output).toMatch(/(No active agents|NAME|broker|relaycast|Failed)/i); - }); - - it('should support --json flag', async () => { - const { stdout, stderr } = await runCli('who --json'); - if (stdout.trim()) { - try { - JSON.parse(stdout); - } catch { - expect(stdout + stderr).toMatch(/(broker|relaycast|Failed)/i); - } - } - }); - }); - - describe('read', () => { - it('should error when message not found', async () => { - const { stderr, code } = await runCli('read nonexistent-message-id'); - expect(code).not.toBe(0); - // Either "not found" or broker-not-running error - expect(stderr).toMatch(/(not found|broker|relaycast|Failed|ENOENT)/i); - }); - }); + const lines = stdout + .split('\n') + .map((line) => line.trim()) + .filter(Boolean); - describe('history', () => { - it('should show history or empty message', async () => { - const { stdout, stderr, code } = await runCli('history --limit 5'); - // When broker is not running, command may fail — that's acceptable - if (code === 0) { - expect(stdout.length).toBeGreaterThan(0); - } else { - expect(stderr).toMatch(/(broker|relaycast|Failed|ENOENT)/i); - } - }); - - it('should support --json flag', async () => { - const { stdout, stderr } = await runCli('history --json --limit 1'); - if (stdout.trim()) { + const hasJsonPayload = lines.some((line) => { try { - JSON.parse(stdout); + JSON.parse(line); + return true; } catch { - expect(stdout + stderr).toMatch(/(broker|relaycast|Failed)/i); + return false; } - } - }); - }); -}); + }); -describe('CLI Helper Functions', () => { - describe('formatRelativeTime', () => { - // Test the time formatting logic indirectly through agents command - it('should format relative times in agents output', async () => { - const { stdout } = await runCli('agents'); - // If agents exist, should show relative time - if (stdout.includes('ago')) { - expect(stdout).toMatch(/\d+[smhd] ago/); + if (!hasJsonPayload) { + expect(`${stdout}${stderr}`).toMatch(/(broker|relaycast|Failed|not running)/i); } - }); - }); - - describe('parseSince', () => { - // Test through history command - it('should parse duration strings', async () => { - const results = await Promise.all([ - runCli('history --since 1h --limit 1'), - runCli('history --since 30m --limit 1'), - runCli('history --since 7d --limit 1'), - ]); - - for (const result of results) { - expect(Number.isInteger(result.code)).toBe(true); - expect(`${result.stdout}${result.stderr}`).not.toMatch(/(invalid|unknown).+since/i); - } - }, 20000); + }, 15000); }); }); diff --git a/src/cli/lib/agent-management-listing.test.ts b/src/cli/lib/agent-management-listing.test.ts new file mode 100644 index 000000000..ceb9e66f3 --- /dev/null +++ b/src/cli/lib/agent-management-listing.test.ts @@ -0,0 +1,105 @@ +import { describe, expect, it, vi } from 'vitest'; + +import { + runAgentsCommand, + runWhoCommand, + type AgentManagementListingDependencies, + type ListingWorkerInfo, +} from './agent-management-listing.js'; + +function createDeps(options?: { workers?: ListingWorkerInfo[]; listAgentsError?: Error; nowIso?: string }) { + const workers = options?.workers ?? []; + const listAgents = options?.listAgentsError + ? vi.fn(async () => { + throw options.listAgentsError; + }) + : vi.fn(async () => workers); + const shutdown = vi.fn(async () => undefined); + const log = vi.fn(() => undefined); + const error = vi.fn(() => undefined); + const exit = vi.fn((code: number) => { + throw new Error(`exit:${code}`); + }) as unknown as AgentManagementListingDependencies['exit']; + + const deps: AgentManagementListingDependencies = { + getProjectRoot: vi.fn(() => '/tmp/project'), + getDataDir: vi.fn(() => '/tmp/data'), + createClient: vi.fn(() => ({ + listAgents, + shutdown, + })), + fileExists: vi.fn(() => false), + readFile: vi.fn(() => ''), + fetch: vi.fn(async () => { + throw new Error('not implemented'); + }), + nowIso: vi.fn(() => options?.nowIso ?? '2026-03-04T00:00:00.000Z'), + log, + error, + exit, + }; + + return { deps, listAgents, shutdown, log, error }; +} + +describe('agent-management-listing JSON output', () => { + it('runAgentsCommand emits deterministic JSON for visible local agents', async () => { + const { deps, log, shutdown } = createDeps({ + workers: [ + { name: 'WorkerA', runtime: 'codex', model: 'o3', team: 'core', pid: 4242 }, + { name: 'Dashboard', runtime: 'pty' }, + ], + }); + + await runAgentsCommand({ json: true }, deps); + + expect(shutdown).toHaveBeenCalledTimes(1); + expect(log).toHaveBeenCalledTimes(1); + expect(JSON.parse(log.mock.calls[0][0] as string)).toEqual([ + { + name: 'WorkerA', + status: 'ONLINE', + cli: 'codex', + model: 'o3', + team: 'core', + pid: 4242, + location: 'local', + }, + ]); + }); + + it('runWhoCommand emits deterministic JSON with timestamp and status', async () => { + const { deps, log, shutdown } = createDeps({ + workers: [ + { name: 'WorkerWho', cli: 'claude' }, + { name: 'Dashboard', runtime: 'pty' }, + ], + nowIso: '2026-03-04T12:34:56.000Z', + }); + + await runWhoCommand({ json: true }, deps); + + expect(shutdown).toHaveBeenCalledTimes(1); + expect(log).toHaveBeenCalledTimes(1); + expect(JSON.parse(log.mock.calls[0][0] as string)).toEqual([ + { + name: 'WorkerWho', + cli: 'claude', + lastSeen: '2026-03-04T12:34:56.000Z', + status: 'ONLINE', + }, + ]); + }); + + it('runAgentsCommand returns [] JSON when listAgents fails', async () => { + const { deps, log, shutdown } = createDeps({ + listAgentsError: new Error('broker unavailable'), + }); + + await runAgentsCommand({ json: true }, deps); + + expect(shutdown).toHaveBeenCalledTimes(1); + expect(log).toHaveBeenCalledTimes(1); + expect(JSON.parse(log.mock.calls[0][0] as string)).toEqual([]); + }); +}); From 8e6d1e71a6b50e0dc03be41ef1100c6f80410d3c Mon Sep 17 00:00:00 2001 From: Khaliq Date: Wed, 4 Mar 2026 13:19:44 -0800 Subject: [PATCH 2/2] reliability --- .github/workflows/publish.yml | 36 ++++--------- .../completed/2026-03/traj_c8y3gakjeuv8.json | 53 +++++++++++++++++++ .../completed/2026-03/traj_c8y3gakjeuv8.md | 31 +++++++++++ .trajectories/index.json | 9 +++- 4 files changed, 103 insertions(+), 26 deletions(-) create mode 100644 .trajectories/completed/2026-03/traj_c8y3gakjeuv8.json create mode 100644 .trajectories/completed/2026-03/traj_c8y3gakjeuv8.md diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index b65d518cd..ad9f79e76 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -75,6 +75,7 @@ jobs: runs-on: ${{ matrix.os }} if: github.event.inputs.package == 'all' || github.event.inputs.package == 'main' || github.event.inputs.package == 'sdk' strategy: + fail-fast: false matrix: include: - os: macos-latest @@ -99,29 +100,15 @@ jobs: with: targets: ${{ matrix.target }} - - name: Install musl cross-compilation tools - if: contains(matrix.target, 'linux-musl') + - name: Install musl tools (x86_64) + if: matrix.target == 'x86_64-unknown-linux-musl' run: | sudo apt-get update sudo apt-get install -y musl-tools - if [[ "${{ matrix.target }}" == "aarch64-unknown-linux-musl" ]]; then - sudo apt-get install -y gcc-aarch64-linux-gnu - MUSL_URL="https://musl.cc/aarch64-linux-musl-cross.tgz" - MUSL_ARCHIVE="/tmp/aarch64-linux-musl-cross.tgz" - for attempt in 1 2 3; do - if curl -fL --connect-timeout 20 --max-time 180 --retry 3 --retry-delay 2 --retry-all-errors \ - "$MUSL_URL" -o "$MUSL_ARCHIVE"; then - break - fi - if [[ "$attempt" -eq 3 ]]; then - echo "Failed to download aarch64 musl cross-compiler" - exit 1 - fi - sleep $((attempt * 5)) - done - sudo tar -xzf "$MUSL_ARCHIVE" -C /opt - echo "/opt/aarch64-linux-musl-cross/bin" >> "$GITHUB_PATH" - fi + + - name: Install cross (aarch64) + if: matrix.target == 'aarch64-unknown-linux-musl' + uses: taiki-e/install-action@cross - name: Cache cargo uses: Swatinem/rust-cache@v2 @@ -131,12 +118,11 @@ jobs: - name: Build broker binary run: | if [[ "${{ matrix.target }}" == "aarch64-unknown-linux-musl" ]]; then - export CARGO_TARGET_AARCH64_UNKNOWN_LINUX_MUSL_LINKER=aarch64-linux-musl-gcc - export CC_aarch64_unknown_linux_musl=aarch64-linux-musl-gcc + RUSTFLAGS="-C target-feature=+crt-static" cross build --release --bin agent-relay-broker --target ${{ matrix.target }} + else + RUSTFLAGS="-C target-feature=+crt-static" cargo build --release --bin agent-relay-broker --target ${{ matrix.target }} fi - RUSTFLAGS="-C target-feature=+crt-static" cargo build --release --bin agent-relay-broker --target ${{ matrix.target }} - strip target/${{ matrix.target }}/release/agent-relay-broker 2>/dev/null || \ - aarch64-linux-musl-strip target/${{ matrix.target }}/release/agent-relay-broker 2>/dev/null || true + strip target/${{ matrix.target }}/release/agent-relay-broker 2>/dev/null || true - name: Copy binary with platform name run: | diff --git a/.trajectories/completed/2026-03/traj_c8y3gakjeuv8.json b/.trajectories/completed/2026-03/traj_c8y3gakjeuv8.json new file mode 100644 index 000000000..47f0536bf --- /dev/null +++ b/.trajectories/completed/2026-03/traj_c8y3gakjeuv8.json @@ -0,0 +1,53 @@ +{ + "id": "traj_c8y3gakjeuv8", + "version": 1, + "task": { + "title": "Fix publish workflow musl.cc timeout for aarch64 broker build" + }, + "status": "completed", + "startedAt": "2026-03-04T21:18:27.095Z", + "agents": [ + { + "name": "default", + "role": "lead", + "joinedAt": "2026-03-04T21:18:54.262Z" + } + ], + "chapters": [ + { + "id": "chap_q0j0s48w0c3e", + "title": "Work", + "agentName": "default", + "startedAt": "2026-03-04T21:18:54.262Z", + "events": [ + { + "ts": 1772659134263, + "type": "decision", + "content": "Replaced publish workflow aarch64 musl toolchain download from musl.cc with cross-rs: Replaced publish workflow aarch64 musl toolchain download from musl.cc with cross-rs", + "raw": { + "question": "Replaced publish workflow aarch64 musl toolchain download from musl.cc with cross-rs", + "chosen": "Replaced publish workflow aarch64 musl toolchain download from musl.cc with cross-rs", + "alternatives": [], + "reasoning": "musl.cc timeouts cause recurring CI failures; cross uses maintained target container and avoids direct dependency on musl.cc" + }, + "significance": "high" + } + ], + "endedAt": "2026-03-04T21:18:54.432Z" + } + ], + "commits": [], + "filesChanged": [], + "projectId": "/Users/khaliqgant/Projects/agent-workforce/relay", + "tags": [], + "_trace": { + "startRef": "30d6ae6ba1230c5ab41937a5e05e8c3790ad2860", + "endRef": "30d6ae6ba1230c5ab41937a5e05e8c3790ad2860" + }, + "completedAt": "2026-03-04T21:18:54.432Z", + "retrospective": { + "summary": "Patched publish.yml build-broker job to use cross for aarch64-unknown-linux-musl, removed musl.cc curl path, and enabled fail-fast=false for broker matrix", + "approach": "Standard approach", + "confidence": 0.95 + } +} \ No newline at end of file diff --git a/.trajectories/completed/2026-03/traj_c8y3gakjeuv8.md b/.trajectories/completed/2026-03/traj_c8y3gakjeuv8.md new file mode 100644 index 000000000..fc61a1fec --- /dev/null +++ b/.trajectories/completed/2026-03/traj_c8y3gakjeuv8.md @@ -0,0 +1,31 @@ +# Trajectory: Fix publish workflow musl.cc timeout for aarch64 broker build + +> **Status:** ✅ Completed +> **Confidence:** 95% +> **Started:** March 4, 2026 at 01:18 PM +> **Completed:** March 4, 2026 at 01:18 PM + +--- + +## Summary + +Patched publish.yml build-broker job to use cross for aarch64-unknown-linux-musl, removed musl.cc curl path, and enabled fail-fast=false for broker matrix + +**Approach:** Standard approach + +--- + +## Key Decisions + +### Replaced publish workflow aarch64 musl toolchain download from musl.cc with cross-rs +- **Chose:** Replaced publish workflow aarch64 musl toolchain download from musl.cc with cross-rs +- **Reasoning:** musl.cc timeouts cause recurring CI failures; cross uses maintained target container and avoids direct dependency on musl.cc + +--- + +## Chapters + +### 1. Work +*Agent: default* + +- Replaced publish workflow aarch64 musl toolchain download from musl.cc with cross-rs: Replaced publish workflow aarch64 musl toolchain download from musl.cc with cross-rs diff --git a/.trajectories/index.json b/.trajectories/index.json index b6705f97e..b69db0f27 100644 --- a/.trajectories/index.json +++ b/.trajectories/index.json @@ -1,6 +1,6 @@ { "version": 1, - "lastUpdated": "2026-03-04T21:15:50.404Z", + "lastUpdated": "2026-03-04T21:18:54.517Z", "trajectories": { "traj_1b1dj40sl6jl": { "title": "Revert aggressive retry logic in relay-pty-orchestrator", @@ -568,6 +568,13 @@ "startedAt": "2026-03-04T21:15:49.943Z", "completedAt": "2026-03-04T21:15:50.309Z", "path": "/Users/khaliqgant/Projects/agent-workforce/relay/.trajectories/completed/2026-03/traj_fxq5ufnzc3k3.json" + }, + "traj_c8y3gakjeuv8": { + "title": "Fix publish workflow musl.cc timeout for aarch64 broker build", + "status": "completed", + "startedAt": "2026-03-04T21:18:27.095Z", + "completedAt": "2026-03-04T21:18:54.432Z", + "path": "/Users/khaliqgant/Projects/agent-workforce/relay/.trajectories/completed/2026-03/traj_c8y3gakjeuv8.json" } } } \ No newline at end of file