opencode-swarm-plugin 0.44.0 → 0.44.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/bin/swarm.serve.test.ts +6 -4
- package/bin/swarm.ts +16 -10
- package/dist/compaction-prompt-scoring.js +139 -0
- package/dist/eval-capture.js +12811 -0
- package/dist/hive.d.ts.map +1 -1
- package/dist/index.js +7644 -62599
- package/dist/plugin.js +23766 -78721
- package/dist/swarm-orchestrate.d.ts.map +1 -1
- package/dist/swarm-prompts.d.ts.map +1 -1
- package/dist/swarm-review.d.ts.map +1 -1
- package/package.json +17 -5
- package/.changeset/swarm-insights-data-layer.md +0 -63
- package/.hive/analysis/eval-failure-analysis-2025-12-25.md +0 -331
- package/.hive/analysis/session-data-quality-audit.md +0 -320
- package/.hive/eval-results.json +0 -483
- package/.hive/issues.jsonl +0 -138
- package/.hive/memories.jsonl +0 -729
- package/.opencode/eval-history.jsonl +0 -327
- package/.turbo/turbo-build.log +0 -9
- package/CHANGELOG.md +0 -2286
- package/SCORER-ANALYSIS.md +0 -598
- package/docs/analysis/subagent-coordination-patterns.md +0 -902
- package/docs/analysis-socratic-planner-pattern.md +0 -504
- package/docs/planning/ADR-001-monorepo-structure.md +0 -171
- package/docs/planning/ADR-002-package-extraction.md +0 -393
- package/docs/planning/ADR-003-performance-improvements.md +0 -451
- package/docs/planning/ADR-004-message-queue-features.md +0 -187
- package/docs/planning/ADR-005-devtools-observability.md +0 -202
- package/docs/planning/ADR-007-swarm-enhancements-worktree-review.md +0 -168
- package/docs/planning/ADR-008-worker-handoff-protocol.md +0 -293
- package/docs/planning/ADR-009-oh-my-opencode-patterns.md +0 -353
- package/docs/planning/ADR-010-cass-inhousing.md +0 -1215
- package/docs/planning/ROADMAP.md +0 -368
- package/docs/semantic-memory-cli-syntax.md +0 -123
- package/docs/swarm-mail-architecture.md +0 -1147
- package/docs/testing/context-recovery-test.md +0 -470
- package/evals/ARCHITECTURE.md +0 -1189
- package/evals/README.md +0 -768
- package/evals/compaction-prompt.eval.ts +0 -149
- package/evals/compaction-resumption.eval.ts +0 -289
- package/evals/coordinator-behavior.eval.ts +0 -307
- package/evals/coordinator-session.eval.ts +0 -154
- package/evals/evalite.config.ts.bak +0 -15
- package/evals/example.eval.ts +0 -31
- package/evals/fixtures/cass-baseline.ts +0 -217
- package/evals/fixtures/compaction-cases.ts +0 -350
- package/evals/fixtures/compaction-prompt-cases.ts +0 -311
- package/evals/fixtures/coordinator-sessions.ts +0 -328
- package/evals/fixtures/decomposition-cases.ts +0 -105
- package/evals/lib/compaction-loader.test.ts +0 -248
- package/evals/lib/compaction-loader.ts +0 -320
- package/evals/lib/data-loader.evalite-test.ts +0 -289
- package/evals/lib/data-loader.test.ts +0 -345
- package/evals/lib/data-loader.ts +0 -281
- package/evals/lib/llm.ts +0 -115
- package/evals/scorers/compaction-prompt-scorers.ts +0 -145
- package/evals/scorers/compaction-scorers.ts +0 -305
- package/evals/scorers/coordinator-discipline.evalite-test.ts +0 -539
- package/evals/scorers/coordinator-discipline.ts +0 -325
- package/evals/scorers/index.test.ts +0 -146
- package/evals/scorers/index.ts +0 -328
- package/evals/scorers/outcome-scorers.evalite-test.ts +0 -27
- package/evals/scorers/outcome-scorers.ts +0 -349
- package/evals/swarm-decomposition.eval.ts +0 -121
- package/examples/commands/swarm.md +0 -745
- package/examples/plugin-wrapper-template.ts +0 -2515
- package/examples/skills/hive-workflow/SKILL.md +0 -212
- package/examples/skills/skill-creator/SKILL.md +0 -223
- package/examples/skills/swarm-coordination/SKILL.md +0 -292
- package/global-skills/cli-builder/SKILL.md +0 -344
- package/global-skills/cli-builder/references/advanced-patterns.md +0 -244
- package/global-skills/learning-systems/SKILL.md +0 -644
- package/global-skills/skill-creator/LICENSE.txt +0 -202
- package/global-skills/skill-creator/SKILL.md +0 -352
- package/global-skills/skill-creator/references/output-patterns.md +0 -82
- package/global-skills/skill-creator/references/workflows.md +0 -28
- package/global-skills/swarm-coordination/SKILL.md +0 -995
- package/global-skills/swarm-coordination/references/coordinator-patterns.md +0 -235
- package/global-skills/swarm-coordination/references/strategies.md +0 -138
- package/global-skills/system-design/SKILL.md +0 -213
- package/global-skills/testing-patterns/SKILL.md +0 -430
- package/global-skills/testing-patterns/references/dependency-breaking-catalog.md +0 -586
- package/opencode-swarm-plugin-0.30.7.tgz +0 -0
- package/opencode-swarm-plugin-0.31.0.tgz +0 -0
- package/scripts/cleanup-test-memories.ts +0 -346
- package/scripts/init-skill.ts +0 -222
- package/scripts/migrate-unknown-sessions.ts +0 -349
- package/scripts/validate-skill.ts +0 -204
- package/src/agent-mail.ts +0 -1724
- package/src/anti-patterns.test.ts +0 -1167
- package/src/anti-patterns.ts +0 -448
- package/src/compaction-capture.integration.test.ts +0 -257
- package/src/compaction-hook.test.ts +0 -838
- package/src/compaction-hook.ts +0 -1204
- package/src/compaction-observability.integration.test.ts +0 -139
- package/src/compaction-observability.test.ts +0 -187
- package/src/compaction-observability.ts +0 -324
- package/src/compaction-prompt-scorers.test.ts +0 -475
- package/src/compaction-prompt-scoring.ts +0 -300
- package/src/contributor-tools.test.ts +0 -133
- package/src/contributor-tools.ts +0 -201
- package/src/dashboard.test.ts +0 -611
- package/src/dashboard.ts +0 -462
- package/src/error-enrichment.test.ts +0 -403
- package/src/error-enrichment.ts +0 -219
- package/src/eval-capture.test.ts +0 -1015
- package/src/eval-capture.ts +0 -929
- package/src/eval-gates.test.ts +0 -306
- package/src/eval-gates.ts +0 -218
- package/src/eval-history.test.ts +0 -508
- package/src/eval-history.ts +0 -214
- package/src/eval-learning.test.ts +0 -378
- package/src/eval-learning.ts +0 -360
- package/src/eval-runner.test.ts +0 -223
- package/src/eval-runner.ts +0 -402
- package/src/export-tools.test.ts +0 -476
- package/src/export-tools.ts +0 -257
- package/src/hive.integration.test.ts +0 -2241
- package/src/hive.ts +0 -1628
- package/src/index.ts +0 -940
- package/src/learning.integration.test.ts +0 -1815
- package/src/learning.ts +0 -1079
- package/src/logger.test.ts +0 -189
- package/src/logger.ts +0 -135
- package/src/mandate-promotion.test.ts +0 -473
- package/src/mandate-promotion.ts +0 -239
- package/src/mandate-storage.integration.test.ts +0 -601
- package/src/mandate-storage.test.ts +0 -578
- package/src/mandate-storage.ts +0 -794
- package/src/mandates.ts +0 -540
- package/src/memory-tools.test.ts +0 -195
- package/src/memory-tools.ts +0 -344
- package/src/memory.integration.test.ts +0 -334
- package/src/memory.test.ts +0 -158
- package/src/memory.ts +0 -527
- package/src/model-selection.test.ts +0 -188
- package/src/model-selection.ts +0 -68
- package/src/observability-tools.test.ts +0 -359
- package/src/observability-tools.ts +0 -871
- package/src/output-guardrails.test.ts +0 -438
- package/src/output-guardrails.ts +0 -381
- package/src/pattern-maturity.test.ts +0 -1160
- package/src/pattern-maturity.ts +0 -525
- package/src/planning-guardrails.test.ts +0 -491
- package/src/planning-guardrails.ts +0 -438
- package/src/plugin.ts +0 -23
- package/src/post-compaction-tracker.test.ts +0 -251
- package/src/post-compaction-tracker.ts +0 -237
- package/src/query-tools.test.ts +0 -636
- package/src/query-tools.ts +0 -324
- package/src/rate-limiter.integration.test.ts +0 -466
- package/src/rate-limiter.ts +0 -774
- package/src/replay-tools.test.ts +0 -496
- package/src/replay-tools.ts +0 -240
- package/src/repo-crawl.integration.test.ts +0 -441
- package/src/repo-crawl.ts +0 -610
- package/src/schemas/cell-events.test.ts +0 -347
- package/src/schemas/cell-events.ts +0 -807
- package/src/schemas/cell.ts +0 -257
- package/src/schemas/evaluation.ts +0 -166
- package/src/schemas/index.test.ts +0 -199
- package/src/schemas/index.ts +0 -286
- package/src/schemas/mandate.ts +0 -232
- package/src/schemas/swarm-context.ts +0 -115
- package/src/schemas/task.ts +0 -161
- package/src/schemas/worker-handoff.test.ts +0 -302
- package/src/schemas/worker-handoff.ts +0 -131
- package/src/sessions/agent-discovery.test.ts +0 -137
- package/src/sessions/agent-discovery.ts +0 -112
- package/src/sessions/index.ts +0 -15
- package/src/skills.integration.test.ts +0 -1192
- package/src/skills.test.ts +0 -643
- package/src/skills.ts +0 -1549
- package/src/storage.integration.test.ts +0 -341
- package/src/storage.ts +0 -884
- package/src/structured.integration.test.ts +0 -817
- package/src/structured.test.ts +0 -1046
- package/src/structured.ts +0 -762
- package/src/swarm-decompose.test.ts +0 -188
- package/src/swarm-decompose.ts +0 -1302
- package/src/swarm-deferred.integration.test.ts +0 -157
- package/src/swarm-deferred.test.ts +0 -38
- package/src/swarm-insights.test.ts +0 -214
- package/src/swarm-insights.ts +0 -459
- package/src/swarm-mail.integration.test.ts +0 -970
- package/src/swarm-mail.ts +0 -739
- package/src/swarm-orchestrate.integration.test.ts +0 -282
- package/src/swarm-orchestrate.test.ts +0 -548
- package/src/swarm-orchestrate.ts +0 -3084
- package/src/swarm-prompts.test.ts +0 -1270
- package/src/swarm-prompts.ts +0 -2077
- package/src/swarm-research.integration.test.ts +0 -701
- package/src/swarm-research.test.ts +0 -698
- package/src/swarm-research.ts +0 -472
- package/src/swarm-review.integration.test.ts +0 -285
- package/src/swarm-review.test.ts +0 -879
- package/src/swarm-review.ts +0 -709
- package/src/swarm-strategies.ts +0 -407
- package/src/swarm-worktree.test.ts +0 -501
- package/src/swarm-worktree.ts +0 -575
- package/src/swarm.integration.test.ts +0 -2377
- package/src/swarm.ts +0 -38
- package/src/tool-adapter.integration.test.ts +0 -1221
- package/src/tool-availability.ts +0 -461
- package/tsconfig.json +0 -28
package/src/schemas/task.ts
DELETED
|
@@ -1,161 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Task decomposition schemas
|
|
3
|
-
*
|
|
4
|
-
* These schemas define the structure for breaking down tasks
|
|
5
|
-
* into parallelizable subtasks for swarm execution.
|
|
6
|
-
*/
|
|
7
|
-
import { z } from "zod";
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* Effort estimation for subtasks.
|
|
11
|
-
*
|
|
12
|
-
* Time ranges:
|
|
13
|
-
* - `trivial`: < 5 minutes (simple rename, typo fix)
|
|
14
|
-
* - `small`: 5-30 minutes (single function, simple feature)
|
|
15
|
-
* - `medium`: 30 min - 2 hours (multi-file change, moderate complexity)
|
|
16
|
-
* - `large`: 2+ hours (significant feature, refactoring)
|
|
17
|
-
*/
|
|
18
|
-
export const EffortLevelSchema = z.enum([
|
|
19
|
-
"trivial",
|
|
20
|
-
"small",
|
|
21
|
-
"medium",
|
|
22
|
-
"large",
|
|
23
|
-
]);
|
|
24
|
-
export type EffortLevel = z.infer<typeof EffortLevelSchema>;
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* Dependency type between subtasks
|
|
28
|
-
*/
|
|
29
|
-
export const DependencyTypeSchema = z.enum([
|
|
30
|
-
"blocks", // Must complete before dependent can start
|
|
31
|
-
"requires", // Needs output from another task
|
|
32
|
-
"related", // Informational relationship
|
|
33
|
-
]);
|
|
34
|
-
export type DependencyType = z.infer<typeof DependencyTypeSchema>;
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* Subtask in a decomposition
|
|
38
|
-
*/
|
|
39
|
-
export const DecomposedSubtaskSchema = z.object({
|
|
40
|
-
title: z.string().min(1),
|
|
41
|
-
description: z.string(),
|
|
42
|
-
files: z.array(z.string()), // File paths this subtask will modify
|
|
43
|
-
estimated_effort: EffortLevelSchema,
|
|
44
|
-
/** Potential risks or complications (e.g., 'tight coupling', 'data migration required', 'breaking change') */
|
|
45
|
-
risks: z.array(z.string()).optional().default([]),
|
|
46
|
-
/**
|
|
47
|
-
* Optional explicit model override for this subtask.
|
|
48
|
-
* If not specified, model will be selected based on file types.
|
|
49
|
-
*/
|
|
50
|
-
model: z.string().optional(),
|
|
51
|
-
});
|
|
52
|
-
export type DecomposedSubtask = z.infer<typeof DecomposedSubtaskSchema>;
|
|
53
|
-
|
|
54
|
-
/**
|
|
55
|
-
* Dependency between subtasks
|
|
56
|
-
*/
|
|
57
|
-
export const SubtaskDependencySchema = z.object({
|
|
58
|
-
/** Zero-based index of the dependency source subtask */
|
|
59
|
-
from: z.number().int().min(0),
|
|
60
|
-
/** Zero-based index of the dependency target subtask */
|
|
61
|
-
to: z.number().int().min(0),
|
|
62
|
-
type: DependencyTypeSchema,
|
|
63
|
-
});
|
|
64
|
-
export type SubtaskDependency = z.infer<typeof SubtaskDependencySchema>;
|
|
65
|
-
|
|
66
|
-
/**
|
|
67
|
-
* Full task decomposition result
|
|
68
|
-
*
|
|
69
|
-
* Returned by the decomposition agent, validated before spawning.
|
|
70
|
-
*/
|
|
71
|
-
export const TaskDecompositionSchema = z.object({
|
|
72
|
-
task: z.string(), // Original task description
|
|
73
|
-
/** Rationale for this decomposition strategy (why these subtasks, why this order) */
|
|
74
|
-
reasoning: z.string().optional(),
|
|
75
|
-
subtasks: z.array(DecomposedSubtaskSchema).min(1),
|
|
76
|
-
dependencies: z.array(SubtaskDependencySchema).optional().default([]),
|
|
77
|
-
/**
|
|
78
|
-
* Context shared with all spawned agents.
|
|
79
|
-
* Examples: API contracts, shared types, project conventions, architectural decisions.
|
|
80
|
-
*/
|
|
81
|
-
shared_context: z.string().optional(),
|
|
82
|
-
});
|
|
83
|
-
export type TaskDecomposition = z.infer<typeof TaskDecompositionSchema>;
|
|
84
|
-
|
|
85
|
-
/**
|
|
86
|
-
* Arguments for task decomposition
|
|
87
|
-
*/
|
|
88
|
-
export const DecomposeArgsSchema = z.object({
|
|
89
|
-
task: z.string().min(1),
|
|
90
|
-
context: z.string().optional(),
|
|
91
|
-
});
|
|
92
|
-
export type DecomposeArgs = z.infer<typeof DecomposeArgsSchema>;
|
|
93
|
-
|
|
94
|
-
/**
|
|
95
|
-
* Spawn result for a single agent
|
|
96
|
-
*/
|
|
97
|
-
export const SpawnedAgentSchema = z.object({
|
|
98
|
-
bead_id: z.string(),
|
|
99
|
-
/**
|
|
100
|
-
* Agent Mail assigned name (e.g., 'BlueLake', 'CrimsonRiver').
|
|
101
|
-
* Generated by Agent Mail on session init.
|
|
102
|
-
*/
|
|
103
|
-
agent_name: z.string(),
|
|
104
|
-
task_id: z.string().optional(), // OpenCode task ID
|
|
105
|
-
status: z.enum(["pending", "running", "completed", "failed"]),
|
|
106
|
-
files: z.array(z.string()), // Reserved files
|
|
107
|
-
/**
|
|
108
|
-
* Agent Mail reservation IDs for file locking.
|
|
109
|
-
* Used to release locks on task completion via agentmail_release.
|
|
110
|
-
*/
|
|
111
|
-
reservation_ids: z.array(z.number()).optional(),
|
|
112
|
-
});
|
|
113
|
-
export type SpawnedAgent = z.infer<typeof SpawnedAgentSchema>;
|
|
114
|
-
|
|
115
|
-
/**
|
|
116
|
-
* Result of spawning a swarm
|
|
117
|
-
*/
|
|
118
|
-
export const SwarmSpawnResultSchema = z.object({
|
|
119
|
-
epic_id: z.string(),
|
|
120
|
-
coordinator_name: z.string(), // Agent Mail name of coordinator
|
|
121
|
-
thread_id: z.string(), // Agent Mail thread for this swarm
|
|
122
|
-
agents: z.array(SpawnedAgentSchema),
|
|
123
|
-
started_at: z.string().datetime({ offset: true }), // ISO-8601 with timezone
|
|
124
|
-
});
|
|
125
|
-
export type SwarmSpawnResult = z.infer<typeof SwarmSpawnResultSchema>;
|
|
126
|
-
|
|
127
|
-
/**
|
|
128
|
-
* Progress update from an agent
|
|
129
|
-
*/
|
|
130
|
-
export const AgentProgressSchema = z
|
|
131
|
-
.object({
|
|
132
|
-
bead_id: z.string(),
|
|
133
|
-
agent_name: z.string(),
|
|
134
|
-
status: z.enum(["in_progress", "blocked", "completed", "failed"]),
|
|
135
|
-
progress_percent: z.number().min(0).max(100).optional(),
|
|
136
|
-
message: z.string().optional(),
|
|
137
|
-
files_touched: z.array(z.string()).optional(),
|
|
138
|
-
blockers: z.array(z.string()).optional(),
|
|
139
|
-
timestamp: z.string().datetime({ offset: true }), // ISO-8601 with timezone
|
|
140
|
-
})
|
|
141
|
-
.refine(
|
|
142
|
-
(data) =>
|
|
143
|
-
data.status !== "blocked" || (data.blockers && data.blockers.length > 0),
|
|
144
|
-
{ message: "blockers array required when status is 'blocked'" },
|
|
145
|
-
);
|
|
146
|
-
export type AgentProgress = z.infer<typeof AgentProgressSchema>;
|
|
147
|
-
|
|
148
|
-
/**
|
|
149
|
-
* Swarm status summary
|
|
150
|
-
*/
|
|
151
|
-
export const SwarmStatusSchema = z.object({
|
|
152
|
-
epic_id: z.string(),
|
|
153
|
-
total_agents: z.number().int().min(0),
|
|
154
|
-
running: z.number().int().min(0),
|
|
155
|
-
completed: z.number().int().min(0),
|
|
156
|
-
failed: z.number().int().min(0),
|
|
157
|
-
blocked: z.number().int().min(0),
|
|
158
|
-
agents: z.array(SpawnedAgentSchema),
|
|
159
|
-
last_update: z.string().datetime({ offset: true }), // ISO-8601 with timezone
|
|
160
|
-
});
|
|
161
|
-
export type SwarmStatus = z.infer<typeof SwarmStatusSchema>;
|
|
@@ -1,302 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Tests for WorkerHandoff schema validation
|
|
3
|
-
*
|
|
4
|
-
* WorkerHandoff replaces prose instructions with structured contracts.
|
|
5
|
-
* These tests ensure runtime validation catches malformed handoffs.
|
|
6
|
-
*/
|
|
7
|
-
import { describe, expect, test } from "bun:test";
|
|
8
|
-
import {
|
|
9
|
-
WorkerHandoffContractSchema,
|
|
10
|
-
WorkerHandoffContextSchema,
|
|
11
|
-
WorkerHandoffEscalationSchema,
|
|
12
|
-
WorkerHandoffSchema,
|
|
13
|
-
type WorkerHandoff,
|
|
14
|
-
type WorkerHandoffContract,
|
|
15
|
-
type WorkerHandoffContext,
|
|
16
|
-
type WorkerHandoffEscalation,
|
|
17
|
-
} from "./worker-handoff";
|
|
18
|
-
|
|
19
|
-
describe("WorkerHandoffContractSchema", () => {
|
|
20
|
-
test("valid contract parses correctly", () => {
|
|
21
|
-
const validContract = {
|
|
22
|
-
task_id: "opencode-swarm-monorepo-lf2p4u-abc123",
|
|
23
|
-
files_owned: ["src/auth/service.ts", "src/auth/schema.ts"],
|
|
24
|
-
files_readonly: ["src/lib/jwt.ts"],
|
|
25
|
-
dependencies_completed: ["opencode-swarm-monorepo-lf2p4u-abc122"],
|
|
26
|
-
success_criteria: [
|
|
27
|
-
"Auth service implements JWT strategy",
|
|
28
|
-
"All tests pass",
|
|
29
|
-
],
|
|
30
|
-
};
|
|
31
|
-
|
|
32
|
-
const result = WorkerHandoffContractSchema.safeParse(validContract);
|
|
33
|
-
expect(result.success).toBe(true);
|
|
34
|
-
if (result.success) {
|
|
35
|
-
expect(result.data.task_id).toBe("opencode-swarm-monorepo-lf2p4u-abc123");
|
|
36
|
-
expect(result.data.files_owned).toHaveLength(2);
|
|
37
|
-
expect(result.data.success_criteria).toHaveLength(2);
|
|
38
|
-
}
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
test("missing task_id fails", () => {
|
|
42
|
-
const invalidContract = {
|
|
43
|
-
files_owned: ["src/auth.ts"],
|
|
44
|
-
files_readonly: [],
|
|
45
|
-
dependencies_completed: [],
|
|
46
|
-
success_criteria: ["Auth works"],
|
|
47
|
-
};
|
|
48
|
-
|
|
49
|
-
const result = WorkerHandoffContractSchema.safeParse(invalidContract);
|
|
50
|
-
expect(result.success).toBe(false);
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
test("empty files_owned is valid (read-only tasks)", () => {
|
|
54
|
-
const readOnlyContract = {
|
|
55
|
-
task_id: "opencode-swarm-monorepo-lf2p4u-abc123",
|
|
56
|
-
files_owned: [], // Read-only task
|
|
57
|
-
files_readonly: ["src/types.ts"],
|
|
58
|
-
dependencies_completed: [],
|
|
59
|
-
success_criteria: ["Documentation updated"],
|
|
60
|
-
};
|
|
61
|
-
|
|
62
|
-
const result = WorkerHandoffContractSchema.safeParse(readOnlyContract);
|
|
63
|
-
expect(result.success).toBe(true);
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
test("empty success_criteria fails", () => {
|
|
67
|
-
const invalidContract = {
|
|
68
|
-
task_id: "opencode-swarm-monorepo-lf2p4u-abc123",
|
|
69
|
-
files_owned: ["src/auth.ts"],
|
|
70
|
-
files_readonly: [],
|
|
71
|
-
dependencies_completed: [],
|
|
72
|
-
success_criteria: [], // Must have at least one
|
|
73
|
-
};
|
|
74
|
-
|
|
75
|
-
const result = WorkerHandoffContractSchema.safeParse(invalidContract);
|
|
76
|
-
expect(result.success).toBe(false);
|
|
77
|
-
if (!result.success) {
|
|
78
|
-
expect(result.error.issues[0].message).toContain(
|
|
79
|
-
"at least one success criterion",
|
|
80
|
-
);
|
|
81
|
-
}
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
test("empty task_id fails", () => {
|
|
85
|
-
const invalidContract = {
|
|
86
|
-
task_id: "", // Empty string not allowed
|
|
87
|
-
files_owned: ["src/auth.ts"],
|
|
88
|
-
files_readonly: [],
|
|
89
|
-
dependencies_completed: [],
|
|
90
|
-
success_criteria: ["Auth works"],
|
|
91
|
-
};
|
|
92
|
-
|
|
93
|
-
const result = WorkerHandoffContractSchema.safeParse(invalidContract);
|
|
94
|
-
expect(result.success).toBe(false);
|
|
95
|
-
if (!result.success) {
|
|
96
|
-
expect(result.error.issues[0].message).toContain("cannot be empty");
|
|
97
|
-
}
|
|
98
|
-
});
|
|
99
|
-
|
|
100
|
-
test("short project name with hash is valid", () => {
|
|
101
|
-
// Regression test: single-word project names like "swarm-lf2p4u-abc123" should work
|
|
102
|
-
const shortProjectContract = {
|
|
103
|
-
task_id: "swarm-lf2p4u-abc123", // Only 2 segments before timestamp
|
|
104
|
-
files_owned: ["src/auth.ts"],
|
|
105
|
-
files_readonly: [],
|
|
106
|
-
dependencies_completed: [],
|
|
107
|
-
success_criteria: ["Auth works"],
|
|
108
|
-
};
|
|
109
|
-
|
|
110
|
-
const result = WorkerHandoffContractSchema.safeParse(shortProjectContract);
|
|
111
|
-
expect(result.success).toBe(true);
|
|
112
|
-
});
|
|
113
|
-
|
|
114
|
-
test("partial hash is valid (resolvePartialId will expand it)", () => {
|
|
115
|
-
// Partial hashes should be accepted - resolvePartialId will expand them
|
|
116
|
-
const partialHashContract = {
|
|
117
|
-
task_id: "mjd4pjuj", // Short hash only
|
|
118
|
-
files_owned: ["src/auth.ts"],
|
|
119
|
-
files_readonly: [],
|
|
120
|
-
dependencies_completed: [],
|
|
121
|
-
success_criteria: ["Auth works"],
|
|
122
|
-
};
|
|
123
|
-
|
|
124
|
-
const result = WorkerHandoffContractSchema.safeParse(partialHashContract);
|
|
125
|
-
expect(result.success).toBe(true);
|
|
126
|
-
});
|
|
127
|
-
});
|
|
128
|
-
|
|
129
|
-
describe("WorkerHandoffContextSchema", () => {
|
|
130
|
-
test("valid context parses correctly", () => {
|
|
131
|
-
const validContext = {
|
|
132
|
-
epic_summary: "Add authentication system",
|
|
133
|
-
your_role: "Implement JWT auth service",
|
|
134
|
-
what_others_did: "Schema defined by agent-1",
|
|
135
|
-
what_comes_next: "Integration tests in next subtask",
|
|
136
|
-
};
|
|
137
|
-
|
|
138
|
-
const result = WorkerHandoffContextSchema.safeParse(validContext);
|
|
139
|
-
expect(result.success).toBe(true);
|
|
140
|
-
});
|
|
141
|
-
|
|
142
|
-
test("missing required fields fails", () => {
|
|
143
|
-
const invalidContext = {
|
|
144
|
-
epic_summary: "Add auth",
|
|
145
|
-
your_role: "Implement service",
|
|
146
|
-
// Missing what_others_did and what_comes_next
|
|
147
|
-
};
|
|
148
|
-
|
|
149
|
-
const result = WorkerHandoffContextSchema.safeParse(invalidContext);
|
|
150
|
-
expect(result.success).toBe(false);
|
|
151
|
-
});
|
|
152
|
-
|
|
153
|
-
test("empty strings are valid", () => {
|
|
154
|
-
const contextWithEmptyStrings = {
|
|
155
|
-
epic_summary: "Add auth",
|
|
156
|
-
your_role: "Implement service",
|
|
157
|
-
what_others_did: "", // Valid for first subtask
|
|
158
|
-
what_comes_next: "", // Valid for last subtask
|
|
159
|
-
};
|
|
160
|
-
|
|
161
|
-
const result = WorkerHandoffContextSchema.safeParse(
|
|
162
|
-
contextWithEmptyStrings,
|
|
163
|
-
);
|
|
164
|
-
expect(result.success).toBe(true);
|
|
165
|
-
});
|
|
166
|
-
});
|
|
167
|
-
|
|
168
|
-
describe("WorkerHandoffEscalationSchema", () => {
|
|
169
|
-
test("valid escalation parses correctly", () => {
|
|
170
|
-
const validEscalation = {
|
|
171
|
-
blocked_contact: "Message coordinator via swarmmail_send(importance='high')",
|
|
172
|
-
scope_change_protocol:
|
|
173
|
-
"Request approval before expanding scope beyond files_owned",
|
|
174
|
-
};
|
|
175
|
-
|
|
176
|
-
const result = WorkerHandoffEscalationSchema.safeParse(validEscalation);
|
|
177
|
-
expect(result.success).toBe(true);
|
|
178
|
-
});
|
|
179
|
-
|
|
180
|
-
test("missing required fields fails", () => {
|
|
181
|
-
const invalidEscalation = {
|
|
182
|
-
blocked_contact: "Message coordinator",
|
|
183
|
-
// Missing scope_change_protocol
|
|
184
|
-
};
|
|
185
|
-
|
|
186
|
-
const result = WorkerHandoffEscalationSchema.safeParse(invalidEscalation);
|
|
187
|
-
expect(result.success).toBe(false);
|
|
188
|
-
});
|
|
189
|
-
});
|
|
190
|
-
|
|
191
|
-
describe("WorkerHandoffSchema", () => {
|
|
192
|
-
test("complete valid handoff parses correctly", () => {
|
|
193
|
-
const validHandoff = {
|
|
194
|
-
contract: {
|
|
195
|
-
task_id: "opencode-swarm-monorepo-lf2p4u-abc123",
|
|
196
|
-
files_owned: ["src/auth/service.ts"],
|
|
197
|
-
files_readonly: ["src/lib/jwt.ts"],
|
|
198
|
-
dependencies_completed: [],
|
|
199
|
-
success_criteria: ["Service implemented", "Tests pass"],
|
|
200
|
-
},
|
|
201
|
-
context: {
|
|
202
|
-
epic_summary: "Add authentication",
|
|
203
|
-
your_role: "Implement auth service",
|
|
204
|
-
what_others_did: "Schema defined",
|
|
205
|
-
what_comes_next: "Integration tests",
|
|
206
|
-
},
|
|
207
|
-
escalation: {
|
|
208
|
-
blocked_contact: "Message coordinator",
|
|
209
|
-
scope_change_protocol: "Request approval first",
|
|
210
|
-
},
|
|
211
|
-
};
|
|
212
|
-
|
|
213
|
-
const result = WorkerHandoffSchema.safeParse(validHandoff);
|
|
214
|
-
expect(result.success).toBe(true);
|
|
215
|
-
if (result.success) {
|
|
216
|
-
const handoff: WorkerHandoff = result.data;
|
|
217
|
-
expect(handoff.contract.task_id).toBe(
|
|
218
|
-
"opencode-swarm-monorepo-lf2p4u-abc123",
|
|
219
|
-
);
|
|
220
|
-
expect(handoff.context.your_role).toBe("Implement auth service");
|
|
221
|
-
expect(handoff.escalation.blocked_contact).toBe("Message coordinator");
|
|
222
|
-
}
|
|
223
|
-
});
|
|
224
|
-
|
|
225
|
-
test("missing contract section fails", () => {
|
|
226
|
-
const invalidHandoff = {
|
|
227
|
-
context: {
|
|
228
|
-
epic_summary: "Add auth",
|
|
229
|
-
your_role: "Implement",
|
|
230
|
-
what_others_did: "Schema",
|
|
231
|
-
what_comes_next: "Tests",
|
|
232
|
-
},
|
|
233
|
-
escalation: {
|
|
234
|
-
blocked_contact: "Message coordinator",
|
|
235
|
-
scope_change_protocol: "Request approval",
|
|
236
|
-
},
|
|
237
|
-
};
|
|
238
|
-
|
|
239
|
-
const result = WorkerHandoffSchema.safeParse(invalidHandoff);
|
|
240
|
-
expect(result.success).toBe(false);
|
|
241
|
-
});
|
|
242
|
-
|
|
243
|
-
test("nested validation catches contract errors", () => {
|
|
244
|
-
const handoffWithInvalidContract = {
|
|
245
|
-
contract: {
|
|
246
|
-
task_id: "opencode-swarm-monorepo-lf2p4u-abc123",
|
|
247
|
-
files_owned: ["src/auth.ts"],
|
|
248
|
-
files_readonly: [],
|
|
249
|
-
dependencies_completed: [],
|
|
250
|
-
success_criteria: [], // Invalid: empty
|
|
251
|
-
},
|
|
252
|
-
context: {
|
|
253
|
-
epic_summary: "Add auth",
|
|
254
|
-
your_role: "Implement",
|
|
255
|
-
what_others_did: "Schema",
|
|
256
|
-
what_comes_next: "Tests",
|
|
257
|
-
},
|
|
258
|
-
escalation: {
|
|
259
|
-
blocked_contact: "Message coordinator",
|
|
260
|
-
scope_change_protocol: "Request approval",
|
|
261
|
-
},
|
|
262
|
-
};
|
|
263
|
-
|
|
264
|
-
const result = WorkerHandoffSchema.safeParse(handoffWithInvalidContract);
|
|
265
|
-
expect(result.success).toBe(false);
|
|
266
|
-
if (!result.success) {
|
|
267
|
-
const errorMessage = result.error.issues[0].message;
|
|
268
|
-
expect(errorMessage).toContain("at least one success criterion");
|
|
269
|
-
}
|
|
270
|
-
});
|
|
271
|
-
|
|
272
|
-
test("type inference works correctly", () => {
|
|
273
|
-
const handoff: WorkerHandoff = {
|
|
274
|
-
contract: {
|
|
275
|
-
task_id: "opencode-swarm-monorepo-lf2p4u-abc123",
|
|
276
|
-
files_owned: [],
|
|
277
|
-
files_readonly: [],
|
|
278
|
-
dependencies_completed: [],
|
|
279
|
-
success_criteria: ["Done"],
|
|
280
|
-
},
|
|
281
|
-
context: {
|
|
282
|
-
epic_summary: "Summary",
|
|
283
|
-
your_role: "Role",
|
|
284
|
-
what_others_did: "Nothing",
|
|
285
|
-
what_comes_next: "More work",
|
|
286
|
-
},
|
|
287
|
-
escalation: {
|
|
288
|
-
blocked_contact: "Coordinator",
|
|
289
|
-
scope_change_protocol: "Ask first",
|
|
290
|
-
},
|
|
291
|
-
};
|
|
292
|
-
|
|
293
|
-
// Type check only - verify TypeScript inference
|
|
294
|
-
const contract: WorkerHandoffContract = handoff.contract;
|
|
295
|
-
const context: WorkerHandoffContext = handoff.context;
|
|
296
|
-
const escalation: WorkerHandoffEscalation = handoff.escalation;
|
|
297
|
-
|
|
298
|
-
expect(contract.task_id).toBeDefined();
|
|
299
|
-
expect(context.your_role).toBeDefined();
|
|
300
|
-
expect(escalation.blocked_contact).toBeDefined();
|
|
301
|
-
});
|
|
302
|
-
});
|
|
@@ -1,131 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* WorkerHandoff schemas - structured contracts replacing prose instructions
|
|
3
|
-
*
|
|
4
|
-
* Replaces the 400-line SUBTASK_PROMPT_V2 with machine-readable contracts.
|
|
5
|
-
* Workers receive typed handoffs with explicit files, criteria, and escalation paths.
|
|
6
|
-
*/
|
|
7
|
-
import { z } from "zod";
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* Contract section - the binding agreement between coordinator and worker
|
|
11
|
-
*
|
|
12
|
-
* Defines:
|
|
13
|
-
* - What task to complete (task_id)
|
|
14
|
-
* - What files to modify (files_owned) vs read (files_readonly)
|
|
15
|
-
* - What's already done (dependencies_completed)
|
|
16
|
-
* - How to know you're done (success_criteria)
|
|
17
|
-
*/
|
|
18
|
-
export const WorkerHandoffContractSchema = z.object({
|
|
19
|
-
/**
|
|
20
|
-
* Cell ID for this subtask.
|
|
21
|
-
* Can be a full cell ID, hash, or partial hash.
|
|
22
|
-
* Examples:
|
|
23
|
-
* - Full ID: `opencode-swarm-monorepo-lf2p4u-abc123`
|
|
24
|
-
* - Hash only: `lf2p4u`
|
|
25
|
-
* - Partial hash: `mjd4pjuj`
|
|
26
|
-
*
|
|
27
|
-
* The hive tools use resolvePartialId() to expand short IDs before lookup.
|
|
28
|
-
*/
|
|
29
|
-
task_id: z
|
|
30
|
-
.string()
|
|
31
|
-
.min(1, "Task ID cannot be empty"),
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* Files this worker owns (exclusive write access).
|
|
35
|
-
* Empty array is valid for read-only tasks (e.g., documentation review).
|
|
36
|
-
*/
|
|
37
|
-
files_owned: z.array(z.string()).default([]),
|
|
38
|
-
|
|
39
|
-
/**
|
|
40
|
-
* Files this worker can read but must not modify.
|
|
41
|
-
* Coordinator reserves these for other workers.
|
|
42
|
-
*/
|
|
43
|
-
files_readonly: z.array(z.string()).default([]),
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* Subtask IDs that must complete before this one.
|
|
47
|
-
* Empty if no dependencies (can start immediately).
|
|
48
|
-
*/
|
|
49
|
-
dependencies_completed: z.array(z.string()).default([]),
|
|
50
|
-
|
|
51
|
-
/**
|
|
52
|
-
* Success criteria - how to know the task is complete.
|
|
53
|
-
* Must have at least one criterion to prevent ambiguous completion.
|
|
54
|
-
*/
|
|
55
|
-
success_criteria: z
|
|
56
|
-
.array(z.string())
|
|
57
|
-
.min(1, "Must have at least one success criterion"),
|
|
58
|
-
});
|
|
59
|
-
export type WorkerHandoffContract = z.infer<typeof WorkerHandoffContractSchema>;
|
|
60
|
-
|
|
61
|
-
/**
|
|
62
|
-
* Context section - the narrative explaining the "why"
|
|
63
|
-
*
|
|
64
|
-
* Provides:
|
|
65
|
-
* - Big picture (epic_summary)
|
|
66
|
-
* - This worker's specific role
|
|
67
|
-
* - What's already been done
|
|
68
|
-
* - What comes after
|
|
69
|
-
*/
|
|
70
|
-
export const WorkerHandoffContextSchema = z.object({
|
|
71
|
-
/**
|
|
72
|
-
* High-level summary of the entire epic.
|
|
73
|
-
* Helps worker understand how their piece fits.
|
|
74
|
-
*/
|
|
75
|
-
epic_summary: z.string(),
|
|
76
|
-
|
|
77
|
-
/**
|
|
78
|
-
* This worker's specific role/responsibility.
|
|
79
|
-
* Should align with files_owned in contract.
|
|
80
|
-
*/
|
|
81
|
-
your_role: z.string(),
|
|
82
|
-
|
|
83
|
-
/**
|
|
84
|
-
* What previous subtasks accomplished.
|
|
85
|
-
* Empty string is valid for first subtask.
|
|
86
|
-
*/
|
|
87
|
-
what_others_did: z.string(),
|
|
88
|
-
|
|
89
|
-
/**
|
|
90
|
-
* What happens after this subtask completes.
|
|
91
|
-
* Empty string is valid for last subtask.
|
|
92
|
-
*/
|
|
93
|
-
what_comes_next: z.string(),
|
|
94
|
-
});
|
|
95
|
-
export type WorkerHandoffContext = z.infer<typeof WorkerHandoffContextSchema>;
|
|
96
|
-
|
|
97
|
-
/**
|
|
98
|
-
* Escalation section - what to do when things go wrong
|
|
99
|
-
*
|
|
100
|
-
* Defines:
|
|
101
|
-
* - How to report blockers
|
|
102
|
-
* - Protocol for scope changes
|
|
103
|
-
*/
|
|
104
|
-
export const WorkerHandoffEscalationSchema = z.object({
|
|
105
|
-
/**
|
|
106
|
-
* Instructions for reporting blockers.
|
|
107
|
-
* Typically: "Message coordinator via swarmmail_send(importance='high')"
|
|
108
|
-
*/
|
|
109
|
-
blocked_contact: z.string(),
|
|
110
|
-
|
|
111
|
-
/**
|
|
112
|
-
* Protocol for requesting scope changes.
|
|
113
|
-
* Typically: "Request approval before expanding scope beyond files_owned"
|
|
114
|
-
*/
|
|
115
|
-
scope_change_protocol: z.string(),
|
|
116
|
-
});
|
|
117
|
-
export type WorkerHandoffEscalation = z.infer<
|
|
118
|
-
typeof WorkerHandoffEscalationSchema
|
|
119
|
-
>;
|
|
120
|
-
|
|
121
|
-
/**
|
|
122
|
-
* Complete WorkerHandoff - combines all three sections
|
|
123
|
-
*
|
|
124
|
-
* This is the full structured contract that replaces prose instructions.
|
|
125
|
-
*/
|
|
126
|
-
export const WorkerHandoffSchema = z.object({
|
|
127
|
-
contract: WorkerHandoffContractSchema,
|
|
128
|
-
context: WorkerHandoffContextSchema,
|
|
129
|
-
escalation: WorkerHandoffEscalationSchema,
|
|
130
|
-
});
|
|
131
|
-
export type WorkerHandoff = z.infer<typeof WorkerHandoffSchema>;
|