opencode-swarm-plugin 0.44.0 → 0.44.2
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 +18 -12
- 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/hive.js +14834 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7743 -62593
- package/dist/plugin.js +24052 -78907
- package/dist/swarm-orchestrate.d.ts.map +1 -1
- package/dist/swarm-prompts.d.ts.map +1 -1
- package/dist/swarm-prompts.js +39407 -0
- package/dist/swarm-review.d.ts.map +1 -1
- package/dist/swarm-validation.d.ts +127 -0
- package/dist/swarm-validation.d.ts.map +1 -0
- package/dist/validators/index.d.ts +7 -0
- package/dist/validators/index.d.ts.map +1 -0
- package/dist/validators/schema-validator.d.ts +58 -0
- package/dist/validators/schema-validator.d.ts.map +1 -0
- 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
|
@@ -1,491 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, beforeEach, afterEach } from "bun:test";
|
|
2
|
-
import {
|
|
3
|
-
analyzeTodoWrite,
|
|
4
|
-
shouldAnalyzeTool,
|
|
5
|
-
detectCoordinatorViolation,
|
|
6
|
-
setCoordinatorContext,
|
|
7
|
-
getCoordinatorContext,
|
|
8
|
-
clearCoordinatorContext,
|
|
9
|
-
isInCoordinatorContext,
|
|
10
|
-
type ViolationDetectionResult,
|
|
11
|
-
} from "./planning-guardrails";
|
|
12
|
-
import * as fs from "node:fs";
|
|
13
|
-
import * as path from "node:path";
|
|
14
|
-
import * as os from "os";
|
|
15
|
-
|
|
16
|
-
describe("planning-guardrails", () => {
|
|
17
|
-
describe("shouldAnalyzeTool", () => {
|
|
18
|
-
it("returns true for todowrite", () => {
|
|
19
|
-
expect(shouldAnalyzeTool("todowrite")).toBe(true);
|
|
20
|
-
expect(shouldAnalyzeTool("TodoWrite")).toBe(true);
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
it("returns false for other tools", () => {
|
|
24
|
-
expect(shouldAnalyzeTool("hive_create")).toBe(false);
|
|
25
|
-
expect(shouldAnalyzeTool("swarm_decompose")).toBe(false);
|
|
26
|
-
expect(shouldAnalyzeTool("read")).toBe(false);
|
|
27
|
-
});
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
describe("analyzeTodoWrite", () => {
|
|
31
|
-
it("returns no warning for small todo lists", () => {
|
|
32
|
-
const result = analyzeTodoWrite({
|
|
33
|
-
todos: [
|
|
34
|
-
{ content: "Implement feature A", status: "pending" },
|
|
35
|
-
{ content: "Add tests", status: "pending" },
|
|
36
|
-
],
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
expect(result.looksLikeParallelWork).toBe(false);
|
|
40
|
-
expect(result.warning).toBeUndefined();
|
|
41
|
-
expect(result.totalCount).toBe(2);
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
it("warns for 6+ file modification todos", () => {
|
|
45
|
-
const result = analyzeTodoWrite({
|
|
46
|
-
todos: [
|
|
47
|
-
{ content: "Implement src/auth/login.ts", status: "pending" },
|
|
48
|
-
{ content: "Create src/auth/logout.ts", status: "pending" },
|
|
49
|
-
{ content: "Add src/auth/types.ts", status: "pending" },
|
|
50
|
-
{ content: "Update src/auth/index.ts", status: "pending" },
|
|
51
|
-
{ content: "Refactor src/lib/session.ts", status: "pending" },
|
|
52
|
-
{ content: "Modify src/middleware/auth.ts", status: "pending" },
|
|
53
|
-
],
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
expect(result.looksLikeParallelWork).toBe(true);
|
|
57
|
-
expect(result.warning).toBeDefined();
|
|
58
|
-
expect(result.warning).toContain("multi-file implementation plan");
|
|
59
|
-
expect(result.warning).toContain("swarm");
|
|
60
|
-
expect(result.fileModificationCount).toBeGreaterThanOrEqual(4);
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
it("does not warn for tracking/coordination todos", () => {
|
|
64
|
-
const result = analyzeTodoWrite({
|
|
65
|
-
todos: [
|
|
66
|
-
{ content: "Review PR #123", status: "pending" },
|
|
67
|
-
{ content: "Check tests pass", status: "pending" },
|
|
68
|
-
{ content: "Verify deployment", status: "pending" },
|
|
69
|
-
{ content: "Run integration tests", status: "pending" },
|
|
70
|
-
{ content: "Merge to main", status: "pending" },
|
|
71
|
-
{ content: "Push to production", status: "pending" },
|
|
72
|
-
],
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
expect(result.looksLikeParallelWork).toBe(false);
|
|
76
|
-
expect(result.warning).toBeUndefined();
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
it("does not warn for mixed todos with few file modifications", () => {
|
|
80
|
-
const result = analyzeTodoWrite({
|
|
81
|
-
todos: [
|
|
82
|
-
{ content: "Implement src/feature.ts", status: "pending" },
|
|
83
|
-
{ content: "Review changes", status: "pending" },
|
|
84
|
-
{ content: "Run tests", status: "pending" },
|
|
85
|
-
{ content: "Check linting", status: "pending" },
|
|
86
|
-
{ content: "Deploy to staging", status: "pending" },
|
|
87
|
-
{ content: "Verify in browser", status: "pending" },
|
|
88
|
-
],
|
|
89
|
-
});
|
|
90
|
-
|
|
91
|
-
// Only 1 file modification out of 6 - should not trigger
|
|
92
|
-
expect(result.looksLikeParallelWork).toBe(false);
|
|
93
|
-
expect(result.warning).toBeUndefined();
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
it("handles empty or missing todos", () => {
|
|
97
|
-
expect(analyzeTodoWrite({}).looksLikeParallelWork).toBe(false);
|
|
98
|
-
expect(analyzeTodoWrite({ todos: [] }).looksLikeParallelWork).toBe(false);
|
|
99
|
-
expect(analyzeTodoWrite({ todos: undefined as any }).looksLikeParallelWork).toBe(false);
|
|
100
|
-
});
|
|
101
|
-
|
|
102
|
-
it("handles malformed todo items", () => {
|
|
103
|
-
const result = analyzeTodoWrite({
|
|
104
|
-
todos: [
|
|
105
|
-
null,
|
|
106
|
-
undefined,
|
|
107
|
-
"string instead of object",
|
|
108
|
-
{ noContent: true },
|
|
109
|
-
{ content: "Implement src/valid.ts", status: "pending" },
|
|
110
|
-
{ content: "Create src/another.ts", status: "pending" },
|
|
111
|
-
] as any,
|
|
112
|
-
});
|
|
113
|
-
|
|
114
|
-
// Should handle gracefully without crashing
|
|
115
|
-
expect(result.totalCount).toBe(6);
|
|
116
|
-
});
|
|
117
|
-
});
|
|
118
|
-
|
|
119
|
-
describe("detectCoordinatorViolation", () => {
|
|
120
|
-
const sessionId = "test-session-123";
|
|
121
|
-
const epicId = "test-epic-456";
|
|
122
|
-
|
|
123
|
-
// Clean up session files after tests
|
|
124
|
-
afterEach(() => {
|
|
125
|
-
const sessionDir = path.join(os.homedir(), ".config", "swarm-tools", "sessions");
|
|
126
|
-
const sessionPath = path.join(sessionDir, `${sessionId}.jsonl`);
|
|
127
|
-
if (fs.existsSync(sessionPath)) {
|
|
128
|
-
fs.unlinkSync(sessionPath);
|
|
129
|
-
}
|
|
130
|
-
});
|
|
131
|
-
|
|
132
|
-
describe("coordinator_edited_file violation", () => {
|
|
133
|
-
it("detects Edit tool call from coordinator", () => {
|
|
134
|
-
const result = detectCoordinatorViolation({
|
|
135
|
-
sessionId,
|
|
136
|
-
epicId,
|
|
137
|
-
toolName: "edit",
|
|
138
|
-
toolArgs: { filePath: "/path/to/file.ts", oldString: "old", newString: "new" },
|
|
139
|
-
agentContext: "coordinator",
|
|
140
|
-
});
|
|
141
|
-
|
|
142
|
-
expect(result.isViolation).toBe(true);
|
|
143
|
-
expect(result.violationType).toBe("coordinator_edited_file");
|
|
144
|
-
expect(result.message).toContain("Coordinators should spawn workers");
|
|
145
|
-
expect(result.payload.tool).toBe("edit");
|
|
146
|
-
expect(result.payload.file).toBe("/path/to/file.ts");
|
|
147
|
-
});
|
|
148
|
-
|
|
149
|
-
it("detects Write tool call from coordinator", () => {
|
|
150
|
-
const result = detectCoordinatorViolation({
|
|
151
|
-
sessionId,
|
|
152
|
-
epicId,
|
|
153
|
-
toolName: "write",
|
|
154
|
-
toolArgs: { filePath: "/path/to/new-file.ts", content: "export const foo = 1;" },
|
|
155
|
-
agentContext: "coordinator",
|
|
156
|
-
});
|
|
157
|
-
|
|
158
|
-
expect(result.isViolation).toBe(true);
|
|
159
|
-
expect(result.violationType).toBe("coordinator_edited_file");
|
|
160
|
-
expect(result.message).toContain("Coordinators should spawn workers");
|
|
161
|
-
expect(result.payload.tool).toBe("write");
|
|
162
|
-
expect(result.payload.file).toBe("/path/to/new-file.ts");
|
|
163
|
-
});
|
|
164
|
-
|
|
165
|
-
it("does not detect edit from worker agent", () => {
|
|
166
|
-
const result = detectCoordinatorViolation({
|
|
167
|
-
sessionId,
|
|
168
|
-
epicId,
|
|
169
|
-
toolName: "edit",
|
|
170
|
-
toolArgs: { filePath: "/path/to/file.ts", oldString: "old", newString: "new" },
|
|
171
|
-
agentContext: "worker",
|
|
172
|
-
});
|
|
173
|
-
|
|
174
|
-
expect(result.isViolation).toBe(false);
|
|
175
|
-
});
|
|
176
|
-
|
|
177
|
-
it("does not detect Read tool (read-only)", () => {
|
|
178
|
-
const result = detectCoordinatorViolation({
|
|
179
|
-
sessionId,
|
|
180
|
-
epicId,
|
|
181
|
-
toolName: "read",
|
|
182
|
-
toolArgs: { filePath: "/path/to/file.ts" },
|
|
183
|
-
agentContext: "coordinator",
|
|
184
|
-
});
|
|
185
|
-
|
|
186
|
-
expect(result.isViolation).toBe(false);
|
|
187
|
-
});
|
|
188
|
-
});
|
|
189
|
-
|
|
190
|
-
describe("coordinator_ran_tests violation", () => {
|
|
191
|
-
it("detects bash test execution from coordinator", () => {
|
|
192
|
-
const result = detectCoordinatorViolation({
|
|
193
|
-
sessionId,
|
|
194
|
-
epicId,
|
|
195
|
-
toolName: "bash",
|
|
196
|
-
toolArgs: { command: "bun test src/module.test.ts" },
|
|
197
|
-
agentContext: "coordinator",
|
|
198
|
-
});
|
|
199
|
-
|
|
200
|
-
expect(result.isViolation).toBe(true);
|
|
201
|
-
expect(result.violationType).toBe("coordinator_ran_tests");
|
|
202
|
-
expect(result.message).toContain("Workers run tests");
|
|
203
|
-
expect(result.payload.command).toContain("bun test");
|
|
204
|
-
});
|
|
205
|
-
|
|
206
|
-
it("detects npm test from coordinator", () => {
|
|
207
|
-
const result = detectCoordinatorViolation({
|
|
208
|
-
sessionId,
|
|
209
|
-
epicId,
|
|
210
|
-
toolName: "bash",
|
|
211
|
-
toolArgs: { command: "npm run test:unit" },
|
|
212
|
-
agentContext: "coordinator",
|
|
213
|
-
});
|
|
214
|
-
|
|
215
|
-
expect(result.isViolation).toBe(true);
|
|
216
|
-
expect(result.violationType).toBe("coordinator_ran_tests");
|
|
217
|
-
});
|
|
218
|
-
|
|
219
|
-
it("detects jest from coordinator", () => {
|
|
220
|
-
const result = detectCoordinatorViolation({
|
|
221
|
-
sessionId,
|
|
222
|
-
epicId,
|
|
223
|
-
toolName: "bash",
|
|
224
|
-
toolArgs: { command: "jest --coverage" },
|
|
225
|
-
agentContext: "coordinator",
|
|
226
|
-
});
|
|
227
|
-
|
|
228
|
-
expect(result.isViolation).toBe(true);
|
|
229
|
-
expect(result.violationType).toBe("coordinator_ran_tests");
|
|
230
|
-
});
|
|
231
|
-
|
|
232
|
-
it("does not detect non-test bash commands", () => {
|
|
233
|
-
const result = detectCoordinatorViolation({
|
|
234
|
-
sessionId,
|
|
235
|
-
epicId,
|
|
236
|
-
toolName: "bash",
|
|
237
|
-
toolArgs: { command: "git status" },
|
|
238
|
-
agentContext: "coordinator",
|
|
239
|
-
});
|
|
240
|
-
|
|
241
|
-
expect(result.isViolation).toBe(false);
|
|
242
|
-
});
|
|
243
|
-
|
|
244
|
-
it("does not detect test execution from worker", () => {
|
|
245
|
-
const result = detectCoordinatorViolation({
|
|
246
|
-
sessionId,
|
|
247
|
-
epicId,
|
|
248
|
-
toolName: "bash",
|
|
249
|
-
toolArgs: { command: "bun test" },
|
|
250
|
-
agentContext: "worker",
|
|
251
|
-
});
|
|
252
|
-
|
|
253
|
-
expect(result.isViolation).toBe(false);
|
|
254
|
-
});
|
|
255
|
-
});
|
|
256
|
-
|
|
257
|
-
describe("coordinator_reserved_files violation", () => {
|
|
258
|
-
it("detects swarmmail_reserve from coordinator", () => {
|
|
259
|
-
const result = detectCoordinatorViolation({
|
|
260
|
-
sessionId,
|
|
261
|
-
epicId,
|
|
262
|
-
toolName: "swarmmail_reserve",
|
|
263
|
-
toolArgs: { paths: ["src/auth/**"], reason: "Working on auth" },
|
|
264
|
-
agentContext: "coordinator",
|
|
265
|
-
});
|
|
266
|
-
|
|
267
|
-
expect(result.isViolation).toBe(true);
|
|
268
|
-
expect(result.violationType).toBe("coordinator_reserved_files");
|
|
269
|
-
expect(result.message).toContain("Workers reserve files");
|
|
270
|
-
expect(result.payload.paths).toEqual(["src/auth/**"]);
|
|
271
|
-
});
|
|
272
|
-
|
|
273
|
-
it("detects agentmail_reserve from coordinator", () => {
|
|
274
|
-
const result = detectCoordinatorViolation({
|
|
275
|
-
sessionId,
|
|
276
|
-
epicId,
|
|
277
|
-
toolName: "agentmail_reserve",
|
|
278
|
-
toolArgs: { paths: ["src/lib/**"], reason: "Refactoring" },
|
|
279
|
-
agentContext: "coordinator",
|
|
280
|
-
});
|
|
281
|
-
|
|
282
|
-
expect(result.isViolation).toBe(true);
|
|
283
|
-
expect(result.violationType).toBe("coordinator_reserved_files");
|
|
284
|
-
});
|
|
285
|
-
|
|
286
|
-
it("does not detect reserve from worker", () => {
|
|
287
|
-
const result = detectCoordinatorViolation({
|
|
288
|
-
sessionId,
|
|
289
|
-
epicId,
|
|
290
|
-
toolName: "swarmmail_reserve",
|
|
291
|
-
toolArgs: { paths: ["src/auth/**"], reason: "Working on auth" },
|
|
292
|
-
agentContext: "worker",
|
|
293
|
-
});
|
|
294
|
-
|
|
295
|
-
expect(result.isViolation).toBe(false);
|
|
296
|
-
});
|
|
297
|
-
});
|
|
298
|
-
|
|
299
|
-
describe("no_worker_spawned violation", () => {
|
|
300
|
-
it("detects no spawn after decomposition", () => {
|
|
301
|
-
const result = detectCoordinatorViolation({
|
|
302
|
-
sessionId,
|
|
303
|
-
epicId,
|
|
304
|
-
toolName: "hive_create_epic",
|
|
305
|
-
toolArgs: {
|
|
306
|
-
epic_title: "Add feature",
|
|
307
|
-
subtasks: [
|
|
308
|
-
{ title: "Task 1", files: ["a.ts"] },
|
|
309
|
-
{ title: "Task 2", files: ["b.ts"] },
|
|
310
|
-
],
|
|
311
|
-
},
|
|
312
|
-
agentContext: "coordinator",
|
|
313
|
-
checkNoSpawn: true,
|
|
314
|
-
});
|
|
315
|
-
|
|
316
|
-
expect(result.isViolation).toBe(true);
|
|
317
|
-
expect(result.violationType).toBe("no_worker_spawned");
|
|
318
|
-
expect(result.message).toContain("decomposition without spawning");
|
|
319
|
-
expect(result.payload.epic_title).toBe("Add feature");
|
|
320
|
-
expect(result.payload.subtask_count).toBe(2);
|
|
321
|
-
});
|
|
322
|
-
|
|
323
|
-
it("does not flag if workers were spawned", () => {
|
|
324
|
-
const result = detectCoordinatorViolation({
|
|
325
|
-
sessionId,
|
|
326
|
-
epicId,
|
|
327
|
-
toolName: "hive_create_epic",
|
|
328
|
-
toolArgs: {
|
|
329
|
-
epic_title: "Add feature",
|
|
330
|
-
subtasks: [
|
|
331
|
-
{ title: "Task 1", files: ["a.ts"] },
|
|
332
|
-
{ title: "Task 2", files: ["b.ts"] },
|
|
333
|
-
],
|
|
334
|
-
},
|
|
335
|
-
agentContext: "coordinator",
|
|
336
|
-
checkNoSpawn: false, // Workers were spawned
|
|
337
|
-
});
|
|
338
|
-
|
|
339
|
-
expect(result.isViolation).toBe(false);
|
|
340
|
-
});
|
|
341
|
-
|
|
342
|
-
it("does not flag from worker agent", () => {
|
|
343
|
-
const result = detectCoordinatorViolation({
|
|
344
|
-
sessionId,
|
|
345
|
-
epicId,
|
|
346
|
-
toolName: "hive_create_epic",
|
|
347
|
-
toolArgs: {
|
|
348
|
-
epic_title: "Add feature",
|
|
349
|
-
subtasks: [{ title: "Task 1", files: ["a.ts"] }],
|
|
350
|
-
},
|
|
351
|
-
agentContext: "worker",
|
|
352
|
-
checkNoSpawn: true,
|
|
353
|
-
});
|
|
354
|
-
|
|
355
|
-
expect(result.isViolation).toBe(false);
|
|
356
|
-
});
|
|
357
|
-
});
|
|
358
|
-
|
|
359
|
-
describe("event capture integration", () => {
|
|
360
|
-
it("captures violation event to session file when violation detected", () => {
|
|
361
|
-
const result = detectCoordinatorViolation({
|
|
362
|
-
sessionId,
|
|
363
|
-
epicId,
|
|
364
|
-
toolName: "edit",
|
|
365
|
-
toolArgs: { filePath: "/test.ts", oldString: "a", newString: "b" },
|
|
366
|
-
agentContext: "coordinator",
|
|
367
|
-
});
|
|
368
|
-
|
|
369
|
-
expect(result.isViolation).toBe(true);
|
|
370
|
-
|
|
371
|
-
// Verify event was written to session file
|
|
372
|
-
const sessionDir = path.join(os.homedir(), ".config", "swarm-tools", "sessions");
|
|
373
|
-
const sessionPath = path.join(sessionDir, `${sessionId}.jsonl`);
|
|
374
|
-
expect(fs.existsSync(sessionPath)).toBe(true);
|
|
375
|
-
|
|
376
|
-
const content = fs.readFileSync(sessionPath, "utf-8");
|
|
377
|
-
const lines = content.trim().split("\n");
|
|
378
|
-
expect(lines.length).toBe(1);
|
|
379
|
-
|
|
380
|
-
const event = JSON.parse(lines[0]);
|
|
381
|
-
expect(event.event_type).toBe("VIOLATION");
|
|
382
|
-
expect(event.violation_type).toBe("coordinator_edited_file");
|
|
383
|
-
expect(event.session_id).toBe(sessionId);
|
|
384
|
-
expect(event.epic_id).toBe(epicId);
|
|
385
|
-
expect(event.payload.tool).toBe("edit");
|
|
386
|
-
});
|
|
387
|
-
|
|
388
|
-
it("does not capture event when no violation", () => {
|
|
389
|
-
const result = detectCoordinatorViolation({
|
|
390
|
-
sessionId,
|
|
391
|
-
epicId,
|
|
392
|
-
toolName: "read",
|
|
393
|
-
toolArgs: { filePath: "/test.ts" },
|
|
394
|
-
agentContext: "coordinator",
|
|
395
|
-
});
|
|
396
|
-
|
|
397
|
-
expect(result.isViolation).toBe(false);
|
|
398
|
-
|
|
399
|
-
// Verify no session file created
|
|
400
|
-
const sessionDir = path.join(os.homedir(), ".config", "swarm-tools", "sessions");
|
|
401
|
-
const sessionPath = path.join(sessionDir, `${sessionId}.jsonl`);
|
|
402
|
-
expect(fs.existsSync(sessionPath)).toBe(false);
|
|
403
|
-
});
|
|
404
|
-
});
|
|
405
|
-
});
|
|
406
|
-
|
|
407
|
-
describe("coordinator context", () => {
|
|
408
|
-
beforeEach(() => {
|
|
409
|
-
clearCoordinatorContext();
|
|
410
|
-
});
|
|
411
|
-
|
|
412
|
-
afterEach(() => {
|
|
413
|
-
clearCoordinatorContext();
|
|
414
|
-
});
|
|
415
|
-
|
|
416
|
-
describe("setCoordinatorContext", () => {
|
|
417
|
-
it("sets coordinator context", () => {
|
|
418
|
-
setCoordinatorContext({
|
|
419
|
-
isCoordinator: true,
|
|
420
|
-
epicId: "test-epic-123",
|
|
421
|
-
sessionId: "test-session-456",
|
|
422
|
-
});
|
|
423
|
-
|
|
424
|
-
const ctx = getCoordinatorContext();
|
|
425
|
-
expect(ctx.isCoordinator).toBe(true);
|
|
426
|
-
expect(ctx.epicId).toBe("test-epic-123");
|
|
427
|
-
expect(ctx.sessionId).toBe("test-session-456");
|
|
428
|
-
expect(ctx.activatedAt).toBeDefined();
|
|
429
|
-
});
|
|
430
|
-
|
|
431
|
-
it("merges with existing context", () => {
|
|
432
|
-
setCoordinatorContext({
|
|
433
|
-
isCoordinator: true,
|
|
434
|
-
sessionId: "session-1",
|
|
435
|
-
});
|
|
436
|
-
|
|
437
|
-
setCoordinatorContext({
|
|
438
|
-
epicId: "epic-1",
|
|
439
|
-
});
|
|
440
|
-
|
|
441
|
-
const ctx = getCoordinatorContext();
|
|
442
|
-
expect(ctx.isCoordinator).toBe(true);
|
|
443
|
-
expect(ctx.sessionId).toBe("session-1");
|
|
444
|
-
expect(ctx.epicId).toBe("epic-1");
|
|
445
|
-
});
|
|
446
|
-
});
|
|
447
|
-
|
|
448
|
-
describe("isInCoordinatorContext", () => {
|
|
449
|
-
it("returns false when not in coordinator context", () => {
|
|
450
|
-
expect(isInCoordinatorContext()).toBe(false);
|
|
451
|
-
});
|
|
452
|
-
|
|
453
|
-
it("returns true when in coordinator context", () => {
|
|
454
|
-
setCoordinatorContext({
|
|
455
|
-
isCoordinator: true,
|
|
456
|
-
epicId: "test-epic",
|
|
457
|
-
});
|
|
458
|
-
|
|
459
|
-
expect(isInCoordinatorContext()).toBe(true);
|
|
460
|
-
});
|
|
461
|
-
|
|
462
|
-
it("returns false after context is cleared", () => {
|
|
463
|
-
setCoordinatorContext({
|
|
464
|
-
isCoordinator: true,
|
|
465
|
-
epicId: "test-epic",
|
|
466
|
-
});
|
|
467
|
-
|
|
468
|
-
clearCoordinatorContext();
|
|
469
|
-
|
|
470
|
-
expect(isInCoordinatorContext()).toBe(false);
|
|
471
|
-
});
|
|
472
|
-
});
|
|
473
|
-
|
|
474
|
-
describe("clearCoordinatorContext", () => {
|
|
475
|
-
it("clears all context", () => {
|
|
476
|
-
setCoordinatorContext({
|
|
477
|
-
isCoordinator: true,
|
|
478
|
-
epicId: "test-epic",
|
|
479
|
-
sessionId: "test-session",
|
|
480
|
-
});
|
|
481
|
-
|
|
482
|
-
clearCoordinatorContext();
|
|
483
|
-
|
|
484
|
-
const ctx = getCoordinatorContext();
|
|
485
|
-
expect(ctx.isCoordinator).toBe(false);
|
|
486
|
-
expect(ctx.epicId).toBeUndefined();
|
|
487
|
-
expect(ctx.sessionId).toBeUndefined();
|
|
488
|
-
});
|
|
489
|
-
});
|
|
490
|
-
});
|
|
491
|
-
});
|