@soleri/core 2.4.0 → 2.5.0
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/dist/brain/brain.d.ts +7 -0
- package/dist/brain/brain.d.ts.map +1 -1
- package/dist/brain/brain.js +56 -9
- package/dist/brain/brain.js.map +1 -1
- package/dist/brain/types.d.ts +2 -2
- package/dist/brain/types.d.ts.map +1 -1
- package/dist/cognee/client.d.ts +3 -0
- package/dist/cognee/client.d.ts.map +1 -1
- package/dist/cognee/client.js +17 -0
- package/dist/cognee/client.js.map +1 -1
- package/dist/cognee/sync-manager.d.ts +94 -0
- package/dist/cognee/sync-manager.d.ts.map +1 -0
- package/dist/cognee/sync-manager.js +293 -0
- package/dist/cognee/sync-manager.js.map +1 -0
- package/dist/curator/curator.d.ts +8 -1
- package/dist/curator/curator.d.ts.map +1 -1
- package/dist/curator/curator.js +64 -1
- package/dist/curator/curator.js.map +1 -1
- package/dist/errors/classify.d.ts +13 -0
- package/dist/errors/classify.d.ts.map +1 -0
- package/dist/errors/classify.js +97 -0
- package/dist/errors/classify.js.map +1 -0
- package/dist/errors/index.d.ts +6 -0
- package/dist/errors/index.d.ts.map +1 -0
- package/dist/errors/index.js +4 -0
- package/dist/errors/index.js.map +1 -0
- package/dist/errors/retry.d.ts +40 -0
- package/dist/errors/retry.d.ts.map +1 -0
- package/dist/errors/retry.js +97 -0
- package/dist/errors/retry.js.map +1 -0
- package/dist/errors/types.d.ts +48 -0
- package/dist/errors/types.d.ts.map +1 -0
- package/dist/errors/types.js +59 -0
- package/dist/errors/types.js.map +1 -0
- package/dist/index.d.ts +25 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +21 -3
- package/dist/index.js.map +1 -1
- package/dist/intake/content-classifier.d.ts +14 -0
- package/dist/intake/content-classifier.d.ts.map +1 -0
- package/dist/intake/content-classifier.js +125 -0
- package/dist/intake/content-classifier.js.map +1 -0
- package/dist/intake/dedup-gate.d.ts +17 -0
- package/dist/intake/dedup-gate.d.ts.map +1 -0
- package/dist/intake/dedup-gate.js +66 -0
- package/dist/intake/dedup-gate.js.map +1 -0
- package/dist/intake/intake-pipeline.d.ts +63 -0
- package/dist/intake/intake-pipeline.d.ts.map +1 -0
- package/dist/intake/intake-pipeline.js +373 -0
- package/dist/intake/intake-pipeline.js.map +1 -0
- package/dist/intake/types.d.ts +65 -0
- package/dist/intake/types.d.ts.map +1 -0
- package/dist/intake/types.js +3 -0
- package/dist/intake/types.js.map +1 -0
- package/dist/intelligence/loader.js +1 -1
- package/dist/intelligence/loader.js.map +1 -1
- package/dist/intelligence/types.d.ts +3 -1
- package/dist/intelligence/types.d.ts.map +1 -1
- package/dist/loop/loop-manager.d.ts +58 -7
- package/dist/loop/loop-manager.d.ts.map +1 -1
- package/dist/loop/loop-manager.js +280 -6
- package/dist/loop/loop-manager.js.map +1 -1
- package/dist/loop/types.d.ts +69 -1
- package/dist/loop/types.d.ts.map +1 -1
- package/dist/loop/types.js +4 -1
- package/dist/loop/types.js.map +1 -1
- package/dist/persistence/index.d.ts +3 -0
- package/dist/persistence/index.d.ts.map +1 -0
- package/dist/persistence/index.js +2 -0
- package/dist/persistence/index.js.map +1 -0
- package/dist/persistence/sqlite-provider.d.ts +25 -0
- package/dist/persistence/sqlite-provider.d.ts.map +1 -0
- package/dist/persistence/sqlite-provider.js +59 -0
- package/dist/persistence/sqlite-provider.js.map +1 -0
- package/dist/persistence/types.d.ts +36 -0
- package/dist/persistence/types.d.ts.map +1 -0
- package/dist/persistence/types.js +8 -0
- package/dist/persistence/types.js.map +1 -0
- package/dist/planning/gap-analysis.d.ts +47 -4
- package/dist/planning/gap-analysis.d.ts.map +1 -1
- package/dist/planning/gap-analysis.js +190 -13
- package/dist/planning/gap-analysis.js.map +1 -1
- package/dist/planning/gap-types.d.ts +1 -1
- package/dist/planning/gap-types.d.ts.map +1 -1
- package/dist/planning/gap-types.js.map +1 -1
- package/dist/planning/planner.d.ts +277 -9
- package/dist/planning/planner.d.ts.map +1 -1
- package/dist/planning/planner.js +611 -46
- package/dist/planning/planner.js.map +1 -1
- package/dist/playbooks/generic/brainstorming.d.ts +9 -0
- package/dist/playbooks/generic/brainstorming.d.ts.map +1 -0
- package/dist/playbooks/generic/brainstorming.js +105 -0
- package/dist/playbooks/generic/brainstorming.js.map +1 -0
- package/dist/playbooks/generic/code-review.d.ts +11 -0
- package/dist/playbooks/generic/code-review.d.ts.map +1 -0
- package/dist/playbooks/generic/code-review.js +176 -0
- package/dist/playbooks/generic/code-review.js.map +1 -0
- package/dist/playbooks/generic/subagent-execution.d.ts +9 -0
- package/dist/playbooks/generic/subagent-execution.d.ts.map +1 -0
- package/dist/playbooks/generic/subagent-execution.js +68 -0
- package/dist/playbooks/generic/subagent-execution.js.map +1 -0
- package/dist/playbooks/generic/systematic-debugging.d.ts +9 -0
- package/dist/playbooks/generic/systematic-debugging.d.ts.map +1 -0
- package/dist/playbooks/generic/systematic-debugging.js +87 -0
- package/dist/playbooks/generic/systematic-debugging.js.map +1 -0
- package/dist/playbooks/generic/tdd.d.ts +9 -0
- package/dist/playbooks/generic/tdd.d.ts.map +1 -0
- package/dist/playbooks/generic/tdd.js +70 -0
- package/dist/playbooks/generic/tdd.js.map +1 -0
- package/dist/playbooks/generic/verification.d.ts +9 -0
- package/dist/playbooks/generic/verification.d.ts.map +1 -0
- package/dist/playbooks/generic/verification.js +74 -0
- package/dist/playbooks/generic/verification.js.map +1 -0
- package/dist/playbooks/index.d.ts +4 -0
- package/dist/playbooks/index.d.ts.map +1 -0
- package/dist/playbooks/index.js +5 -0
- package/dist/playbooks/index.js.map +1 -0
- package/dist/playbooks/playbook-registry.d.ts +42 -0
- package/dist/playbooks/playbook-registry.d.ts.map +1 -0
- package/dist/playbooks/playbook-registry.js +227 -0
- package/dist/playbooks/playbook-registry.js.map +1 -0
- package/dist/playbooks/playbook-seeder.d.ts +47 -0
- package/dist/playbooks/playbook-seeder.d.ts.map +1 -0
- package/dist/playbooks/playbook-seeder.js +104 -0
- package/dist/playbooks/playbook-seeder.js.map +1 -0
- package/dist/playbooks/playbook-types.d.ts +132 -0
- package/dist/playbooks/playbook-types.d.ts.map +1 -0
- package/dist/playbooks/playbook-types.js +12 -0
- package/dist/playbooks/playbook-types.js.map +1 -0
- package/dist/project/project-registry.d.ts.map +1 -1
- package/dist/project/project-registry.js +9 -11
- package/dist/project/project-registry.js.map +1 -1
- package/dist/prompts/index.d.ts +4 -0
- package/dist/prompts/index.d.ts.map +1 -0
- package/dist/prompts/index.js +3 -0
- package/dist/prompts/index.js.map +1 -0
- package/dist/prompts/parser.d.ts +17 -0
- package/dist/prompts/parser.d.ts.map +1 -0
- package/dist/prompts/parser.js +47 -0
- package/dist/prompts/parser.js.map +1 -0
- package/dist/prompts/template-manager.d.ts +25 -0
- package/dist/prompts/template-manager.d.ts.map +1 -0
- package/dist/prompts/template-manager.js +71 -0
- package/dist/prompts/template-manager.js.map +1 -0
- package/dist/prompts/types.d.ts +26 -0
- package/dist/prompts/types.d.ts.map +1 -0
- package/dist/prompts/types.js +5 -0
- package/dist/prompts/types.js.map +1 -0
- package/dist/runtime/admin-extra-ops.d.ts +5 -3
- package/dist/runtime/admin-extra-ops.d.ts.map +1 -1
- package/dist/runtime/admin-extra-ops.js +322 -11
- package/dist/runtime/admin-extra-ops.js.map +1 -1
- package/dist/runtime/admin-ops.d.ts.map +1 -1
- package/dist/runtime/admin-ops.js +10 -3
- package/dist/runtime/admin-ops.js.map +1 -1
- package/dist/runtime/capture-ops.d.ts.map +1 -1
- package/dist/runtime/capture-ops.js +20 -2
- package/dist/runtime/capture-ops.js.map +1 -1
- package/dist/runtime/cognee-sync-ops.d.ts +12 -0
- package/dist/runtime/cognee-sync-ops.d.ts.map +1 -0
- package/dist/runtime/cognee-sync-ops.js +55 -0
- package/dist/runtime/cognee-sync-ops.js.map +1 -0
- package/dist/runtime/core-ops.d.ts +8 -6
- package/dist/runtime/core-ops.d.ts.map +1 -1
- package/dist/runtime/core-ops.js +226 -9
- package/dist/runtime/core-ops.js.map +1 -1
- package/dist/runtime/curator-extra-ops.d.ts +2 -2
- package/dist/runtime/curator-extra-ops.d.ts.map +1 -1
- package/dist/runtime/curator-extra-ops.js +15 -3
- package/dist/runtime/curator-extra-ops.js.map +1 -1
- package/dist/runtime/domain-ops.js +2 -2
- package/dist/runtime/domain-ops.js.map +1 -1
- package/dist/runtime/grading-ops.d.ts.map +1 -1
- package/dist/runtime/grading-ops.js.map +1 -1
- package/dist/runtime/intake-ops.d.ts +14 -0
- package/dist/runtime/intake-ops.d.ts.map +1 -0
- package/dist/runtime/intake-ops.js +110 -0
- package/dist/runtime/intake-ops.js.map +1 -0
- package/dist/runtime/loop-ops.d.ts +5 -4
- package/dist/runtime/loop-ops.d.ts.map +1 -1
- package/dist/runtime/loop-ops.js +84 -12
- package/dist/runtime/loop-ops.js.map +1 -1
- package/dist/runtime/memory-cross-project-ops.d.ts.map +1 -1
- package/dist/runtime/memory-cross-project-ops.js.map +1 -1
- package/dist/runtime/memory-extra-ops.js +5 -5
- package/dist/runtime/memory-extra-ops.js.map +1 -1
- package/dist/runtime/orchestrate-ops.d.ts.map +1 -1
- package/dist/runtime/orchestrate-ops.js +8 -2
- package/dist/runtime/orchestrate-ops.js.map +1 -1
- package/dist/runtime/planning-extra-ops.d.ts +13 -5
- package/dist/runtime/planning-extra-ops.d.ts.map +1 -1
- package/dist/runtime/planning-extra-ops.js +381 -18
- package/dist/runtime/planning-extra-ops.js.map +1 -1
- package/dist/runtime/playbook-ops.d.ts +14 -0
- package/dist/runtime/playbook-ops.d.ts.map +1 -0
- package/dist/runtime/playbook-ops.js +141 -0
- package/dist/runtime/playbook-ops.js.map +1 -0
- package/dist/runtime/project-ops.d.ts.map +1 -1
- package/dist/runtime/project-ops.js +7 -2
- package/dist/runtime/project-ops.js.map +1 -1
- package/dist/runtime/runtime.d.ts.map +1 -1
- package/dist/runtime/runtime.js +27 -8
- package/dist/runtime/runtime.js.map +1 -1
- package/dist/runtime/types.d.ts +8 -0
- package/dist/runtime/types.d.ts.map +1 -1
- package/dist/runtime/vault-extra-ops.d.ts +3 -2
- package/dist/runtime/vault-extra-ops.d.ts.map +1 -1
- package/dist/runtime/vault-extra-ops.js +345 -4
- package/dist/runtime/vault-extra-ops.js.map +1 -1
- package/dist/vault/playbook.d.ts +34 -0
- package/dist/vault/playbook.d.ts.map +1 -0
- package/dist/vault/playbook.js +60 -0
- package/dist/vault/playbook.js.map +1 -0
- package/dist/vault/vault.d.ts +31 -32
- package/dist/vault/vault.d.ts.map +1 -1
- package/dist/vault/vault.js +201 -181
- package/dist/vault/vault.js.map +1 -1
- package/package.json +7 -3
- package/src/__tests__/admin-extra-ops.test.ts +62 -15
- package/src/__tests__/admin-ops.test.ts +2 -2
- package/src/__tests__/brain.test.ts +3 -3
- package/src/__tests__/cognee-integration.test.ts +80 -0
- package/src/__tests__/cognee-sync-manager.test.ts +103 -0
- package/src/__tests__/core-ops.test.ts +30 -4
- package/src/__tests__/curator-extra-ops.test.ts +24 -2
- package/src/__tests__/errors.test.ts +388 -0
- package/src/__tests__/grading-ops.test.ts +28 -7
- package/src/__tests__/intake-pipeline.test.ts +162 -0
- package/src/__tests__/loop-ops.test.ts +74 -3
- package/src/__tests__/memory-cross-project-ops.test.ts +3 -1
- package/src/__tests__/orchestrate-ops.test.ts +8 -3
- package/src/__tests__/persistence.test.ts +225 -0
- package/src/__tests__/planner.test.ts +99 -21
- package/src/__tests__/planning-extra-ops.test.ts +168 -10
- package/src/__tests__/playbook-registry.test.ts +326 -0
- package/src/__tests__/playbook-seeder.test.ts +163 -0
- package/src/__tests__/playbook.test.ts +389 -0
- package/src/__tests__/project-ops.test.ts +18 -4
- package/src/__tests__/template-manager.test.ts +222 -0
- package/src/__tests__/vault-extra-ops.test.ts +82 -7
- package/src/brain/brain.ts +71 -9
- package/src/brain/types.ts +2 -2
- package/src/cognee/client.ts +18 -0
- package/src/cognee/sync-manager.ts +389 -0
- package/src/curator/curator.ts +88 -7
- package/src/errors/classify.ts +102 -0
- package/src/errors/index.ts +5 -0
- package/src/errors/retry.ts +132 -0
- package/src/errors/types.ts +81 -0
- package/src/index.ts +114 -3
- package/src/intake/content-classifier.ts +146 -0
- package/src/intake/dedup-gate.ts +92 -0
- package/src/intake/intake-pipeline.ts +503 -0
- package/src/intake/types.ts +69 -0
- package/src/intelligence/loader.ts +1 -1
- package/src/intelligence/types.ts +3 -1
- package/src/loop/loop-manager.ts +325 -7
- package/src/loop/types.ts +72 -1
- package/src/persistence/index.ts +7 -0
- package/src/persistence/sqlite-provider.ts +62 -0
- package/src/persistence/types.ts +44 -0
- package/src/planning/gap-analysis.ts +286 -17
- package/src/planning/gap-types.ts +4 -1
- package/src/planning/planner.ts +828 -55
- package/src/playbooks/generic/brainstorming.ts +110 -0
- package/src/playbooks/generic/code-review.ts +181 -0
- package/src/playbooks/generic/subagent-execution.ts +74 -0
- package/src/playbooks/generic/systematic-debugging.ts +92 -0
- package/src/playbooks/generic/tdd.ts +75 -0
- package/src/playbooks/generic/verification.ts +79 -0
- package/src/playbooks/index.ts +27 -0
- package/src/playbooks/playbook-registry.ts +284 -0
- package/src/playbooks/playbook-seeder.ts +119 -0
- package/src/playbooks/playbook-types.ts +162 -0
- package/src/project/project-registry.ts +29 -17
- package/src/prompts/index.ts +3 -0
- package/src/prompts/parser.ts +59 -0
- package/src/prompts/template-manager.ts +77 -0
- package/src/prompts/types.ts +28 -0
- package/src/runtime/admin-extra-ops.ts +358 -13
- package/src/runtime/admin-ops.ts +17 -6
- package/src/runtime/capture-ops.ts +25 -6
- package/src/runtime/cognee-sync-ops.ts +63 -0
- package/src/runtime/core-ops.ts +258 -8
- package/src/runtime/curator-extra-ops.ts +17 -3
- package/src/runtime/domain-ops.ts +2 -2
- package/src/runtime/grading-ops.ts +11 -2
- package/src/runtime/intake-ops.ts +126 -0
- package/src/runtime/loop-ops.ts +96 -13
- package/src/runtime/memory-cross-project-ops.ts +1 -2
- package/src/runtime/memory-extra-ops.ts +5 -5
- package/src/runtime/orchestrate-ops.ts +8 -2
- package/src/runtime/planning-extra-ops.ts +414 -23
- package/src/runtime/playbook-ops.ts +169 -0
- package/src/runtime/project-ops.ts +9 -3
- package/src/runtime/runtime.ts +35 -9
- package/src/runtime/types.ts +8 -0
- package/src/runtime/vault-extra-ops.ts +385 -4
- package/src/vault/playbook.ts +87 -0
- package/src/vault/vault.ts +301 -235
|
@@ -34,8 +34,8 @@ describe('createPlanningExtraOps', () => {
|
|
|
34
34
|
return op;
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
-
it('should return
|
|
38
|
-
expect(ops.length).toBe(
|
|
37
|
+
it('should return 22 ops', () => {
|
|
38
|
+
expect(ops.length).toBe(22);
|
|
39
39
|
});
|
|
40
40
|
|
|
41
41
|
it('should have all expected op names', () => {
|
|
@@ -49,6 +49,26 @@ describe('createPlanningExtraOps', () => {
|
|
|
49
49
|
expect(names).toContain('plan_archive');
|
|
50
50
|
expect(names).toContain('plan_list_tasks');
|
|
51
51
|
expect(names).toContain('plan_stats');
|
|
52
|
+
// #148: Evidence
|
|
53
|
+
expect(names).toContain('plan_submit_evidence');
|
|
54
|
+
expect(names).toContain('plan_verify_task');
|
|
55
|
+
expect(names).toContain('plan_verify_plan');
|
|
56
|
+
// #149: Subagent dispatch
|
|
57
|
+
expect(names).toContain('plan_review_spec');
|
|
58
|
+
expect(names).toContain('plan_review_quality');
|
|
59
|
+
expect(names).toContain('plan_review_outcome');
|
|
60
|
+
// #150: Brainstorm
|
|
61
|
+
expect(names).toContain('plan_brainstorm');
|
|
62
|
+
// #151: Auto-reconcile
|
|
63
|
+
expect(names).toContain('plan_auto_reconcile');
|
|
64
|
+
// #152: Validate
|
|
65
|
+
expect(names).toContain('plan_validate');
|
|
66
|
+
// #80: Execution metrics
|
|
67
|
+
expect(names).toContain('plan_execution_metrics');
|
|
68
|
+
expect(names).toContain('plan_record_task_metrics');
|
|
69
|
+
// #83: Deliverables
|
|
70
|
+
expect(names).toContain('plan_submit_deliverable');
|
|
71
|
+
expect(names).toContain('plan_verify_deliverables');
|
|
52
72
|
});
|
|
53
73
|
|
|
54
74
|
it('should assign correct auth levels', () => {
|
|
@@ -115,7 +135,7 @@ describe('createPlanningExtraOps', () => {
|
|
|
115
135
|
planId: plan.id,
|
|
116
136
|
objective: 'Updated',
|
|
117
137
|
})) as { error: string };
|
|
118
|
-
expect(result.error).toContain("must be 'draft'");
|
|
138
|
+
expect(result.error).toContain("must be 'draft' or 'brainstorming'");
|
|
119
139
|
});
|
|
120
140
|
|
|
121
141
|
it('should return error for unknown plan', async () => {
|
|
@@ -138,7 +158,11 @@ describe('createPlanningExtraOps', () => {
|
|
|
138
158
|
{ title: 'Implement', description: 'Core implementation', dependsOn: ['task-1'] },
|
|
139
159
|
{ title: 'Test', description: 'Write tests', dependsOn: ['task-2'] },
|
|
140
160
|
],
|
|
141
|
-
})) as {
|
|
161
|
+
})) as {
|
|
162
|
+
split: boolean;
|
|
163
|
+
taskCount: number;
|
|
164
|
+
plan: { tasks: Array<{ dependsOn?: string[] }> };
|
|
165
|
+
};
|
|
142
166
|
expect(result.split).toBe(true);
|
|
143
167
|
expect(result.taskCount).toBe(3);
|
|
144
168
|
expect(result.plan.tasks[1].dependsOn).toEqual(['task-1']);
|
|
@@ -161,9 +185,7 @@ describe('createPlanningExtraOps', () => {
|
|
|
161
185
|
runtime.planner.approve(plan.id);
|
|
162
186
|
const result = (await findOp('plan_split').handler({
|
|
163
187
|
planId: plan.id,
|
|
164
|
-
tasks: [
|
|
165
|
-
{ title: 'Only task', description: 'Single task' },
|
|
166
|
-
],
|
|
188
|
+
tasks: [{ title: 'Only task', description: 'Single task' }],
|
|
167
189
|
})) as { split: boolean; taskCount: number };
|
|
168
190
|
expect(result.split).toBe(true);
|
|
169
191
|
expect(result.taskCount).toBe(1);
|
|
@@ -177,7 +199,7 @@ describe('createPlanningExtraOps', () => {
|
|
|
177
199
|
planId: plan.id,
|
|
178
200
|
tasks: [{ title: 'T', description: 'D' }],
|
|
179
201
|
})) as { error: string };
|
|
180
|
-
expect(result.error).toContain("must be 'draft' or 'approved'");
|
|
202
|
+
expect(result.error).toContain("must be 'brainstorming', 'draft', or 'approved'");
|
|
181
203
|
});
|
|
182
204
|
});
|
|
183
205
|
|
|
@@ -201,7 +223,7 @@ describe('createPlanningExtraOps', () => {
|
|
|
201
223
|
],
|
|
202
224
|
})) as { reconciled: boolean; accuracy: number; driftCount: number };
|
|
203
225
|
expect(result.reconciled).toBe(true);
|
|
204
|
-
expect(result.accuracy).toBe(
|
|
226
|
+
expect(result.accuracy).toBe(95); // 1 low-impact drift = 100 - 5 = 95
|
|
205
227
|
expect(result.driftCount).toBe(1);
|
|
206
228
|
});
|
|
207
229
|
|
|
@@ -236,7 +258,7 @@ describe('createPlanningExtraOps', () => {
|
|
|
236
258
|
planId: plan.id,
|
|
237
259
|
actualOutcome: 'Done',
|
|
238
260
|
})) as { error: string };
|
|
239
|
-
expect(result.error).toContain("must be 'executing' or '
|
|
261
|
+
expect(result.error).toContain("must be 'executing', 'validating', or 'reconciling'");
|
|
240
262
|
});
|
|
241
263
|
});
|
|
242
264
|
|
|
@@ -246,6 +268,7 @@ describe('createPlanningExtraOps', () => {
|
|
|
246
268
|
const plan = createDraftPlan();
|
|
247
269
|
runtime.planner.approve(plan.id);
|
|
248
270
|
runtime.planner.startExecution(plan.id);
|
|
271
|
+
runtime.planner.startReconciliation(plan.id);
|
|
249
272
|
runtime.planner.complete(plan.id);
|
|
250
273
|
|
|
251
274
|
const result = (await findOp('plan_complete_lifecycle').handler({
|
|
@@ -272,6 +295,7 @@ describe('createPlanningExtraOps', () => {
|
|
|
272
295
|
const plan = createDraftPlan();
|
|
273
296
|
runtime.planner.approve(plan.id);
|
|
274
297
|
runtime.planner.startExecution(plan.id);
|
|
298
|
+
runtime.planner.startReconciliation(plan.id);
|
|
275
299
|
runtime.planner.complete(plan.id);
|
|
276
300
|
|
|
277
301
|
const result = (await findOp('plan_complete_lifecycle').handler({
|
|
@@ -419,6 +443,7 @@ describe('createPlanningExtraOps', () => {
|
|
|
419
443
|
const plan = createDraftPlan();
|
|
420
444
|
runtime.planner.approve(plan.id);
|
|
421
445
|
runtime.planner.startExecution(plan.id);
|
|
446
|
+
runtime.planner.startReconciliation(plan.id);
|
|
422
447
|
runtime.planner.complete(plan.id);
|
|
423
448
|
|
|
424
449
|
// Hack: set updatedAt to 60 days ago
|
|
@@ -446,6 +471,7 @@ describe('createPlanningExtraOps', () => {
|
|
|
446
471
|
const plan = createDraftPlan();
|
|
447
472
|
runtime.planner.approve(plan.id);
|
|
448
473
|
runtime.planner.startExecution(plan.id);
|
|
474
|
+
runtime.planner.startReconciliation(plan.id);
|
|
449
475
|
runtime.planner.complete(plan.id);
|
|
450
476
|
|
|
451
477
|
const result = (await findOp('plan_archive').handler({
|
|
@@ -501,6 +527,137 @@ describe('createPlanningExtraOps', () => {
|
|
|
501
527
|
});
|
|
502
528
|
});
|
|
503
529
|
|
|
530
|
+
// ─── plan_execution_metrics ─────────────────────────────────────
|
|
531
|
+
describe('plan_execution_metrics', () => {
|
|
532
|
+
it('should return metrics for a plan with timed tasks', async () => {
|
|
533
|
+
const plan = createDraftPlan();
|
|
534
|
+
runtime.planner.approve(plan.id);
|
|
535
|
+
runtime.planner.startExecution(plan.id);
|
|
536
|
+
runtime.planner.updateTask(plan.id, 'task-1', 'in_progress');
|
|
537
|
+
runtime.planner.updateTask(plan.id, 'task-1', 'completed');
|
|
538
|
+
|
|
539
|
+
const result = (await findOp('plan_execution_metrics').handler({
|
|
540
|
+
planId: plan.id,
|
|
541
|
+
})) as {
|
|
542
|
+
planId: string;
|
|
543
|
+
taskMetrics: Array<{ id: string; startedAt: number | null; completedAt: number | null }>;
|
|
544
|
+
executionSummary: { tasksCompleted: number } | null;
|
|
545
|
+
};
|
|
546
|
+
|
|
547
|
+
expect(result.planId).toBe(plan.id);
|
|
548
|
+
expect(result.taskMetrics).toHaveLength(2);
|
|
549
|
+
// task-1 was transitioned, should have timing
|
|
550
|
+
const task1 = result.taskMetrics.find((t) => t.id === 'task-1')!;
|
|
551
|
+
expect(task1.startedAt).not.toBeNull();
|
|
552
|
+
expect(task1.completedAt).not.toBeNull();
|
|
553
|
+
});
|
|
554
|
+
|
|
555
|
+
it('should return error for unknown plan', async () => {
|
|
556
|
+
const result = (await findOp('plan_execution_metrics').handler({
|
|
557
|
+
planId: 'nonexistent',
|
|
558
|
+
})) as { error: string };
|
|
559
|
+
expect(result.error).toContain('Plan not found');
|
|
560
|
+
});
|
|
561
|
+
});
|
|
562
|
+
|
|
563
|
+
// ─── plan_record_task_metrics ─────────────────────────────────
|
|
564
|
+
describe('plan_record_task_metrics', () => {
|
|
565
|
+
it('should record tool calls and model tier on a task', async () => {
|
|
566
|
+
const plan = createDraftPlan();
|
|
567
|
+
runtime.planner.approve(plan.id);
|
|
568
|
+
runtime.planner.startExecution(plan.id);
|
|
569
|
+
|
|
570
|
+
const result = (await findOp('plan_record_task_metrics').handler({
|
|
571
|
+
planId: plan.id,
|
|
572
|
+
taskId: 'task-1',
|
|
573
|
+
toolCalls: 15,
|
|
574
|
+
modelTier: 'opus',
|
|
575
|
+
})) as {
|
|
576
|
+
recorded: boolean;
|
|
577
|
+
taskId: string;
|
|
578
|
+
metrics: { toolCalls: number; modelTier: string };
|
|
579
|
+
};
|
|
580
|
+
|
|
581
|
+
expect(result.recorded).toBe(true);
|
|
582
|
+
expect(result.taskId).toBe('task-1');
|
|
583
|
+
expect(result.metrics.toolCalls).toBe(15);
|
|
584
|
+
expect(result.metrics.modelTier).toBe('opus');
|
|
585
|
+
});
|
|
586
|
+
|
|
587
|
+
it('should return error for unknown task', async () => {
|
|
588
|
+
const plan = createDraftPlan();
|
|
589
|
+
const result = (await findOp('plan_record_task_metrics').handler({
|
|
590
|
+
planId: plan.id,
|
|
591
|
+
taskId: 'task-99',
|
|
592
|
+
toolCalls: 5,
|
|
593
|
+
})) as { error: string };
|
|
594
|
+
expect(result.error).toContain('Task not found');
|
|
595
|
+
});
|
|
596
|
+
});
|
|
597
|
+
|
|
598
|
+
// ─── plan_submit_deliverable ──────────────────────────────────
|
|
599
|
+
describe('plan_submit_deliverable', () => {
|
|
600
|
+
it('should record a vault_entry deliverable on a task', async () => {
|
|
601
|
+
const plan = createDraftPlan();
|
|
602
|
+
|
|
603
|
+
const result = (await findOp('plan_submit_deliverable').handler({
|
|
604
|
+
planId: plan.id,
|
|
605
|
+
taskId: 'task-1',
|
|
606
|
+
type: 'vault_entry',
|
|
607
|
+
path: 'patterns/my-pattern',
|
|
608
|
+
})) as { submitted: boolean; taskId: string; deliverableCount: number };
|
|
609
|
+
|
|
610
|
+
expect(result.submitted).toBe(true);
|
|
611
|
+
expect(result.taskId).toBe('task-1');
|
|
612
|
+
expect(result.deliverableCount).toBe(1);
|
|
613
|
+
});
|
|
614
|
+
|
|
615
|
+
it('should return error for unknown plan', async () => {
|
|
616
|
+
const result = (await findOp('plan_submit_deliverable').handler({
|
|
617
|
+
planId: 'nonexistent',
|
|
618
|
+
taskId: 'task-1',
|
|
619
|
+
type: 'file',
|
|
620
|
+
path: '/tmp/test.txt',
|
|
621
|
+
})) as { error: string };
|
|
622
|
+
expect(result.error).toContain('Plan not found');
|
|
623
|
+
});
|
|
624
|
+
});
|
|
625
|
+
|
|
626
|
+
// ─── plan_verify_deliverables ─────────────────────────────────
|
|
627
|
+
describe('plan_verify_deliverables', () => {
|
|
628
|
+
it('should verify deliverables on a task', async () => {
|
|
629
|
+
const plan = createDraftPlan();
|
|
630
|
+
// Submit a vault_entry deliverable
|
|
631
|
+
await runtime.planner.submitDeliverable(plan.id, 'task-1', {
|
|
632
|
+
type: 'vault_entry',
|
|
633
|
+
path: 'nonexistent-entry',
|
|
634
|
+
});
|
|
635
|
+
|
|
636
|
+
const result = (await findOp('plan_verify_deliverables').handler({
|
|
637
|
+
planId: plan.id,
|
|
638
|
+
taskId: 'task-1',
|
|
639
|
+
})) as { verified: boolean; deliverables: Array<{ stale: boolean }>; staleCount: number };
|
|
640
|
+
|
|
641
|
+
expect(result.verified).toBe(false); // vault entry doesn't exist
|
|
642
|
+
expect(result.deliverables).toHaveLength(1);
|
|
643
|
+
expect(result.staleCount).toBe(1);
|
|
644
|
+
});
|
|
645
|
+
|
|
646
|
+
it('should return error for task with no deliverables', async () => {
|
|
647
|
+
const plan = createDraftPlan();
|
|
648
|
+
|
|
649
|
+
const result = (await findOp('plan_verify_deliverables').handler({
|
|
650
|
+
planId: plan.id,
|
|
651
|
+
taskId: 'task-1',
|
|
652
|
+
})) as { verified: boolean; deliverables: unknown[]; staleCount: number };
|
|
653
|
+
|
|
654
|
+
// No deliverables means verified = true (nothing to check)
|
|
655
|
+
expect(result.verified).toBe(true);
|
|
656
|
+
expect(result.deliverables).toHaveLength(0);
|
|
657
|
+
expect(result.staleCount).toBe(0);
|
|
658
|
+
});
|
|
659
|
+
});
|
|
660
|
+
|
|
504
661
|
// ─── plan_stats ───────────────────────────────────────────────────
|
|
505
662
|
describe('plan_stats', () => {
|
|
506
663
|
it('should return zero stats when no plans exist', async () => {
|
|
@@ -527,6 +684,7 @@ describe('createPlanningExtraOps', () => {
|
|
|
527
684
|
runtime.planner.approve(plan2.id);
|
|
528
685
|
runtime.planner.startExecution(plan2.id);
|
|
529
686
|
runtime.planner.updateTask(plan2.id, 'task-1', 'completed');
|
|
687
|
+
runtime.planner.startReconciliation(plan2.id);
|
|
530
688
|
runtime.planner.complete(plan2.id);
|
|
531
689
|
|
|
532
690
|
const result = (await findOp('plan_stats').handler({})) as {
|
|
@@ -0,0 +1,326 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import {
|
|
3
|
+
scorePlaybook,
|
|
4
|
+
matchPlaybooks,
|
|
5
|
+
mergePlaybooks,
|
|
6
|
+
getBuiltinPlaybook,
|
|
7
|
+
getAllBuiltinPlaybooks,
|
|
8
|
+
} from '../playbooks/playbook-registry.js';
|
|
9
|
+
import type { PlaybookDefinition } from '../playbooks/playbook-types.js';
|
|
10
|
+
|
|
11
|
+
describe('scorePlaybook', () => {
|
|
12
|
+
it('should add 10 for intent match', () => {
|
|
13
|
+
const pb = getBuiltinPlaybook('generic-tdd')!;
|
|
14
|
+
expect(scorePlaybook(pb, 'BUILD', '')).toBe(10);
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it('should add 5 per keyword match', () => {
|
|
18
|
+
const pb = getBuiltinPlaybook('generic-tdd')!;
|
|
19
|
+
// 'implement' and 'build' are keywords
|
|
20
|
+
expect(scorePlaybook(pb, undefined, 'implement and build')).toBe(10);
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it('should combine intent + keyword scores', () => {
|
|
24
|
+
const pb = getBuiltinPlaybook('generic-tdd')!;
|
|
25
|
+
// BUILD intent (10) + 'implement' (5) + 'feature' (5) = 20
|
|
26
|
+
expect(scorePlaybook(pb, 'BUILD', 'implement a feature')).toBe(20);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it('should return 0 for unrelated content', () => {
|
|
30
|
+
const pb = getBuiltinPlaybook('generic-tdd')!;
|
|
31
|
+
expect(scorePlaybook(pb, 'REVIEW', 'nothing related here')).toBe(0);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it('should be case-insensitive for keywords', () => {
|
|
35
|
+
const pb = getBuiltinPlaybook('generic-systematic-debugging')!;
|
|
36
|
+
expect(scorePlaybook(pb, undefined, 'FIX THE BUG')).toBeGreaterThan(0);
|
|
37
|
+
});
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
describe('matchPlaybooks', () => {
|
|
41
|
+
it('should match TDD for BUILD intent with "implement"', () => {
|
|
42
|
+
const result = matchPlaybooks('BUILD', 'implement the new feature');
|
|
43
|
+
expect(result.playbook).not.toBeNull();
|
|
44
|
+
expect(result.genericMatch?.id).toBe('generic-tdd');
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it('should match systematic-debugging for FIX intent', () => {
|
|
48
|
+
const result = matchPlaybooks('FIX', 'fix the broken authentication bug');
|
|
49
|
+
expect(result.playbook).not.toBeNull();
|
|
50
|
+
expect(result.genericMatch?.id).toBe('generic-systematic-debugging');
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it('should match brainstorming for PLAN intent', () => {
|
|
54
|
+
const result = matchPlaybooks('PLAN', 'design a new architecture from scratch');
|
|
55
|
+
expect(result.playbook).not.toBeNull();
|
|
56
|
+
expect(result.genericMatch?.id).toBe('generic-brainstorming');
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it('should match code-review for REVIEW intent', () => {
|
|
60
|
+
const result = matchPlaybooks('REVIEW', 'review this pull request');
|
|
61
|
+
expect(result.playbook).not.toBeNull();
|
|
62
|
+
expect(result.genericMatch?.id).toBe('generic-code-review');
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it('should return null playbook for unrelated text with no intent', () => {
|
|
66
|
+
const result = matchPlaybooks(undefined, 'random unrelated text xyz');
|
|
67
|
+
expect(result.playbook).toBeNull();
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it('should prefer vault playbooks over builtins', () => {
|
|
71
|
+
const vaultPlaybook: PlaybookDefinition = {
|
|
72
|
+
id: 'custom-tdd',
|
|
73
|
+
tier: 'generic',
|
|
74
|
+
title: 'Custom TDD',
|
|
75
|
+
trigger: 'Custom TDD trigger',
|
|
76
|
+
description: 'Custom TDD description',
|
|
77
|
+
steps: 'Custom steps',
|
|
78
|
+
expectedOutcome: 'Custom outcome',
|
|
79
|
+
category: 'methodology',
|
|
80
|
+
tags: ['tdd'],
|
|
81
|
+
matchIntents: ['BUILD', 'FIX'],
|
|
82
|
+
matchKeywords: [
|
|
83
|
+
'implement',
|
|
84
|
+
'build',
|
|
85
|
+
'create',
|
|
86
|
+
'add',
|
|
87
|
+
'fix',
|
|
88
|
+
'refactor',
|
|
89
|
+
'feature',
|
|
90
|
+
'code',
|
|
91
|
+
'custom-unique-keyword',
|
|
92
|
+
],
|
|
93
|
+
gates: [],
|
|
94
|
+
taskTemplates: [],
|
|
95
|
+
toolInjections: [],
|
|
96
|
+
verificationCriteria: [],
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
// The vault playbook should score higher because it has more keyword matches
|
|
100
|
+
const result = matchPlaybooks(
|
|
101
|
+
'BUILD',
|
|
102
|
+
'implement and build and create with custom-unique-keyword',
|
|
103
|
+
[vaultPlaybook],
|
|
104
|
+
);
|
|
105
|
+
expect(result.genericMatch?.id).toBe('custom-tdd');
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
it('should resolve domain extends to correct generic', () => {
|
|
109
|
+
const domainPlaybook: PlaybookDefinition = {
|
|
110
|
+
id: 'domain-component-build',
|
|
111
|
+
tier: 'domain',
|
|
112
|
+
title: 'Component Build',
|
|
113
|
+
trigger: 'Build a component',
|
|
114
|
+
description: 'Build with design tokens',
|
|
115
|
+
steps: 'Domain steps',
|
|
116
|
+
expectedOutcome: 'Component built with tokens',
|
|
117
|
+
extends: 'generic-tdd',
|
|
118
|
+
category: 'design-system',
|
|
119
|
+
tags: ['component', 'build'],
|
|
120
|
+
matchIntents: ['BUILD'],
|
|
121
|
+
matchKeywords: ['component', 'build'],
|
|
122
|
+
gates: [{ phase: 'completion', requirement: 'Tokens validated', checkType: 'token-check' }],
|
|
123
|
+
taskTemplates: [],
|
|
124
|
+
toolInjections: ['validate_tokens'],
|
|
125
|
+
verificationCriteria: ['All tokens are semantic'],
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
const result = matchPlaybooks('BUILD', 'build a component', [domainPlaybook]);
|
|
129
|
+
expect(result.domainMatch?.id).toBe('domain-component-build');
|
|
130
|
+
// Should resolve the extended generic
|
|
131
|
+
expect(result.genericMatch?.id).toBe('generic-tdd');
|
|
132
|
+
expect(result.playbook?.label).toContain('extends');
|
|
133
|
+
});
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
describe('mergePlaybooks', () => {
|
|
137
|
+
it('should concatenate gates (generic first, then domain)', () => {
|
|
138
|
+
const generic = getBuiltinPlaybook('generic-tdd')!;
|
|
139
|
+
const domain: PlaybookDefinition = {
|
|
140
|
+
id: 'domain-test',
|
|
141
|
+
tier: 'domain',
|
|
142
|
+
title: 'Domain Test',
|
|
143
|
+
trigger: '',
|
|
144
|
+
description: '',
|
|
145
|
+
steps: '',
|
|
146
|
+
expectedOutcome: '',
|
|
147
|
+
category: 'test',
|
|
148
|
+
tags: [],
|
|
149
|
+
matchIntents: [],
|
|
150
|
+
matchKeywords: [],
|
|
151
|
+
gates: [{ phase: 'completion', requirement: 'Domain gate', checkType: 'domain-gate' }],
|
|
152
|
+
taskTemplates: [],
|
|
153
|
+
toolInjections: [],
|
|
154
|
+
verificationCriteria: [],
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
const merged = mergePlaybooks(generic, domain);
|
|
158
|
+
expect(merged.mergedGates.length).toBe(generic.gates.length + 1);
|
|
159
|
+
expect(merged.mergedGates[0]).toEqual(generic.gates[0]);
|
|
160
|
+
expect(merged.mergedGates[merged.mergedGates.length - 1].checkType).toBe('domain-gate');
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
it('should override tasks at same order+taskType', () => {
|
|
164
|
+
const generic: PlaybookDefinition = {
|
|
165
|
+
id: 'g',
|
|
166
|
+
tier: 'generic',
|
|
167
|
+
title: 'G',
|
|
168
|
+
trigger: '',
|
|
169
|
+
description: '',
|
|
170
|
+
steps: '',
|
|
171
|
+
expectedOutcome: '',
|
|
172
|
+
category: 'test',
|
|
173
|
+
tags: [],
|
|
174
|
+
matchIntents: [],
|
|
175
|
+
matchKeywords: [],
|
|
176
|
+
gates: [],
|
|
177
|
+
taskTemplates: [
|
|
178
|
+
{
|
|
179
|
+
taskType: 'test',
|
|
180
|
+
titleTemplate: 'Generic test',
|
|
181
|
+
acceptanceCriteria: ['generic'],
|
|
182
|
+
tools: [],
|
|
183
|
+
order: 'before-implementation',
|
|
184
|
+
},
|
|
185
|
+
],
|
|
186
|
+
toolInjections: [],
|
|
187
|
+
verificationCriteria: [],
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
const domain: PlaybookDefinition = {
|
|
191
|
+
...generic,
|
|
192
|
+
id: 'd',
|
|
193
|
+
tier: 'domain',
|
|
194
|
+
title: 'D',
|
|
195
|
+
taskTemplates: [
|
|
196
|
+
{
|
|
197
|
+
taskType: 'test',
|
|
198
|
+
titleTemplate: 'Domain test',
|
|
199
|
+
acceptanceCriteria: ['domain'],
|
|
200
|
+
tools: ['custom_tool'],
|
|
201
|
+
order: 'before-implementation',
|
|
202
|
+
},
|
|
203
|
+
],
|
|
204
|
+
};
|
|
205
|
+
|
|
206
|
+
const merged = mergePlaybooks(generic, domain);
|
|
207
|
+
// Domain should override the same order+taskType
|
|
208
|
+
expect(merged.mergedTasks).toHaveLength(1);
|
|
209
|
+
expect(merged.mergedTasks[0].titleTemplate).toBe('Domain test');
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
it('should deduplicate tools', () => {
|
|
213
|
+
const generic: PlaybookDefinition = {
|
|
214
|
+
id: 'g',
|
|
215
|
+
tier: 'generic',
|
|
216
|
+
title: 'G',
|
|
217
|
+
trigger: '',
|
|
218
|
+
description: '',
|
|
219
|
+
steps: '',
|
|
220
|
+
expectedOutcome: '',
|
|
221
|
+
category: 'test',
|
|
222
|
+
tags: [],
|
|
223
|
+
matchIntents: [],
|
|
224
|
+
matchKeywords: [],
|
|
225
|
+
gates: [],
|
|
226
|
+
taskTemplates: [],
|
|
227
|
+
toolInjections: ['search_intelligent', 'shared_tool'],
|
|
228
|
+
verificationCriteria: [],
|
|
229
|
+
};
|
|
230
|
+
|
|
231
|
+
const domain: PlaybookDefinition = {
|
|
232
|
+
...generic,
|
|
233
|
+
id: 'd',
|
|
234
|
+
tier: 'domain',
|
|
235
|
+
title: 'D',
|
|
236
|
+
toolInjections: ['shared_tool', 'domain_tool'],
|
|
237
|
+
};
|
|
238
|
+
|
|
239
|
+
const merged = mergePlaybooks(generic, domain);
|
|
240
|
+
expect(merged.mergedTools).toHaveLength(3);
|
|
241
|
+
expect(merged.mergedTools).toContain('search_intelligent');
|
|
242
|
+
expect(merged.mergedTools).toContain('shared_tool');
|
|
243
|
+
expect(merged.mergedTools).toContain('domain_tool');
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
it('should deduplicate verification criteria', () => {
|
|
247
|
+
const generic: PlaybookDefinition = {
|
|
248
|
+
id: 'g',
|
|
249
|
+
tier: 'generic',
|
|
250
|
+
title: 'G',
|
|
251
|
+
trigger: '',
|
|
252
|
+
description: '',
|
|
253
|
+
steps: '',
|
|
254
|
+
expectedOutcome: '',
|
|
255
|
+
category: 'test',
|
|
256
|
+
tags: [],
|
|
257
|
+
matchIntents: [],
|
|
258
|
+
matchKeywords: [],
|
|
259
|
+
gates: [],
|
|
260
|
+
taskTemplates: [],
|
|
261
|
+
toolInjections: [],
|
|
262
|
+
verificationCriteria: ['Tests pass', 'Build succeeds'],
|
|
263
|
+
};
|
|
264
|
+
|
|
265
|
+
const domain: PlaybookDefinition = {
|
|
266
|
+
...generic,
|
|
267
|
+
id: 'd',
|
|
268
|
+
tier: 'domain',
|
|
269
|
+
title: 'D',
|
|
270
|
+
verificationCriteria: ['Tests pass', 'Tokens validated'],
|
|
271
|
+
};
|
|
272
|
+
|
|
273
|
+
const merged = mergePlaybooks(generic, domain);
|
|
274
|
+
expect(merged.mergedVerification).toHaveLength(3);
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
it('should build correct label for generic + domain', () => {
|
|
278
|
+
const generic = getBuiltinPlaybook('generic-tdd')!;
|
|
279
|
+
const domain: PlaybookDefinition = {
|
|
280
|
+
id: 'd',
|
|
281
|
+
tier: 'domain',
|
|
282
|
+
title: 'Component Build',
|
|
283
|
+
trigger: '',
|
|
284
|
+
description: '',
|
|
285
|
+
steps: '',
|
|
286
|
+
expectedOutcome: '',
|
|
287
|
+
category: 'test',
|
|
288
|
+
tags: [],
|
|
289
|
+
matchIntents: [],
|
|
290
|
+
matchKeywords: [],
|
|
291
|
+
gates: [],
|
|
292
|
+
taskTemplates: [],
|
|
293
|
+
toolInjections: [],
|
|
294
|
+
verificationCriteria: [],
|
|
295
|
+
};
|
|
296
|
+
|
|
297
|
+
const merged = mergePlaybooks(generic, domain);
|
|
298
|
+
expect(merged.label).toBe('Component Build (extends Test-Driven Development)');
|
|
299
|
+
});
|
|
300
|
+
|
|
301
|
+
it('should handle generic-only merge', () => {
|
|
302
|
+
const generic = getBuiltinPlaybook('generic-tdd')!;
|
|
303
|
+
const merged = mergePlaybooks(generic, undefined);
|
|
304
|
+
expect(merged.label).toBe(generic.title);
|
|
305
|
+
expect(merged.generic).toBe(generic);
|
|
306
|
+
expect(merged.domain).toBeUndefined();
|
|
307
|
+
});
|
|
308
|
+
});
|
|
309
|
+
|
|
310
|
+
describe('getAllBuiltinPlaybooks', () => {
|
|
311
|
+
it('should return 6 built-in playbooks', () => {
|
|
312
|
+
const all = getAllBuiltinPlaybooks();
|
|
313
|
+
expect(all).toHaveLength(6);
|
|
314
|
+
});
|
|
315
|
+
|
|
316
|
+
it('should all be generic tier', () => {
|
|
317
|
+
const all = getAllBuiltinPlaybooks();
|
|
318
|
+
expect(all.every((p) => p.tier === 'generic')).toBe(true);
|
|
319
|
+
});
|
|
320
|
+
|
|
321
|
+
it('should have unique IDs', () => {
|
|
322
|
+
const all = getAllBuiltinPlaybooks();
|
|
323
|
+
const ids = all.map((p) => p.id);
|
|
324
|
+
expect(new Set(ids).size).toBe(ids.length);
|
|
325
|
+
});
|
|
326
|
+
});
|