macro-agent 0.0.17 → 0.1.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.
- package/.claude/settings.local.json +3 -1
- package/.sudocode/specs.jsonl +4 -0
- package/CLAUDE.md +16 -14
- package/README.md +11 -29
- package/dist/acp/macro-agent.d.ts +17 -0
- package/dist/acp/macro-agent.d.ts.map +1 -1
- package/dist/acp/macro-agent.js +183 -55
- package/dist/acp/macro-agent.js.map +1 -1
- package/dist/acp/types.d.ts +32 -1
- package/dist/acp/types.d.ts.map +1 -1
- package/dist/acp/types.js.map +1 -1
- package/dist/agent/agent-manager.d.ts +65 -1
- package/dist/agent/agent-manager.d.ts.map +1 -1
- package/dist/agent/agent-manager.js +464 -183
- package/dist/agent/agent-manager.js.map +1 -1
- package/dist/agent/types.d.ts +1 -1
- package/dist/agent/types.d.ts.map +1 -1
- package/dist/api/server.d.ts +3 -0
- package/dist/api/server.d.ts.map +1 -1
- package/dist/api/server.js +37 -6
- package/dist/api/server.js.map +1 -1
- package/dist/auth/index.d.ts +2 -0
- package/dist/auth/index.d.ts.map +1 -0
- package/dist/auth/index.js +2 -0
- package/dist/auth/index.js.map +1 -0
- package/dist/auth/token.d.ts +41 -0
- package/dist/auth/token.d.ts.map +1 -0
- package/dist/auth/token.js +73 -0
- package/dist/auth/token.js.map +1 -0
- package/dist/cli/acp.d.ts +2 -23
- package/dist/cli/acp.d.ts.map +1 -1
- package/dist/cli/acp.js +127 -61
- package/dist/cli/acp.js.map +1 -1
- package/dist/cli/index.js +147 -15
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/mcp.d.ts +6 -0
- package/dist/cli/mcp.d.ts.map +1 -1
- package/dist/cli/mcp.js +268 -181
- package/dist/cli/mcp.js.map +1 -1
- package/dist/cli/parse-args.d.ts +20 -0
- package/dist/cli/parse-args.d.ts.map +1 -0
- package/dist/cli/parse-args.js +43 -0
- package/dist/cli/parse-args.js.map +1 -0
- package/dist/cli/stable-instance-id.d.ts +8 -0
- package/dist/cli/stable-instance-id.d.ts.map +1 -0
- package/dist/cli/stable-instance-id.js +14 -0
- package/dist/cli/stable-instance-id.js.map +1 -0
- package/dist/config/project-config.d.ts +74 -7
- package/dist/config/project-config.d.ts.map +1 -1
- package/dist/config/project-config.js +123 -20
- package/dist/config/project-config.js.map +1 -1
- package/dist/map/adapter/acp-over-map.d.ts +23 -0
- package/dist/map/adapter/acp-over-map.d.ts.map +1 -1
- package/dist/map/adapter/acp-over-map.js +482 -55
- package/dist/map/adapter/acp-over-map.js.map +1 -1
- package/dist/map/adapter/connection-manager.d.ts.map +1 -1
- package/dist/map/adapter/connection-manager.js +3 -0
- package/dist/map/adapter/connection-manager.js.map +1 -1
- package/dist/map/adapter/event-log.d.ts +87 -0
- package/dist/map/adapter/event-log.d.ts.map +1 -0
- package/dist/map/adapter/event-log.js +122 -0
- package/dist/map/adapter/event-log.js.map +1 -0
- package/dist/map/adapter/event-translator.js +6 -6
- package/dist/map/adapter/event-translator.js.map +1 -1
- package/dist/map/adapter/extensions/agent-lifecycle.d.ts +82 -0
- package/dist/map/adapter/extensions/agent-lifecycle.d.ts.map +1 -0
- package/dist/map/adapter/extensions/agent-lifecycle.js +164 -0
- package/dist/map/adapter/extensions/agent-lifecycle.js.map +1 -0
- package/dist/map/adapter/extensions/index.d.ts +10 -1
- package/dist/map/adapter/extensions/index.d.ts.map +1 -1
- package/dist/map/adapter/extensions/index.js +34 -0
- package/dist/map/adapter/extensions/index.js.map +1 -1
- package/dist/map/adapter/extensions/mcp-bridge.d.ts +57 -0
- package/dist/map/adapter/extensions/mcp-bridge.d.ts.map +1 -0
- package/dist/map/adapter/extensions/mcp-bridge.js +745 -0
- package/dist/map/adapter/extensions/mcp-bridge.js.map +1 -0
- package/dist/map/adapter/extensions/rename.d.ts +29 -0
- package/dist/map/adapter/extensions/rename.d.ts.map +1 -0
- package/dist/map/adapter/extensions/rename.js +49 -0
- package/dist/map/adapter/extensions/rename.js.map +1 -0
- package/dist/map/adapter/extensions/task.d.ts.map +1 -1
- package/dist/map/adapter/extensions/task.js +10 -0
- package/dist/map/adapter/extensions/task.js.map +1 -1
- package/dist/map/adapter/extensions/update-metadata.d.ts +29 -0
- package/dist/map/adapter/extensions/update-metadata.d.ts.map +1 -0
- package/dist/map/adapter/extensions/update-metadata.js +67 -0
- package/dist/map/adapter/extensions/update-metadata.js.map +1 -0
- package/dist/map/adapter/index.d.ts +2 -1
- package/dist/map/adapter/index.d.ts.map +1 -1
- package/dist/map/adapter/index.js +8 -2
- package/dist/map/adapter/index.js.map +1 -1
- package/dist/map/adapter/interface.d.ts +2 -0
- package/dist/map/adapter/interface.d.ts.map +1 -1
- package/dist/map/adapter/map-adapter.d.ts +4 -0
- package/dist/map/adapter/map-adapter.d.ts.map +1 -1
- package/dist/map/adapter/map-adapter.js +302 -30
- package/dist/map/adapter/map-adapter.js.map +1 -1
- package/dist/map/adapter/subscription-manager.d.ts.map +1 -1
- package/dist/map/adapter/subscription-manager.js +5 -1
- package/dist/map/adapter/subscription-manager.js.map +1 -1
- package/dist/map/adapter/types.d.ts +2 -0
- package/dist/map/adapter/types.d.ts.map +1 -1
- package/dist/mcp/map-client.d.ts +39 -0
- package/dist/mcp/map-client.d.ts.map +1 -0
- package/dist/mcp/map-client.js +129 -0
- package/dist/mcp/map-client.js.map +1 -0
- package/dist/mcp/mcp-server.d.ts +14 -0
- package/dist/mcp/mcp-server.d.ts.map +1 -1
- package/dist/mcp/mcp-server.js +113 -85
- package/dist/mcp/mcp-server.js.map +1 -1
- package/dist/mcp/types.d.ts +9 -1
- package/dist/mcp/types.d.ts.map +1 -1
- package/dist/mcp/types.js.map +1 -1
- package/dist/metrics/metrics.js +1 -1
- package/dist/metrics/metrics.js.map +1 -1
- package/dist/roles/capabilities.d.ts +3 -1
- package/dist/roles/capabilities.d.ts.map +1 -1
- package/dist/roles/capabilities.js +17 -7
- package/dist/roles/capabilities.js.map +1 -1
- package/dist/roles/config-loader.d.ts +6 -6
- package/dist/roles/config-loader.d.ts.map +1 -1
- package/dist/roles/config-loader.js +6 -6
- package/dist/roles/config-loader.js.map +1 -1
- package/dist/roles/registry.d.ts +2 -2
- package/dist/roles/registry.js +2 -2
- package/dist/server/combined-server.d.ts +20 -0
- package/dist/server/combined-server.d.ts.map +1 -1
- package/dist/server/combined-server.js +107 -8
- package/dist/server/combined-server.js.map +1 -1
- package/dist/store/event-store.d.ts +7 -1
- package/dist/store/event-store.d.ts.map +1 -1
- package/dist/store/event-store.js +91 -8
- package/dist/store/event-store.js.map +1 -1
- package/dist/store/types/agents.d.ts +23 -0
- package/dist/store/types/agents.d.ts.map +1 -1
- package/dist/store/types/events.d.ts +1 -1
- package/dist/store/types/events.d.ts.map +1 -1
- package/dist/task/backend/index.d.ts +47 -29
- package/dist/task/backend/index.d.ts.map +1 -1
- package/dist/task/backend/index.js +109 -71
- package/dist/task/backend/index.js.map +1 -1
- package/dist/task/backend/memory.d.ts +1 -0
- package/dist/task/backend/memory.d.ts.map +1 -1
- package/dist/task/backend/memory.js +3 -0
- package/dist/task/backend/memory.js.map +1 -1
- package/dist/task/backend/opentasks/backend.d.ts +140 -0
- package/dist/task/backend/opentasks/backend.d.ts.map +1 -0
- package/dist/task/backend/opentasks/backend.js +1023 -0
- package/dist/task/backend/opentasks/backend.js.map +1 -0
- package/dist/task/backend/opentasks/client.d.ts +337 -0
- package/dist/task/backend/opentasks/client.d.ts.map +1 -0
- package/dist/task/backend/opentasks/client.js +225 -0
- package/dist/task/backend/opentasks/client.js.map +1 -0
- package/dist/task/backend/opentasks/daemon-manager.d.ts +89 -0
- package/dist/task/backend/opentasks/daemon-manager.d.ts.map +1 -0
- package/dist/task/backend/opentasks/daemon-manager.js +195 -0
- package/dist/task/backend/opentasks/daemon-manager.js.map +1 -0
- package/dist/task/backend/opentasks/index.d.ts +21 -0
- package/dist/task/backend/opentasks/index.d.ts.map +1 -0
- package/dist/task/backend/opentasks/index.js +21 -0
- package/dist/task/backend/opentasks/index.js.map +1 -0
- package/dist/task/backend/opentasks/mapping.d.ts +48 -0
- package/dist/task/backend/opentasks/mapping.d.ts.map +1 -0
- package/dist/task/backend/opentasks/mapping.js +77 -0
- package/dist/task/backend/opentasks/mapping.js.map +1 -0
- package/dist/task/backend/types.d.ts +33 -53
- package/dist/task/backend/types.d.ts.map +1 -1
- package/dist/task/backend/types.js +7 -11
- package/dist/task/backend/types.js.map +1 -1
- package/dist/task/backend/unified-tool-provider.d.ts +57 -0
- package/dist/task/backend/unified-tool-provider.d.ts.map +1 -0
- package/dist/task/backend/unified-tool-provider.js +623 -0
- package/dist/task/backend/unified-tool-provider.js.map +1 -0
- package/dist/teams/team-loader.d.ts +2 -2
- package/dist/teams/team-loader.js +3 -3
- package/dist/teams/team-loader.js.map +1 -1
- package/dist/teams/team-runtime.d.ts.map +1 -1
- package/dist/teams/team-runtime.js +2 -0
- package/dist/teams/team-runtime.js.map +1 -1
- package/docs/architecture.md +7 -6
- package/docs/configuration.md +26 -62
- package/docs/implementation-details.md +5 -5
- package/docs/implementation-summary.md +17 -17
- package/docs/plan-self-driving-support.md +4 -4
- package/docs/spec-self-driving-support.md +10 -10
- package/docs/team-templates.md +2 -2
- package/docs/teams.md +3 -3
- package/docs/troubleshooting.md +10 -11
- package/package.json +6 -4
- package/src/__tests__/e2e/agent-spawn-visibility.e2e.test.ts +761 -0
- package/src/__tests__/e2e/full-agent-conflict-resolution.e2e.test.ts +2 -2
- package/src/__tests__/e2e/mcp-thin-client-bridge.e2e.test.ts +304 -0
- package/src/__tests__/e2e/mcp-tools-available.e2e.test.ts +324 -0
- package/src/__tests__/e2e/multi-agent.e2e.test.ts +5 -5
- package/src/__tests__/e2e/spawn-session-streaming.e2e.test.ts +563 -0
- package/src/acp/__tests__/history.test.ts +8 -4
- package/src/acp/__tests__/integration.test.ts +56 -31
- package/src/acp/__tests__/macro-agent.test.ts +16 -7
- package/src/acp/macro-agent.ts +230 -62
- package/src/acp/types.ts +46 -1
- package/src/agent/__tests__/agent-manager.test.ts +228 -2
- package/src/agent/agent-manager.ts +714 -261
- package/src/agent/types.ts +3 -1
- package/src/api/server.ts +41 -7
- package/src/auth/__tests__/token.test.ts +100 -0
- package/src/auth/index.ts +1 -0
- package/src/auth/token.ts +82 -0
- package/src/cli/__tests__/acp.test.ts +1 -1
- package/src/cli/__tests__/stable-instance-id.test.ts +1 -1
- package/src/cli/acp.ts +130 -72
- package/src/cli/index.ts +120 -14
- package/src/cli/mcp.ts +311 -207
- package/src/cli/parse-args.ts +54 -0
- package/src/cli/stable-instance-id.ts +14 -0
- package/src/config/project-config.ts +190 -27
- package/src/lifecycle/__tests__/cascade-termination.test.ts +1 -1
- package/src/map/adapter/__tests__/acp-over-map-cancel.test.ts +820 -0
- package/src/map/adapter/__tests__/acp-over-map-getmodels.test.ts +355 -0
- package/src/map/adapter/__tests__/acp-over-map-history.test.ts +724 -2
- package/src/map/adapter/__tests__/acp-over-map-persistence.e2e.test.ts +1 -1
- package/src/map/adapter/__tests__/event-broadcast.test.ts +420 -0
- package/src/map/adapter/__tests__/event-log.test.ts +527 -0
- package/src/map/adapter/__tests__/event-translator.test.ts +3 -3
- package/src/map/adapter/__tests__/extensions.test.ts +408 -0
- package/src/map/adapter/__tests__/map-adapter.test.ts +99 -0
- package/src/map/adapter/__tests__/mcp-bridge.test.ts +1187 -0
- package/src/map/adapter/__tests__/multi-client-broadcast.test.ts +711 -0
- package/src/map/adapter/__tests__/websocket-integration.test.ts +218 -0
- package/src/map/adapter/acp-over-map.ts +777 -92
- package/src/map/adapter/connection-manager.ts +3 -0
- package/src/map/adapter/event-log.ts +208 -0
- package/src/map/adapter/event-translator.ts +6 -6
- package/src/map/adapter/extensions/agent-lifecycle.ts +267 -0
- package/src/map/adapter/extensions/index.ts +60 -0
- package/src/map/adapter/extensions/mcp-bridge.ts +995 -0
- package/src/map/adapter/extensions/task.ts +11 -0
- package/src/map/adapter/extensions/update-metadata.ts +126 -0
- package/src/map/adapter/index.ts +28 -0
- package/src/map/adapter/interface.ts +2 -0
- package/src/map/adapter/map-adapter.ts +373 -38
- package/src/map/adapter/subscription-manager.ts +5 -1
- package/src/map/adapter/types.ts +2 -0
- package/src/mcp/__tests__/map-client.test.ts +386 -0
- package/src/mcp/__tests__/mcp-server-thin-client.test.ts +368 -0
- package/src/mcp/__tests__/mcp-server.test.ts +100 -1
- package/src/mcp/map-client.ts +177 -0
- package/src/mcp/mcp-server.ts +191 -100
- package/src/mcp/types.ts +6 -1
- package/src/metrics/metrics.ts +1 -1
- package/src/monitor/__tests__/stale-agent-flow.integration.test.ts +1 -1
- package/src/roles/__tests__/config-loader.test.ts +7 -7
- package/src/roles/capabilities.ts +17 -7
- package/src/roles/config-loader.ts +6 -6
- package/src/roles/registry.ts +2 -2
- package/src/server/__tests__/combined-server.test.ts +94 -21
- package/src/server/combined-server.ts +189 -33
- package/src/steering/__tests__/steering-integration.test.ts +1 -1
- package/src/store/__tests__/event-store.test.ts +236 -1
- package/src/store/__tests__/instance.test.ts +3 -3
- package/src/store/event-store.ts +109 -8
- package/src/store/types/agents.ts +16 -0
- package/src/store/types/events.ts +1 -1
- package/src/task/backend/__tests__/create-task-backend.test.ts +225 -0
- package/src/task/backend/__tests__/e2e/unified-tool-provider-opentasks.e2e.test.ts +524 -0
- package/src/task/backend/__tests__/unified-tool-provider.test.ts +579 -0
- package/src/task/backend/index.ts +156 -106
- package/src/task/backend/memory.ts +4 -0
- package/src/task/backend/opentasks/__tests__/backend.test.ts +968 -0
- package/src/task/backend/opentasks/__tests__/daemon-manager.test.ts +406 -0
- package/src/task/backend/opentasks/__tests__/mapping.test.ts +84 -0
- package/src/task/backend/opentasks/__tests__/opentasks-backend.e2e.test.ts +1338 -0
- package/src/task/backend/opentasks/backend.ts +1323 -0
- package/src/task/backend/opentasks/client.ts +652 -0
- package/src/task/backend/opentasks/daemon-manager.ts +253 -0
- package/src/task/backend/opentasks/index.ts +69 -0
- package/src/task/backend/opentasks/mapping.ts +94 -0
- package/src/task/backend/types.ts +42 -66
- package/src/task/backend/unified-tool-provider.ts +779 -0
- package/src/teams/__tests__/cross-subsystem.integration.test.ts +1 -1
- package/src/teams/team-loader.ts +3 -3
- package/src/teams/team-runtime.ts +2 -0
- package/test_fixtures/README.md +2 -3
- package/test_fixtures/fixtures/index.ts +0 -3
- package/test_fixtures/fixtures/projects/project-with-specs.ts +7 -149
- package/test_fixtures/fixtures/repos/index.ts +1 -3
- package/test_fixtures/fixtures/repos/temp-repo-factory.ts +0 -116
- package/test_fixtures/fixtures/repos/types.ts +0 -11
- package/test_fixtures/harness/__tests__/fixtures.test.ts +10 -102
- package/test_fixtures/harness/__tests__/temp-repo-and-simulator.test.ts +0 -33
- package/test_fixtures/harness/simulator/agent-simulator.ts +4 -4
- package/vitest.config.ts +1 -1
- package/vitest.e2e.config.ts +1 -1
- package/vitest.setup.ts +1 -30
- package/.macro-agent/teams/self-driving/prompts/grinder.md +0 -27
- package/.macro-agent/teams/self-driving/prompts/judge.md +0 -27
- package/.macro-agent/teams/self-driving/prompts/planner.md +0 -33
- package/.macro-agent/teams/self-driving/roles/grinder.yaml +0 -17
- package/.macro-agent/teams/self-driving/roles/judge.yaml +0 -24
- package/.macro-agent/teams/self-driving/roles/planner.yaml +0 -18
- package/.macro-agent/teams/self-driving/team.yaml +0 -103
- package/.macro-agent/teams/structured/prompts/developer.md +0 -26
- package/.macro-agent/teams/structured/prompts/lead.md +0 -25
- package/.macro-agent/teams/structured/prompts/reviewer.md +0 -24
- package/.macro-agent/teams/structured/roles/developer.yaml +0 -12
- package/.macro-agent/teams/structured/roles/lead.yaml +0 -11
- package/.macro-agent/teams/structured/roles/reviewer.yaml +0 -19
- package/.macro-agent/teams/structured/team.yaml +0 -89
- package/docs/sudocode-integration.md +0 -383
- package/src/task/backend/__tests__/backend-parity.test.ts +0 -451
- package/src/task/backend/__tests__/tool-provider-edge-cases.test.ts +0 -430
- package/src/task/backend/__tests__/tool-provider.test.ts +0 -983
- package/src/task/backend/sudocode/__tests__/backend-edge-cases.test.ts +0 -575
- package/src/task/backend/sudocode/__tests__/backend.test.ts +0 -1194
- package/src/task/backend/sudocode/__tests__/client-integration.test.ts +0 -418
- package/src/task/backend/sudocode/__tests__/client.test.ts +0 -345
- package/src/task/backend/sudocode/__tests__/e2e/backend.e2e.test.ts +0 -753
- package/src/task/backend/sudocode/__tests__/e2e/server-client.e2e.test.ts +0 -680
- package/src/task/backend/sudocode/__tests__/e2e-workflow.test.ts +0 -666
- package/src/task/backend/sudocode/__tests__/integration/standalone-client.integration.test.ts +0 -396
- package/src/task/backend/sudocode/__tests__/integration/sudocode-cli.integration.test.ts +0 -328
- package/src/task/backend/sudocode/__tests__/integration/test-utils.ts +0 -175
- package/src/task/backend/sudocode/__tests__/mapping-edge-cases.test.ts +0 -265
- package/src/task/backend/sudocode/__tests__/server-client.test.ts +0 -675
- package/src/task/backend/sudocode/__tests__/sync-policy-edge-cases.test.ts +0 -521
- package/src/task/backend/sudocode/__tests__/sync-policy.test.ts +0 -519
- package/src/task/backend/sudocode/__tests__/tools.test.ts +0 -471
- package/src/task/backend/sudocode/backend.ts +0 -1237
- package/src/task/backend/sudocode/client.ts +0 -515
- package/src/task/backend/sudocode/index.ts +0 -120
- package/src/task/backend/sudocode/mapping.ts +0 -93
- package/src/task/backend/sudocode/server-client.ts +0 -522
- package/src/task/backend/sudocode/standalone-client.ts +0 -623
- package/src/task/backend/sudocode/sync-policy.ts +0 -387
- package/src/task/backend/sudocode/tools.ts +0 -896
- package/src/task/backend/tool-provider.ts +0 -506
- package/test_fixtures/fixtures/sudocode/index.ts +0 -29
- package/test_fixtures/fixtures/sudocode/issues.ts +0 -185
- package/test_fixtures/fixtures/sudocode/specs.ts +0 -159
|
@@ -1,575 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* SudocodeTaskBackend Edge Case Tests
|
|
3
|
-
*
|
|
4
|
-
* Tests for edge cases, race conditions, and error handling in
|
|
5
|
-
* the SudocodeTaskBackend implementation.
|
|
6
|
-
*
|
|
7
|
-
* @module task/backend/sudocode/__tests__/backend-edge-cases.test
|
|
8
|
-
* @see s-8472 Pluggable Task Backend Integration
|
|
9
|
-
* @see s-1zcx Multi-Agent Orchestration Testing Strategy
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
|
|
13
|
-
import {
|
|
14
|
-
SudocodeTaskBackend,
|
|
15
|
-
SudocodeTaskBackendError,
|
|
16
|
-
createSudocodeTaskBackend,
|
|
17
|
-
} from "../backend.js";
|
|
18
|
-
import { createEventStore, type EventStore } from "../../../../store/event-store.js";
|
|
19
|
-
import type { Task, TaskStatus } from "../../../../store/types/index.js";
|
|
20
|
-
import type { SudocodeClient, IssueChangeCallback, Issue } from "../client.js";
|
|
21
|
-
import type { TaskChangeEvent } from "../../types.js";
|
|
22
|
-
|
|
23
|
-
// Create a more realistic mock client
|
|
24
|
-
function createMockClient(): SudocodeClient & {
|
|
25
|
-
_issues: Map<string, Issue>;
|
|
26
|
-
_blockers: Map<string, Issue[]>;
|
|
27
|
-
_blocking: Map<string, Issue[]>;
|
|
28
|
-
_callbacks: IssueChangeCallback[];
|
|
29
|
-
_triggerIssueChange: (event: Parameters<IssueChangeCallback>[0]) => void;
|
|
30
|
-
} {
|
|
31
|
-
const issues = new Map<string, Issue>();
|
|
32
|
-
const blockers = new Map<string, Issue[]>();
|
|
33
|
-
const blocking = new Map<string, Issue[]>();
|
|
34
|
-
const callbacks: IssueChangeCallback[] = [];
|
|
35
|
-
|
|
36
|
-
// Add some test issues
|
|
37
|
-
const testIssue: Issue = {
|
|
38
|
-
id: "i-test1",
|
|
39
|
-
uuid: "uuid-1",
|
|
40
|
-
title: "Test Issue",
|
|
41
|
-
status: "open",
|
|
42
|
-
priority: 2,
|
|
43
|
-
created_at: new Date().toISOString(),
|
|
44
|
-
updated_at: new Date().toISOString(),
|
|
45
|
-
};
|
|
46
|
-
issues.set("i-test1", testIssue);
|
|
47
|
-
|
|
48
|
-
const blockerIssue: Issue = {
|
|
49
|
-
id: "i-blocker",
|
|
50
|
-
uuid: "uuid-blocker",
|
|
51
|
-
title: "Blocker Issue",
|
|
52
|
-
status: "open",
|
|
53
|
-
priority: 1,
|
|
54
|
-
created_at: new Date().toISOString(),
|
|
55
|
-
updated_at: new Date().toISOString(),
|
|
56
|
-
};
|
|
57
|
-
issues.set("i-blocker", blockerIssue);
|
|
58
|
-
|
|
59
|
-
return {
|
|
60
|
-
_issues: issues,
|
|
61
|
-
_blockers: blockers,
|
|
62
|
-
_blocking: blocking,
|
|
63
|
-
_callbacks: callbacks,
|
|
64
|
-
_triggerIssueChange: (event) => {
|
|
65
|
-
for (const cb of callbacks) {
|
|
66
|
-
cb(event);
|
|
67
|
-
}
|
|
68
|
-
},
|
|
69
|
-
|
|
70
|
-
getIssue: vi.fn(async (id: string) => issues.get(id) ?? null),
|
|
71
|
-
createIssue: vi.fn(async (data: Partial<Issue>) => {
|
|
72
|
-
const id = `i-${Date.now()}`;
|
|
73
|
-
const issue: Issue = {
|
|
74
|
-
id,
|
|
75
|
-
uuid: `uuid-${id}`,
|
|
76
|
-
title: data.title ?? "New Issue",
|
|
77
|
-
status: data.status ?? "open",
|
|
78
|
-
priority: data.priority ?? 2,
|
|
79
|
-
created_at: new Date().toISOString(),
|
|
80
|
-
updated_at: new Date().toISOString(),
|
|
81
|
-
};
|
|
82
|
-
issues.set(id, issue);
|
|
83
|
-
return issue;
|
|
84
|
-
}),
|
|
85
|
-
updateIssue: vi.fn(async (id: string, updates: Partial<Issue>) => {
|
|
86
|
-
const issue = issues.get(id);
|
|
87
|
-
if (!issue) throw new Error(`Issue not found: ${id}`);
|
|
88
|
-
Object.assign(issue, updates);
|
|
89
|
-
issue.updated_at = new Date().toISOString();
|
|
90
|
-
return issue;
|
|
91
|
-
}),
|
|
92
|
-
getReadyIssues: vi.fn(async () => {
|
|
93
|
-
return Array.from(issues.values()).filter((i) => {
|
|
94
|
-
const issueBlockers = blockers.get(i.id) ?? [];
|
|
95
|
-
return issueBlockers.every((b) => b.status === "closed");
|
|
96
|
-
});
|
|
97
|
-
}),
|
|
98
|
-
getBlockers: vi.fn(async (id: string) => blockers.get(id) ?? []),
|
|
99
|
-
getBlocking: vi.fn(async (id: string) => blocking.get(id) ?? []),
|
|
100
|
-
createLink: vi.fn(async (from: string, to: string, type: string) => {
|
|
101
|
-
if (type === "blocks") {
|
|
102
|
-
const fromIssue = issues.get(from);
|
|
103
|
-
if (!fromIssue) return;
|
|
104
|
-
const existing = blockers.get(to) ?? [];
|
|
105
|
-
if (!existing.some((b) => b.id === from)) {
|
|
106
|
-
blockers.set(to, [...existing, fromIssue]);
|
|
107
|
-
}
|
|
108
|
-
const blockingList = blocking.get(from) ?? [];
|
|
109
|
-
const toIssue = issues.get(to);
|
|
110
|
-
if (toIssue && !blockingList.some((b) => b.id === to)) {
|
|
111
|
-
blocking.set(from, [...blockingList, toIssue]);
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
}),
|
|
115
|
-
removeLink: vi.fn(async (from: string, to: string, type: string) => {
|
|
116
|
-
if (type === "blocks") {
|
|
117
|
-
const existing = blockers.get(to) ?? [];
|
|
118
|
-
blockers.set(to, existing.filter((b) => b.id !== from));
|
|
119
|
-
const blockingList = blocking.get(from) ?? [];
|
|
120
|
-
blocking.set(from, blockingList.filter((b) => b.id !== to));
|
|
121
|
-
}
|
|
122
|
-
}),
|
|
123
|
-
onIssueChange: vi.fn((callback: IssueChangeCallback) => {
|
|
124
|
-
callbacks.push(callback);
|
|
125
|
-
return () => {
|
|
126
|
-
const idx = callbacks.indexOf(callback);
|
|
127
|
-
if (idx >= 0) callbacks.splice(idx, 1);
|
|
128
|
-
};
|
|
129
|
-
}),
|
|
130
|
-
close: vi.fn(),
|
|
131
|
-
isReady: vi.fn(() => true),
|
|
132
|
-
} as unknown as SudocodeClient & {
|
|
133
|
-
_issues: Map<string, Issue>;
|
|
134
|
-
_blockers: Map<string, Issue[]>;
|
|
135
|
-
_blocking: Map<string, Issue[]>;
|
|
136
|
-
_callbacks: IssueChangeCallback[];
|
|
137
|
-
_triggerIssueChange: (event: Parameters<IssueChangeCallback>[0]) => void;
|
|
138
|
-
};
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
describe("SudocodeTaskBackend Edge Cases", () => {
|
|
142
|
-
let eventStore: EventStore;
|
|
143
|
-
let backend: SudocodeTaskBackend;
|
|
144
|
-
let mockClient: ReturnType<typeof createMockClient>;
|
|
145
|
-
const testAgentId = "agent_test";
|
|
146
|
-
|
|
147
|
-
beforeEach(async () => {
|
|
148
|
-
eventStore = await createEventStore({ inMemory: true });
|
|
149
|
-
mockClient = createMockClient();
|
|
150
|
-
backend = createSudocodeTaskBackend(eventStore, mockClient, {
|
|
151
|
-
syncStatus: true,
|
|
152
|
-
autoCloseIssues: false,
|
|
153
|
-
});
|
|
154
|
-
});
|
|
155
|
-
|
|
156
|
-
afterEach(async () => {
|
|
157
|
-
backend.close();
|
|
158
|
-
await eventStore.close();
|
|
159
|
-
});
|
|
160
|
-
|
|
161
|
-
describe("onTaskChange isBlocked accuracy", () => {
|
|
162
|
-
it("onTaskChange correctly reports isBlocked for local blockers", async () => {
|
|
163
|
-
const events: TaskChangeEvent[] = [];
|
|
164
|
-
const unsubscribe = backend.onTaskChange((event) => {
|
|
165
|
-
events.push(event);
|
|
166
|
-
});
|
|
167
|
-
|
|
168
|
-
// Create a blocked task
|
|
169
|
-
const blocker = await backend.create({
|
|
170
|
-
description: "Blocker",
|
|
171
|
-
created_by: testAgentId,
|
|
172
|
-
});
|
|
173
|
-
const blocked = await backend.create({
|
|
174
|
-
description: "Blocked",
|
|
175
|
-
created_by: testAgentId,
|
|
176
|
-
});
|
|
177
|
-
await backend.addBlocker(blocked.id, blocker.id);
|
|
178
|
-
|
|
179
|
-
// The task IS blocked
|
|
180
|
-
const task = await backend.get(blocked.id);
|
|
181
|
-
expect(task?.isBlocked).toBe(true);
|
|
182
|
-
|
|
183
|
-
// The event should also show isBlocked as true (for local blockers)
|
|
184
|
-
const blockedEvents = events.filter((e) => e.taskId === blocked.id);
|
|
185
|
-
expect(blockedEvents.length).toBeGreaterThan(0);
|
|
186
|
-
|
|
187
|
-
// Last event should show correct isBlocked status
|
|
188
|
-
const lastEvent = blockedEvents[blockedEvents.length - 1];
|
|
189
|
-
expect(lastEvent.task.isBlocked).toBe(true);
|
|
190
|
-
|
|
191
|
-
unsubscribe();
|
|
192
|
-
});
|
|
193
|
-
|
|
194
|
-
it("onTaskChange updates isBlocked when blocker completes", async () => {
|
|
195
|
-
const events: TaskChangeEvent[] = [];
|
|
196
|
-
const unsubscribe = backend.onTaskChange((event) => {
|
|
197
|
-
events.push(event);
|
|
198
|
-
});
|
|
199
|
-
|
|
200
|
-
const blocker = await backend.create({
|
|
201
|
-
description: "Blocker",
|
|
202
|
-
created_by: testAgentId,
|
|
203
|
-
});
|
|
204
|
-
const blocked = await backend.create({
|
|
205
|
-
description: "Blocked",
|
|
206
|
-
created_by: testAgentId,
|
|
207
|
-
});
|
|
208
|
-
await backend.addBlocker(blocked.id, blocker.id);
|
|
209
|
-
|
|
210
|
-
// Complete the blocker
|
|
211
|
-
await backend.start(blocker.id);
|
|
212
|
-
await backend.complete(blocker.id);
|
|
213
|
-
|
|
214
|
-
// Check the blocked task
|
|
215
|
-
const task = await backend.get(blocked.id);
|
|
216
|
-
expect(task?.isBlocked).toBe(false);
|
|
217
|
-
|
|
218
|
-
unsubscribe();
|
|
219
|
-
});
|
|
220
|
-
});
|
|
221
|
-
|
|
222
|
-
describe("delete behavior", () => {
|
|
223
|
-
it("delete throws not supported for all task states", async () => {
|
|
224
|
-
const pendingTask = await backend.create({
|
|
225
|
-
description: "Pending",
|
|
226
|
-
created_by: testAgentId,
|
|
227
|
-
});
|
|
228
|
-
|
|
229
|
-
const completedTask = await backend.create({
|
|
230
|
-
description: "Completed",
|
|
231
|
-
created_by: testAgentId,
|
|
232
|
-
});
|
|
233
|
-
await backend.start(completedTask.id);
|
|
234
|
-
await backend.complete(completedTask.id);
|
|
235
|
-
|
|
236
|
-
// Delete should throw "not supported" for all states
|
|
237
|
-
await expect(backend.delete(pendingTask.id)).rejects.toThrow(
|
|
238
|
-
"not supported"
|
|
239
|
-
);
|
|
240
|
-
await expect(backend.delete(completedTask.id)).rejects.toThrow(
|
|
241
|
-
"not supported"
|
|
242
|
-
);
|
|
243
|
-
|
|
244
|
-
// Tasks should remain unchanged
|
|
245
|
-
const pending = await backend.get(pendingTask.id);
|
|
246
|
-
const completed = await backend.get(completedTask.id);
|
|
247
|
-
expect(pending?.status).toBe("pending");
|
|
248
|
-
expect(completed?.status).toBe("completed");
|
|
249
|
-
});
|
|
250
|
-
});
|
|
251
|
-
|
|
252
|
-
describe("circular dependency detection", () => {
|
|
253
|
-
it("should handle self-blocking gracefully", async () => {
|
|
254
|
-
const task = await backend.create({
|
|
255
|
-
description: "Self",
|
|
256
|
-
created_by: testAgentId,
|
|
257
|
-
});
|
|
258
|
-
|
|
259
|
-
// Self-blocking should either be prevented or handled
|
|
260
|
-
await backend.addBlocker(task.id, task.id);
|
|
261
|
-
|
|
262
|
-
const selfBlocked = await backend.get(task.id);
|
|
263
|
-
// If allowed, the task would be permanently blocked by itself
|
|
264
|
-
// The implementation should either:
|
|
265
|
-
// 1. Throw an error
|
|
266
|
-
// 2. Silently ignore
|
|
267
|
-
// 3. Not mark it as blocked by itself
|
|
268
|
-
expect(selfBlocked).toBeDefined();
|
|
269
|
-
});
|
|
270
|
-
|
|
271
|
-
it("should detect circular dependency A -> B -> A", async () => {
|
|
272
|
-
const taskA = await backend.create({
|
|
273
|
-
description: "Task A",
|
|
274
|
-
created_by: testAgentId,
|
|
275
|
-
});
|
|
276
|
-
const taskB = await backend.create({
|
|
277
|
-
description: "Task B",
|
|
278
|
-
created_by: testAgentId,
|
|
279
|
-
});
|
|
280
|
-
|
|
281
|
-
// A blocks B
|
|
282
|
-
await backend.addBlocker(taskB.id, taskA.id);
|
|
283
|
-
|
|
284
|
-
// B blocks A - this creates a cycle
|
|
285
|
-
// The implementation should handle this gracefully
|
|
286
|
-
await backend.addBlocker(taskA.id, taskB.id);
|
|
287
|
-
|
|
288
|
-
// Both tasks are now blocked by each other
|
|
289
|
-
const a = await backend.get(taskA.id);
|
|
290
|
-
const b = await backend.get(taskB.id);
|
|
291
|
-
|
|
292
|
-
// At least one should be workable (or an error should have been thrown)
|
|
293
|
-
// Currently, both become permanently blocked - this is problematic
|
|
294
|
-
expect(a?.isBlocked).toBe(true);
|
|
295
|
-
expect(b?.isBlocked).toBe(true);
|
|
296
|
-
|
|
297
|
-
// listReady should not return blocked tasks
|
|
298
|
-
const ready = await backend.listReady();
|
|
299
|
-
expect(ready.find((t) => t.id === taskA.id)).toBeUndefined();
|
|
300
|
-
expect(ready.find((t) => t.id === taskB.id)).toBeUndefined();
|
|
301
|
-
});
|
|
302
|
-
});
|
|
303
|
-
|
|
304
|
-
describe("listReady with status filter", () => {
|
|
305
|
-
it("should respect provided status filter", async () => {
|
|
306
|
-
const task = await backend.create({
|
|
307
|
-
description: "Test",
|
|
308
|
-
created_by: testAgentId,
|
|
309
|
-
});
|
|
310
|
-
|
|
311
|
-
await backend.start(task.id);
|
|
312
|
-
|
|
313
|
-
const inProgressTask = await backend.get(task.id);
|
|
314
|
-
expect(inProgressTask?.status).toBe("in_progress");
|
|
315
|
-
|
|
316
|
-
// listReady with in_progress status filter should include the task
|
|
317
|
-
// because the implementation uses: filter?.status ?? ["pending", "assigned"]
|
|
318
|
-
// where ?? only applies when status is nullish
|
|
319
|
-
const ready = await backend.listReady({ status: "in_progress" });
|
|
320
|
-
expect(ready.length).toBe(1);
|
|
321
|
-
expect(ready[0].id).toBe(task.id);
|
|
322
|
-
});
|
|
323
|
-
|
|
324
|
-
it("should default to pending/assigned when no status filter provided", async () => {
|
|
325
|
-
const pending = await backend.create({
|
|
326
|
-
description: "Pending",
|
|
327
|
-
created_by: testAgentId,
|
|
328
|
-
});
|
|
329
|
-
const inProgress = await backend.create({
|
|
330
|
-
description: "In Progress",
|
|
331
|
-
created_by: testAgentId,
|
|
332
|
-
});
|
|
333
|
-
await backend.start(inProgress.id);
|
|
334
|
-
|
|
335
|
-
// listReady without filter defaults to pending/assigned
|
|
336
|
-
const ready = await backend.listReady();
|
|
337
|
-
expect(ready.length).toBe(1);
|
|
338
|
-
expect(ready[0].id).toBe(pending.id);
|
|
339
|
-
expect(ready.find((t) => t.id === inProgress.id)).toBeUndefined();
|
|
340
|
-
});
|
|
341
|
-
});
|
|
342
|
-
|
|
343
|
-
describe("external issue binding", () => {
|
|
344
|
-
it("should validate issue exists before binding", async () => {
|
|
345
|
-
const task = await backend.create({
|
|
346
|
-
description: "Test",
|
|
347
|
-
created_by: testAgentId,
|
|
348
|
-
});
|
|
349
|
-
|
|
350
|
-
// Try to bind to non-existent issue
|
|
351
|
-
await expect(
|
|
352
|
-
backend.bindToIssue(task.id, "i-nonexistent")
|
|
353
|
-
).rejects.toThrow("Issue not found");
|
|
354
|
-
});
|
|
355
|
-
|
|
356
|
-
it("should unbind correctly", async () => {
|
|
357
|
-
const task = await backend.create({
|
|
358
|
-
description: "Test",
|
|
359
|
-
created_by: testAgentId,
|
|
360
|
-
external_id: "i-test1",
|
|
361
|
-
});
|
|
362
|
-
|
|
363
|
-
expect(backend.getIssueForTask(task.id)).toBe("i-test1");
|
|
364
|
-
|
|
365
|
-
await backend.unbindFromIssue(task.id);
|
|
366
|
-
|
|
367
|
-
expect(backend.getIssueForTask(task.id)).toBeUndefined();
|
|
368
|
-
});
|
|
369
|
-
|
|
370
|
-
it("should handle rebinding to different issue", async () => {
|
|
371
|
-
const task = await backend.create({
|
|
372
|
-
description: "Test",
|
|
373
|
-
created_by: testAgentId,
|
|
374
|
-
external_id: "i-test1",
|
|
375
|
-
});
|
|
376
|
-
|
|
377
|
-
// Create another issue
|
|
378
|
-
mockClient._issues.set("i-test2", {
|
|
379
|
-
id: "i-test2",
|
|
380
|
-
uuid: "uuid-2",
|
|
381
|
-
title: "Test Issue 2",
|
|
382
|
-
status: "open",
|
|
383
|
-
priority: 2,
|
|
384
|
-
created_at: new Date().toISOString(),
|
|
385
|
-
updated_at: new Date().toISOString(),
|
|
386
|
-
});
|
|
387
|
-
|
|
388
|
-
await backend.bindToIssue(task.id, "i-test2");
|
|
389
|
-
|
|
390
|
-
expect(backend.getIssueForTask(task.id)).toBe("i-test2");
|
|
391
|
-
expect(backend.getTasksByIssue("i-test1")).not.toContain(task.id);
|
|
392
|
-
expect(backend.getTasksByIssue("i-test2")).toContain(task.id);
|
|
393
|
-
});
|
|
394
|
-
});
|
|
395
|
-
|
|
396
|
-
describe("issue change propagation", () => {
|
|
397
|
-
it("should update task status when issue status changes", async () => {
|
|
398
|
-
const task = await backend.create({
|
|
399
|
-
description: "Test",
|
|
400
|
-
created_by: testAgentId,
|
|
401
|
-
external_id: "i-test1",
|
|
402
|
-
});
|
|
403
|
-
|
|
404
|
-
// Initially pending
|
|
405
|
-
let currentTask = await backend.get(task.id);
|
|
406
|
-
expect(currentTask?.status).toBe("pending");
|
|
407
|
-
|
|
408
|
-
// Simulate issue status change to in_progress
|
|
409
|
-
mockClient._issues.get("i-test1")!.status = "in_progress";
|
|
410
|
-
mockClient._triggerIssueChange({
|
|
411
|
-
type: "status_changed",
|
|
412
|
-
issueId: "i-test1",
|
|
413
|
-
issue: mockClient._issues.get("i-test1")!,
|
|
414
|
-
});
|
|
415
|
-
|
|
416
|
-
// Give async handlers time to run
|
|
417
|
-
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
418
|
-
|
|
419
|
-
currentTask = await backend.get(task.id);
|
|
420
|
-
expect(currentTask?.status).toBe("in_progress");
|
|
421
|
-
});
|
|
422
|
-
|
|
423
|
-
it("should not update completed task status", async () => {
|
|
424
|
-
const task = await backend.create({
|
|
425
|
-
description: "Test",
|
|
426
|
-
created_by: testAgentId,
|
|
427
|
-
external_id: "i-test1",
|
|
428
|
-
});
|
|
429
|
-
|
|
430
|
-
// Complete the task
|
|
431
|
-
await backend.start(task.id);
|
|
432
|
-
await backend.complete(task.id);
|
|
433
|
-
|
|
434
|
-
// Simulate issue status change back to open
|
|
435
|
-
mockClient._issues.get("i-test1")!.status = "open";
|
|
436
|
-
mockClient._triggerIssueChange({
|
|
437
|
-
type: "status_changed",
|
|
438
|
-
issueId: "i-test1",
|
|
439
|
-
issue: mockClient._issues.get("i-test1")!,
|
|
440
|
-
});
|
|
441
|
-
|
|
442
|
-
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
443
|
-
|
|
444
|
-
// Task should remain completed
|
|
445
|
-
const currentTask = await backend.get(task.id);
|
|
446
|
-
expect(currentTask?.status).toBe("completed");
|
|
447
|
-
});
|
|
448
|
-
});
|
|
449
|
-
|
|
450
|
-
describe("concurrent operations", () => {
|
|
451
|
-
it("should handle concurrent task creations", async () => {
|
|
452
|
-
const promises = Array.from({ length: 10 }, (_, i) =>
|
|
453
|
-
backend.create({
|
|
454
|
-
description: `Task ${i}`,
|
|
455
|
-
created_by: testAgentId,
|
|
456
|
-
})
|
|
457
|
-
);
|
|
458
|
-
|
|
459
|
-
const tasks = await Promise.all(promises);
|
|
460
|
-
|
|
461
|
-
// All tasks should have unique IDs
|
|
462
|
-
const ids = new Set(tasks.map((t) => t.id));
|
|
463
|
-
expect(ids.size).toBe(10);
|
|
464
|
-
});
|
|
465
|
-
|
|
466
|
-
it("should handle concurrent blocker operations", async () => {
|
|
467
|
-
const blocker = await backend.create({
|
|
468
|
-
description: "Blocker",
|
|
469
|
-
created_by: testAgentId,
|
|
470
|
-
});
|
|
471
|
-
|
|
472
|
-
const blocked = await Promise.all(
|
|
473
|
-
Array.from({ length: 5 }, (_, i) =>
|
|
474
|
-
backend.create({
|
|
475
|
-
description: `Blocked ${i}`,
|
|
476
|
-
created_by: testAgentId,
|
|
477
|
-
})
|
|
478
|
-
)
|
|
479
|
-
);
|
|
480
|
-
|
|
481
|
-
// Add blockers concurrently
|
|
482
|
-
await Promise.all(
|
|
483
|
-
blocked.map((task) => backend.addBlocker(task.id, blocker.id))
|
|
484
|
-
);
|
|
485
|
-
|
|
486
|
-
// All should be blocked
|
|
487
|
-
for (const task of blocked) {
|
|
488
|
-
const current = await backend.get(task.id);
|
|
489
|
-
expect(current?.isBlocked).toBe(true);
|
|
490
|
-
}
|
|
491
|
-
});
|
|
492
|
-
});
|
|
493
|
-
|
|
494
|
-
describe("sudocode blocker integration", () => {
|
|
495
|
-
it("should check sudocode blockers for isBlocked", async () => {
|
|
496
|
-
// Create task bound to i-test1
|
|
497
|
-
const task = await backend.create({
|
|
498
|
-
description: "Test",
|
|
499
|
-
created_by: testAgentId,
|
|
500
|
-
external_id: "i-test1",
|
|
501
|
-
});
|
|
502
|
-
|
|
503
|
-
// Set up blocker in mock client
|
|
504
|
-
mockClient._blockers.set("i-test1", [mockClient._issues.get("i-blocker")!]);
|
|
505
|
-
|
|
506
|
-
const currentTask = await backend.get(task.id);
|
|
507
|
-
expect(currentTask?.isBlocked).toBe(true);
|
|
508
|
-
|
|
509
|
-
// Mark blocker as closed
|
|
510
|
-
mockClient._issues.get("i-blocker")!.status = "closed";
|
|
511
|
-
|
|
512
|
-
const afterClose = await backend.get(task.id);
|
|
513
|
-
expect(afterClose?.isBlocked).toBe(false);
|
|
514
|
-
});
|
|
515
|
-
|
|
516
|
-
it("should sync blocker relationships to sudocode", async () => {
|
|
517
|
-
// Create tasks bound to issues
|
|
518
|
-
const blocker = await backend.create({
|
|
519
|
-
description: "Blocker",
|
|
520
|
-
created_by: testAgentId,
|
|
521
|
-
external_id: "i-blocker",
|
|
522
|
-
});
|
|
523
|
-
const blocked = await backend.create({
|
|
524
|
-
description: "Blocked",
|
|
525
|
-
created_by: testAgentId,
|
|
526
|
-
external_id: "i-test1",
|
|
527
|
-
});
|
|
528
|
-
|
|
529
|
-
await backend.addBlocker(blocked.id, blocker.id);
|
|
530
|
-
|
|
531
|
-
// Verify createLink was called
|
|
532
|
-
expect(mockClient.createLink).toHaveBeenCalledWith(
|
|
533
|
-
"i-blocker",
|
|
534
|
-
"i-test1",
|
|
535
|
-
"blocks"
|
|
536
|
-
);
|
|
537
|
-
});
|
|
538
|
-
});
|
|
539
|
-
|
|
540
|
-
describe("error handling", () => {
|
|
541
|
-
it("should handle sudocode client errors gracefully", async () => {
|
|
542
|
-
const task = await backend.create({
|
|
543
|
-
description: "Test",
|
|
544
|
-
created_by: testAgentId,
|
|
545
|
-
external_id: "i-test1",
|
|
546
|
-
});
|
|
547
|
-
|
|
548
|
-
// Make client throw on getBlockers
|
|
549
|
-
vi.mocked(mockClient.getBlockers).mockRejectedValueOnce(
|
|
550
|
-
new Error("Network error")
|
|
551
|
-
);
|
|
552
|
-
|
|
553
|
-
// Should not throw, assumes unblocked when can't check
|
|
554
|
-
const current = await backend.get(task.id);
|
|
555
|
-
expect(current?.isBlocked).toBe(false);
|
|
556
|
-
});
|
|
557
|
-
|
|
558
|
-
it("should handle sudocode sync errors without failing operations", async () => {
|
|
559
|
-
// Make updateIssue fail
|
|
560
|
-
vi.mocked(mockClient.updateIssue).mockRejectedValue(
|
|
561
|
-
new Error("Sync error")
|
|
562
|
-
);
|
|
563
|
-
|
|
564
|
-
const task = await backend.create({
|
|
565
|
-
description: "Test",
|
|
566
|
-
created_by: testAgentId,
|
|
567
|
-
external_id: "i-test1",
|
|
568
|
-
});
|
|
569
|
-
|
|
570
|
-
// These should still succeed even if sync fails
|
|
571
|
-
await expect(backend.assign(task.id, "agent_a")).resolves.not.toThrow();
|
|
572
|
-
await expect(backend.start(task.id)).resolves.not.toThrow();
|
|
573
|
-
});
|
|
574
|
-
});
|
|
575
|
-
});
|