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
|
@@ -1,578 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Tests for mandate storage
|
|
3
|
-
*/
|
|
4
|
-
import { describe, it, expect, beforeEach } from "vitest";
|
|
5
|
-
import {
|
|
6
|
-
createMandateStorage,
|
|
7
|
-
InMemoryMandateStorage,
|
|
8
|
-
updateMandateStatus,
|
|
9
|
-
updateAllMandateStatuses,
|
|
10
|
-
type MandateStorage,
|
|
11
|
-
} from "./mandate-storage";
|
|
12
|
-
import type { MandateEntry, Vote } from "./schemas/mandate";
|
|
13
|
-
|
|
14
|
-
describe("InMemoryMandateStorage", () => {
|
|
15
|
-
let storage: MandateStorage;
|
|
16
|
-
|
|
17
|
-
beforeEach(() => {
|
|
18
|
-
storage = createMandateStorage({ backend: "memory" });
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
describe("Entry operations", () => {
|
|
22
|
-
it("should store and retrieve mandate entry", async () => {
|
|
23
|
-
const entry: MandateEntry = {
|
|
24
|
-
id: "mandate-1",
|
|
25
|
-
content: "Always use Effect for async operations",
|
|
26
|
-
content_type: "tip",
|
|
27
|
-
author_agent: "BlueLake",
|
|
28
|
-
created_at: new Date().toISOString(),
|
|
29
|
-
status: "candidate",
|
|
30
|
-
tags: ["async", "effect"],
|
|
31
|
-
};
|
|
32
|
-
|
|
33
|
-
await storage.store(entry);
|
|
34
|
-
const retrieved = await storage.get("mandate-1");
|
|
35
|
-
|
|
36
|
-
expect(retrieved).toEqual(entry);
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
it("should return null for non-existent mandate", async () => {
|
|
40
|
-
const result = await storage.get("non-existent");
|
|
41
|
-
expect(result).toBeNull();
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
it("should find mandates by query", async () => {
|
|
45
|
-
const entry1: MandateEntry = {
|
|
46
|
-
id: "mandate-1",
|
|
47
|
-
content: "Use Effect for async operations",
|
|
48
|
-
content_type: "tip",
|
|
49
|
-
author_agent: "BlueLake",
|
|
50
|
-
created_at: new Date().toISOString(),
|
|
51
|
-
status: "candidate",
|
|
52
|
-
tags: ["async"],
|
|
53
|
-
};
|
|
54
|
-
|
|
55
|
-
const entry2: MandateEntry = {
|
|
56
|
-
id: "mandate-2",
|
|
57
|
-
content: "Prefer semantic memory for persistence",
|
|
58
|
-
content_type: "tip",
|
|
59
|
-
author_agent: "GreenRiver",
|
|
60
|
-
created_at: new Date().toISOString(),
|
|
61
|
-
status: "candidate",
|
|
62
|
-
tags: ["storage"],
|
|
63
|
-
};
|
|
64
|
-
|
|
65
|
-
await storage.store(entry1);
|
|
66
|
-
await storage.store(entry2);
|
|
67
|
-
|
|
68
|
-
const results = await storage.find("Effect");
|
|
69
|
-
expect(results).toHaveLength(1);
|
|
70
|
-
expect(results[0].id).toBe("mandate-1");
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
it("should list all mandates", async () => {
|
|
74
|
-
const entry1: MandateEntry = {
|
|
75
|
-
id: "mandate-1",
|
|
76
|
-
content: "Tip 1",
|
|
77
|
-
content_type: "tip",
|
|
78
|
-
author_agent: "BlueLake",
|
|
79
|
-
created_at: new Date().toISOString(),
|
|
80
|
-
status: "candidate",
|
|
81
|
-
tags: [],
|
|
82
|
-
};
|
|
83
|
-
|
|
84
|
-
const entry2: MandateEntry = {
|
|
85
|
-
id: "mandate-2",
|
|
86
|
-
content: "Idea 1",
|
|
87
|
-
content_type: "idea",
|
|
88
|
-
author_agent: "GreenRiver",
|
|
89
|
-
created_at: new Date().toISOString(),
|
|
90
|
-
status: "mandate",
|
|
91
|
-
tags: [],
|
|
92
|
-
};
|
|
93
|
-
|
|
94
|
-
await storage.store(entry1);
|
|
95
|
-
await storage.store(entry2);
|
|
96
|
-
|
|
97
|
-
const all = await storage.list();
|
|
98
|
-
expect(all).toHaveLength(2);
|
|
99
|
-
});
|
|
100
|
-
|
|
101
|
-
it("should filter mandates by status", async () => {
|
|
102
|
-
const entry1: MandateEntry = {
|
|
103
|
-
id: "mandate-1",
|
|
104
|
-
content: "Tip 1",
|
|
105
|
-
content_type: "tip",
|
|
106
|
-
author_agent: "BlueLake",
|
|
107
|
-
created_at: new Date().toISOString(),
|
|
108
|
-
status: "candidate",
|
|
109
|
-
tags: [],
|
|
110
|
-
};
|
|
111
|
-
|
|
112
|
-
const entry2: MandateEntry = {
|
|
113
|
-
id: "mandate-2",
|
|
114
|
-
content: "Idea 1",
|
|
115
|
-
content_type: "idea",
|
|
116
|
-
author_agent: "GreenRiver",
|
|
117
|
-
created_at: new Date().toISOString(),
|
|
118
|
-
status: "mandate",
|
|
119
|
-
tags: [],
|
|
120
|
-
};
|
|
121
|
-
|
|
122
|
-
await storage.store(entry1);
|
|
123
|
-
await storage.store(entry2);
|
|
124
|
-
|
|
125
|
-
const mandates = await storage.list({ status: "mandate" });
|
|
126
|
-
expect(mandates).toHaveLength(1);
|
|
127
|
-
expect(mandates[0].id).toBe("mandate-2");
|
|
128
|
-
});
|
|
129
|
-
|
|
130
|
-
it("should filter mandates by content_type", async () => {
|
|
131
|
-
const entry1: MandateEntry = {
|
|
132
|
-
id: "mandate-1",
|
|
133
|
-
content: "Tip 1",
|
|
134
|
-
content_type: "tip",
|
|
135
|
-
author_agent: "BlueLake",
|
|
136
|
-
created_at: new Date().toISOString(),
|
|
137
|
-
status: "candidate",
|
|
138
|
-
tags: [],
|
|
139
|
-
};
|
|
140
|
-
|
|
141
|
-
const entry2: MandateEntry = {
|
|
142
|
-
id: "mandate-2",
|
|
143
|
-
content: "Idea 1",
|
|
144
|
-
content_type: "idea",
|
|
145
|
-
author_agent: "GreenRiver",
|
|
146
|
-
created_at: new Date().toISOString(),
|
|
147
|
-
status: "candidate",
|
|
148
|
-
tags: [],
|
|
149
|
-
};
|
|
150
|
-
|
|
151
|
-
await storage.store(entry1);
|
|
152
|
-
await storage.store(entry2);
|
|
153
|
-
|
|
154
|
-
const tips = await storage.list({ content_type: "tip" });
|
|
155
|
-
expect(tips).toHaveLength(1);
|
|
156
|
-
expect(tips[0].id).toBe("mandate-1");
|
|
157
|
-
});
|
|
158
|
-
|
|
159
|
-
it("should update mandate entry", async () => {
|
|
160
|
-
const entry: MandateEntry = {
|
|
161
|
-
id: "mandate-1",
|
|
162
|
-
content: "Original content",
|
|
163
|
-
content_type: "tip",
|
|
164
|
-
author_agent: "BlueLake",
|
|
165
|
-
created_at: new Date().toISOString(),
|
|
166
|
-
status: "candidate",
|
|
167
|
-
tags: [],
|
|
168
|
-
};
|
|
169
|
-
|
|
170
|
-
await storage.store(entry);
|
|
171
|
-
await storage.update("mandate-1", { content: "Updated content" });
|
|
172
|
-
|
|
173
|
-
const updated = await storage.get("mandate-1");
|
|
174
|
-
expect(updated?.content).toBe("Updated content");
|
|
175
|
-
});
|
|
176
|
-
|
|
177
|
-
it("should throw when updating non-existent mandate", async () => {
|
|
178
|
-
await expect(
|
|
179
|
-
storage.update("non-existent", { content: "Updated" }),
|
|
180
|
-
).rejects.toThrow("Mandate 'non-existent' not found");
|
|
181
|
-
});
|
|
182
|
-
});
|
|
183
|
-
|
|
184
|
-
describe("Vote operations", () => {
|
|
185
|
-
beforeEach(async () => {
|
|
186
|
-
// Create a mandate to vote on
|
|
187
|
-
const entry: MandateEntry = {
|
|
188
|
-
id: "mandate-1",
|
|
189
|
-
content: "Test mandate",
|
|
190
|
-
content_type: "tip",
|
|
191
|
-
author_agent: "BlueLake",
|
|
192
|
-
created_at: new Date().toISOString(),
|
|
193
|
-
status: "candidate",
|
|
194
|
-
tags: [],
|
|
195
|
-
};
|
|
196
|
-
await storage.store(entry);
|
|
197
|
-
});
|
|
198
|
-
|
|
199
|
-
it("should cast vote and verify storage", async () => {
|
|
200
|
-
const vote: Vote = {
|
|
201
|
-
id: "vote-1",
|
|
202
|
-
mandate_id: "mandate-1",
|
|
203
|
-
agent_name: "GreenRiver",
|
|
204
|
-
vote_type: "upvote",
|
|
205
|
-
timestamp: new Date().toISOString(),
|
|
206
|
-
weight: 1.0,
|
|
207
|
-
};
|
|
208
|
-
|
|
209
|
-
await storage.vote(vote);
|
|
210
|
-
|
|
211
|
-
const votes = await storage.getVotes("mandate-1");
|
|
212
|
-
expect(votes).toHaveLength(1);
|
|
213
|
-
expect(votes[0]).toEqual(vote);
|
|
214
|
-
});
|
|
215
|
-
|
|
216
|
-
it("should prevent duplicate votes from same agent", async () => {
|
|
217
|
-
const vote1: Vote = {
|
|
218
|
-
id: "vote-1",
|
|
219
|
-
mandate_id: "mandate-1",
|
|
220
|
-
agent_name: "GreenRiver",
|
|
221
|
-
vote_type: "upvote",
|
|
222
|
-
timestamp: new Date().toISOString(),
|
|
223
|
-
weight: 1.0,
|
|
224
|
-
};
|
|
225
|
-
|
|
226
|
-
await storage.vote(vote1);
|
|
227
|
-
|
|
228
|
-
const vote2: Vote = {
|
|
229
|
-
id: "vote-2",
|
|
230
|
-
mandate_id: "mandate-1",
|
|
231
|
-
agent_name: "GreenRiver",
|
|
232
|
-
vote_type: "downvote",
|
|
233
|
-
timestamp: new Date().toISOString(),
|
|
234
|
-
weight: 1.0,
|
|
235
|
-
};
|
|
236
|
-
|
|
237
|
-
await expect(storage.vote(vote2)).rejects.toThrow(
|
|
238
|
-
"Agent 'GreenRiver' has already voted on mandate 'mandate-1'",
|
|
239
|
-
);
|
|
240
|
-
});
|
|
241
|
-
|
|
242
|
-
it("should check if agent has voted", async () => {
|
|
243
|
-
const vote: Vote = {
|
|
244
|
-
id: "vote-1",
|
|
245
|
-
mandate_id: "mandate-1",
|
|
246
|
-
agent_name: "GreenRiver",
|
|
247
|
-
vote_type: "upvote",
|
|
248
|
-
timestamp: new Date().toISOString(),
|
|
249
|
-
weight: 1.0,
|
|
250
|
-
};
|
|
251
|
-
|
|
252
|
-
await storage.vote(vote);
|
|
253
|
-
|
|
254
|
-
const hasVoted = await storage.hasVoted("mandate-1", "GreenRiver");
|
|
255
|
-
expect(hasVoted).toBe(true);
|
|
256
|
-
|
|
257
|
-
const hasNotVoted = await storage.hasVoted("mandate-1", "BlueLake");
|
|
258
|
-
expect(hasNotVoted).toBe(false);
|
|
259
|
-
});
|
|
260
|
-
|
|
261
|
-
it("should get all votes for a mandate", async () => {
|
|
262
|
-
const vote1: Vote = {
|
|
263
|
-
id: "vote-1",
|
|
264
|
-
mandate_id: "mandate-1",
|
|
265
|
-
agent_name: "GreenRiver",
|
|
266
|
-
vote_type: "upvote",
|
|
267
|
-
timestamp: new Date().toISOString(),
|
|
268
|
-
weight: 1.0,
|
|
269
|
-
};
|
|
270
|
-
|
|
271
|
-
const vote2: Vote = {
|
|
272
|
-
id: "vote-2",
|
|
273
|
-
mandate_id: "mandate-1",
|
|
274
|
-
agent_name: "RedMountain",
|
|
275
|
-
vote_type: "upvote",
|
|
276
|
-
timestamp: new Date().toISOString(),
|
|
277
|
-
weight: 1.0,
|
|
278
|
-
};
|
|
279
|
-
|
|
280
|
-
await storage.vote(vote1);
|
|
281
|
-
await storage.vote(vote2);
|
|
282
|
-
|
|
283
|
-
const votes = await storage.getVotes("mandate-1");
|
|
284
|
-
expect(votes).toHaveLength(2);
|
|
285
|
-
});
|
|
286
|
-
});
|
|
287
|
-
|
|
288
|
-
describe("Score calculation with decay", () => {
|
|
289
|
-
beforeEach(async () => {
|
|
290
|
-
// Create a mandate to vote on
|
|
291
|
-
const entry: MandateEntry = {
|
|
292
|
-
id: "mandate-1",
|
|
293
|
-
content: "Test mandate",
|
|
294
|
-
content_type: "tip",
|
|
295
|
-
author_agent: "BlueLake",
|
|
296
|
-
created_at: new Date().toISOString(),
|
|
297
|
-
status: "candidate",
|
|
298
|
-
tags: [],
|
|
299
|
-
};
|
|
300
|
-
await storage.store(entry);
|
|
301
|
-
});
|
|
302
|
-
|
|
303
|
-
it("should calculate score with no votes", async () => {
|
|
304
|
-
const score = await storage.calculateScore("mandate-1");
|
|
305
|
-
|
|
306
|
-
expect(score.mandate_id).toBe("mandate-1");
|
|
307
|
-
expect(score.raw_upvotes).toBe(0);
|
|
308
|
-
expect(score.raw_downvotes).toBe(0);
|
|
309
|
-
expect(score.decayed_upvotes).toBe(0);
|
|
310
|
-
expect(score.decayed_downvotes).toBe(0);
|
|
311
|
-
expect(score.net_votes).toBe(0);
|
|
312
|
-
expect(score.vote_ratio).toBe(0);
|
|
313
|
-
expect(score.decayed_score).toBe(0);
|
|
314
|
-
});
|
|
315
|
-
|
|
316
|
-
it("should calculate score with recent upvotes", async () => {
|
|
317
|
-
const vote1: Vote = {
|
|
318
|
-
id: "vote-1",
|
|
319
|
-
mandate_id: "mandate-1",
|
|
320
|
-
agent_name: "GreenRiver",
|
|
321
|
-
vote_type: "upvote",
|
|
322
|
-
timestamp: new Date().toISOString(),
|
|
323
|
-
weight: 1.0,
|
|
324
|
-
};
|
|
325
|
-
|
|
326
|
-
const vote2: Vote = {
|
|
327
|
-
id: "vote-2",
|
|
328
|
-
mandate_id: "mandate-1",
|
|
329
|
-
agent_name: "RedMountain",
|
|
330
|
-
vote_type: "upvote",
|
|
331
|
-
timestamp: new Date().toISOString(),
|
|
332
|
-
weight: 1.0,
|
|
333
|
-
};
|
|
334
|
-
|
|
335
|
-
await storage.vote(vote1);
|
|
336
|
-
await storage.vote(vote2);
|
|
337
|
-
|
|
338
|
-
const score = await storage.calculateScore("mandate-1");
|
|
339
|
-
|
|
340
|
-
expect(score.raw_upvotes).toBe(2);
|
|
341
|
-
expect(score.raw_downvotes).toBe(0);
|
|
342
|
-
expect(score.decayed_upvotes).toBeCloseTo(2.0, 1); // Recent votes, minimal decay
|
|
343
|
-
expect(score.net_votes).toBeGreaterThan(1.5);
|
|
344
|
-
expect(score.vote_ratio).toBe(1.0); // 100% upvotes
|
|
345
|
-
expect(score.decayed_score).toBeGreaterThan(1.5);
|
|
346
|
-
});
|
|
347
|
-
|
|
348
|
-
it("should calculate score with mixed votes", async () => {
|
|
349
|
-
const vote1: Vote = {
|
|
350
|
-
id: "vote-1",
|
|
351
|
-
mandate_id: "mandate-1",
|
|
352
|
-
agent_name: "GreenRiver",
|
|
353
|
-
vote_type: "upvote",
|
|
354
|
-
timestamp: new Date().toISOString(),
|
|
355
|
-
weight: 1.0,
|
|
356
|
-
};
|
|
357
|
-
|
|
358
|
-
const vote2: Vote = {
|
|
359
|
-
id: "vote-2",
|
|
360
|
-
mandate_id: "mandate-1",
|
|
361
|
-
agent_name: "RedMountain",
|
|
362
|
-
vote_type: "downvote",
|
|
363
|
-
timestamp: new Date().toISOString(),
|
|
364
|
-
weight: 1.0,
|
|
365
|
-
};
|
|
366
|
-
|
|
367
|
-
await storage.vote(vote1);
|
|
368
|
-
await storage.vote(vote2);
|
|
369
|
-
|
|
370
|
-
const score = await storage.calculateScore("mandate-1");
|
|
371
|
-
|
|
372
|
-
expect(score.raw_upvotes).toBe(1);
|
|
373
|
-
expect(score.raw_downvotes).toBe(1);
|
|
374
|
-
expect(score.vote_ratio).toBeCloseTo(0.5, 1); // 50% ratio
|
|
375
|
-
expect(score.net_votes).toBeCloseTo(0, 1); // Equal votes cancel out
|
|
376
|
-
});
|
|
377
|
-
|
|
378
|
-
it("should apply decay to old votes", async () => {
|
|
379
|
-
// Vote from 90 days ago (one half-life)
|
|
380
|
-
const ninetyDaysAgo = new Date();
|
|
381
|
-
ninetyDaysAgo.setDate(ninetyDaysAgo.getDate() - 90);
|
|
382
|
-
|
|
383
|
-
const oldVote: Vote = {
|
|
384
|
-
id: "vote-1",
|
|
385
|
-
mandate_id: "mandate-1",
|
|
386
|
-
agent_name: "GreenRiver",
|
|
387
|
-
vote_type: "upvote",
|
|
388
|
-
timestamp: ninetyDaysAgo.toISOString(),
|
|
389
|
-
weight: 1.0,
|
|
390
|
-
};
|
|
391
|
-
|
|
392
|
-
await storage.vote(oldVote);
|
|
393
|
-
|
|
394
|
-
const score = await storage.calculateScore("mandate-1");
|
|
395
|
-
|
|
396
|
-
expect(score.raw_upvotes).toBe(1);
|
|
397
|
-
expect(score.decayed_upvotes).toBeCloseTo(0.5, 1); // ~50% after 90 days
|
|
398
|
-
expect(score.net_votes).toBeCloseTo(0.5, 1);
|
|
399
|
-
});
|
|
400
|
-
|
|
401
|
-
it("should handle vote weights", async () => {
|
|
402
|
-
const vote1: Vote = {
|
|
403
|
-
id: "vote-1",
|
|
404
|
-
mandate_id: "mandate-1",
|
|
405
|
-
agent_name: "GreenRiver",
|
|
406
|
-
vote_type: "upvote",
|
|
407
|
-
timestamp: new Date().toISOString(),
|
|
408
|
-
weight: 0.5, // Partial weight
|
|
409
|
-
};
|
|
410
|
-
|
|
411
|
-
await storage.vote(vote1);
|
|
412
|
-
|
|
413
|
-
const score = await storage.calculateScore("mandate-1");
|
|
414
|
-
|
|
415
|
-
expect(score.raw_upvotes).toBe(1); // Count is still 1
|
|
416
|
-
expect(score.decayed_upvotes).toBeCloseTo(0.5, 1); // But weighted value is 0.5
|
|
417
|
-
});
|
|
418
|
-
});
|
|
419
|
-
|
|
420
|
-
describe("Status updates based on score", () => {
|
|
421
|
-
it("should transition to mandate status with high score", async () => {
|
|
422
|
-
const entry: MandateEntry = {
|
|
423
|
-
id: "mandate-1",
|
|
424
|
-
content: "Test mandate",
|
|
425
|
-
content_type: "tip",
|
|
426
|
-
author_agent: "BlueLake",
|
|
427
|
-
created_at: new Date().toISOString(),
|
|
428
|
-
status: "candidate",
|
|
429
|
-
tags: [],
|
|
430
|
-
};
|
|
431
|
-
await storage.store(entry);
|
|
432
|
-
|
|
433
|
-
// Add 6 upvotes to exceed threshold (net_votes >= 5, ratio >= 0.7)
|
|
434
|
-
for (let i = 0; i < 6; i++) {
|
|
435
|
-
const vote: Vote = {
|
|
436
|
-
id: `vote-${i}`,
|
|
437
|
-
mandate_id: "mandate-1",
|
|
438
|
-
agent_name: `Agent${i}`,
|
|
439
|
-
vote_type: "upvote",
|
|
440
|
-
timestamp: new Date().toISOString(),
|
|
441
|
-
weight: 1.0,
|
|
442
|
-
};
|
|
443
|
-
await storage.vote(vote);
|
|
444
|
-
}
|
|
445
|
-
|
|
446
|
-
const result = await updateMandateStatus("mandate-1", storage);
|
|
447
|
-
|
|
448
|
-
expect(result.status_changed).toBe(true);
|
|
449
|
-
expect(result.previous_status).toBe("candidate");
|
|
450
|
-
expect(result.new_status).toBe("mandate");
|
|
451
|
-
expect(result.score.net_votes).toBeGreaterThanOrEqual(5);
|
|
452
|
-
expect(result.score.vote_ratio).toBeGreaterThanOrEqual(0.7);
|
|
453
|
-
});
|
|
454
|
-
|
|
455
|
-
it("should transition to established status", async () => {
|
|
456
|
-
const entry: MandateEntry = {
|
|
457
|
-
id: "mandate-1",
|
|
458
|
-
content: "Test mandate",
|
|
459
|
-
content_type: "tip",
|
|
460
|
-
author_agent: "BlueLake",
|
|
461
|
-
created_at: new Date().toISOString(),
|
|
462
|
-
status: "candidate",
|
|
463
|
-
tags: [],
|
|
464
|
-
};
|
|
465
|
-
await storage.store(entry);
|
|
466
|
-
|
|
467
|
-
// Add 3 upvotes (net_votes >= 2 but < 5)
|
|
468
|
-
for (let i = 0; i < 3; i++) {
|
|
469
|
-
const vote: Vote = {
|
|
470
|
-
id: `vote-${i}`,
|
|
471
|
-
mandate_id: "mandate-1",
|
|
472
|
-
agent_name: `Agent${i}`,
|
|
473
|
-
vote_type: "upvote",
|
|
474
|
-
timestamp: new Date().toISOString(),
|
|
475
|
-
weight: 1.0,
|
|
476
|
-
};
|
|
477
|
-
await storage.vote(vote);
|
|
478
|
-
}
|
|
479
|
-
|
|
480
|
-
const result = await updateMandateStatus("mandate-1", storage);
|
|
481
|
-
|
|
482
|
-
expect(result.status_changed).toBe(true);
|
|
483
|
-
expect(result.new_status).toBe("established");
|
|
484
|
-
});
|
|
485
|
-
|
|
486
|
-
it("should transition to rejected status", async () => {
|
|
487
|
-
const entry: MandateEntry = {
|
|
488
|
-
id: "mandate-1",
|
|
489
|
-
content: "Test mandate",
|
|
490
|
-
content_type: "tip",
|
|
491
|
-
author_agent: "BlueLake",
|
|
492
|
-
created_at: new Date().toISOString(),
|
|
493
|
-
status: "candidate",
|
|
494
|
-
tags: [],
|
|
495
|
-
};
|
|
496
|
-
await storage.store(entry);
|
|
497
|
-
|
|
498
|
-
// Add 4 downvotes (net_votes <= -3)
|
|
499
|
-
for (let i = 0; i < 4; i++) {
|
|
500
|
-
const vote: Vote = {
|
|
501
|
-
id: `vote-${i}`,
|
|
502
|
-
mandate_id: "mandate-1",
|
|
503
|
-
agent_name: `Agent${i}`,
|
|
504
|
-
vote_type: "downvote",
|
|
505
|
-
timestamp: new Date().toISOString(),
|
|
506
|
-
weight: 1.0,
|
|
507
|
-
};
|
|
508
|
-
await storage.vote(vote);
|
|
509
|
-
}
|
|
510
|
-
|
|
511
|
-
const result = await updateMandateStatus("mandate-1", storage);
|
|
512
|
-
|
|
513
|
-
expect(result.status_changed).toBe(true);
|
|
514
|
-
expect(result.new_status).toBe("rejected");
|
|
515
|
-
});
|
|
516
|
-
|
|
517
|
-
it("should batch update all mandates", async () => {
|
|
518
|
-
// Create multiple mandates
|
|
519
|
-
const entry1: MandateEntry = {
|
|
520
|
-
id: "mandate-1",
|
|
521
|
-
content: "Tip 1",
|
|
522
|
-
content_type: "tip",
|
|
523
|
-
author_agent: "BlueLake",
|
|
524
|
-
created_at: new Date().toISOString(),
|
|
525
|
-
status: "candidate",
|
|
526
|
-
tags: [],
|
|
527
|
-
};
|
|
528
|
-
|
|
529
|
-
const entry2: MandateEntry = {
|
|
530
|
-
id: "mandate-2",
|
|
531
|
-
content: "Tip 2",
|
|
532
|
-
content_type: "tip",
|
|
533
|
-
author_agent: "GreenRiver",
|
|
534
|
-
created_at: new Date().toISOString(),
|
|
535
|
-
status: "candidate",
|
|
536
|
-
tags: [],
|
|
537
|
-
};
|
|
538
|
-
|
|
539
|
-
await storage.store(entry1);
|
|
540
|
-
await storage.store(entry2);
|
|
541
|
-
|
|
542
|
-
// Add votes to first mandate
|
|
543
|
-
for (let i = 0; i < 6; i++) {
|
|
544
|
-
const vote: Vote = {
|
|
545
|
-
id: `vote-${i}`,
|
|
546
|
-
mandate_id: "mandate-1",
|
|
547
|
-
agent_name: `Agent${i}`,
|
|
548
|
-
vote_type: "upvote",
|
|
549
|
-
timestamp: new Date().toISOString(),
|
|
550
|
-
weight: 1.0,
|
|
551
|
-
};
|
|
552
|
-
await storage.vote(vote);
|
|
553
|
-
}
|
|
554
|
-
|
|
555
|
-
const results = await updateAllMandateStatuses(storage);
|
|
556
|
-
|
|
557
|
-
expect(results).toHaveLength(2);
|
|
558
|
-
expect(results[0].status_changed).toBe(true);
|
|
559
|
-
expect(results[0].new_status).toBe("mandate");
|
|
560
|
-
expect(results[1].status_changed).toBe(false); // No votes, stays candidate
|
|
561
|
-
});
|
|
562
|
-
});
|
|
563
|
-
|
|
564
|
-
describe("Factory", () => {
|
|
565
|
-
it("should create in-memory storage", () => {
|
|
566
|
-
const storage = createMandateStorage({ backend: "memory" });
|
|
567
|
-
expect(storage).toBeInstanceOf(InMemoryMandateStorage);
|
|
568
|
-
});
|
|
569
|
-
|
|
570
|
-
it("should throw on unknown backend", () => {
|
|
571
|
-
expect(() =>
|
|
572
|
-
createMandateStorage({
|
|
573
|
-
backend: "unknown" as "semantic-memory" | "memory",
|
|
574
|
-
}),
|
|
575
|
-
).toThrow("Unknown storage backend: 'unknown'");
|
|
576
|
-
});
|
|
577
|
-
});
|
|
578
|
-
});
|