@soleri/core 9.2.0 → 9.3.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/data/flows/build.flow.yaml +8 -9
- package/data/flows/deliver.flow.yaml +9 -10
- package/data/flows/design.flow.yaml +3 -4
- package/data/flows/enhance.flow.yaml +5 -6
- package/data/flows/explore.flow.yaml +3 -4
- package/data/flows/fix.flow.yaml +5 -6
- package/data/flows/plan.flow.yaml +4 -5
- package/data/flows/review.flow.yaml +3 -4
- package/dist/curator/curator.d.ts.map +1 -1
- package/dist/curator/curator.js +98 -22
- package/dist/curator/curator.js.map +1 -1
- package/dist/engine/bin/soleri-engine.js.map +1 -1
- package/dist/engine/module-manifest.d.ts.map +1 -1
- package/dist/engine/module-manifest.js +21 -1
- package/dist/engine/module-manifest.js.map +1 -1
- package/dist/engine/register-engine.d.ts.map +1 -1
- package/dist/engine/register-engine.js +25 -1
- package/dist/engine/register-engine.js.map +1 -1
- package/dist/flows/gate-evaluator.js.map +1 -1
- package/dist/operator/operator-profile.d.ts.map +1 -1
- package/dist/operator/operator-profile.js +11 -5
- package/dist/operator/operator-profile.js.map +1 -1
- package/dist/operator/operator-signals.d.ts.map +1 -1
- package/dist/operator/operator-signals.js.map +1 -1
- package/dist/planning/evidence-collector.js.map +1 -1
- package/dist/planning/gap-passes.d.ts.map +1 -1
- package/dist/planning/gap-passes.js +23 -6
- package/dist/planning/gap-passes.js.map +1 -1
- package/dist/planning/gap-patterns.d.ts.map +1 -1
- package/dist/planning/gap-patterns.js +57 -11
- package/dist/planning/gap-patterns.js.map +1 -1
- package/dist/planning/github-projection.d.ts.map +1 -1
- package/dist/planning/github-projection.js +39 -20
- package/dist/planning/github-projection.js.map +1 -1
- package/dist/planning/impact-analyzer.d.ts.map +1 -1
- package/dist/planning/impact-analyzer.js +20 -18
- package/dist/planning/impact-analyzer.js.map +1 -1
- package/dist/planning/plan-lifecycle.d.ts.map +1 -1
- package/dist/planning/plan-lifecycle.js +22 -9
- package/dist/planning/plan-lifecycle.js.map +1 -1
- package/dist/planning/planner.d.ts.map +1 -1
- package/dist/planning/planner.js +60 -17
- package/dist/planning/planner.js.map +1 -1
- package/dist/planning/rationalization-detector.d.ts.map +1 -1
- package/dist/planning/rationalization-detector.js.map +1 -1
- package/dist/planning/reconciliation-engine.d.ts.map +1 -1
- package/dist/planning/reconciliation-engine.js.map +1 -1
- package/dist/planning/task-verifier.d.ts.map +1 -1
- package/dist/planning/task-verifier.js +14 -6
- package/dist/planning/task-verifier.js.map +1 -1
- package/dist/runtime/admin-setup-ops.d.ts.map +1 -1
- package/dist/runtime/admin-setup-ops.js +2 -1
- package/dist/runtime/admin-setup-ops.js.map +1 -1
- package/dist/runtime/branching-ops.d.ts +12 -0
- package/dist/runtime/branching-ops.d.ts.map +1 -0
- package/dist/runtime/branching-ops.js +100 -0
- package/dist/runtime/branching-ops.js.map +1 -0
- package/dist/runtime/context-health.d.ts.map +1 -1
- package/dist/runtime/context-health.js.map +1 -1
- package/dist/runtime/facades/branching-facade.d.ts +7 -0
- package/dist/runtime/facades/branching-facade.d.ts.map +1 -0
- package/dist/runtime/facades/branching-facade.js +8 -0
- package/dist/runtime/facades/branching-facade.js.map +1 -0
- package/dist/runtime/facades/chat-service-ops.d.ts.map +1 -1
- package/dist/runtime/facades/chat-service-ops.js +3 -1
- package/dist/runtime/facades/chat-service-ops.js.map +1 -1
- package/dist/runtime/facades/chat-transport-ops.d.ts.map +1 -1
- package/dist/runtime/facades/chat-transport-ops.js.map +1 -1
- package/dist/runtime/facades/index.d.ts.map +1 -1
- package/dist/runtime/facades/index.js +42 -0
- package/dist/runtime/facades/index.js.map +1 -1
- package/dist/runtime/facades/intake-facade.d.ts +9 -0
- package/dist/runtime/facades/intake-facade.d.ts.map +1 -0
- package/dist/runtime/facades/intake-facade.js +11 -0
- package/dist/runtime/facades/intake-facade.js.map +1 -0
- package/dist/runtime/facades/links-facade.d.ts +9 -0
- package/dist/runtime/facades/links-facade.d.ts.map +1 -0
- package/dist/runtime/facades/links-facade.js +10 -0
- package/dist/runtime/facades/links-facade.js.map +1 -0
- package/dist/runtime/facades/operator-facade.d.ts.map +1 -1
- package/dist/runtime/facades/operator-facade.js.map +1 -1
- package/dist/runtime/facades/plan-facade.d.ts.map +1 -1
- package/dist/runtime/facades/plan-facade.js +4 -1
- package/dist/runtime/facades/plan-facade.js.map +1 -1
- package/dist/runtime/facades/tier-facade.d.ts +7 -0
- package/dist/runtime/facades/tier-facade.d.ts.map +1 -0
- package/dist/runtime/facades/tier-facade.js +8 -0
- package/dist/runtime/facades/tier-facade.js.map +1 -0
- package/dist/runtime/facades/vault-facade.d.ts +9 -1
- package/dist/runtime/facades/vault-facade.d.ts.map +1 -1
- package/dist/runtime/facades/vault-facade.js +44 -187
- package/dist/runtime/facades/vault-facade.js.map +1 -1
- package/dist/runtime/github-integration.d.ts.map +1 -1
- package/dist/runtime/github-integration.js +11 -4
- package/dist/runtime/github-integration.js.map +1 -1
- package/dist/runtime/orchestrate-ops.d.ts.map +1 -1
- package/dist/runtime/orchestrate-ops.js +32 -10
- package/dist/runtime/orchestrate-ops.js.map +1 -1
- package/dist/runtime/planning-extra-ops.d.ts.map +1 -1
- package/dist/runtime/planning-extra-ops.js.map +1 -1
- package/dist/runtime/runtime.d.ts.map +1 -1
- package/dist/runtime/runtime.js +3 -1
- package/dist/runtime/runtime.js.map +1 -1
- package/dist/runtime/session-briefing.d.ts.map +1 -1
- package/dist/runtime/session-briefing.js +5 -1
- package/dist/runtime/session-briefing.js.map +1 -1
- package/dist/runtime/tier-ops.d.ts +13 -0
- package/dist/runtime/tier-ops.d.ts.map +1 -0
- package/dist/runtime/tier-ops.js +110 -0
- package/dist/runtime/tier-ops.js.map +1 -0
- package/dist/skills/sync-skills.d.ts.map +1 -1
- package/dist/skills/sync-skills.js +1 -1
- package/dist/skills/sync-skills.js.map +1 -1
- package/dist/vault/linking.d.ts.map +1 -1
- package/dist/vault/linking.js +41 -5
- package/dist/vault/linking.js.map +1 -1
- package/dist/vault/vault-entries.d.ts.map +1 -1
- package/dist/vault/vault-entries.js +68 -26
- package/dist/vault/vault-entries.js.map +1 -1
- package/dist/vault/vault-maintenance.d.ts.map +1 -1
- package/dist/vault/vault-maintenance.js +6 -2
- package/dist/vault/vault-maintenance.js.map +1 -1
- package/dist/vault/vault-markdown-sync.d.ts.map +1 -1
- package/dist/vault/vault-markdown-sync.js.map +1 -1
- package/dist/vault/vault-memories.d.ts.map +1 -1
- package/dist/vault/vault-memories.js +3 -1
- package/dist/vault/vault-memories.js.map +1 -1
- package/dist/vault/vault-schema.js +36 -10
- package/dist/vault/vault-schema.js.map +1 -1
- package/dist/vault/vault.d.ts.map +1 -1
- package/dist/vault/vault.js +5 -1
- package/dist/vault/vault.js.map +1 -1
- package/package.json +7 -7
- package/src/agency/agency-manager.test.ts +60 -40
- package/src/agency/default-rules.test.ts +17 -9
- package/src/capabilities/registry.test.ts +2 -12
- package/src/chat/agent-loop.test.ts +33 -43
- package/src/chat/mcp-bridge.test.ts +7 -2
- package/src/claudemd/inject.test.ts +2 -12
- package/src/context/context-engine.test.ts +96 -51
- package/src/control/intent-router.test.ts +3 -3
- package/src/curator/classifier.test.ts +14 -8
- package/src/curator/contradiction-detector.test.ts +30 -5
- package/src/curator/curator.ts +278 -56
- package/src/curator/duplicate-detector.test.ts +77 -15
- package/src/curator/quality-gate.test.ts +71 -31
- package/src/curator/tag-manager.test.ts +12 -4
- package/src/domain-packs/knowledge-installer.test.ts +2 -10
- package/src/domain-packs/token-resolver.test.ts +1 -3
- package/src/domain-packs/types.test.ts +16 -2
- package/src/enforcement/registry.test.ts +2 -8
- package/src/engine/bin/soleri-engine.ts +3 -1
- package/src/engine/module-manifest.test.ts +5 -4
- package/src/engine/module-manifest.ts +21 -1
- package/src/engine/register-engine.test.ts +6 -1
- package/src/engine/register-engine.ts +26 -3
- package/src/errors/classify.test.ts +6 -2
- package/src/errors/retry.test.ts +1 -4
- package/src/facades/facade-factory.test.ts +110 -64
- package/src/flows/epilogue.test.ts +16 -10
- package/src/flows/gate-evaluator.test.ts +12 -6
- package/src/flows/gate-evaluator.ts +1 -3
- package/src/governance/governance.test.ts +137 -21
- package/src/health/health-registry.test.ts +8 -1
- package/src/intake/content-classifier.test.ts +121 -51
- package/src/intake/dedup-gate.test.ts +38 -22
- package/src/intake/intake-pipeline.test.ts +5 -3
- package/src/intake/text-ingester.test.ts +26 -20
- package/src/llm/key-pool.test.ts +1 -3
- package/src/llm/llm-client.test.ts +1 -4
- package/src/llm/oauth-discovery.test.ts +16 -16
- package/src/llm/utils.test.ts +62 -18
- package/src/logging/logger.test.ts +4 -1
- package/src/loop/loop-manager.test.ts +2 -6
- package/src/migrations/migration-runner.edge-cases.test.ts +2 -7
- package/src/operator/operator-profile-extended.test.ts +15 -5
- package/src/operator/operator-profile.test.ts +26 -8
- package/src/operator/operator-profile.ts +38 -22
- package/src/operator/operator-signals-extended.test.ts +35 -23
- package/src/operator/operator-signals.test.ts +6 -10
- package/src/operator/operator-signals.ts +2 -1
- package/src/operator/prompts/hook-precompact-operator-dispatch.md +10 -6
- package/src/operator/prompts/subagent-soft-signal-extractor.md +5 -0
- package/src/operator/prompts/subagent-synthesis-cognition.md +19 -10
- package/src/operator/prompts/subagent-synthesis-communication.md +13 -7
- package/src/operator/prompts/subagent-synthesis-technical.md +19 -9
- package/src/operator/prompts/subagent-synthesis-trust.md +27 -21
- package/src/persona/defaults.test.ts +1 -5
- package/src/planning/evidence-collector.test.ts +147 -38
- package/src/planning/evidence-collector.ts +1 -4
- package/src/planning/gap-analysis-alternatives.test.ts +41 -11
- package/src/planning/gap-passes.test.ts +215 -33
- package/src/planning/gap-passes.ts +115 -46
- package/src/planning/gap-patterns.test.ts +87 -13
- package/src/planning/gap-patterns.ts +114 -31
- package/src/planning/github-projection.test.ts +6 -1
- package/src/planning/github-projection.ts +41 -20
- package/src/planning/impact-analyzer.test.ts +10 -23
- package/src/planning/impact-analyzer.ts +33 -46
- package/src/planning/plan-lifecycle.test.ts +103 -36
- package/src/planning/plan-lifecycle.ts +49 -18
- package/src/planning/planner.test.ts +12 -2
- package/src/planning/planner.ts +198 -58
- package/src/planning/rationalization-detector.test.ts +5 -20
- package/src/planning/rationalization-detector.ts +14 -16
- package/src/planning/reconciliation-engine.test.ts +20 -3
- package/src/planning/reconciliation-engine.ts +1 -2
- package/src/planning/task-verifier.test.ts +59 -27
- package/src/planning/task-verifier.ts +15 -9
- package/src/playbooks/playbook-executor.test.ts +1 -3
- package/src/plugins/plugin-loader.test.ts +19 -14
- package/src/plugins/plugin-registry.test.ts +45 -33
- package/src/project/project-registry.test.ts +23 -12
- package/src/prompts/template-manager.test.ts +4 -1
- package/src/queue/job-queue.test.ts +10 -14
- package/src/runtime/admin-extra-ops.test.ts +5 -19
- package/src/runtime/admin-ops.test.ts +1 -3
- package/src/runtime/admin-setup-ops.test.ts +3 -4
- package/src/runtime/admin-setup-ops.ts +9 -2
- package/src/runtime/archive-ops.test.ts +4 -1
- package/src/runtime/branching-ops.test.ts +144 -0
- package/src/runtime/branching-ops.ts +107 -0
- package/src/runtime/capture-ops.test.ts +7 -21
- package/src/runtime/chain-ops.test.ts +16 -6
- package/src/runtime/claude-md-helpers.test.ts +1 -3
- package/src/runtime/context-health.test.ts +1 -3
- package/src/runtime/context-health.ts +1 -3
- package/src/runtime/curator-extra-ops.test.ts +3 -1
- package/src/runtime/domain-ops.test.ts +46 -36
- package/src/runtime/facades/admin-facade.test.ts +1 -4
- package/src/runtime/facades/archive-facade.test.ts +21 -7
- package/src/runtime/facades/brain-facade.test.ts +176 -72
- package/src/runtime/facades/branching-facade.test.ts +43 -0
- package/src/runtime/facades/branching-facade.ts +11 -0
- package/src/runtime/facades/chat-facade.test.ts +81 -28
- package/src/runtime/facades/chat-service-ops.test.ts +178 -73
- package/src/runtime/facades/chat-service-ops.ts +3 -1
- package/src/runtime/facades/chat-session-ops.test.ts +25 -10
- package/src/runtime/facades/chat-transport-ops.test.ts +101 -34
- package/src/runtime/facades/chat-transport-ops.ts +0 -1
- package/src/runtime/facades/context-facade.test.ts +19 -4
- package/src/runtime/facades/control-facade.test.ts +3 -3
- package/src/runtime/facades/index.ts +42 -0
- package/src/runtime/facades/intake-facade.test.ts +215 -0
- package/src/runtime/facades/intake-facade.ts +14 -0
- package/src/runtime/facades/links-facade.test.ts +203 -0
- package/src/runtime/facades/links-facade.ts +13 -0
- package/src/runtime/facades/loop-facade.test.ts +22 -5
- package/src/runtime/facades/memory-facade.test.ts +19 -5
- package/src/runtime/facades/operator-facade.test.ts +17 -4
- package/src/runtime/facades/operator-facade.ts +11 -3
- package/src/runtime/facades/orchestrate-facade.test.ts +7 -1
- package/src/runtime/facades/plan-facade.test.ts +29 -12
- package/src/runtime/facades/plan-facade.ts +7 -2
- package/src/runtime/facades/tier-facade.test.ts +47 -0
- package/src/runtime/facades/tier-facade.ts +11 -0
- package/src/runtime/facades/vault-facade.test.ts +174 -242
- package/src/runtime/facades/vault-facade.ts +55 -199
- package/src/runtime/github-integration.ts +11 -8
- package/src/runtime/grading-ops.test.ts +39 -8
- package/src/runtime/intake-ops.test.ts +69 -16
- package/src/runtime/loop-ops.test.ts +16 -6
- package/src/runtime/memory-cross-project-ops.test.ts +25 -14
- package/src/runtime/orchestrate-ops.ts +54 -27
- package/src/runtime/pack-ops.test.ts +23 -6
- package/src/runtime/planning-extra-ops.test.ts +17 -7
- package/src/runtime/planning-extra-ops.ts +3 -1
- package/src/runtime/playbook-ops.test.ts +26 -3
- package/src/runtime/plugin-ops.test.ts +83 -25
- package/src/runtime/project-ops.test.ts +26 -6
- package/src/runtime/runtime.ts +3 -1
- package/src/runtime/session-briefing.test.ts +183 -54
- package/src/runtime/session-briefing.ts +8 -2
- package/src/runtime/sync-ops.test.ts +3 -12
- package/src/runtime/telemetry-ops.test.ts +31 -6
- package/src/runtime/tier-ops.test.ts +159 -0
- package/src/runtime/tier-ops.ts +119 -0
- package/src/runtime/vault-extra-ops.test.ts +32 -8
- package/src/runtime/vault-sharing-ops.test.ts +1 -4
- package/src/skills/sync-skills.ts +2 -12
- package/src/transport/ws-server.test.ts +7 -4
- package/src/vault/__tests__/vault-characterization.test.ts +492 -81
- package/src/vault/linking.test.ts +50 -17
- package/src/vault/linking.ts +48 -7
- package/src/vault/obsidian-sync.test.ts +6 -3
- package/src/vault/scope-detector.test.ts +1 -3
- package/src/vault/vault-branching.test.ts +9 -7
- package/src/vault/vault-entries.ts +209 -65
- package/src/vault/vault-maintenance.ts +7 -12
- package/src/vault/vault-manager.test.ts +10 -10
- package/src/vault/vault-markdown-sync.ts +4 -1
- package/src/vault/vault-memories.ts +7 -7
- package/src/vault/vault-schema.ts +72 -15
- package/src/vault/vault.ts +55 -9
- package/src/brain/strength-scorer.ts +0 -404
- package/src/engine/index.ts +0 -21
- package/src/persona/index.ts +0 -9
- package/src/vault/vault-interfaces.ts +0 -56
|
@@ -30,13 +30,40 @@ function makePlan(overrides: Partial<Plan> = {}): Plan {
|
|
|
30
30
|
scope: 'Auth module, middleware, and user service. Does not include OAuth providers.',
|
|
31
31
|
status: 'draft',
|
|
32
32
|
decisions: [
|
|
33
|
-
{
|
|
33
|
+
{
|
|
34
|
+
decision: 'Use JWT for stateless auth',
|
|
35
|
+
rationale: 'Because it scales horizontally without shared session store',
|
|
36
|
+
},
|
|
34
37
|
],
|
|
35
38
|
tasks: [
|
|
36
|
-
{
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
39
|
+
{
|
|
40
|
+
id: 'task-1',
|
|
41
|
+
title: 'Add JWT signing',
|
|
42
|
+
description: 'Implement JWT sign/verify using built-in crypto module',
|
|
43
|
+
status: 'pending',
|
|
44
|
+
updatedAt: Date.now(),
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
id: 'task-2',
|
|
48
|
+
title: 'Add auth middleware',
|
|
49
|
+
description: 'Create Express middleware that validates JWT from Authorization header',
|
|
50
|
+
status: 'pending',
|
|
51
|
+
updatedAt: Date.now(),
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
id: 'task-3',
|
|
55
|
+
title: 'Add login endpoint',
|
|
56
|
+
description: 'POST /auth/login returns JWT after verifying credentials',
|
|
57
|
+
status: 'pending',
|
|
58
|
+
updatedAt: Date.now(),
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
id: 'task-4',
|
|
62
|
+
title: 'Add test coverage',
|
|
63
|
+
description: 'Test JWT signing, middleware rejection, and login flow end-to-end',
|
|
64
|
+
status: 'pending',
|
|
65
|
+
updatedAt: Date.now(),
|
|
66
|
+
},
|
|
40
67
|
],
|
|
41
68
|
checks: [],
|
|
42
69
|
createdAt: Date.now(),
|
|
@@ -178,7 +205,9 @@ describe('Pass 1: Structure', () => {
|
|
|
178
205
|
it('returns critical gap for missing objective', () => {
|
|
179
206
|
const gaps = analyzeStructure(makePlan({ objective: '' }));
|
|
180
207
|
expect(gaps.some((g) => g._trigger === 'missing_or_short_objective')).toBe(true);
|
|
181
|
-
expect(gaps.find((g) => g._trigger === 'missing_or_short_objective')?.severity).toBe(
|
|
208
|
+
expect(gaps.find((g) => g._trigger === 'missing_or_short_objective')?.severity).toBe(
|
|
209
|
+
'critical',
|
|
210
|
+
);
|
|
182
211
|
});
|
|
183
212
|
|
|
184
213
|
it('returns critical gap for short objective', () => {
|
|
@@ -258,10 +287,37 @@ describe('Pass 3: Feasibility', () => {
|
|
|
258
287
|
it('does not flag when tasks have dependsOn', () => {
|
|
259
288
|
const plan = makePlan({
|
|
260
289
|
tasks: [
|
|
261
|
-
{
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
290
|
+
{
|
|
291
|
+
id: 't1',
|
|
292
|
+
title: 'Setup',
|
|
293
|
+
description: 'Setup project',
|
|
294
|
+
status: 'pending',
|
|
295
|
+
updatedAt: Date.now(),
|
|
296
|
+
},
|
|
297
|
+
{
|
|
298
|
+
id: 't2',
|
|
299
|
+
title: 'Build',
|
|
300
|
+
description: 'Build feature',
|
|
301
|
+
status: 'pending',
|
|
302
|
+
dependsOn: ['t1'],
|
|
303
|
+
updatedAt: Date.now(),
|
|
304
|
+
},
|
|
305
|
+
{
|
|
306
|
+
id: 't3',
|
|
307
|
+
title: 'Test',
|
|
308
|
+
description: 'Test feature',
|
|
309
|
+
status: 'pending',
|
|
310
|
+
dependsOn: ['t2'],
|
|
311
|
+
updatedAt: Date.now(),
|
|
312
|
+
},
|
|
313
|
+
{
|
|
314
|
+
id: 't4',
|
|
315
|
+
title: 'Deploy',
|
|
316
|
+
description: 'Deploy to prod',
|
|
317
|
+
status: 'pending',
|
|
318
|
+
dependsOn: ['t3'],
|
|
319
|
+
updatedAt: Date.now(),
|
|
320
|
+
},
|
|
265
321
|
],
|
|
266
322
|
});
|
|
267
323
|
const gaps = analyzeFeasibility(plan);
|
|
@@ -271,8 +327,20 @@ describe('Pass 3: Feasibility', () => {
|
|
|
271
327
|
it('does not flag when 3 or fewer tasks', () => {
|
|
272
328
|
const plan = makePlan({
|
|
273
329
|
tasks: [
|
|
274
|
-
{
|
|
275
|
-
|
|
330
|
+
{
|
|
331
|
+
id: 't1',
|
|
332
|
+
title: 'Task 1',
|
|
333
|
+
description: 'Do something',
|
|
334
|
+
status: 'pending',
|
|
335
|
+
updatedAt: Date.now(),
|
|
336
|
+
},
|
|
337
|
+
{
|
|
338
|
+
id: 't2',
|
|
339
|
+
title: 'Task 2',
|
|
340
|
+
description: 'Do another thing',
|
|
341
|
+
status: 'pending',
|
|
342
|
+
updatedAt: Date.now(),
|
|
343
|
+
},
|
|
276
344
|
],
|
|
277
345
|
});
|
|
278
346
|
const gaps = analyzeFeasibility(plan);
|
|
@@ -305,7 +373,13 @@ describe('Pass 4: Risk', () => {
|
|
|
305
373
|
scope: 'Auth module only. Not including user service.',
|
|
306
374
|
decisions: [{ decision: 'Use new pattern', rationale: 'Because it is cleaner code' }],
|
|
307
375
|
tasks: [
|
|
308
|
-
{
|
|
376
|
+
{
|
|
377
|
+
id: 't1',
|
|
378
|
+
title: 'Refactor auth',
|
|
379
|
+
description: 'Change the auth flow implementation',
|
|
380
|
+
status: 'pending',
|
|
381
|
+
updatedAt: Date.now(),
|
|
382
|
+
},
|
|
309
383
|
],
|
|
310
384
|
});
|
|
311
385
|
const gaps = analyzeRisk(plan);
|
|
@@ -59,35 +59,81 @@ export function containsAny(text: string, patterns: string[]): boolean {
|
|
|
59
59
|
// ─── Pattern Constants (Passes 1-4) ─────────────────────────────
|
|
60
60
|
|
|
61
61
|
export const METRIC_PATTERNS = [
|
|
62
|
-
/\d+/,
|
|
63
|
-
/
|
|
62
|
+
/\d+/,
|
|
63
|
+
/percent/i,
|
|
64
|
+
/reduce/i,
|
|
65
|
+
/increase/i,
|
|
66
|
+
/measure/i,
|
|
67
|
+
/target/i,
|
|
68
|
+
/goal/i,
|
|
69
|
+
/kpi/i,
|
|
70
|
+
/metric/i,
|
|
71
|
+
/benchmark/i,
|
|
64
72
|
];
|
|
65
73
|
|
|
66
74
|
export const EXCLUSION_KEYWORDS = [
|
|
67
|
-
'not',
|
|
75
|
+
'not',
|
|
76
|
+
'exclude',
|
|
77
|
+
'outside',
|
|
78
|
+
'beyond',
|
|
79
|
+
'limit',
|
|
80
|
+
'except',
|
|
81
|
+
"won't",
|
|
82
|
+
'will not',
|
|
68
83
|
];
|
|
69
84
|
|
|
70
85
|
export const OVERLY_BROAD_PATTERNS = [
|
|
71
|
-
'everything',
|
|
72
|
-
'
|
|
86
|
+
'everything',
|
|
87
|
+
'all systems',
|
|
88
|
+
'entire codebase',
|
|
89
|
+
'complete rewrite',
|
|
90
|
+
'from scratch',
|
|
91
|
+
'total overhaul',
|
|
92
|
+
'rewrite everything',
|
|
73
93
|
];
|
|
74
94
|
|
|
75
95
|
export const DEPENDENCY_KEYWORDS = [
|
|
76
|
-
'depends',
|
|
96
|
+
'depends',
|
|
97
|
+
'dependency',
|
|
98
|
+
'prerequisite',
|
|
99
|
+
'requires',
|
|
100
|
+
'blocked',
|
|
101
|
+
'before',
|
|
77
102
|
];
|
|
78
103
|
|
|
79
104
|
export const BREAKING_CHANGE_KEYWORDS = [
|
|
80
|
-
'breaking change',
|
|
81
|
-
'
|
|
105
|
+
'breaking change',
|
|
106
|
+
'breaking',
|
|
107
|
+
'migration',
|
|
108
|
+
'deprecate',
|
|
109
|
+
'remove api',
|
|
110
|
+
'remove endpoint',
|
|
111
|
+
'schema change',
|
|
112
|
+
'database migration',
|
|
82
113
|
];
|
|
83
114
|
|
|
84
115
|
export const MITIGATION_KEYWORDS = [
|
|
85
|
-
'rollback',
|
|
86
|
-
'
|
|
116
|
+
'rollback',
|
|
117
|
+
'backward compatible',
|
|
118
|
+
'backwards compatible',
|
|
119
|
+
'feature flag',
|
|
120
|
+
'gradual',
|
|
121
|
+
'phased',
|
|
122
|
+
'fallback',
|
|
123
|
+
'backup',
|
|
124
|
+
'canary',
|
|
125
|
+
'blue-green',
|
|
87
126
|
];
|
|
88
127
|
|
|
89
128
|
export const VERIFICATION_KEYWORDS = [
|
|
90
|
-
'test',
|
|
129
|
+
'test',
|
|
130
|
+
'verify',
|
|
131
|
+
'validate',
|
|
132
|
+
'check',
|
|
133
|
+
'assert',
|
|
134
|
+
'confirm',
|
|
135
|
+
'spec',
|
|
136
|
+
'coverage',
|
|
91
137
|
];
|
|
92
138
|
|
|
93
139
|
// ─── Pass 1: Structure ───────────────────────────────────────────
|
|
@@ -98,12 +144,14 @@ export function analyzeStructure(plan: Plan): PlanGap[] {
|
|
|
98
144
|
if (!plan.objective || plan.objective.trim().length < MIN_OBJECTIVE_LENGTH) {
|
|
99
145
|
gaps.push(
|
|
100
146
|
gap(
|
|
101
|
-
'critical',
|
|
147
|
+
'critical',
|
|
148
|
+
'structure',
|
|
102
149
|
plan.objective
|
|
103
150
|
? `Objective too short (${plan.objective.trim().length} chars, need ${MIN_OBJECTIVE_LENGTH}+).`
|
|
104
151
|
: 'Plan has no objective.',
|
|
105
152
|
'Add a clear objective describing what this plan achieves.',
|
|
106
|
-
'objective',
|
|
153
|
+
'objective',
|
|
154
|
+
'missing_or_short_objective',
|
|
107
155
|
),
|
|
108
156
|
);
|
|
109
157
|
}
|
|
@@ -111,21 +159,28 @@ export function analyzeStructure(plan: Plan): PlanGap[] {
|
|
|
111
159
|
if (!plan.scope || plan.scope.trim().length < MIN_SCOPE_LENGTH) {
|
|
112
160
|
gaps.push(
|
|
113
161
|
gap(
|
|
114
|
-
'critical',
|
|
162
|
+
'critical',
|
|
163
|
+
'structure',
|
|
115
164
|
plan.scope
|
|
116
165
|
? `Scope too short (${plan.scope.trim().length} chars, need ${MIN_SCOPE_LENGTH}+).`
|
|
117
166
|
: 'Plan has no scope defined.',
|
|
118
167
|
'Define the scope — what is included and excluded.',
|
|
119
|
-
'scope',
|
|
168
|
+
'scope',
|
|
169
|
+
'missing_or_short_scope',
|
|
120
170
|
),
|
|
121
171
|
);
|
|
122
172
|
}
|
|
123
173
|
|
|
124
174
|
if (plan.tasks.length === 0) {
|
|
125
175
|
gaps.push(
|
|
126
|
-
gap(
|
|
176
|
+
gap(
|
|
177
|
+
'critical',
|
|
178
|
+
'structure',
|
|
179
|
+
'Plan has no tasks.',
|
|
127
180
|
'Add at least one task to make the plan actionable.',
|
|
128
|
-
'tasks',
|
|
181
|
+
'tasks',
|
|
182
|
+
'no_tasks',
|
|
183
|
+
),
|
|
129
184
|
);
|
|
130
185
|
}
|
|
131
186
|
|
|
@@ -139,10 +194,14 @@ export function analyzeCompleteness(plan: Plan): PlanGap[] {
|
|
|
139
194
|
|
|
140
195
|
if (plan.objective && !METRIC_PATTERNS.some((p) => p.test(plan.objective))) {
|
|
141
196
|
gaps.push(
|
|
142
|
-
gap(
|
|
197
|
+
gap(
|
|
198
|
+
'minor',
|
|
199
|
+
'completeness',
|
|
143
200
|
'Objective has no measurable targets or metrics.',
|
|
144
201
|
'Include quantifiable success criteria (numbers, percentages, concrete outcomes).',
|
|
145
|
-
'objective',
|
|
202
|
+
'objective',
|
|
203
|
+
'no_metrics_in_objective',
|
|
204
|
+
),
|
|
146
205
|
);
|
|
147
206
|
}
|
|
148
207
|
|
|
@@ -152,10 +211,14 @@ export function analyzeCompleteness(plan: Plan): PlanGap[] {
|
|
|
152
211
|
const text = decisionText(d);
|
|
153
212
|
if (text.trim().length < MIN_DECISION_LENGTH) {
|
|
154
213
|
gaps.push(
|
|
155
|
-
gap(
|
|
214
|
+
gap(
|
|
215
|
+
'major',
|
|
216
|
+
'completeness',
|
|
156
217
|
`Decision ${i + 1} is too short (${text.trim().length} chars) — lacks rationale.`,
|
|
157
218
|
'Expand each decision to include the reasoning (why this choice over alternatives).',
|
|
158
|
-
`decisions[${i}]`,
|
|
219
|
+
`decisions[${i}]`,
|
|
220
|
+
'short_decision',
|
|
221
|
+
),
|
|
159
222
|
);
|
|
160
223
|
}
|
|
161
224
|
}
|
|
@@ -163,10 +226,14 @@ export function analyzeCompleteness(plan: Plan): PlanGap[] {
|
|
|
163
226
|
|
|
164
227
|
if (plan.scope && !containsAny(plan.scope, EXCLUSION_KEYWORDS)) {
|
|
165
228
|
gaps.push(
|
|
166
|
-
gap(
|
|
229
|
+
gap(
|
|
230
|
+
'minor',
|
|
231
|
+
'completeness',
|
|
167
232
|
'Scope does not mention what is excluded.',
|
|
168
233
|
'Add explicit exclusions to prevent scope creep (e.g., "does NOT include…").',
|
|
169
|
-
'scope',
|
|
234
|
+
'scope',
|
|
235
|
+
'no_exclusions_in_scope',
|
|
236
|
+
),
|
|
170
237
|
);
|
|
171
238
|
}
|
|
172
239
|
|
|
@@ -181,10 +248,14 @@ export function analyzeFeasibility(plan: Plan): PlanGap[] {
|
|
|
181
248
|
|
|
182
249
|
if (containsAny(scopeAndTasks, OVERLY_BROAD_PATTERNS)) {
|
|
183
250
|
gaps.push(
|
|
184
|
-
gap(
|
|
251
|
+
gap(
|
|
252
|
+
'major',
|
|
253
|
+
'feasibility',
|
|
185
254
|
'Scope contains overly broad indicators — risk of unrealistic delivery.',
|
|
186
255
|
'Narrow the scope to a well-defined subset. Prefer incremental delivery over big-bang rewrites.',
|
|
187
|
-
'scope',
|
|
256
|
+
'scope',
|
|
257
|
+
'overly_broad_scope',
|
|
258
|
+
),
|
|
188
259
|
);
|
|
189
260
|
}
|
|
190
261
|
|
|
@@ -192,10 +263,14 @@ export function analyzeFeasibility(plan: Plan): PlanGap[] {
|
|
|
192
263
|
const hasDeps = plan.tasks.some((t) => t.dependsOn && t.dependsOn.length > 0);
|
|
193
264
|
if (!hasDeps) {
|
|
194
265
|
gaps.push(
|
|
195
|
-
gap(
|
|
266
|
+
gap(
|
|
267
|
+
'minor',
|
|
268
|
+
'feasibility',
|
|
196
269
|
`${plan.tasks.length} tasks with no dependency mentions — execution order unclear.`,
|
|
197
270
|
'Identify dependencies between tasks or add explicit ordering notes.',
|
|
198
|
-
'tasks',
|
|
271
|
+
'tasks',
|
|
272
|
+
'no_dependency_awareness',
|
|
273
|
+
),
|
|
199
274
|
);
|
|
200
275
|
}
|
|
201
276
|
}
|
|
@@ -214,19 +289,27 @@ export function analyzeRisk(plan: Plan): PlanGap[] {
|
|
|
214
289
|
!containsAny(allText, MITIGATION_KEYWORDS)
|
|
215
290
|
) {
|
|
216
291
|
gaps.push(
|
|
217
|
-
gap(
|
|
292
|
+
gap(
|
|
293
|
+
'major',
|
|
294
|
+
'risk',
|
|
218
295
|
'Plan involves breaking changes but mentions no mitigation strategy.',
|
|
219
296
|
'Add a rollback plan, feature flags, or phased migration approach.',
|
|
220
|
-
undefined,
|
|
297
|
+
undefined,
|
|
298
|
+
'breaking_without_mitigation',
|
|
299
|
+
),
|
|
221
300
|
);
|
|
222
301
|
}
|
|
223
302
|
|
|
224
303
|
if (plan.tasks.length > 0 && !containsAny(allText, VERIFICATION_KEYWORDS)) {
|
|
225
304
|
gaps.push(
|
|
226
|
-
gap(
|
|
305
|
+
gap(
|
|
306
|
+
'minor',
|
|
307
|
+
'risk',
|
|
227
308
|
'No verification or testing mentioned in the plan.',
|
|
228
309
|
'Add at least one task or note about testing/validation.',
|
|
229
|
-
'tasks',
|
|
310
|
+
'tasks',
|
|
311
|
+
'no_verification_mentioned',
|
|
312
|
+
),
|
|
230
313
|
);
|
|
231
314
|
}
|
|
232
315
|
|
|
@@ -129,7 +129,12 @@ describe('github-projection', () => {
|
|
|
129
129
|
],
|
|
130
130
|
tasks: [
|
|
131
131
|
{ id: 'task-1', title: 'Design auth schema', description: 'Create DB tables' },
|
|
132
|
-
{
|
|
132
|
+
{
|
|
133
|
+
id: 'task-2',
|
|
134
|
+
title: 'Implement login',
|
|
135
|
+
description: 'Login endpoint',
|
|
136
|
+
dependsOn: ['task-1'],
|
|
137
|
+
},
|
|
133
138
|
],
|
|
134
139
|
};
|
|
135
140
|
|
|
@@ -129,7 +129,12 @@ export function listMilestones(repo: GitHubRepo): GitHubMilestone[] {
|
|
|
129
129
|
try {
|
|
130
130
|
const output = execFileSync(
|
|
131
131
|
'gh',
|
|
132
|
-
[
|
|
132
|
+
[
|
|
133
|
+
'api',
|
|
134
|
+
`repos/${repo.owner}/${repo.repo}/milestones`,
|
|
135
|
+
'--jq',
|
|
136
|
+
'.[] | {number, title, state}',
|
|
137
|
+
],
|
|
133
138
|
{
|
|
134
139
|
encoding: 'utf-8',
|
|
135
140
|
timeout: 10000,
|
|
@@ -157,11 +162,16 @@ export function listOpenIssues(repo: GitHubRepo, limit: number = 100): GitHubIss
|
|
|
157
162
|
const output = execFileSync(
|
|
158
163
|
'gh',
|
|
159
164
|
[
|
|
160
|
-
'issue',
|
|
161
|
-
'
|
|
162
|
-
'--
|
|
163
|
-
|
|
164
|
-
'--
|
|
165
|
+
'issue',
|
|
166
|
+
'list',
|
|
167
|
+
'--repo',
|
|
168
|
+
`${repo.owner}/${repo.repo}`,
|
|
169
|
+
'--state',
|
|
170
|
+
'open',
|
|
171
|
+
'--limit',
|
|
172
|
+
String(limit),
|
|
173
|
+
'--json',
|
|
174
|
+
'number,title,state,body',
|
|
165
175
|
],
|
|
166
176
|
{
|
|
167
177
|
encoding: 'utf-8',
|
|
@@ -184,11 +194,7 @@ export function listLabels(repo: GitHubRepo): GitHubLabel[] {
|
|
|
184
194
|
try {
|
|
185
195
|
const output = execFileSync(
|
|
186
196
|
'gh',
|
|
187
|
-
[
|
|
188
|
-
'label', 'list',
|
|
189
|
-
'--repo', `${repo.owner}/${repo.repo}`,
|
|
190
|
-
'--json', 'name,color',
|
|
191
|
-
],
|
|
197
|
+
['label', 'list', '--repo', `${repo.owner}/${repo.repo}`, '--json', 'name,color'],
|
|
192
198
|
{
|
|
193
199
|
encoding: 'utf-8',
|
|
194
200
|
timeout: 10000,
|
|
@@ -273,10 +279,18 @@ export function findDuplicateIssue(
|
|
|
273
279
|
taskTitle: string,
|
|
274
280
|
existingIssues: GitHubIssue[],
|
|
275
281
|
): GitHubIssue | null {
|
|
276
|
-
const taskWords = new Set(
|
|
282
|
+
const taskWords = new Set(
|
|
283
|
+
taskTitle
|
|
284
|
+
.toLowerCase()
|
|
285
|
+
.split(/\s+/)
|
|
286
|
+
.filter((w) => w.length > 2),
|
|
287
|
+
);
|
|
277
288
|
|
|
278
289
|
for (const issue of existingIssues) {
|
|
279
|
-
const issueWords = issue.title
|
|
290
|
+
const issueWords = issue.title
|
|
291
|
+
.toLowerCase()
|
|
292
|
+
.split(/\s+/)
|
|
293
|
+
.filter((w) => w.length > 2);
|
|
280
294
|
let overlap = 0;
|
|
281
295
|
for (const w of issueWords) {
|
|
282
296
|
if (taskWords.has(w)) overlap++;
|
|
@@ -367,10 +381,14 @@ export function createGitHubIssue(
|
|
|
367
381
|
): number | null {
|
|
368
382
|
try {
|
|
369
383
|
const args = [
|
|
370
|
-
'issue',
|
|
371
|
-
'
|
|
372
|
-
'--
|
|
373
|
-
|
|
384
|
+
'issue',
|
|
385
|
+
'create',
|
|
386
|
+
'--repo',
|
|
387
|
+
`${repo.owner}/${repo.repo}`,
|
|
388
|
+
'--title',
|
|
389
|
+
title,
|
|
390
|
+
'--body',
|
|
391
|
+
body,
|
|
374
392
|
];
|
|
375
393
|
|
|
376
394
|
if (options?.milestone) {
|
|
@@ -407,10 +425,13 @@ export function updateGitHubIssueBody(
|
|
|
407
425
|
execFileSync(
|
|
408
426
|
'gh',
|
|
409
427
|
[
|
|
410
|
-
'issue',
|
|
411
|
-
'
|
|
428
|
+
'issue',
|
|
429
|
+
'edit',
|
|
430
|
+
'--repo',
|
|
431
|
+
`${repo.owner}/${repo.repo}`,
|
|
412
432
|
String(issueNumber),
|
|
413
|
-
'--body',
|
|
433
|
+
'--body',
|
|
434
|
+
body,
|
|
414
435
|
],
|
|
415
436
|
{
|
|
416
437
|
encoding: 'utf-8',
|
|
@@ -14,7 +14,9 @@ function setup() {
|
|
|
14
14
|
function teardown() {
|
|
15
15
|
try {
|
|
16
16
|
rmSync(TMP, { recursive: true, force: true });
|
|
17
|
-
} catch {
|
|
17
|
+
} catch {
|
|
18
|
+
/* ignore */
|
|
19
|
+
}
|
|
18
20
|
}
|
|
19
21
|
|
|
20
22
|
describe('ImpactAnalyzer', () => {
|
|
@@ -65,10 +67,7 @@ describe('ImpactAnalyzer', () => {
|
|
|
65
67
|
join(TMP, 'src', 'components', 'b.ts'),
|
|
66
68
|
"import { format } from '../utils/format';",
|
|
67
69
|
);
|
|
68
|
-
writeFileSync(
|
|
69
|
-
join(TMP, 'src', 'components', 'c.ts'),
|
|
70
|
-
"const f = require('../utils/format');",
|
|
71
|
-
);
|
|
70
|
+
writeFileSync(join(TMP, 'src', 'components', 'c.ts'), "const f = require('../utils/format');");
|
|
72
71
|
|
|
73
72
|
const report = analyzer.analyzeImpact(['src/utils/format.ts'], TMP);
|
|
74
73
|
|
|
@@ -100,11 +99,9 @@ describe('ImpactAnalyzer', () => {
|
|
|
100
99
|
it('detects scope violations when files are outside plan scope', () => {
|
|
101
100
|
writeFileSync(join(TMP, 'src', 'utils', 'misc.ts'), 'export const x = 1;');
|
|
102
101
|
|
|
103
|
-
const report = analyzer.analyzeImpact(
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
['components'],
|
|
107
|
-
);
|
|
102
|
+
const report = analyzer.analyzeImpact(['src/utils/misc.ts', 'src/components/button.ts'], TMP, [
|
|
103
|
+
'components',
|
|
104
|
+
]);
|
|
108
105
|
|
|
109
106
|
expect(report.scopeViolations).toContain('src/utils/misc.ts');
|
|
110
107
|
expect(report.scopeViolations).not.toContain('src/components/button.ts');
|
|
@@ -134,11 +131,7 @@ describe('ImpactAnalyzer', () => {
|
|
|
134
131
|
it('includes scope violation in recommendations', () => {
|
|
135
132
|
writeFileSync(join(TMP, 'src', 'utils', 'oops.ts'), 'export const x = 1;');
|
|
136
133
|
|
|
137
|
-
const report = analyzer.analyzeImpact(
|
|
138
|
-
['src/utils/oops.ts'],
|
|
139
|
-
TMP,
|
|
140
|
-
['components'],
|
|
141
|
-
);
|
|
134
|
+
const report = analyzer.analyzeImpact(['src/utils/oops.ts'], TMP, ['components']);
|
|
142
135
|
|
|
143
136
|
const recText = report.recommendations.join(' ');
|
|
144
137
|
expect(recText).toContain('Scope violation');
|
|
@@ -148,10 +141,7 @@ describe('ImpactAnalyzer', () => {
|
|
|
148
141
|
|
|
149
142
|
it('separates test files from untested consumers', () => {
|
|
150
143
|
writeFileSync(join(TMP, 'src', 'utils', 'api.ts'), 'export const api = 1;');
|
|
151
|
-
writeFileSync(
|
|
152
|
-
join(TMP, 'src', 'components', 'page.ts'),
|
|
153
|
-
"import { api } from '../utils/api';",
|
|
154
|
-
);
|
|
144
|
+
writeFileSync(join(TMP, 'src', 'components', 'page.ts'), "import { api } from '../utils/api';");
|
|
155
145
|
writeFileSync(
|
|
156
146
|
join(TMP, 'src', '__tests__', 'api.test.ts'),
|
|
157
147
|
"import { api } from '../utils/api';",
|
|
@@ -168,10 +158,7 @@ describe('ImpactAnalyzer', () => {
|
|
|
168
158
|
// ─── Graceful when project path doesn't exist ──────────────
|
|
169
159
|
|
|
170
160
|
it('returns low risk when project path does not exist', () => {
|
|
171
|
-
const report = analyzer.analyzeImpact(
|
|
172
|
-
['src/foo.ts'],
|
|
173
|
-
'/nonexistent/path/12345',
|
|
174
|
-
);
|
|
161
|
+
const report = analyzer.analyzeImpact(['src/foo.ts'], '/nonexistent/path/12345');
|
|
175
162
|
|
|
176
163
|
expect(report.riskLevel).toBe('low');
|
|
177
164
|
expect(report.affectedConsumers).toHaveLength(0);
|