@soleri/core 2.4.0 → 2.6.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/intelligence.d.ts +1 -0
- package/dist/brain/intelligence.d.ts.map +1 -1
- package/dist/brain/intelligence.js +164 -148
- package/dist/brain/intelligence.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/control/identity-manager.d.ts +3 -1
- package/dist/control/identity-manager.d.ts.map +1 -1
- package/dist/control/identity-manager.js +49 -51
- package/dist/control/identity-manager.js.map +1 -1
- package/dist/control/intent-router.d.ts +1 -0
- package/dist/control/intent-router.d.ts.map +1 -1
- package/dist/control/intent-router.js +32 -32
- package/dist/control/intent-router.js.map +1 -1
- package/dist/curator/curator.d.ts +9 -1
- package/dist/curator/curator.d.ts.map +1 -1
- package/dist/curator/curator.js +104 -92
- 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/governance/governance.d.ts +1 -0
- package/dist/governance/governance.d.ts.map +1 -1
- package/dist/governance/governance.js +51 -68
- package/dist/governance/governance.js.map +1 -1
- package/dist/index.d.ts +26 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +22 -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 +4 -0
- package/dist/persistence/index.d.ts.map +1 -0
- package/dist/persistence/index.js +3 -0
- package/dist/persistence/index.js.map +1 -0
- package/dist/persistence/postgres-provider.d.ts +46 -0
- package/dist/persistence/postgres-provider.d.ts.map +1 -0
- package/dist/persistence/postgres-provider.js +115 -0
- package/dist/persistence/postgres-provider.js.map +1 -0
- package/dist/persistence/sqlite-provider.d.ts +28 -0
- package/dist/persistence/sqlite-provider.d.ts.map +1 -0
- package/dist/persistence/sqlite-provider.js +97 -0
- package/dist/persistence/sqlite-provider.js.map +1 -0
- package/dist/persistence/types.d.ts +58 -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 +4 -4
- package/dist/project/project-registry.d.ts.map +1 -1
- package/dist/project/project-registry.js +30 -57
- 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 +348 -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 +28 -9
- 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 +4 -2
- package/dist/runtime/vault-extra-ops.d.ts.map +1 -1
- package/dist/runtime/vault-extra-ops.js +383 -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 +52 -32
- package/dist/vault/vault.d.ts.map +1 -1
- package/dist/vault/vault.js +300 -181
- package/dist/vault/vault.js.map +1 -1
- package/package.json +9 -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 +36 -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 +291 -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__/postgres-provider.test.ts +58 -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/__tests__/vault.test.ts +184 -0
- package/src/brain/brain.ts +71 -9
- package/src/brain/intelligence.ts +258 -307
- package/src/brain/types.ts +2 -2
- package/src/cognee/client.ts +18 -0
- package/src/cognee/sync-manager.ts +389 -0
- package/src/control/identity-manager.ts +77 -75
- package/src/control/intent-router.ts +55 -57
- package/src/curator/curator.ts +199 -139
- 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/governance/governance.ts +90 -107
- package/src/index.ts +116 -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 +9 -0
- package/src/persistence/postgres-provider.ts +157 -0
- package/src/persistence/sqlite-provider.ts +115 -0
- package/src/persistence/types.ts +74 -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 +81 -74
- 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 +391 -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 +36 -10
- package/src/runtime/types.ts +8 -0
- package/src/runtime/vault-extra-ops.ts +425 -4
- package/src/vault/playbook.ts +87 -0
- package/src/vault/vault.ts +419 -235
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SQLite persistence provider backed by better-sqlite3.
|
|
3
|
+
*
|
|
4
|
+
* Supports both positional (array) and named (object) parameters.
|
|
5
|
+
* Exposes getDatabase() for backward-compat consumers that need the raw db.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import Database from 'better-sqlite3';
|
|
9
|
+
import { mkdirSync } from 'node:fs';
|
|
10
|
+
import { dirname } from 'node:path';
|
|
11
|
+
import type {
|
|
12
|
+
PersistenceProvider,
|
|
13
|
+
PersistenceParams,
|
|
14
|
+
RunResult,
|
|
15
|
+
FtsSearchOptions,
|
|
16
|
+
} from './types.js';
|
|
17
|
+
|
|
18
|
+
export class SQLitePersistenceProvider implements PersistenceProvider {
|
|
19
|
+
readonly backend = 'sqlite' as const;
|
|
20
|
+
private db: Database.Database;
|
|
21
|
+
|
|
22
|
+
constructor(path: string = ':memory:') {
|
|
23
|
+
if (path !== ':memory:') mkdirSync(dirname(path), { recursive: true });
|
|
24
|
+
this.db = new Database(path);
|
|
25
|
+
if (path !== ':memory:') {
|
|
26
|
+
this.db.pragma('cache_size = -64000'); // 64MB
|
|
27
|
+
this.db.pragma('temp_store = MEMORY');
|
|
28
|
+
this.db.pragma('mmap_size = 268435456'); // 256MB
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
execSql(sql: string): void {
|
|
33
|
+
this.db.exec(sql);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
run(sql: string, params?: PersistenceParams): RunResult {
|
|
37
|
+
const stmt = this.db.prepare(sql);
|
|
38
|
+
if (!params) return stmt.run();
|
|
39
|
+
if (Array.isArray(params)) return stmt.run(...params);
|
|
40
|
+
return stmt.run(params);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
get<T = Record<string, unknown>>(sql: string, params?: PersistenceParams): T | undefined {
|
|
44
|
+
const stmt = this.db.prepare(sql);
|
|
45
|
+
if (!params) return stmt.get() as T | undefined;
|
|
46
|
+
if (Array.isArray(params)) return stmt.get(...params) as T | undefined;
|
|
47
|
+
return stmt.get(params) as T | undefined;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
all<T = Record<string, unknown>>(sql: string, params?: PersistenceParams): T[] {
|
|
51
|
+
const stmt = this.db.prepare(sql);
|
|
52
|
+
if (!params) return stmt.all() as T[];
|
|
53
|
+
if (Array.isArray(params)) return stmt.all(...params) as T[];
|
|
54
|
+
return stmt.all(params) as T[];
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
transaction<T>(fn: () => T): T {
|
|
58
|
+
return this.db.transaction(fn)();
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
ftsSearch<T = Record<string, unknown>>(
|
|
62
|
+
table: string,
|
|
63
|
+
query: string,
|
|
64
|
+
options?: FtsSearchOptions,
|
|
65
|
+
): T[] {
|
|
66
|
+
const ftsTable = `${table}_fts`;
|
|
67
|
+
const cols = options?.columns?.length ? options.columns.join(', ') : `${table}.*`;
|
|
68
|
+
const orderBy = options?.orderByRank !== false ? `ORDER BY rank` : '';
|
|
69
|
+
const limit = options?.limit ? `LIMIT ${options.limit}` : '';
|
|
70
|
+
const offset = options?.offset ? `OFFSET ${options.offset}` : '';
|
|
71
|
+
|
|
72
|
+
const filterClauses: string[] = [];
|
|
73
|
+
const filterParams: unknown[] = [query];
|
|
74
|
+
|
|
75
|
+
if (options?.filters) {
|
|
76
|
+
for (const [key, value] of Object.entries(options.filters)) {
|
|
77
|
+
filterClauses.push(`${table}.${key} = ?`);
|
|
78
|
+
filterParams.push(value);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const filterSql = filterClauses.length ? `AND ${filterClauses.join(' AND ')}` : '';
|
|
83
|
+
|
|
84
|
+
const sql = `SELECT ${cols} FROM ${ftsTable} JOIN ${table} ON ${table}.rowid = ${ftsTable}.rowid WHERE ${ftsTable} MATCH ? ${filterSql} ${orderBy} ${limit} ${offset}`;
|
|
85
|
+
|
|
86
|
+
return this.all<T>(sql, filterParams);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
ftsRebuild(table: string): void {
|
|
90
|
+
const ftsTable = `${table}_fts`;
|
|
91
|
+
try {
|
|
92
|
+
this.execSql(`INSERT INTO ${ftsTable}(${ftsTable}) VALUES('rebuild')`);
|
|
93
|
+
} catch {
|
|
94
|
+
// Graceful degradation: FTS table may not exist
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
close(): void {
|
|
99
|
+
this.db.close();
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Escape hatch: get the raw better-sqlite3 Database.
|
|
104
|
+
* Used by modules that need direct db access (ProjectRegistry, BrainIntelligence, etc.).
|
|
105
|
+
* @deprecated Use provider methods instead.
|
|
106
|
+
*/
|
|
107
|
+
getDatabase(): Database.Database {
|
|
108
|
+
if (process.env.NODE_ENV !== 'test' && process.env.VITEST !== 'true') {
|
|
109
|
+
console.warn(
|
|
110
|
+
'SQLitePersistenceProvider.getDatabase() is deprecated. Use provider methods instead.',
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
return this.db;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Abstract persistence layer.
|
|
3
|
+
*
|
|
4
|
+
* Decouples Vault (and future modules) from any specific database engine.
|
|
5
|
+
* The default implementation is SQLitePersistenceProvider (better-sqlite3).
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
export type PersistenceParams = unknown[] | Record<string, unknown>;
|
|
9
|
+
|
|
10
|
+
export interface RunResult {
|
|
11
|
+
changes: number;
|
|
12
|
+
lastInsertRowid: number | bigint;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Minimal database provider interface.
|
|
17
|
+
*
|
|
18
|
+
* Supports both positional (`?`) and named (`@param`) parameter styles.
|
|
19
|
+
* Implementations must handle both array and object params.
|
|
20
|
+
*/
|
|
21
|
+
export interface PersistenceProvider {
|
|
22
|
+
/** Run raw SQL (DDL, multi-statement). No return value. */
|
|
23
|
+
execSql(sql: string): void;
|
|
24
|
+
|
|
25
|
+
/** Run a parameterized statement (INSERT, UPDATE, DELETE). */
|
|
26
|
+
run(sql: string, params?: PersistenceParams): RunResult;
|
|
27
|
+
|
|
28
|
+
/** Get a single row. Returns undefined if no match. */
|
|
29
|
+
get<T = Record<string, unknown>>(sql: string, params?: PersistenceParams): T | undefined;
|
|
30
|
+
|
|
31
|
+
/** Get all matching rows. */
|
|
32
|
+
all<T = Record<string, unknown>>(sql: string, params?: PersistenceParams): T[];
|
|
33
|
+
|
|
34
|
+
/** Run a function inside a transaction. Commits on success, rolls back on error. */
|
|
35
|
+
transaction<T>(fn: () => T): T;
|
|
36
|
+
|
|
37
|
+
/** Identifies the backend engine. */
|
|
38
|
+
readonly backend: 'sqlite' | 'postgres';
|
|
39
|
+
|
|
40
|
+
/** Full-text search abstraction. */
|
|
41
|
+
ftsSearch<T = Record<string, unknown>>(
|
|
42
|
+
table: string,
|
|
43
|
+
query: string,
|
|
44
|
+
options?: FtsSearchOptions,
|
|
45
|
+
): T[];
|
|
46
|
+
|
|
47
|
+
/** Rebuild FTS index for a table. */
|
|
48
|
+
ftsRebuild(table: string): void;
|
|
49
|
+
|
|
50
|
+
/** Close the connection. */
|
|
51
|
+
close(): void;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export interface PersistenceConfig {
|
|
55
|
+
type: 'sqlite' | 'postgres';
|
|
56
|
+
path: string;
|
|
57
|
+
/** PostgreSQL connection string. */
|
|
58
|
+
connectionString?: string;
|
|
59
|
+
/** PostgreSQL pool size. */
|
|
60
|
+
poolSize?: number;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export interface FtsSearchOptions {
|
|
64
|
+
/** Columns to search (default: all FTS columns). */
|
|
65
|
+
columns?: string[];
|
|
66
|
+
/** Max results. */
|
|
67
|
+
limit?: number;
|
|
68
|
+
/** Skip N results. */
|
|
69
|
+
offset?: number;
|
|
70
|
+
/** Additional WHERE conditions on the base table. */
|
|
71
|
+
filters?: Record<string, unknown>;
|
|
72
|
+
/** Order by FTS relevance rank (default: true). */
|
|
73
|
+
orderByRank?: boolean;
|
|
74
|
+
}
|
|
@@ -1,17 +1,23 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
* Ported from Salvador MCP's plan-gap-content.ts / plan-gap-technical.ts
|
|
2
|
+
* Gap analysis engine for plan grading.
|
|
3
|
+
* Ported from Salvador MCP's plan-gap-content.ts / plan-gap-technical.ts /
|
|
4
|
+
* plan-gap-domain.ts / plan-gap-antipattern.ts.
|
|
4
5
|
*
|
|
5
|
-
*
|
|
6
|
+
* 6 built-in passes (always run):
|
|
6
7
|
* 1. Structure — required fields present and sufficiently long
|
|
7
8
|
* 2. Completeness — measurable objectives, decision rationale, scope exclusions
|
|
8
9
|
* 3. Feasibility — overly broad scope, missing dependency awareness
|
|
9
10
|
* 4. Risk — breaking changes without mitigation, missing verification
|
|
10
11
|
* 5. Clarity — ambiguous language, vague criteria
|
|
11
12
|
* 6. Semantic Quality — generic objectives, shallow rationale, non-concrete approach
|
|
13
|
+
*
|
|
14
|
+
* Opt-in pass factories (registered via customPasses):
|
|
15
|
+
* - createToolFeasibilityPass — validates tool_chain entries and ordering
|
|
16
|
+
* - createFlowAlignmentPass — validates flow and target_mode against registries
|
|
17
|
+
* - createAntiPatternPass — detects content anti-patterns and vague criteria
|
|
12
18
|
*/
|
|
13
19
|
|
|
14
|
-
import type { Plan } from './planner.js';
|
|
20
|
+
import type { Plan, PlanDecision } from './planner.js';
|
|
15
21
|
import type { PlanGap, GapSeverity, GapCategory } from './gap-types.js';
|
|
16
22
|
import {
|
|
17
23
|
generateGapId,
|
|
@@ -46,6 +52,16 @@ function taskText(plan: Plan): string {
|
|
|
46
52
|
return plan.tasks.map((t) => `${t.title} ${t.description}`).join(' ');
|
|
47
53
|
}
|
|
48
54
|
|
|
55
|
+
/** Extract text from a decision (supports both string and structured format). */
|
|
56
|
+
function decisionText(d: string | PlanDecision): string {
|
|
57
|
+
return typeof d === 'string' ? d : `${d.decision} ${d.rationale}`;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/** Combine all decisions into a single text blob. */
|
|
61
|
+
function decisionsText(plan: Plan): string {
|
|
62
|
+
return plan.decisions.map(decisionText).join(' ');
|
|
63
|
+
}
|
|
64
|
+
|
|
49
65
|
/** Check if text contains any of the given patterns (case-insensitive). */
|
|
50
66
|
function containsAny(text: string, patterns: string[]): boolean {
|
|
51
67
|
const lower = text.toLowerCase();
|
|
@@ -118,7 +134,16 @@ const METRIC_PATTERNS = [
|
|
|
118
134
|
/benchmark/i,
|
|
119
135
|
];
|
|
120
136
|
|
|
121
|
-
const EXCLUSION_KEYWORDS = [
|
|
137
|
+
const EXCLUSION_KEYWORDS = [
|
|
138
|
+
'not',
|
|
139
|
+
'exclude',
|
|
140
|
+
'outside',
|
|
141
|
+
'beyond',
|
|
142
|
+
'limit',
|
|
143
|
+
'except',
|
|
144
|
+
"won't",
|
|
145
|
+
'will not',
|
|
146
|
+
];
|
|
122
147
|
|
|
123
148
|
function analyzeCompleteness(plan: Plan): PlanGap[] {
|
|
124
149
|
const gaps: PlanGap[] = [];
|
|
@@ -141,12 +166,13 @@ function analyzeCompleteness(plan: Plan): PlanGap[] {
|
|
|
141
166
|
if (plan.decisions.length > 0) {
|
|
142
167
|
for (let i = 0; i < plan.decisions.length; i++) {
|
|
143
168
|
const d = plan.decisions[i];
|
|
144
|
-
|
|
169
|
+
const text = decisionText(d);
|
|
170
|
+
if (text.trim().length < MIN_DECISION_LENGTH) {
|
|
145
171
|
gaps.push(
|
|
146
172
|
gap(
|
|
147
173
|
'major',
|
|
148
174
|
'completeness',
|
|
149
|
-
`Decision ${i + 1} is too short (${
|
|
175
|
+
`Decision ${i + 1} is too short (${text.trim().length} chars) — lacks rationale.`,
|
|
150
176
|
'Expand each decision to include the reasoning (why this choice over alternatives).',
|
|
151
177
|
`decisions[${i}]`,
|
|
152
178
|
'short_decision',
|
|
@@ -185,7 +211,14 @@ const OVERLY_BROAD_PATTERNS = [
|
|
|
185
211
|
'rewrite everything',
|
|
186
212
|
];
|
|
187
213
|
|
|
188
|
-
const DEPENDENCY_KEYWORDS = [
|
|
214
|
+
const DEPENDENCY_KEYWORDS = [
|
|
215
|
+
'depends',
|
|
216
|
+
'dependency',
|
|
217
|
+
'prerequisite',
|
|
218
|
+
'requires',
|
|
219
|
+
'blocked',
|
|
220
|
+
'before',
|
|
221
|
+
];
|
|
189
222
|
|
|
190
223
|
function analyzeFeasibility(plan: Plan): PlanGap[] {
|
|
191
224
|
const gaps: PlanGap[] = [];
|
|
@@ -251,14 +284,26 @@ const MITIGATION_KEYWORDS = [
|
|
|
251
284
|
'blue-green',
|
|
252
285
|
];
|
|
253
286
|
|
|
254
|
-
const VERIFICATION_KEYWORDS = [
|
|
287
|
+
const VERIFICATION_KEYWORDS = [
|
|
288
|
+
'test',
|
|
289
|
+
'verify',
|
|
290
|
+
'validate',
|
|
291
|
+
'check',
|
|
292
|
+
'assert',
|
|
293
|
+
'confirm',
|
|
294
|
+
'spec',
|
|
295
|
+
'coverage',
|
|
296
|
+
];
|
|
255
297
|
|
|
256
298
|
function analyzeRisk(plan: Plan): PlanGap[] {
|
|
257
299
|
const gaps: PlanGap[] = [];
|
|
258
|
-
const allText = `${plan.objective} ${plan.scope} ${taskText(plan)} ${plan
|
|
300
|
+
const allText = `${plan.objective} ${plan.scope} ${taskText(plan)} ${decisionsText(plan)}`;
|
|
259
301
|
|
|
260
302
|
// Breaking changes without mitigation
|
|
261
|
-
if (
|
|
303
|
+
if (
|
|
304
|
+
containsAny(allText, BREAKING_CHANGE_KEYWORDS) &&
|
|
305
|
+
!containsAny(allText, MITIGATION_KEYWORDS)
|
|
306
|
+
) {
|
|
262
307
|
gaps.push(
|
|
263
308
|
gap(
|
|
264
309
|
'major',
|
|
@@ -310,7 +355,7 @@ const AMBIGUOUS_WORDS = [
|
|
|
310
355
|
|
|
311
356
|
function analyzeClarity(plan: Plan): PlanGap[] {
|
|
312
357
|
const gaps: PlanGap[] = [];
|
|
313
|
-
const allText = `${plan.objective} ${plan.scope} ${plan
|
|
358
|
+
const allText = `${plan.objective} ${plan.scope} ${decisionsText(plan)}`;
|
|
314
359
|
const lower = allText.toLowerCase();
|
|
315
360
|
|
|
316
361
|
// Ambiguous language
|
|
@@ -333,9 +378,7 @@ function analyzeClarity(plan: Plan): PlanGap[] {
|
|
|
333
378
|
}
|
|
334
379
|
|
|
335
380
|
// Tasks with very short or missing descriptions
|
|
336
|
-
const shortTasks = plan.tasks.filter(
|
|
337
|
-
(t) => !t.description || t.description.trim().length < 10,
|
|
338
|
-
);
|
|
381
|
+
const shortTasks = plan.tasks.filter((t) => !t.description || t.description.trim().length < 10);
|
|
339
382
|
if (shortTasks.length > 0) {
|
|
340
383
|
gaps.push(
|
|
341
384
|
gap(
|
|
@@ -360,7 +403,15 @@ const GENERIC_OBJECTIVE_PATTERNS = [
|
|
|
360
403
|
/^update\s+\w+$/i,
|
|
361
404
|
];
|
|
362
405
|
|
|
363
|
-
const RATIONALE_INDICATORS = [
|
|
406
|
+
const RATIONALE_INDICATORS = [
|
|
407
|
+
'because',
|
|
408
|
+
'since',
|
|
409
|
+
'due to',
|
|
410
|
+
'in order to',
|
|
411
|
+
'so that',
|
|
412
|
+
'given that',
|
|
413
|
+
'as a result',
|
|
414
|
+
];
|
|
364
415
|
const SHALLOW_INDICATORS = ['better', 'good', 'best', 'nice', 'great', 'improved'];
|
|
365
416
|
|
|
366
417
|
function analyzeSemanticQuality(plan: Plan): PlanGap[] {
|
|
@@ -412,7 +463,7 @@ function analyzeSemanticQuality(plan: Plan): PlanGap[] {
|
|
|
412
463
|
|
|
413
464
|
// Decisions with shallow rationale (uses "better/good" without "because/since")
|
|
414
465
|
for (let i = 0; i < plan.decisions.length; i++) {
|
|
415
|
-
const d = plan.decisions[i];
|
|
466
|
+
const d = decisionText(plan.decisions[i]);
|
|
416
467
|
const hasShallow = containsAny(d, SHALLOW_INDICATORS);
|
|
417
468
|
const hasRationale = containsAny(d, RATIONALE_INDICATORS);
|
|
418
469
|
if (hasShallow && !hasRationale) {
|
|
@@ -476,6 +527,224 @@ export interface GapAnalysisOptions {
|
|
|
476
527
|
customPasses?: GapAnalysisPass[];
|
|
477
528
|
}
|
|
478
529
|
|
|
530
|
+
// ─── Opt-In Pass Factories ──────────────────────────────────────
|
|
531
|
+
// Ported from Salvador's plan-gap-technical.ts, plan-gap-domain.ts,
|
|
532
|
+
// and plan-gap-antipattern.ts. These are parameterized factories that
|
|
533
|
+
// agents register via customPasses.
|
|
534
|
+
|
|
535
|
+
/**
|
|
536
|
+
* Factory: tool chain feasibility pass.
|
|
537
|
+
* Validates that tool_chain entries are known and ordering rules are respected.
|
|
538
|
+
* Ported from Salvador's analyzeToolFeasibility.
|
|
539
|
+
*
|
|
540
|
+
* @param validTools - Set of valid tool names for this agent
|
|
541
|
+
* @param orderingRules - Ordering constraints (e.g., search before create)
|
|
542
|
+
*/
|
|
543
|
+
export function createToolFeasibilityPass(
|
|
544
|
+
validTools: Set<string>,
|
|
545
|
+
orderingRules?: Array<{ before: string; after: string; reason: string }>,
|
|
546
|
+
): GapAnalysisPass {
|
|
547
|
+
return (plan: Plan) => {
|
|
548
|
+
const gaps: PlanGap[] = [];
|
|
549
|
+
const toolChain = plan.tool_chain;
|
|
550
|
+
if (!toolChain || toolChain.length === 0) return gaps;
|
|
551
|
+
|
|
552
|
+
// Validate tool names
|
|
553
|
+
const invalidTools = toolChain.filter((t) => !validTools.has(t));
|
|
554
|
+
if (invalidTools.length > 0) {
|
|
555
|
+
gaps.push(
|
|
556
|
+
gap(
|
|
557
|
+
'critical',
|
|
558
|
+
'tool-feasibility',
|
|
559
|
+
`Invalid tool names in tool_chain: ${invalidTools.join(', ')}`,
|
|
560
|
+
'Use valid tool names. Check available tools for this agent.',
|
|
561
|
+
'tool_chain',
|
|
562
|
+
`invalid_tools:${invalidTools.join(',')}`,
|
|
563
|
+
),
|
|
564
|
+
);
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
// Validate ordering rules
|
|
568
|
+
if (orderingRules) {
|
|
569
|
+
for (const rule of orderingRules) {
|
|
570
|
+
const beforeIndex = toolChain.indexOf(rule.before);
|
|
571
|
+
const afterIndex = toolChain.indexOf(rule.after);
|
|
572
|
+
if (beforeIndex !== -1 && afterIndex !== -1 && beforeIndex > afterIndex) {
|
|
573
|
+
gaps.push(
|
|
574
|
+
gap(
|
|
575
|
+
'major',
|
|
576
|
+
'tool-feasibility',
|
|
577
|
+
`Tool ordering violation: ${rule.before} must come before ${rule.after}`,
|
|
578
|
+
rule.reason,
|
|
579
|
+
'tool_chain',
|
|
580
|
+
`ordering:${rule.before}>${rule.after}`,
|
|
581
|
+
),
|
|
582
|
+
);
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
return gaps;
|
|
588
|
+
};
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
/**
|
|
592
|
+
* Factory: flow and mode alignment pass.
|
|
593
|
+
* Validates flow and target_mode against known values and intent alignment.
|
|
594
|
+
* Ported from Salvador's analyzeFlowAlignment.
|
|
595
|
+
*
|
|
596
|
+
* @param validFlows - Set of valid flow names for this agent
|
|
597
|
+
* @param validModes - Set of valid operational modes for this agent
|
|
598
|
+
* @param intentFlowMap - Maps detected intents to expected flows
|
|
599
|
+
*/
|
|
600
|
+
export function createFlowAlignmentPass(
|
|
601
|
+
validFlows: Set<string>,
|
|
602
|
+
validModes: Set<string>,
|
|
603
|
+
intentFlowMap?: Record<string, string[]>,
|
|
604
|
+
): GapAnalysisPass {
|
|
605
|
+
return (plan: Plan) => {
|
|
606
|
+
const gaps: PlanGap[] = [];
|
|
607
|
+
|
|
608
|
+
if (plan.flow && !validFlows.has(plan.flow)) {
|
|
609
|
+
gaps.push(
|
|
610
|
+
gap(
|
|
611
|
+
'major',
|
|
612
|
+
'flow-alignment',
|
|
613
|
+
`Invalid flow: ${plan.flow}`,
|
|
614
|
+
`Valid flows are: ${Array.from(validFlows).join(', ')}`,
|
|
615
|
+
'flow',
|
|
616
|
+
`invalid_flow:${plan.flow}`,
|
|
617
|
+
),
|
|
618
|
+
);
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
if (plan.target_mode && !validModes.has(plan.target_mode)) {
|
|
622
|
+
gaps.push(
|
|
623
|
+
gap(
|
|
624
|
+
'major',
|
|
625
|
+
'flow-alignment',
|
|
626
|
+
`Invalid target_mode: ${plan.target_mode}`,
|
|
627
|
+
`Valid modes are: ${Array.from(validModes).join(', ')}`,
|
|
628
|
+
'target_mode',
|
|
629
|
+
`invalid_mode:${plan.target_mode}`,
|
|
630
|
+
),
|
|
631
|
+
);
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
// Intent-flow alignment (optional)
|
|
635
|
+
if (intentFlowMap && plan.flow) {
|
|
636
|
+
const objectiveLower = (plan.objective || '').toLowerCase();
|
|
637
|
+
let detectedIntent: string | null = null;
|
|
638
|
+
|
|
639
|
+
if (/\b(create|build|implement|add|new)\b/.test(objectiveLower)) {
|
|
640
|
+
detectedIntent = 'CREATE';
|
|
641
|
+
} else if (/\b(fix|debug|repair|resolve|bug)\b/.test(objectiveLower)) {
|
|
642
|
+
detectedIntent = 'FIX';
|
|
643
|
+
} else if (/\b(review|audit|check|validate|inspect)\b/.test(objectiveLower)) {
|
|
644
|
+
detectedIntent = 'REVIEW';
|
|
645
|
+
} else if (/\b(plan|design|architect|structure)\b/.test(objectiveLower)) {
|
|
646
|
+
detectedIntent = 'PLAN';
|
|
647
|
+
} else if (/\b(enhance|improve|refactor|optimize)\b/.test(objectiveLower)) {
|
|
648
|
+
detectedIntent = 'ENHANCE';
|
|
649
|
+
} else if (/\b(deliver|package|publish|deploy|release)\b/.test(objectiveLower)) {
|
|
650
|
+
detectedIntent = 'DELIVER';
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
if (detectedIntent) {
|
|
654
|
+
const expectedFlows = intentFlowMap[detectedIntent] || [];
|
|
655
|
+
if (expectedFlows.length > 0 && !expectedFlows.includes(plan.flow)) {
|
|
656
|
+
gaps.push(
|
|
657
|
+
gap(
|
|
658
|
+
'minor',
|
|
659
|
+
'flow-alignment',
|
|
660
|
+
`Flow '${plan.flow}' may not align with detected intent '${detectedIntent}'`,
|
|
661
|
+
`Consider using flow: ${expectedFlows.join(' or ')}`,
|
|
662
|
+
'flow',
|
|
663
|
+
`intent_flow_mismatch:${detectedIntent}->${plan.flow}`,
|
|
664
|
+
),
|
|
665
|
+
);
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
return gaps;
|
|
671
|
+
};
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
/**
|
|
675
|
+
* Factory: content anti-pattern pass.
|
|
676
|
+
* Detects common anti-patterns in plan content.
|
|
677
|
+
* Ported from Salvador's analyzeContentAntiPatterns.
|
|
678
|
+
*
|
|
679
|
+
* @param antiPatterns - Regex patterns to check against approach text
|
|
680
|
+
* @param mitigationPatterns - Patterns that indicate the plan is already mitigating
|
|
681
|
+
*/
|
|
682
|
+
export function createAntiPatternPass(
|
|
683
|
+
antiPatterns?: Array<{
|
|
684
|
+
pattern: RegExp;
|
|
685
|
+
severity: GapSeverity;
|
|
686
|
+
description: string;
|
|
687
|
+
recommendation: string;
|
|
688
|
+
}>,
|
|
689
|
+
mitigationPatterns?: RegExp[],
|
|
690
|
+
): GapAnalysisPass {
|
|
691
|
+
const VAGUE_CRITERIA_PATTERNS: RegExp[] = [
|
|
692
|
+
/^it (looks?|works?|is) (good|nice|fine|great|ok|correct)/i,
|
|
693
|
+
/^(looks?|works?) (good|nice|fine|great|ok)/i,
|
|
694
|
+
/^it('s| is) (done|complete|finished)/i,
|
|
695
|
+
/\bmy machine\b/i,
|
|
696
|
+
];
|
|
697
|
+
|
|
698
|
+
return (plan: Plan) => {
|
|
699
|
+
const gaps: PlanGap[] = [];
|
|
700
|
+
const approach = (plan.approach || '').toLowerCase();
|
|
701
|
+
const criteria = plan.success_criteria || [];
|
|
702
|
+
const fullText = [approach, ...criteria].join(' ').toLowerCase();
|
|
703
|
+
|
|
704
|
+
// Check if plan is actively mitigating known anti-patterns
|
|
705
|
+
const isMitigating = mitigationPatterns
|
|
706
|
+
? mitigationPatterns.some((p) => p.test(fullText))
|
|
707
|
+
: false;
|
|
708
|
+
|
|
709
|
+
// Custom anti-pattern checks
|
|
710
|
+
if (!isMitigating && antiPatterns) {
|
|
711
|
+
for (const ap of antiPatterns) {
|
|
712
|
+
if (ap.pattern.test(approach)) {
|
|
713
|
+
gaps.push(
|
|
714
|
+
gap(
|
|
715
|
+
ap.severity,
|
|
716
|
+
'anti-pattern',
|
|
717
|
+
ap.description,
|
|
718
|
+
ap.recommendation,
|
|
719
|
+
'approach',
|
|
720
|
+
`anti_pattern:${ap.description}`,
|
|
721
|
+
),
|
|
722
|
+
);
|
|
723
|
+
}
|
|
724
|
+
}
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
// Vague success criteria (generic — always checked)
|
|
728
|
+
for (const criterion of criteria) {
|
|
729
|
+
const isVague = VAGUE_CRITERIA_PATTERNS.some((p) => p.test(criterion));
|
|
730
|
+
if (isVague) {
|
|
731
|
+
gaps.push(
|
|
732
|
+
gap(
|
|
733
|
+
'minor',
|
|
734
|
+
'anti-pattern',
|
|
735
|
+
`Success criterion is not measurable: "${criterion}"`,
|
|
736
|
+
'Rewrite as a verifiable assertion: "Component renders X", "All Y pass Z", "No A in B".',
|
|
737
|
+
'success_criteria',
|
|
738
|
+
`vague_criterion:${criterion}`,
|
|
739
|
+
),
|
|
740
|
+
);
|
|
741
|
+
}
|
|
742
|
+
}
|
|
743
|
+
|
|
744
|
+
return gaps;
|
|
745
|
+
};
|
|
746
|
+
}
|
|
747
|
+
|
|
479
748
|
// ─── Orchestrator ────────────────────────────────────────────────
|
|
480
749
|
|
|
481
750
|
/**
|