opencode-swarm-plugin 0.43.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/cass.characterization.test.ts +422 -0
- package/bin/swarm.serve.test.ts +6 -4
- package/bin/swarm.test.ts +68 -0
- package/bin/swarm.ts +81 -8
- package/dist/compaction-prompt-scoring.js +139 -0
- package/dist/contributor-tools.d.ts +42 -0
- package/dist/contributor-tools.d.ts.map +1 -0
- package/dist/eval-capture.js +12811 -0
- package/dist/hive.d.ts.map +1 -1
- package/dist/index.d.ts +12 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7728 -62590
- package/dist/plugin.js +23833 -78695
- package/dist/sessions/agent-discovery.d.ts +59 -0
- package/dist/sessions/agent-discovery.d.ts.map +1 -0
- package/dist/sessions/index.d.ts +10 -0
- package/dist/sessions/index.d.ts.map +1 -0
- 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 -2255
- 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/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/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 -2426
- 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/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 -935
- 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/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,473 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Tests for mandate promotion engine
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import { describe, expect, it } from "vitest";
|
|
6
|
-
import {
|
|
7
|
-
evaluateBatchPromotions,
|
|
8
|
-
evaluatePromotion,
|
|
9
|
-
formatPromotionResult,
|
|
10
|
-
getStatusChanges,
|
|
11
|
-
groupByTransition,
|
|
12
|
-
shouldPromote,
|
|
13
|
-
} from "./mandate-promotion";
|
|
14
|
-
import { DEFAULT_MANDATE_DECAY_CONFIG } from "./schemas/mandate";
|
|
15
|
-
import type { MandateEntry, MandateScore } from "./schemas/mandate";
|
|
16
|
-
|
|
17
|
-
// ============================================================================
|
|
18
|
-
// Test Helpers
|
|
19
|
-
// ============================================================================
|
|
20
|
-
|
|
21
|
-
function createMockEntry(
|
|
22
|
-
id: string,
|
|
23
|
-
status: "candidate" | "established" | "mandate" | "rejected" = "candidate",
|
|
24
|
-
): MandateEntry {
|
|
25
|
-
return {
|
|
26
|
-
id,
|
|
27
|
-
content: "Test mandate content",
|
|
28
|
-
content_type: "idea",
|
|
29
|
-
author_agent: "TestAgent",
|
|
30
|
-
created_at: new Date().toISOString(),
|
|
31
|
-
status,
|
|
32
|
-
tags: [],
|
|
33
|
-
};
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
function createMockScore(
|
|
37
|
-
mandate_id: string,
|
|
38
|
-
net_votes: number,
|
|
39
|
-
vote_ratio: number,
|
|
40
|
-
raw_upvotes: number = Math.max(0, net_votes),
|
|
41
|
-
raw_downvotes: number = 0,
|
|
42
|
-
): MandateScore {
|
|
43
|
-
return {
|
|
44
|
-
mandate_id,
|
|
45
|
-
net_votes,
|
|
46
|
-
vote_ratio,
|
|
47
|
-
decayed_score: net_votes * vote_ratio,
|
|
48
|
-
last_calculated: new Date().toISOString(),
|
|
49
|
-
raw_upvotes,
|
|
50
|
-
raw_downvotes,
|
|
51
|
-
decayed_upvotes: raw_upvotes,
|
|
52
|
-
decayed_downvotes: raw_downvotes,
|
|
53
|
-
};
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
// ============================================================================
|
|
57
|
-
// shouldPromote Tests
|
|
58
|
-
// ============================================================================
|
|
59
|
-
|
|
60
|
-
describe("shouldPromote", () => {
|
|
61
|
-
it("candidate stays candidate with insufficient votes", () => {
|
|
62
|
-
const score = createMockScore("m1", 1, 1.0);
|
|
63
|
-
const result = shouldPromote(score, "candidate");
|
|
64
|
-
expect(result).toBe("candidate");
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
it("candidate → established at threshold (net_votes >= 2)", () => {
|
|
68
|
-
const score = createMockScore("m1", 2, 1.0);
|
|
69
|
-
const result = shouldPromote(score, "candidate");
|
|
70
|
-
expect(result).toBe("established");
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
it("candidate → established above threshold", () => {
|
|
74
|
-
const score = createMockScore("m1", 3, 0.9);
|
|
75
|
-
const result = shouldPromote(score, "candidate");
|
|
76
|
-
expect(result).toBe("established");
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
it("established stays established with insufficient mandate votes", () => {
|
|
80
|
-
const score = createMockScore("m1", 3, 0.8);
|
|
81
|
-
const result = shouldPromote(score, "established");
|
|
82
|
-
expect(result).toBe("established");
|
|
83
|
-
});
|
|
84
|
-
|
|
85
|
-
it("established stays established with low vote ratio", () => {
|
|
86
|
-
const score = createMockScore("m1", 6, 0.6); // net_votes OK, but ratio < 0.7
|
|
87
|
-
const result = shouldPromote(score, "established");
|
|
88
|
-
expect(result).toBe("established");
|
|
89
|
-
});
|
|
90
|
-
|
|
91
|
-
it("established → mandate at threshold (net >= 5, ratio >= 0.7)", () => {
|
|
92
|
-
const score = createMockScore("m1", 5, 0.7);
|
|
93
|
-
const result = shouldPromote(score, "established");
|
|
94
|
-
expect(result).toBe("mandate");
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
it("established → mandate above threshold", () => {
|
|
98
|
-
const score = createMockScore("m1", 10, 0.9);
|
|
99
|
-
const result = shouldPromote(score, "established");
|
|
100
|
-
expect(result).toBe("mandate");
|
|
101
|
-
});
|
|
102
|
-
|
|
103
|
-
it("mandate stays mandate (no demotion)", () => {
|
|
104
|
-
const score = createMockScore("m1", 3, 0.5); // Degraded score
|
|
105
|
-
const result = shouldPromote(score, "mandate");
|
|
106
|
-
expect(result).toBe("mandate");
|
|
107
|
-
});
|
|
108
|
-
|
|
109
|
-
it("candidate → rejected with negative votes", () => {
|
|
110
|
-
const score = createMockScore("m1", -3, 0.2);
|
|
111
|
-
const result = shouldPromote(score, "candidate");
|
|
112
|
-
expect(result).toBe("rejected");
|
|
113
|
-
});
|
|
114
|
-
|
|
115
|
-
it("established → rejected with negative votes", () => {
|
|
116
|
-
const score = createMockScore("m1", -4, 0.1);
|
|
117
|
-
const result = shouldPromote(score, "established");
|
|
118
|
-
expect(result).toBe("rejected");
|
|
119
|
-
});
|
|
120
|
-
|
|
121
|
-
it("rejected stays rejected (permanent)", () => {
|
|
122
|
-
const score = createMockScore("m1", 5, 0.9); // Even with good score
|
|
123
|
-
const result = shouldPromote(score, "rejected");
|
|
124
|
-
expect(result).toBe("rejected");
|
|
125
|
-
});
|
|
126
|
-
|
|
127
|
-
it("uses custom config thresholds", () => {
|
|
128
|
-
const score = createMockScore("m1", 3, 0.6);
|
|
129
|
-
const customConfig = {
|
|
130
|
-
...DEFAULT_MANDATE_DECAY_CONFIG,
|
|
131
|
-
establishedNetVotesThreshold: 3,
|
|
132
|
-
mandateNetVotesThreshold: 3,
|
|
133
|
-
mandateVoteRatioThreshold: 0.6,
|
|
134
|
-
};
|
|
135
|
-
const result = shouldPromote(score, "candidate", customConfig);
|
|
136
|
-
expect(result).toBe("established");
|
|
137
|
-
|
|
138
|
-
const result2 = shouldPromote(score, "established", customConfig);
|
|
139
|
-
expect(result2).toBe("mandate");
|
|
140
|
-
});
|
|
141
|
-
});
|
|
142
|
-
|
|
143
|
-
// ============================================================================
|
|
144
|
-
// evaluatePromotion Tests
|
|
145
|
-
// ============================================================================
|
|
146
|
-
|
|
147
|
-
describe("evaluatePromotion", () => {
|
|
148
|
-
it("returns correct promotion result for candidate → established", () => {
|
|
149
|
-
const entry = createMockEntry("m1", "candidate");
|
|
150
|
-
const score = createMockScore("m1", 2, 1.0);
|
|
151
|
-
const result = evaluatePromotion(entry, score);
|
|
152
|
-
|
|
153
|
-
expect(result.mandate_id).toBe("m1");
|
|
154
|
-
expect(result.previous_status).toBe("candidate");
|
|
155
|
-
expect(result.new_status).toBe("established");
|
|
156
|
-
expect(result.promoted).toBe(true);
|
|
157
|
-
expect(result.reason).toContain("Promoted to established");
|
|
158
|
-
expect(result.score).toEqual(score);
|
|
159
|
-
});
|
|
160
|
-
|
|
161
|
-
it("returns correct promotion result for established → mandate", () => {
|
|
162
|
-
const entry = createMockEntry("m1", "established");
|
|
163
|
-
const score = createMockScore("m1", 5, 0.7);
|
|
164
|
-
const result = evaluatePromotion(entry, score);
|
|
165
|
-
|
|
166
|
-
expect(result.mandate_id).toBe("m1");
|
|
167
|
-
expect(result.previous_status).toBe("established");
|
|
168
|
-
expect(result.new_status).toBe("mandate");
|
|
169
|
-
expect(result.promoted).toBe(true);
|
|
170
|
-
expect(result.reason).toContain("Promoted to mandate");
|
|
171
|
-
});
|
|
172
|
-
|
|
173
|
-
it("returns correct promotion result for candidate → rejected", () => {
|
|
174
|
-
const entry = createMockEntry("m1", "candidate");
|
|
175
|
-
const score = createMockScore("m1", -3, 0.2);
|
|
176
|
-
const result = evaluatePromotion(entry, score);
|
|
177
|
-
|
|
178
|
-
expect(result.mandate_id).toBe("m1");
|
|
179
|
-
expect(result.previous_status).toBe("candidate");
|
|
180
|
-
expect(result.new_status).toBe("rejected");
|
|
181
|
-
expect(result.promoted).toBe(true);
|
|
182
|
-
expect(result.reason).toContain("Rejected due to negative consensus");
|
|
183
|
-
});
|
|
184
|
-
|
|
185
|
-
it("returns correct result for no status change", () => {
|
|
186
|
-
const entry = createMockEntry("m1", "candidate");
|
|
187
|
-
const score = createMockScore("m1", 1, 0.8);
|
|
188
|
-
const result = evaluatePromotion(entry, score);
|
|
189
|
-
|
|
190
|
-
expect(result.mandate_id).toBe("m1");
|
|
191
|
-
expect(result.previous_status).toBe("candidate");
|
|
192
|
-
expect(result.new_status).toBe("candidate");
|
|
193
|
-
expect(result.promoted).toBe(false);
|
|
194
|
-
expect(result.reason).toContain("Remains candidate");
|
|
195
|
-
});
|
|
196
|
-
|
|
197
|
-
it("returns correct result for mandate staying mandate", () => {
|
|
198
|
-
const entry = createMockEntry("m1", "mandate");
|
|
199
|
-
const score = createMockScore("m1", 3, 0.5); // Degraded
|
|
200
|
-
const result = evaluatePromotion(entry, score);
|
|
201
|
-
|
|
202
|
-
expect(result.mandate_id).toBe("m1");
|
|
203
|
-
expect(result.previous_status).toBe("mandate");
|
|
204
|
-
expect(result.new_status).toBe("mandate");
|
|
205
|
-
expect(result.promoted).toBe(false);
|
|
206
|
-
expect(result.reason).toContain("Remains mandate");
|
|
207
|
-
});
|
|
208
|
-
|
|
209
|
-
it("returns correct result for rejected staying rejected", () => {
|
|
210
|
-
const entry = createMockEntry("m1", "rejected");
|
|
211
|
-
const score = createMockScore("m1", 10, 0.95); // Good score
|
|
212
|
-
const result = evaluatePromotion(entry, score);
|
|
213
|
-
|
|
214
|
-
expect(result.mandate_id).toBe("m1");
|
|
215
|
-
expect(result.previous_status).toBe("rejected");
|
|
216
|
-
expect(result.new_status).toBe("rejected");
|
|
217
|
-
expect(result.promoted).toBe(false);
|
|
218
|
-
expect(result.reason).toContain("Remains rejected");
|
|
219
|
-
});
|
|
220
|
-
});
|
|
221
|
-
|
|
222
|
-
// ============================================================================
|
|
223
|
-
// Decay Effect Tests
|
|
224
|
-
// ============================================================================
|
|
225
|
-
|
|
226
|
-
describe("decay affects promotion timing", () => {
|
|
227
|
-
it("net_votes can decay below promotion threshold", () => {
|
|
228
|
-
// Entry was established with 2.5 decayed net_votes
|
|
229
|
-
const entry = createMockEntry("m1", "established");
|
|
230
|
-
const score = createMockScore("m1", 1.5, 0.8); // Decayed below threshold
|
|
231
|
-
const result = evaluatePromotion(entry, score);
|
|
232
|
-
|
|
233
|
-
// Stays established (no demotion) even though decayed below candidate→established threshold
|
|
234
|
-
expect(result.new_status).toBe("established");
|
|
235
|
-
expect(result.promoted).toBe(false);
|
|
236
|
-
});
|
|
237
|
-
|
|
238
|
-
it("vote_ratio decay prevents mandate promotion", () => {
|
|
239
|
-
const entry = createMockEntry("m1", "established");
|
|
240
|
-
// High net_votes but low ratio due to decay
|
|
241
|
-
const score = createMockScore("m1", 6, 0.65, 10, 4); // ratio < 0.7
|
|
242
|
-
const result = evaluatePromotion(entry, score);
|
|
243
|
-
|
|
244
|
-
expect(result.new_status).toBe("established");
|
|
245
|
-
expect(result.promoted).toBe(false);
|
|
246
|
-
expect(result.reason).toContain("below mandate threshold");
|
|
247
|
-
});
|
|
248
|
-
|
|
249
|
-
it("fresh votes can push over mandate threshold", () => {
|
|
250
|
-
const entry = createMockEntry("m1", "established");
|
|
251
|
-
const score = createMockScore("m1", 5.1, 0.75, 8, 3); // Fresh votes
|
|
252
|
-
const result = evaluatePromotion(entry, score);
|
|
253
|
-
|
|
254
|
-
expect(result.new_status).toBe("mandate");
|
|
255
|
-
expect(result.promoted).toBe(true);
|
|
256
|
-
});
|
|
257
|
-
});
|
|
258
|
-
|
|
259
|
-
// ============================================================================
|
|
260
|
-
// Utility Functions Tests
|
|
261
|
-
// ============================================================================
|
|
262
|
-
|
|
263
|
-
describe("formatPromotionResult", () => {
|
|
264
|
-
it("formats promoted result with arrow", () => {
|
|
265
|
-
const entry = createMockEntry("m1", "candidate");
|
|
266
|
-
const score = createMockScore("m1", 2, 1.0);
|
|
267
|
-
const result = evaluatePromotion(entry, score);
|
|
268
|
-
const formatted = formatPromotionResult(result);
|
|
269
|
-
|
|
270
|
-
expect(formatted).toContain("[m1]");
|
|
271
|
-
expect(formatted).toContain("candidate → established");
|
|
272
|
-
});
|
|
273
|
-
|
|
274
|
-
it("formats no-change result without arrow", () => {
|
|
275
|
-
const entry = createMockEntry("m1", "candidate");
|
|
276
|
-
const score = createMockScore("m1", 1, 0.8);
|
|
277
|
-
const result = evaluatePromotion(entry, score);
|
|
278
|
-
const formatted = formatPromotionResult(result);
|
|
279
|
-
|
|
280
|
-
expect(formatted).toContain("[m1]");
|
|
281
|
-
expect(formatted).toContain("candidate");
|
|
282
|
-
expect(formatted).not.toContain("→");
|
|
283
|
-
});
|
|
284
|
-
});
|
|
285
|
-
|
|
286
|
-
describe("evaluateBatchPromotions", () => {
|
|
287
|
-
it("evaluates multiple entries", () => {
|
|
288
|
-
const entries = new Map([
|
|
289
|
-
["m1", createMockEntry("m1", "candidate")],
|
|
290
|
-
["m2", createMockEntry("m2", "established")],
|
|
291
|
-
["m3", createMockEntry("m3", "mandate")],
|
|
292
|
-
]);
|
|
293
|
-
|
|
294
|
-
const scores = new Map([
|
|
295
|
-
["m1", createMockScore("m1", 2, 1.0)], // Will promote
|
|
296
|
-
["m2", createMockScore("m2", 5, 0.7)], // Will promote
|
|
297
|
-
["m3", createMockScore("m3", 10, 0.9)], // Stays mandate
|
|
298
|
-
]);
|
|
299
|
-
|
|
300
|
-
const results = evaluateBatchPromotions(entries, scores);
|
|
301
|
-
|
|
302
|
-
expect(results).toHaveLength(3);
|
|
303
|
-
expect(results[0].new_status).toBe("established");
|
|
304
|
-
expect(results[1].new_status).toBe("mandate");
|
|
305
|
-
expect(results[2].new_status).toBe("mandate");
|
|
306
|
-
});
|
|
307
|
-
|
|
308
|
-
it("skips entries without scores", () => {
|
|
309
|
-
const entries = new Map([
|
|
310
|
-
["m1", createMockEntry("m1", "candidate")],
|
|
311
|
-
["m2", createMockEntry("m2", "established")],
|
|
312
|
-
]);
|
|
313
|
-
|
|
314
|
-
const scores = new Map([
|
|
315
|
-
["m1", createMockScore("m1", 2, 1.0)],
|
|
316
|
-
// m2 has no score
|
|
317
|
-
]);
|
|
318
|
-
|
|
319
|
-
const results = evaluateBatchPromotions(entries, scores);
|
|
320
|
-
|
|
321
|
-
expect(results).toHaveLength(1);
|
|
322
|
-
expect(results[0].mandate_id).toBe("m1");
|
|
323
|
-
});
|
|
324
|
-
});
|
|
325
|
-
|
|
326
|
-
describe("getStatusChanges", () => {
|
|
327
|
-
it("filters to only promoted entries", () => {
|
|
328
|
-
const results = [
|
|
329
|
-
{
|
|
330
|
-
mandate_id: "m1",
|
|
331
|
-
previous_status: "candidate" as const,
|
|
332
|
-
new_status: "established" as const,
|
|
333
|
-
score: createMockScore("m1", 2, 1.0),
|
|
334
|
-
promoted: true,
|
|
335
|
-
reason: "Promoted",
|
|
336
|
-
},
|
|
337
|
-
{
|
|
338
|
-
mandate_id: "m2",
|
|
339
|
-
previous_status: "candidate" as const,
|
|
340
|
-
new_status: "candidate" as const,
|
|
341
|
-
score: createMockScore("m2", 1, 0.8),
|
|
342
|
-
promoted: false,
|
|
343
|
-
reason: "No change",
|
|
344
|
-
},
|
|
345
|
-
{
|
|
346
|
-
mandate_id: "m3",
|
|
347
|
-
previous_status: "established" as const,
|
|
348
|
-
new_status: "mandate" as const,
|
|
349
|
-
score: createMockScore("m3", 5, 0.7),
|
|
350
|
-
promoted: true,
|
|
351
|
-
reason: "Promoted",
|
|
352
|
-
},
|
|
353
|
-
];
|
|
354
|
-
|
|
355
|
-
const changes = getStatusChanges(results);
|
|
356
|
-
|
|
357
|
-
expect(changes).toHaveLength(2);
|
|
358
|
-
expect(changes[0].mandate_id).toBe("m1");
|
|
359
|
-
expect(changes[1].mandate_id).toBe("m3");
|
|
360
|
-
});
|
|
361
|
-
});
|
|
362
|
-
|
|
363
|
-
describe("groupByTransition", () => {
|
|
364
|
-
it("groups results by transition type", () => {
|
|
365
|
-
const results = [
|
|
366
|
-
{
|
|
367
|
-
mandate_id: "m1",
|
|
368
|
-
previous_status: "candidate" as const,
|
|
369
|
-
new_status: "established" as const,
|
|
370
|
-
score: createMockScore("m1", 2, 1.0),
|
|
371
|
-
promoted: true,
|
|
372
|
-
reason: "Promoted",
|
|
373
|
-
},
|
|
374
|
-
{
|
|
375
|
-
mandate_id: "m2",
|
|
376
|
-
previous_status: "candidate" as const,
|
|
377
|
-
new_status: "established" as const,
|
|
378
|
-
score: createMockScore("m2", 3, 1.0),
|
|
379
|
-
promoted: true,
|
|
380
|
-
reason: "Promoted",
|
|
381
|
-
},
|
|
382
|
-
{
|
|
383
|
-
mandate_id: "m3",
|
|
384
|
-
previous_status: "established" as const,
|
|
385
|
-
new_status: "mandate" as const,
|
|
386
|
-
score: createMockScore("m3", 5, 0.7),
|
|
387
|
-
promoted: true,
|
|
388
|
-
reason: "Promoted",
|
|
389
|
-
},
|
|
390
|
-
{
|
|
391
|
-
mandate_id: "m4",
|
|
392
|
-
previous_status: "candidate" as const,
|
|
393
|
-
new_status: "candidate" as const,
|
|
394
|
-
score: createMockScore("m4", 1, 0.8),
|
|
395
|
-
promoted: false,
|
|
396
|
-
reason: "No change",
|
|
397
|
-
},
|
|
398
|
-
];
|
|
399
|
-
|
|
400
|
-
const grouped = groupByTransition(results);
|
|
401
|
-
|
|
402
|
-
expect(grouped.size).toBe(3);
|
|
403
|
-
expect(grouped.get("candidate→established")).toHaveLength(2);
|
|
404
|
-
expect(grouped.get("established→mandate")).toHaveLength(1);
|
|
405
|
-
expect(grouped.get("candidate")).toHaveLength(1);
|
|
406
|
-
});
|
|
407
|
-
|
|
408
|
-
it("uses status name for no-change transitions", () => {
|
|
409
|
-
const results = [
|
|
410
|
-
{
|
|
411
|
-
mandate_id: "m1",
|
|
412
|
-
previous_status: "mandate" as const,
|
|
413
|
-
new_status: "mandate" as const,
|
|
414
|
-
score: createMockScore("m1", 10, 0.9),
|
|
415
|
-
promoted: false,
|
|
416
|
-
reason: "Stays",
|
|
417
|
-
},
|
|
418
|
-
{
|
|
419
|
-
mandate_id: "m2",
|
|
420
|
-
previous_status: "rejected" as const,
|
|
421
|
-
new_status: "rejected" as const,
|
|
422
|
-
score: createMockScore("m2", -5, 0.1),
|
|
423
|
-
promoted: false,
|
|
424
|
-
reason: "Stays",
|
|
425
|
-
},
|
|
426
|
-
];
|
|
427
|
-
|
|
428
|
-
const grouped = groupByTransition(results);
|
|
429
|
-
|
|
430
|
-
expect(grouped.size).toBe(2);
|
|
431
|
-
expect(grouped.get("mandate")).toHaveLength(1);
|
|
432
|
-
expect(grouped.get("rejected")).toHaveLength(1);
|
|
433
|
-
});
|
|
434
|
-
});
|
|
435
|
-
|
|
436
|
-
// ============================================================================
|
|
437
|
-
// Edge Cases
|
|
438
|
-
// ============================================================================
|
|
439
|
-
|
|
440
|
-
describe("edge cases", () => {
|
|
441
|
-
it("handles exact threshold values", () => {
|
|
442
|
-
const entry1 = createMockEntry("m1", "candidate");
|
|
443
|
-
const score1 = createMockScore("m1", 2.0, 1.0); // Exact threshold
|
|
444
|
-
const result1 = evaluatePromotion(entry1, score1);
|
|
445
|
-
expect(result1.new_status).toBe("established");
|
|
446
|
-
|
|
447
|
-
const entry2 = createMockEntry("m2", "established");
|
|
448
|
-
const score2 = createMockScore("m2", 5.0, 0.7); // Exact threshold
|
|
449
|
-
const result2 = evaluatePromotion(entry2, score2);
|
|
450
|
-
expect(result2.new_status).toBe("mandate");
|
|
451
|
-
});
|
|
452
|
-
|
|
453
|
-
it("handles zero votes", () => {
|
|
454
|
-
const entry = createMockEntry("m1", "candidate");
|
|
455
|
-
const score = createMockScore("m1", 0, 0);
|
|
456
|
-
const result = evaluatePromotion(entry, score);
|
|
457
|
-
expect(result.new_status).toBe("candidate");
|
|
458
|
-
});
|
|
459
|
-
|
|
460
|
-
it("handles negative vote ratio edge case", () => {
|
|
461
|
-
const entry = createMockEntry("m1", "established");
|
|
462
|
-
const score = createMockScore("m1", 5, 0.2, 1, 4); // Low ratio
|
|
463
|
-
const result = evaluatePromotion(entry, score);
|
|
464
|
-
expect(result.new_status).toBe("established"); // ratio < 0.7
|
|
465
|
-
});
|
|
466
|
-
|
|
467
|
-
it("rejects at exact rejection threshold", () => {
|
|
468
|
-
const entry = createMockEntry("m1", "candidate");
|
|
469
|
-
const score = createMockScore("m1", -3, 0.1);
|
|
470
|
-
const result = evaluatePromotion(entry, score);
|
|
471
|
-
expect(result.new_status).toBe("rejected");
|
|
472
|
-
});
|
|
473
|
-
});
|