maskweaver 0.9.4 → 0.9.6
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/README.ko.md +638 -592
- package/README.md +671 -667
- package/dist/cli/doctor.js +5 -21
- package/dist/cli/install.d.ts +0 -8
- package/dist/cli/install.js +0 -39
- package/dist/context/config.d.ts +0 -22
- package/dist/context/config.js +0 -28
- package/dist/context/feature.d.ts +0 -39
- package/dist/context/feature.js +0 -77
- package/dist/context/files.d.ts +0 -13
- package/dist/context/files.js +1 -24
- package/dist/context/index.d.ts +0 -7
- package/dist/context/index.js +0 -12
- package/dist/context/project.d.ts +0 -21
- package/dist/context/project.js +0 -30
- package/dist/context/types.d.ts +0 -48
- package/dist/context/types.js +0 -12
- package/dist/context/utils.d.ts +0 -18
- package/dist/context/utils.js +0 -27
- package/dist/core/engine/promptBuilder.d.ts +0 -17
- package/dist/core/engine/promptBuilder.js +0 -28
- package/dist/core/index.d.ts +0 -6
- package/dist/core/index.js +0 -9
- package/dist/core/loader/MaskLoader.d.ts +0 -23
- package/dist/core/loader/MaskLoader.js +0 -29
- package/dist/core/schema/types.d.ts +0 -47
- package/dist/core/schema/types.js +0 -6
- package/dist/core/schema/validator.d.ts +0 -14
- package/dist/core/schema/validator.js +0 -18
- package/dist/i18n/index.d.ts +0 -18
- package/dist/i18n/index.js +4 -23
- package/dist/index.d.ts +0 -8
- package/dist/index.js +0 -8
- package/dist/lib.d.ts +0 -5
- package/dist/lib.js +0 -12
- package/dist/memory/chunking.d.ts +0 -22
- package/dist/memory/chunking.js +2 -37
- package/dist/memory/core.d.ts +0 -29
- package/dist/memory/core.js +1 -52
- package/dist/memory/index.d.ts +0 -5
- package/dist/memory/index.js +0 -10
- package/dist/memory/indexer.d.ts +0 -21
- package/dist/memory/indexer.js +0 -44
- package/dist/memory/providers/examples.d.ts +0 -5
- package/dist/memory/providers/examples.js +4 -64
- package/dist/memory/providers/factory.d.ts +0 -44
- package/dist/memory/providers/factory.js +0 -46
- package/dist/memory/providers/index.d.ts +0 -26
- package/dist/memory/providers/index.js +0 -28
- package/dist/memory/providers/ollama.d.ts +0 -6
- package/dist/memory/providers/ollama.js +1 -8
- package/dist/memory/providers/openai.d.ts +0 -6
- package/dist/memory/providers/openai.js +1 -8
- package/dist/memory/providers/openrouter.d.ts +0 -6
- package/dist/memory/providers/openrouter.js +0 -8
- package/dist/memory/providers/text-only.d.ts +0 -13
- package/dist/memory/providers/text-only.js +0 -17
- package/dist/memory/providers/types.d.ts +0 -39
- package/dist/memory/providers/types.js +0 -7
- package/dist/memory/providers/voyage.d.ts +0 -22
- package/dist/memory/providers/voyage.js +1 -24
- package/dist/memory/search/hybrid.d.ts +0 -12
- package/dist/memory/search/hybrid.js +1 -22
- package/dist/memory/store/sqlite.d.ts +0 -72
- package/dist/memory/store/sqlite.js +4 -127
- package/dist/plugin/config/index.d.ts +0 -112
- package/dist/plugin/config/index.js +0 -115
- package/dist/plugin/index.d.ts +0 -13
- package/dist/plugin/index.js +1 -123
- package/dist/plugin/tools/command-registry.d.ts +0 -6
- package/dist/plugin/tools/command-registry.js +0 -14
- package/dist/plugin/tools/context.d.ts +0 -12
- package/dist/plugin/tools/context.js +0 -58
- package/dist/plugin/tools/maskSave.d.ts +0 -3
- package/dist/plugin/tools/maskSave.js +0 -3
- package/dist/plugin/tools/memoryGet.d.ts +0 -3
- package/dist/plugin/tools/memoryGet.js +0 -3
- package/dist/plugin/tools/memoryIndexer.d.ts +0 -3
- package/dist/plugin/tools/memoryIndexer.js +0 -10
- package/dist/plugin/tools/memorySearch.d.ts +0 -31
- package/dist/plugin/tools/memorySearch.js +0 -79
- package/dist/plugin/tools/memoryWrite.d.ts +0 -8
- package/dist/plugin/tools/memoryWrite.js +0 -32
- package/dist/plugin/tools/retrospect.d.ts +0 -3
- package/dist/plugin/tools/retrospect.js +0 -3
- package/dist/plugin/tools/slashcommand.d.ts +0 -11
- package/dist/plugin/tools/slashcommand.js +0 -38
- package/dist/plugin/tools/squad.d.ts +0 -12
- package/dist/plugin/tools/squad.js +11 -83
- package/dist/plugin/tools/weave.d.ts +0 -6
- package/dist/plugin/tools/weave.js +0 -78
- package/dist/plugin/types.d.ts +0 -20
- package/dist/plugin/types.js +0 -7
- package/dist/retrospect/index.d.ts +0 -7
- package/dist/retrospect/index.js +0 -9
- package/dist/retrospect/mask-save.d.ts +0 -12
- package/dist/retrospect/mask-save.js +1 -80
- package/dist/retrospect/retrospect.d.ts +0 -18
- package/dist/retrospect/retrospect.js +0 -63
- package/dist/retrospect/strategies/base.d.ts +0 -15
- package/dist/retrospect/strategies/base.js +0 -7
- package/dist/retrospect/strategies/deep.d.ts +0 -12
- package/dist/retrospect/strategies/deep.js +0 -24
- package/dist/retrospect/strategies/index.d.ts +0 -12
- package/dist/retrospect/strategies/index.js +0 -12
- package/dist/retrospect/strategies/quick.d.ts +0 -12
- package/dist/retrospect/strategies/quick.js +0 -19
- package/dist/retrospect/strategies/standard.d.ts +0 -12
- package/dist/retrospect/strategies/standard.js +0 -15
- package/dist/retrospect/types.d.ts +0 -7
- package/dist/retrospect/types.js +0 -7
- package/dist/shared/config.d.ts +0 -105
- package/dist/shared/config.js +0 -33
- package/dist/shared/errors.d.ts +0 -18
- package/dist/shared/errors.js +0 -19
- package/dist/shared/generate-agents.d.ts +0 -69
- package/dist/shared/generate-agents.js +2 -86
- package/dist/shared/image.d.ts +0 -67
- package/dist/shared/image.js +6 -104
- package/dist/shared/index.d.ts +0 -5
- package/dist/shared/index.js +0 -7
- package/dist/shared/model-registry.d.ts +0 -72
- package/dist/shared/model-registry.js +5 -95
- package/dist/shared/types.d.ts +0 -15
- package/dist/shared/types.js +0 -3
- package/dist/shared-context/dag.d.ts +0 -105
- package/dist/shared-context/dag.js +3 -114
- package/dist/shared-context/index.d.ts +0 -5
- package/dist/shared-context/index.js +0 -15
- package/dist/shared-context/logger.d.ts +0 -37
- package/dist/shared-context/logger.js +0 -41
- package/dist/shared-context/parallel-executor.d.ts +0 -54
- package/dist/shared-context/parallel-executor.js +4 -56
- package/dist/shared-context/session.d.ts +0 -56
- package/dist/shared-context/session.js +0 -47
- package/dist/shared-context/squad.d.ts +0 -68
- package/dist/shared-context/squad.js +0 -63
- package/dist/shared-context/storage.d.ts +0 -132
- package/dist/shared-context/storage.js +0 -116
- package/dist/shared-context/task.d.ts +0 -120
- package/dist/shared-context/task.js +0 -152
- package/dist/shared-context/test/dag.test.js +9 -14
- package/dist/shared-context/test/logger.test.d.ts +0 -8
- package/dist/shared-context/test/logger.test.js +0 -52
- package/dist/shared-context/test/session.test.d.ts +0 -7
- package/dist/shared-context/test/session.test.js +0 -63
- package/dist/shared-context/test/squad.test.d.ts +0 -10
- package/dist/shared-context/test/squad.test.js +2 -68
- package/dist/shared-context/test/storage.test.d.ts +0 -8
- package/dist/shared-context/test/storage.test.js +0 -68
- package/dist/shared-context/test/task.test.d.ts +0 -7
- package/dist/shared-context/test/task.test.js +0 -54
- package/dist/shared-context/test/watchdog.test.d.ts +0 -7
- package/dist/shared-context/test/watchdog.test.js +3 -58
- package/dist/shared-context/types.d.ts +0 -215
- package/dist/shared-context/types.js +0 -125
- package/dist/shared-context/watchdog.d.ts +0 -127
- package/dist/shared-context/watchdog.js +0 -148
- package/dist/shared-context/worktree.d.ts +0 -68
- package/dist/shared-context/worktree.js +2 -34
- package/dist/verify/budget.d.ts +0 -29
- package/dist/verify/budget.js +0 -34
- package/dist/verify/critical-files.d.ts +0 -17
- package/dist/verify/critical-files.js +0 -37
- package/dist/verify/escalation.d.ts +0 -20
- package/dist/verify/escalation.js +0 -22
- package/dist/verify/index.d.ts +0 -5
- package/dist/verify/index.js +0 -11
- package/dist/verify/prompts.d.ts +0 -20
- package/dist/verify/prompts.js +0 -20
- package/dist/verify/types.d.ts +0 -26
- package/dist/verify/types.js +1 -12
- package/dist/verify/verifier.d.ts +0 -29
- package/dist/verify/verifier.js +0 -54
- package/dist/version.d.ts +1 -16
- package/dist/version.js +1 -16
- package/dist/weave/bridge.d.ts +0 -35
- package/dist/weave/bridge.js +0 -51
- package/dist/weave/environment/detector.d.ts +0 -6
- package/dist/weave/environment/detector.js +4 -45
- package/dist/weave/environment/index.d.ts +0 -19
- package/dist/weave/environment/index.js +1 -39
- package/dist/weave/environment/issues.d.ts +0 -35
- package/dist/weave/environment/issues.js +0 -59
- package/dist/weave/git.d.ts +0 -8
- package/dist/weave/git.js +0 -8
- package/dist/weave/index.d.ts +0 -13
- package/dist/weave/index.js +2 -28
- package/dist/weave/knowledge/global.d.ts +0 -39
- package/dist/weave/knowledge/global.js +2 -78
- package/dist/weave/loop.js +0 -3
- package/dist/weave/orchestrator.d.ts +0 -69
- package/dist/weave/orchestrator.js +1 -101
- package/dist/weave/phase-manager.d.ts +0 -64
- package/dist/weave/phase-manager.js +0 -89
- package/dist/weave/security/secret-scan.d.ts +0 -14
- package/dist/weave/security/secret-scan.js +0 -19
- package/dist/weave/stages/build.js +0 -15
- package/dist/weave/stages/execute.d.ts +0 -42
- package/dist/weave/stages/execute.js +4 -86
- package/dist/weave/stages/handoff.d.ts +0 -7
- package/dist/weave/stages/handoff.js +0 -43
- package/dist/weave/stages/index.d.ts +0 -3
- package/dist/weave/stages/index.js +0 -3
- package/dist/weave/stages/intake.d.ts +0 -8
- package/dist/weave/stages/intake.js +5 -65
- package/dist/weave/stages/map.d.ts +0 -1
- package/dist/weave/stages/openspec.d.ts +0 -1
- package/dist/weave/stages/plan.d.ts +0 -11
- package/dist/weave/stages/plan.js +1 -53
- package/dist/weave/stages/refine.d.ts +0 -7
- package/dist/weave/stages/refine.js +0 -7
- package/dist/weave/stages/research.d.ts +0 -6
- package/dist/weave/stages/research.js +0 -6
- package/dist/weave/stages/spec.d.ts +0 -12
- package/dist/weave/stages/spec.js +0 -17
- package/dist/weave/types.d.ts +0 -20
- package/dist/weave/types.js +0 -5
- package/dist/weave/verification/commands.d.ts +0 -12
- package/dist/weave/verification/commands.js +0 -19
- package/dist/weave/verification/index.d.ts +0 -6
- package/dist/weave/verification/index.js +1 -19
- package/dist/weave/verification/playwright.d.ts +0 -47
- package/dist/weave/verification/playwright.js +1 -90
- package/dist/weave/worktree.d.ts +0 -16
- package/dist/weave/worktree.js +0 -23
- package/dist/weave/yaml-repair.d.ts +0 -39
- package/dist/weave/yaml-repair.js +13 -116
- package/package.json +1 -1
|
@@ -1,54 +1,10 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Task Management
|
|
3
|
-
*
|
|
4
|
-
* Tasks are the atomic units of work within a squad.
|
|
5
|
-
* "Make the implicit explicit" - Eric Evans
|
|
6
|
-
*
|
|
7
|
-
* This module embodies the Task aggregate, ensuring domain invariants:
|
|
8
|
-
* - A task always belongs to exactly one squad
|
|
9
|
-
* - Task count per squad is bounded (LIMITS.maxTasksPerSquad)
|
|
10
|
-
* - Task identity is immutable once assigned
|
|
11
|
-
*
|
|
12
|
-
* @author Kent Beck's Dummy Human
|
|
13
|
-
*/
|
|
14
1
|
import { randomUUID } from "crypto";
|
|
15
2
|
import { LIMITS, validateCreateTaskOptions, validateTaskResult } from "./types.js";
|
|
16
3
|
import { getSquad, updateSquadState } from "./squad.js";
|
|
17
4
|
import { logEvent } from "./logger.js";
|
|
18
5
|
import { ValidationError, StorageError } from "../shared/errors.js";
|
|
19
|
-
// ============================================================================
|
|
20
|
-
// Task Assignment - The heart of work distribution
|
|
21
|
-
// ============================================================================
|
|
22
|
-
/**
|
|
23
|
-
* Assign a new task to an agent within a squad.
|
|
24
|
-
*
|
|
25
|
-
* This is a domain operation that enforces invariants:
|
|
26
|
-
* 1. The target squad must exist
|
|
27
|
-
* 2. The squad's task capacity must not be exceeded
|
|
28
|
-
* 3. Task options must be valid at the boundary
|
|
29
|
-
*
|
|
30
|
-
* The task starts in "pending" status, awaiting execution.
|
|
31
|
-
*
|
|
32
|
-
* @param session - The parent session containing the squad
|
|
33
|
-
* @param squadId - ID of the squad to assign the task to
|
|
34
|
-
* @param options - Task creation options (assignee, description, priority, etc.)
|
|
35
|
-
* @returns The newly created TaskState
|
|
36
|
-
* @throws {StorageError} If the squad doesn't exist
|
|
37
|
-
* @throws {ValidationError} If task limit is exceeded or options are invalid
|
|
38
|
-
*
|
|
39
|
-
* @example
|
|
40
|
-
* const task = await assignTask(session, "squad-a1b2c3d4", {
|
|
41
|
-
* assignee: "worker-1",
|
|
42
|
-
* description: "Implement user login form",
|
|
43
|
-
* priority: "high",
|
|
44
|
-
* dependencies: ["task-setup"]
|
|
45
|
-
* });
|
|
46
|
-
* console.log(`Assigned task ${task.taskId} to ${task.assignee}`);
|
|
47
|
-
*/
|
|
48
6
|
export async function assignTask(session, squadId, options) {
|
|
49
|
-
// Validate at boundary - Parse, don't validate
|
|
50
7
|
const validatedOptions = validateCreateTaskOptions(options);
|
|
51
|
-
// Retrieve the squad - domain aggregate root
|
|
52
8
|
const squad = await getSquad(session, squadId);
|
|
53
9
|
if (!squad) {
|
|
54
10
|
throw new StorageError(`Squad not found: ${squadId}`, {
|
|
@@ -56,7 +12,6 @@ export async function assignTask(session, squadId, options) {
|
|
|
56
12
|
sessionId: session.manifest.sessionId,
|
|
57
13
|
});
|
|
58
14
|
}
|
|
59
|
-
// Enforce domain invariant: task capacity
|
|
60
15
|
if (squad.state.tasks.length >= LIMITS.maxTasksPerSquad) {
|
|
61
16
|
throw new ValidationError("Maximum tasks per squad exceeded", {
|
|
62
17
|
squadId,
|
|
@@ -64,7 +19,6 @@ export async function assignTask(session, squadId, options) {
|
|
|
64
19
|
current: squad.state.tasks.length,
|
|
65
20
|
});
|
|
66
21
|
}
|
|
67
|
-
// Create the task entity with identity
|
|
68
22
|
const taskId = `task-${randomUUID().slice(0, 8)}`;
|
|
69
23
|
const now = new Date().toISOString();
|
|
70
24
|
const task = {
|
|
@@ -76,12 +30,10 @@ export async function assignTask(session, squadId, options) {
|
|
|
76
30
|
dependencies: validatedOptions.dependencies,
|
|
77
31
|
createdAt: now,
|
|
78
32
|
};
|
|
79
|
-
// Update squad aggregate - append task to collection
|
|
80
33
|
const updatedTasks = [...squad.state.tasks, task];
|
|
81
34
|
await updateSquadState(session, squadId, {
|
|
82
35
|
tasks: updatedTasks,
|
|
83
36
|
});
|
|
84
|
-
// Emit domain event for observability
|
|
85
37
|
await logEvent(session, squadId, {
|
|
86
38
|
type: "task_assigned",
|
|
87
39
|
taskId: task.taskId,
|
|
@@ -90,29 +42,7 @@ export async function assignTask(session, squadId, options) {
|
|
|
90
42
|
});
|
|
91
43
|
return task;
|
|
92
44
|
}
|
|
93
|
-
// ============================================================================
|
|
94
|
-
// Task Query - Domain Entity Lookup
|
|
95
|
-
// ============================================================================
|
|
96
|
-
/**
|
|
97
|
-
* Get a specific task from a squad by taskId.
|
|
98
|
-
*
|
|
99
|
-
* "Make implicit concepts explicit" - Eric Evans
|
|
100
|
-
* Task lookup is a first-class domain operation, not hidden in aggregate traversal.
|
|
101
|
-
*
|
|
102
|
-
* @param session - Parent session
|
|
103
|
-
* @param squadId - Squad containing the task
|
|
104
|
-
* @param taskId - Task ID to find
|
|
105
|
-
* @returns TaskState or null if not found (no error thrown for missing task)
|
|
106
|
-
* @throws {StorageError} If the squad doesn't exist
|
|
107
|
-
*
|
|
108
|
-
* @example
|
|
109
|
-
* const task = await getTask(session, "squad-a1b2c3d4", "task-12345678");
|
|
110
|
-
* if (task) {
|
|
111
|
-
* console.log(`Task ${task.taskId} is ${task.status}`);
|
|
112
|
-
* }
|
|
113
|
-
*/
|
|
114
45
|
export async function getTask(session, squadId, taskId) {
|
|
115
|
-
// Retrieve the squad aggregate
|
|
116
46
|
const squad = await getSquad(session, squadId);
|
|
117
47
|
if (!squad) {
|
|
118
48
|
throw new StorageError(`Squad not found: ${squadId}`, {
|
|
@@ -120,45 +50,10 @@ export async function getTask(session, squadId, taskId) {
|
|
|
120
50
|
sessionId: session.manifest.sessionId,
|
|
121
51
|
});
|
|
122
52
|
}
|
|
123
|
-
// Search for task within the squad's task collection
|
|
124
53
|
const task = squad.state.tasks.find((t) => t.taskId === taskId);
|
|
125
|
-
// Return null for not found (expected case, not an error)
|
|
126
54
|
return task ?? null;
|
|
127
55
|
}
|
|
128
|
-
// ============================================================================
|
|
129
|
-
// Task Update - State Transition through Aggregate Root
|
|
130
|
-
// ============================================================================
|
|
131
|
-
/**
|
|
132
|
-
* Update a task's status and optionally record progress.
|
|
133
|
-
* This is the primary way to advance task lifecycle.
|
|
134
|
-
*
|
|
135
|
-
* DDD Principle: All state mutations go through the Aggregate Root (Squad).
|
|
136
|
-
* "Make implicit concepts explicit" - Eric Evans
|
|
137
|
-
*
|
|
138
|
-
* @param session - Parent session
|
|
139
|
-
* @param squadId - Squad containing the task
|
|
140
|
-
* @param taskId - Task to update
|
|
141
|
-
* @param updates - Partial updates (status, result, etc.)
|
|
142
|
-
* @returns Updated TaskState
|
|
143
|
-
* @throws {StorageError} If squad or task not found
|
|
144
|
-
*
|
|
145
|
-
* @example
|
|
146
|
-
* // Start a task
|
|
147
|
-
* const updated = await updateTask(session, squadId, taskId, {
|
|
148
|
-
* status: "active",
|
|
149
|
-
* startedAt: new Date().toISOString()
|
|
150
|
-
* });
|
|
151
|
-
*
|
|
152
|
-
* @example
|
|
153
|
-
* // Complete a task with result
|
|
154
|
-
* const completed = await updateTask(session, squadId, taskId, {
|
|
155
|
-
* status: "completed",
|
|
156
|
-
* completedAt: new Date().toISOString(),
|
|
157
|
-
* result: { success: true, output: "Feature implemented" }
|
|
158
|
-
* });
|
|
159
|
-
*/
|
|
160
56
|
export async function updateTask(session, squadId, taskId, updates) {
|
|
161
|
-
// Retrieve the squad aggregate root
|
|
162
57
|
const squad = await getSquad(session, squadId);
|
|
163
58
|
if (!squad) {
|
|
164
59
|
throw new StorageError(`Squad not found: ${squadId}`, {
|
|
@@ -166,7 +61,6 @@ export async function updateTask(session, squadId, taskId, updates) {
|
|
|
166
61
|
sessionId: session.manifest.sessionId,
|
|
167
62
|
});
|
|
168
63
|
}
|
|
169
|
-
// Find the task within the aggregate
|
|
170
64
|
const taskIndex = squad.state.tasks.findIndex((t) => t.taskId === taskId);
|
|
171
65
|
if (taskIndex === -1) {
|
|
172
66
|
throw new StorageError(`Task not found: ${taskId}`, {
|
|
@@ -177,19 +71,15 @@ export async function updateTask(session, squadId, taskId, updates) {
|
|
|
177
71
|
}
|
|
178
72
|
const existingTask = squad.state.tasks[taskIndex];
|
|
179
73
|
const previousStatus = existingTask.status;
|
|
180
|
-
// Apply updates via spread - immutable update pattern
|
|
181
74
|
const updatedTask = {
|
|
182
75
|
...existingTask,
|
|
183
76
|
...updates,
|
|
184
77
|
};
|
|
185
|
-
// Update the tasks array immutably
|
|
186
78
|
const updatedTasks = [...squad.state.tasks];
|
|
187
79
|
updatedTasks[taskIndex] = updatedTask;
|
|
188
|
-
// Persist through aggregate root
|
|
189
80
|
await updateSquadState(session, squadId, {
|
|
190
81
|
tasks: updatedTasks,
|
|
191
82
|
});
|
|
192
|
-
// Log status change as domain event (only if status actually changed)
|
|
193
83
|
if (updates.status && updates.status !== previousStatus) {
|
|
194
84
|
if (updates.status === "completed") {
|
|
195
85
|
await logEvent(session, squadId, {
|
|
@@ -200,8 +90,6 @@ export async function updateTask(session, squadId, taskId, updates) {
|
|
|
200
90
|
});
|
|
201
91
|
}
|
|
202
92
|
else {
|
|
203
|
-
// Generic status change - use error event type for now
|
|
204
|
-
// (Could extend LogEvent union for task_status_changed)
|
|
205
93
|
await logEvent(session, squadId, {
|
|
206
94
|
type: "error",
|
|
207
95
|
message: `Task ${taskId} status changed: ${previousStatus} -> ${updates.status}`,
|
|
@@ -210,49 +98,9 @@ export async function updateTask(session, squadId, taskId, updates) {
|
|
|
210
98
|
}
|
|
211
99
|
return updatedTask;
|
|
212
100
|
}
|
|
213
|
-
// ============================================================================
|
|
214
|
-
// Task Completion - Explicit Domain Operation
|
|
215
|
-
// ============================================================================
|
|
216
|
-
/**
|
|
217
|
-
* Complete a task with a result.
|
|
218
|
-
*
|
|
219
|
-
* "Make implicit concepts explicit" - Eric Evans
|
|
220
|
-
* Task completion is a first-class domain operation with its own semantics.
|
|
221
|
-
*
|
|
222
|
-
* This is a convenience wrapper around updateTask() that:
|
|
223
|
-
* - Validates the TaskResult at the boundary
|
|
224
|
-
* - Sets the appropriate status based on result.success
|
|
225
|
-
* - Records completedAt timestamp
|
|
226
|
-
*
|
|
227
|
-
* @param session - Parent session
|
|
228
|
-
* @param squadId - Squad containing the task
|
|
229
|
-
* @param taskId - Task to complete
|
|
230
|
-
* @param result - TaskResult with success/failure info
|
|
231
|
-
* @returns Updated TaskState
|
|
232
|
-
* @throws {StorageError} If squad or task not found
|
|
233
|
-
* @throws {ValidationError} If result is invalid
|
|
234
|
-
*
|
|
235
|
-
* @example
|
|
236
|
-
* // Success case
|
|
237
|
-
* const completed = await completeTask(session, squadId, taskId, {
|
|
238
|
-
* success: true,
|
|
239
|
-
* output: { files: ["src/auth/login.ts"] },
|
|
240
|
-
* metrics: { duration: 45000, tokensUsed: 1500 }
|
|
241
|
-
* });
|
|
242
|
-
*
|
|
243
|
-
* @example
|
|
244
|
-
* // Failure case
|
|
245
|
-
* const failed = await completeTask(session, squadId, taskId, {
|
|
246
|
-
* success: false,
|
|
247
|
-
* error: { code: "TIMEOUT", message: "Task exceeded timeout" }
|
|
248
|
-
* });
|
|
249
|
-
*/
|
|
250
101
|
export async function completeTask(session, squadId, taskId, result) {
|
|
251
|
-
// Validate at boundary
|
|
252
102
|
const validatedResult = validateTaskResult(result);
|
|
253
|
-
// Determine status from result
|
|
254
103
|
const status = validatedResult.success ? "completed" : "failed";
|
|
255
|
-
// Delegate to updateTask with appropriate fields
|
|
256
104
|
return updateTask(session, squadId, taskId, {
|
|
257
105
|
status,
|
|
258
106
|
completedAt: new Date().toISOString(),
|
|
@@ -1,8 +1,6 @@
|
|
|
1
|
-
// src/shared-context/test/dag.test.ts
|
|
2
1
|
import { describe, test, expect } from 'vitest';
|
|
3
2
|
import { buildDAG, getReadyTasks, areDependenciesMet, validateDependencies, } from '../dag.js';
|
|
4
3
|
import { ValidationError } from '../../shared/errors.js';
|
|
5
|
-
// Helper to create a TaskState object for tests
|
|
6
4
|
const createTask = (taskId, dependencies = [], status = 'pending', priority = 'medium') => ({
|
|
7
5
|
taskId,
|
|
8
6
|
assignee: 'test-assignee',
|
|
@@ -25,7 +23,7 @@ describe('DAG Module', () => {
|
|
|
25
23
|
expect(dag.waves).toEqual([
|
|
26
24
|
{ waveIndex: 0, taskIds: ['A', 'B', 'C'].sort() },
|
|
27
25
|
]);
|
|
28
|
-
expect(dag.criticalPath.length).toBe(1);
|
|
26
|
+
expect(dag.criticalPath.length).toBe(1);
|
|
29
27
|
expect(dag.parallelismFactor).toBeCloseTo(3);
|
|
30
28
|
});
|
|
31
29
|
test('should correctly build DAG for linear dependencies (A->B->C)', () => {
|
|
@@ -58,16 +56,15 @@ describe('DAG Module', () => {
|
|
|
58
56
|
{ waveIndex: 1, taskIds: ['B', 'C'].sort() },
|
|
59
57
|
{ waveIndex: 2, taskIds: ['D'] },
|
|
60
58
|
]);
|
|
61
|
-
// Critical path can be A->B->D or A->C->D, both have length 3
|
|
62
59
|
expect(dag.criticalPath.length).toBe(3);
|
|
63
60
|
expect(dag.criticalPath[0]).toBe('A');
|
|
64
61
|
expect(['B', 'C']).toContain(dag.criticalPath[1]);
|
|
65
62
|
expect(dag.criticalPath[2]).toBe('D');
|
|
66
|
-
expect(dag.parallelismFactor).toBeCloseTo(4 / 3);
|
|
63
|
+
expect(dag.parallelismFactor).toBeCloseTo(4 / 3);
|
|
67
64
|
});
|
|
68
65
|
test('should detect a cycle and throw ValidationError (A->B->C->A)', () => {
|
|
69
66
|
const tasks = [
|
|
70
|
-
createTask('A', ['C']),
|
|
67
|
+
createTask('A', ['C']),
|
|
71
68
|
createTask('B', ['A']),
|
|
72
69
|
createTask('C', ['B']),
|
|
73
70
|
];
|
|
@@ -100,7 +97,7 @@ describe('DAG Module', () => {
|
|
|
100
97
|
test('should throw ValidationError for dangling dependencies', () => {
|
|
101
98
|
const tasks = [
|
|
102
99
|
createTask('A'),
|
|
103
|
-
createTask('B', ['X']),
|
|
100
|
+
createTask('B', ['X']),
|
|
104
101
|
];
|
|
105
102
|
expect(() => buildDAG(tasks)).toThrow(ValidationError);
|
|
106
103
|
expect(() => buildDAG(tasks)).toThrow("Invalid dependencies: Task B depends on non-existent task X");
|
|
@@ -120,8 +117,8 @@ describe('DAG Module', () => {
|
|
|
120
117
|
{ waveIndex: 1, taskIds: ['B', 'D'].sort() },
|
|
121
118
|
{ waveIndex: 2, taskIds: ['E'] },
|
|
122
119
|
]);
|
|
123
|
-
expect(dag.criticalPath.length).toBe(3);
|
|
124
|
-
expect(dag.criticalPath[0]).toBe('A');
|
|
120
|
+
expect(dag.criticalPath.length).toBe(3);
|
|
121
|
+
expect(dag.criticalPath[0]).toBe('A');
|
|
125
122
|
expect(['B', 'D']).toContain(dag.criticalPath[1]);
|
|
126
123
|
expect(dag.criticalPath[2]).toBe('E');
|
|
127
124
|
expect(dag.parallelismFactor).toBeCloseTo(5 / 3);
|
|
@@ -216,11 +213,9 @@ describe('DAG Module', () => {
|
|
|
216
213
|
test('should return false if a dependency does not exist (handled by validateDependencies)', () => {
|
|
217
214
|
const tasks = [
|
|
218
215
|
createTask('A', [], 'completed'),
|
|
219
|
-
createTask('C', ['A', 'X']),
|
|
216
|
+
createTask('C', ['A', 'X']),
|
|
220
217
|
];
|
|
221
218
|
const taskC = tasks[1];
|
|
222
|
-
// This function assumes valid dependencies. If a dependency is missing,
|
|
223
|
-
// find will return undefined, and (undefined && undefined.status === 'completed') will be false.
|
|
224
219
|
expect(areDependenciesMet(taskC, tasks)).toBe(false);
|
|
225
220
|
});
|
|
226
221
|
});
|
|
@@ -247,8 +242,8 @@ describe('DAG Module', () => {
|
|
|
247
242
|
test('should return valid false and errors for tasks with non-existent dependencies', () => {
|
|
248
243
|
const tasks = [
|
|
249
244
|
createTask('A'),
|
|
250
|
-
createTask('B', ['A', 'X']),
|
|
251
|
-
createTask('C', ['Y']),
|
|
245
|
+
createTask('B', ['A', 'X']),
|
|
246
|
+
createTask('C', ['Y']),
|
|
252
247
|
];
|
|
253
248
|
const result = validateDependencies(tasks);
|
|
254
249
|
expect(result.valid).toBe(false);
|
|
@@ -1,11 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Logger Unit Tests
|
|
3
|
-
*
|
|
4
|
-
* Tests for logEvent and readLog functions
|
|
5
|
-
* Following Red-Green-Refactor cycle
|
|
6
|
-
*
|
|
7
|
-
* @author Kent Beck's TDD Approach
|
|
8
|
-
*/
|
|
9
1
|
import { describe, test, expect, beforeEach, afterEach } from "vitest";
|
|
10
2
|
import { tmpdir } from "os";
|
|
11
3
|
import { join } from "path";
|
|
@@ -14,26 +6,18 @@ import { existsSync } from "fs";
|
|
|
14
6
|
import { logEvent, readLog } from "../logger.js";
|
|
15
7
|
import { FileStorageAdapter } from "../storage.js";
|
|
16
8
|
import { createSession } from "../session.js";
|
|
17
|
-
// ============================================================================
|
|
18
|
-
// Test Setup
|
|
19
|
-
// ============================================================================
|
|
20
9
|
let tempDir;
|
|
21
10
|
let storage;
|
|
22
11
|
beforeEach(async () => {
|
|
23
|
-
// Create unique temp directory for each test
|
|
24
12
|
tempDir = join(tmpdir(), `logger-test-${Date.now()}-${Math.random().toString(36).slice(2)}`);
|
|
25
13
|
await mkdir(tempDir, { recursive: true });
|
|
26
14
|
storage = new FileStorageAdapter(tempDir);
|
|
27
15
|
});
|
|
28
16
|
afterEach(async () => {
|
|
29
|
-
// Clean up temp directory
|
|
30
17
|
if (existsSync(tempDir)) {
|
|
31
18
|
await rm(tempDir, { recursive: true, force: true });
|
|
32
19
|
}
|
|
33
20
|
});
|
|
34
|
-
// ============================================================================
|
|
35
|
-
// Helper Functions
|
|
36
|
-
// ============================================================================
|
|
37
21
|
async function createTestSession() {
|
|
38
22
|
return createSession(storage, {
|
|
39
23
|
goal: "Test goal",
|
|
@@ -41,35 +25,26 @@ async function createTestSession() {
|
|
|
41
25
|
});
|
|
42
26
|
}
|
|
43
27
|
async function createSquadDir(session, squadId) {
|
|
44
|
-
// Use mkdir directly with absolute path to avoid ensureDir's path handling issues
|
|
45
28
|
const fullPath = storage.getFullPath(`${session.sessionPath}/squads/${squadId}`);
|
|
46
29
|
await mkdir(fullPath, { recursive: true });
|
|
47
30
|
}
|
|
48
|
-
// ============================================================================
|
|
49
|
-
// logEvent Tests
|
|
50
|
-
// ============================================================================
|
|
51
31
|
describe("logEvent", () => {
|
|
52
32
|
test("should create log.jsonl file after logging an event", async () => {
|
|
53
|
-
// Arrange
|
|
54
33
|
const session = await createTestSession();
|
|
55
34
|
const squadId = "squad-001";
|
|
56
35
|
await createSquadDir(session, squadId);
|
|
57
|
-
// Act
|
|
58
36
|
await logEvent(session, squadId, {
|
|
59
37
|
type: "squad_started",
|
|
60
38
|
squadId,
|
|
61
39
|
operator: "operator-agent",
|
|
62
40
|
});
|
|
63
|
-
// Assert
|
|
64
41
|
const logPath = storage.getFullPath(`${session.sessionPath}/squads/${squadId}/log.jsonl`);
|
|
65
42
|
expect(existsSync(logPath)).toBe(true);
|
|
66
43
|
});
|
|
67
44
|
test("should append multiple events in order", async () => {
|
|
68
|
-
// Arrange
|
|
69
45
|
const session = await createTestSession();
|
|
70
46
|
const squadId = "squad-002";
|
|
71
47
|
await createSquadDir(session, squadId);
|
|
72
|
-
// Act - Log three events
|
|
73
48
|
await logEvent(session, squadId, {
|
|
74
49
|
type: "squad_started",
|
|
75
50
|
squadId,
|
|
@@ -87,7 +62,6 @@ describe("logEvent", () => {
|
|
|
87
62
|
assignee: "worker-agent",
|
|
88
63
|
result: { success: true },
|
|
89
64
|
});
|
|
90
|
-
// Assert - Read and verify order
|
|
91
65
|
const events = await readLog(session, squadId);
|
|
92
66
|
expect(events).toHaveLength(3);
|
|
93
67
|
expect(events[0].type).toBe("squad_started");
|
|
@@ -95,39 +69,32 @@ describe("logEvent", () => {
|
|
|
95
69
|
expect(events[2].type).toBe("task_completed");
|
|
96
70
|
});
|
|
97
71
|
test("should auto-generate ts field with ISO timestamp", async () => {
|
|
98
|
-
// Arrange
|
|
99
72
|
const session = await createTestSession();
|
|
100
73
|
const squadId = "squad-003";
|
|
101
74
|
await createSquadDir(session, squadId);
|
|
102
75
|
const beforeTime = new Date().toISOString();
|
|
103
|
-
// Act
|
|
104
76
|
await logEvent(session, squadId, {
|
|
105
77
|
type: "error",
|
|
106
78
|
message: "Something went wrong",
|
|
107
79
|
source: "test-agent",
|
|
108
80
|
});
|
|
109
81
|
const afterTime = new Date().toISOString();
|
|
110
|
-
// Assert
|
|
111
82
|
const events = await readLog(session, squadId);
|
|
112
83
|
expect(events).toHaveLength(1);
|
|
113
84
|
expect(events[0].ts).toBeDefined();
|
|
114
85
|
expect(typeof events[0].ts).toBe("string");
|
|
115
|
-
// Verify ts is between before and after
|
|
116
86
|
expect(events[0].ts >= beforeTime).toBe(true);
|
|
117
87
|
expect(events[0].ts <= afterTime).toBe(true);
|
|
118
88
|
});
|
|
119
89
|
test("should preserve all event properties", async () => {
|
|
120
|
-
// Arrange
|
|
121
90
|
const session = await createTestSession();
|
|
122
91
|
const squadId = "squad-004";
|
|
123
92
|
await createSquadDir(session, squadId);
|
|
124
|
-
// Act
|
|
125
93
|
await logEvent(session, squadId, {
|
|
126
94
|
type: "context_updated",
|
|
127
95
|
key: "shared-data",
|
|
128
96
|
updatedBy: "agent-x",
|
|
129
97
|
});
|
|
130
|
-
// Assert
|
|
131
98
|
const events = await readLog(session, squadId);
|
|
132
99
|
expect(events).toHaveLength(1);
|
|
133
100
|
const event = events[0];
|
|
@@ -136,16 +103,11 @@ describe("logEvent", () => {
|
|
|
136
103
|
expect(event.updatedBy).toBe("agent-x");
|
|
137
104
|
});
|
|
138
105
|
});
|
|
139
|
-
// ============================================================================
|
|
140
|
-
// readLog Tests
|
|
141
|
-
// ============================================================================
|
|
142
106
|
describe("readLog", () => {
|
|
143
107
|
test("should return all logged events", async () => {
|
|
144
|
-
// Arrange
|
|
145
108
|
const session = await createTestSession();
|
|
146
109
|
const squadId = "squad-005";
|
|
147
110
|
await createSquadDir(session, squadId);
|
|
148
|
-
// Log events
|
|
149
111
|
await logEvent(session, squadId, {
|
|
150
112
|
type: "squad_started",
|
|
151
113
|
squadId,
|
|
@@ -156,42 +118,30 @@ describe("readLog", () => {
|
|
|
156
118
|
squadId,
|
|
157
119
|
finalStatus: "completed",
|
|
158
120
|
});
|
|
159
|
-
// Act
|
|
160
121
|
const events = await readLog(session, squadId);
|
|
161
|
-
// Assert
|
|
162
122
|
expect(events).toHaveLength(2);
|
|
163
123
|
expect(events[0].type).toBe("squad_started");
|
|
164
124
|
expect(events[1].type).toBe("squad_completed");
|
|
165
125
|
});
|
|
166
126
|
test("should return empty array for empty log", async () => {
|
|
167
|
-
// Arrange
|
|
168
127
|
const session = await createTestSession();
|
|
169
128
|
const squadId = "squad-006";
|
|
170
129
|
await createSquadDir(session, squadId);
|
|
171
|
-
// Create an empty log file
|
|
172
130
|
const logPath = `${session.sessionPath}/squads/${squadId}/log.jsonl`;
|
|
173
131
|
await writeFile(storage.getFullPath(logPath), "");
|
|
174
|
-
// Act
|
|
175
132
|
const events = await readLog(session, squadId);
|
|
176
|
-
// Assert
|
|
177
133
|
expect(events).toEqual([]);
|
|
178
134
|
});
|
|
179
135
|
test("should return empty array when log file does not exist", async () => {
|
|
180
|
-
// Arrange
|
|
181
136
|
const session = await createTestSession();
|
|
182
137
|
const squadId = "nonexistent-squad";
|
|
183
|
-
// Intentionally NOT creating the squad directory
|
|
184
|
-
// Act
|
|
185
138
|
const events = await readLog(session, squadId);
|
|
186
|
-
// Assert
|
|
187
139
|
expect(events).toEqual([]);
|
|
188
140
|
});
|
|
189
141
|
test("should correctly parse all LogEvent types", async () => {
|
|
190
|
-
// Arrange
|
|
191
142
|
const session = await createTestSession();
|
|
192
143
|
const squadId = "squad-007";
|
|
193
144
|
await createSquadDir(session, squadId);
|
|
194
|
-
// Log all event types
|
|
195
145
|
await logEvent(session, squadId, {
|
|
196
146
|
type: "squad_started",
|
|
197
147
|
squadId,
|
|
@@ -222,9 +172,7 @@ describe("readLog", () => {
|
|
|
222
172
|
type: "error",
|
|
223
173
|
message: "Oops",
|
|
224
174
|
});
|
|
225
|
-
// Act
|
|
226
175
|
const events = await readLog(session, squadId);
|
|
227
|
-
// Assert
|
|
228
176
|
expect(events).toHaveLength(6);
|
|
229
177
|
expect(events.map((e) => e.type)).toEqual([
|
|
230
178
|
"squad_started",
|