[2026-05-14T19:25:38.875Z] RESULT ============================================================ { "success": true, "output": "I have everything I need. Here's the complete analysis.\n\n---\n\n## Test Coverage Analysis\n\n**Current state: 0% — no test files exist.**\n\nThe repository has no application source code — it is a Ruflo agent-orchestration runtime. The only testable artifacts are `data/memory/schema.sql` (309 lines, 15 tables) and `claude-flow.config.json`. All gaps below target those two files.\n\n> **Note:** The prior automated `testgaps` worker analysis (in `.claude-flow/metrics/test-gaps.json`) also contains a factual error: it lists `sessions.status` values as `['active', 'paused', 'completed', 'error']` — but the actual schema uses `'expired'`, not `'error'`. Any tests built from that report would have a false positive.\n\n---\n\n## Coverage Gaps\n\n### Gap 1 — `memory_entries` CHECK constraints and UNIQUE index\n\n`schema.sql:20-44`\n\n| Sub-gap | Risk |\n|---------|------|\n| `type` CHECK not regression-tested | Silent data corruption if constraint is removed |\n| `status` CHECK not tested | `'deleted'` vs `'archived'` semantics could drift |\n| `UNIQUE(namespace, key)` not tested | Duplicate keys silently overwrite on upsert |\n\n```javascript\n// tests/schema/memory-entries.test.js\nconst Database = require('better-sqlite3');\nconst fs = require('fs');\n\nlet db;\nbeforeEach(() => {\n db = new Database(':memory:');\n db.pragma('foreign_keys = ON');\n db.exec(fs.readFileSync('data/memory/schema.sql', 'utf8'));\n});\nafterEach(() => db.close());\n\nconst insert = (ns, key, type, status = 'active') =>\n db.prepare(\n `INSERT INTO memory_entries(id,key,namespace,content,type,status) VALUES('${ns}${key}','${key}','${ns}','v','${type}','${status}')`\n );\n\ntest.each(['semantic','episodic','procedural','working','pattern'])(\n 'memory_entries accepts type: %s', (t) => expect(() => insert('ns', 'k', t).run()).not.toThrow()\n);\ntest('memory_entries rejects invalid type', () =>\n expect(() => insert('ns', 'k', 'INVALID').run()).toThrow()\n);\ntest.each(['active','archived','deleted'])(\n 'memory_entries accepts status: %s', (s) => expect(() => insert('ns', `k${s}`, 'semantic', s).run()).not.toThrow()\n);\ntest('memory_entries rejects invalid status', () =>\n expect(() => insert('ns', 'k', 'semantic', 'gone').run()).toThrow()\n);\ntest('memory_entries enforces UNIQUE(namespace, key)', () => {\n insert('ns', 'dupe', 'semantic').run();\n expect(() => insert('ns', 'dupe', 'episodic').run()).toThrow(/UNIQUE/);\n});\n```\n\n---\n\n### Gap 2 — `patterns.confidence` has no CHECK bounds (silent data corruption)\n\n`schema.sql:77` — `confidence REAL DEFAULT 0.5` with no `CHECK(confidence BETWEEN 0.0 AND 1.0)`.\n\n```javascript\n// tests/schema/patterns.test.js\nconst ins = (type, confidence) =>\n db.prepare(\n `INSERT INTO patterns(id,name,pattern_type,condition,action,confidence)\n VALUES('p1','n','${type}','c','a',${confidence})`\n );\n\n// BUG DOCUMENTATION: this test should throw but currently passes\ntest('patterns.confidence accepts 1.5 (missing CHECK — bug)', () =>\n expect(() => ins('task-routing', 1.5).run()).not.toThrow()\n);\ntest('patterns.confidence accepts -0.1 (missing CHECK — bug)', () =>\n expect(() => ins('task-routing', -0.1).run()).not.toThrow()\n);\n// Fix: ALTER TABLE patterns ADD CHECK(confidence BETWEEN 0.0 AND 1.0)\n\ntest.each(['task-routing','error-recovery','optimization','learning',\n 'coordination','prediction','code-pattern','workflow'])(\n 'patterns accepts pattern_type: %s', (t) =>\n expect(() => ins(t, 0.5).run()).not.toThrow()\n);\ntest('patterns rejects unknown pattern_type', () =>\n expect(() => ins('UNKNOWN', 0.5).run()).toThrow()\n);\ntest.each(['active','archived','deprecated','experimental'])(\n 'patterns accepts status: %s', (s) => {\n db.prepare(\n `INSERT INTO patterns(id,name,pattern_type,condition,action,status)\n VALUES('p${s}','n','task-routing','c','a','${s}')`\n ).run();\n }\n);\n```\n\n---\n\n### Gap 3 — Vector dimension regression guard (bug #1947)\n\n`schema.sql:302-308` documents the exact bug: if `vector_indexes.dimensions` diverges from the ONNX model's 384-dim output, every `memory_store --vector` and `memory_search` call silently breaks.\n\n```javascript\n// tests/schema/vector-indexes.test.js\nconst config = require('../../claude-flow.config.json');\n\ntest('config vector dimensions match schema default 384 (bug #1947 guard)', () => {\n const configDims = config.memory?.vectorDimensions\n ?? config.memory?.hnsw?.dimensions\n ?? config.memory?.embeddings?.dimensions;\n // If config doesn't specify, the default is 384 per schema.sql:306-308\n if (configDims !== undefined) expect(configDims).toBe(384);\n});\n\ntest('default vector_index row has 384 dimensions', () => {\n const row = db.prepare(\"SELECT dimensions FROM vector_indexes WHERE name='default'\").get();\n expect(row?.dimensions).toBe(384);\n});\n\ntest.each(['cosine','euclidean','dot'])(\n 'vector_indexes accepts metric: %s', (m) =>\n db.prepare(\n `INSERT INTO vector_indexes(id,name,dimensions,metric) VALUES('v${m}','idx${m}',384,'${m}')`\n ).run()\n);\ntest('vector_indexes rejects unknown metric', () =>\n expect(() =>\n db.prepare(\"INSERT INTO vector_indexes(id,name,dimensions,metric) VALUES('vx','badmetric',384,'hamming')\").run()\n ).toThrow()\n);\ntest.each(['none','scalar','product'])(\n 'vector_indexes accepts quantization_type: %s', (q) =>\n db.prepare(\n `INSERT INTO vector_indexes(id,name,dimensions,quantization_type) VALUES('vq${q}','qi${q}',384,'${q}')`\n ).run()\n);\ntest('vector_indexes rejects invalid quantization_type', () =>\n expect(() =>\n db.prepare(\"INSERT INTO vector_indexes(id,name,dimensions,quantization_type) VALUES('vqx','qbad',384,'float16')\").run()\n ).toThrow()\n);\n```\n\n---\n\n### Gap 4 — Foreign key enforcement requires explicit PRAGMA per connection\n\n`schema.sql:8` sets `PRAGMA foreign_keys = ON` at schema creation time, but SQLite FKs are **per-connection** — a fresh connection without the pragma silently bypasses all FK constraints.\n\n```javascript\n// tests/schema/foreign-keys.test.js\ntest('FK violations silently pass without PRAGMA (footgun documentation)', () => {\n const fresh = new Database(':memory:');\n // No pragma set\n fresh.exec(fs.readFileSync('data/memory/schema.sql', 'utf8'));\n expect(() =>\n fresh.prepare(\n \"INSERT INTO trajectory_steps(trajectory_id,step_index,action) VALUES('nonexistent',0,'x')\"\n ).run()\n ).not.toThrow(); // passes — FKs disabled without pragma\n fresh.close();\n});\n\ntest('FK violations ARE rejected when PRAGMA foreign_keys = ON', () => {\n expect(() =>\n db.prepare(\n \"INSERT INTO trajectory_steps(trajectory_id,step_index,action) VALUES('nonexistent',0,'x')\"\n ).run()\n ).toThrow(/FOREIGN KEY/);\n});\n\ntest('trajectory_steps cascade-deletes when parent trajectory is deleted', () => {\n db.prepare(\"INSERT INTO trajectories(id) VALUES('t1')\").run();\n db.prepare(\"INSERT INTO trajectory_steps(trajectory_id,step_number,action) VALUES('t1',1,'act')\").run();\n db.prepare(\"DELETE FROM trajectories WHERE id='t1'\").run();\n const rows = db.prepare(\"SELECT * FROM trajectory_steps WHERE trajectory_id='t1'\").all();\n // Documents whether ON DELETE CASCADE is defined (it is NOT in current schema — orphans remain)\n expect(rows.length).toBe(1); // BUG: orphan row exists — schema missing ON DELETE CASCADE\n});\n```\n\n---\n\n### Gap 5 — `sessions.status` CHECK uses `'expired'` not `'error'`\n\n`schema.sql:235` — CHECK is `('active', 'paused', 'completed', 'expired')`. The prior automated analysis incorrectly listed `'error'` as valid.\n\n```javascript\n// tests/schema/sessions.test.js\nconst VALID = ['active', 'paused', 'completed', 'expired'];\nconst INVALID = ['error', 'running', 'done', 'ACTIVE', 'failed'];\n\ntest.each(VALID)('sessions accepts status: %s', (s) =>\n expect(() =>\n db.prepare(`INSERT INTO sessions(id,state,status) VALUES('s${s}','{}','${s}')`).run()\n ).not.toThrow()\n);\ntest.each(INVALID)('sessions rejects status: %s', (s) =>\n expect(() =>\n db.prepare(`INSERT INTO sessions(id,state,status) VALUES('sx','{}','${s}')`).run()\n ).toThrow()\n);\n```\n\n---\n\n### Gap 6 — `migration_state.status` and `pattern_history.change_type` CHECKs untested\n\n`schema.sql:195,129`\n\n```javascript\n// tests/schema/migration-state.test.js\nconst VALID_MIG = ['pending','in_progress','completed','failed','rolled_back'];\ntest.each(VALID_MIG)('migration_state accepts status: %s', (s) =>\n expect(() =>\n db.prepare(\n `INSERT INTO migration_state(id,migration_type,status) VALUES('m${s}','v2-to-v3','${s}')`\n ).run()\n ).not.toThrow()\n);\ntest('migration_state rejects invalid status', () =>\n expect(() =>\n db.prepare(\"INSERT INTO migration_state(id,migration_type,status) VALUES('mx','v2-to-v3','cancelled')\").run()\n ).toThrow()\n);\n\nconst VALID_CHANGE = ['created','updated','success','failure','decay','merged','split'];\ntest.each(VALID_CHANGE)('pattern_history accepts change_type: %s', (c) => {\n db.prepare(\n `INSERT INTO patterns(id,name,pattern_type,condition,action) VALUES('ph${c}','n','task-routing','c','a')`\n ).run();\n expect(() =>\n db.prepare(\n `INSERT INTO pattern_history(pattern_id,version,change_type) VALUES('ph${c}',1,'${c}')`\n ).run()\n ).not.toThrow();\n});\n```\n\n---\n\n### Gap 7 — `trajectories` status/verdict CHECKs and self-referential `patterns.parent_id`\n\n```javascript\n// tests/schema/trajectories.test.js\ntest.each(['active','completed','failed','abandoned'])(\n 'trajectories accepts status: %s', (s) =>\n db.prepare(`INSERT INTO trajectories(id,status) VALUES('t${s}','${s}')`).run()\n);\ntest.each(['success','failure','partial'])(\n 'trajectories accepts verdict: %s', (v) => {\n db.prepare(`INSERT INTO trajectories(id,status,verdict) VALUES('tv${v}','completed','${v}')`).run();\n }\n);\ntest('trajectories accepts NULL verdict', () =>\n db.prepare(\"INSERT INTO trajectories(id,status,verdict) VALUES('tnull','active',NULL)\").run()\n);\n\n// Self-referential FK: patterns.parent_id → patterns.id\ntest('patterns parent_id FK is enforced', () =>\n expect(() =>\n db.prepare(\n \"INSERT INTO patterns(id,name,pattern_type,condition,action,parent_id) VALUES('child','n','task-routing','c','a','nonexistent')\"\n ).run()\n ).toThrow(/FOREIGN KEY/)\n);\n```\n\n---\n\n## To Wire These Up\n\nAdd to `package.json`:\n\n```json\n{\n \"scripts\": {\n \"test\": \"jest --testPathPattern=tests/\"\n },\n \"devDependencies\": {\n \"jest\": \"^29\",\n \"better-sqlite3\": \"^9\"\n }\n}\n```\n\nRun with: `npm install --save-dev jest better-sqlite3 && npm test`\n\n---\n\n## Summary Table\n\n| Gap | File | Risk | Priority |\n|-----|------|------|----------|\n| `patterns.confidence` no bounds CHECK | `schema.sql:77` | Silent data corruption | **Critical** |\n| Vector dimension drift (bug #1947) | `schema.sql:302-308` | All vector search broken on fresh install | **Critical** |\n| FK pragma not enforced per connection | `schema.sql:8` | Orphan rows, broken referential integrity | **High** |\n| `trajectory_steps` missing `ON DELETE CASCADE` | `schema.sql:169` | Orphan steps after trajectory delete | **High** |\n| `sessions.status` CHECK (prior analysis wrong) | `schema.sql:235` | Tests built from old report will be incorrect | **Medium** |\n| All other enum CHECKs untested | multiple tables | Regression if constraints are dropped | **Medium** |\n| `claude-flow.config.json` no schema validation | root config | Bad config silently accepted | **Low** |\n", "parsedOutput": { "sections": [ { "title": "Test Coverage Analysis", "content": "\n**Current state: 0% — no test files exist.**\n\nThe repository has no application source code — it is a Ruflo agent-orchestration runtime. The only testable artifacts are `data/memory/schema.sql` (309 lines, 15 tables) and `claude-flow.config.json`. All gaps below target those two files.\n\n> **Note:** The prior automated `testgaps` worker analysis (in `.claude-flow/metrics/test-gaps.json`) also contains a factual error: it lists `sessions.status` values as `['active', 'paused', 'completed', 'error']` — but the actual schema uses `'expired'`, not `'error'`. Any tests built from that report would have a false positive.\n\n---\n\n", "level": 2 }, { "title": "Coverage Gaps", "content": "\n", "level": 2 }, { "title": "Gap 1 — `memory_entries` CHECK constraints and UNIQUE index", "content": "\n`schema.sql:20-44`\n\n| Sub-gap | Risk |\n|---------|------|\n| `type` CHECK not regression-tested | Silent data corruption if constraint is removed |\n| `status` CHECK not tested | `'deleted'` vs `'archived'` semantics could drift |\n| `UNIQUE(namespace, key)` not tested | Duplicate keys silently overwrite on upsert |\n\n```javascript\n// tests/schema/memory-entries.test.js\nconst Database = require('better-sqlite3');\nconst fs = require('fs');\n\nlet db;\nbeforeEach(() => {\n db = new Database(':memory:');\n db.pragma('foreign_keys = ON');\n db.exec(fs.readFileSync('data/memory/schema.sql', 'utf8'));\n});\nafterEach(() => db.close());\n\nconst insert = (ns, key, type, status = 'active') =>\n db.prepare(\n `INSERT INTO memory_entries(id,key,namespace,content,type,status) VALUES('${ns}${key}','${key}','${ns}','v','${type}','${status}')`\n );\n\ntest.each(['semantic','episodic','procedural','working','pattern'])(\n 'memory_entries accepts type: %s', (t) => expect(() => insert('ns', 'k', t).run()).not.toThrow()\n);\ntest('memory_entries rejects invalid type', () =>\n expect(() => insert('ns', 'k', 'INVALID').run()).toThrow()\n);\ntest.each(['active','archived','deleted'])(\n 'memory_entries accepts status: %s', (s) => expect(() => insert('ns', `k${s}`, 'semantic', s).run()).not.toThrow()\n);\ntest('memory_entries rejects invalid status', () =>\n expect(() => insert('ns', 'k', 'semantic', 'gone').run()).toThrow()\n);\ntest('memory_entries enforces UNIQUE(namespace, key)', () => {\n insert('ns', 'dupe', 'semantic').run();\n expect(() => insert('ns', 'dupe', 'episodic').run()).toThrow(/UNIQUE/);\n});\n```\n\n---\n\n", "level": 3 }, { "title": "Gap 2 — `patterns.confidence` has no CHECK bounds (silent data corruption)", "content": "\n`schema.sql:77` — `confidence REAL DEFAULT 0.5` with no `CHECK(confidence BETWEEN 0.0 AND 1.0)`.\n\n```javascript\n// tests/schema/patterns.test.js\nconst ins = (type, confidence) =>\n db.prepare(\n `INSERT INTO patterns(id,name,pattern_type,condition,action,confidence)\n VALUES('p1','n','${type}','c','a',${confidence})`\n );\n\n// BUG DOCUMENTATION: this test should throw but currently passes\ntest('patterns.confidence accepts 1.5 (missing CHECK — bug)', () =>\n expect(() => ins('task-routing', 1.5).run()).not.toThrow()\n);\ntest('patterns.confidence accepts -0.1 (missing CHECK — bug)', () =>\n expect(() => ins('task-routing', -0.1).run()).not.toThrow()\n);\n// Fix: ALTER TABLE patterns ADD CHECK(confidence BETWEEN 0.0 AND 1.0)\n\ntest.each(['task-routing','error-recovery','optimization','learning',\n 'coordination','prediction','code-pattern','workflow'])(\n 'patterns accepts pattern_type: %s', (t) =>\n expect(() => ins(t, 0.5).run()).not.toThrow()\n);\ntest('patterns rejects unknown pattern_type', () =>\n expect(() => ins('UNKNOWN', 0.5).run()).toThrow()\n);\ntest.each(['active','archived','deprecated','experimental'])(\n 'patterns accepts status: %s', (s) => {\n db.prepare(\n `INSERT INTO patterns(id,name,pattern_type,condition,action,status)\n VALUES('p${s}','n','task-routing','c','a','${s}')`\n ).run();\n }\n);\n```\n\n---\n\n", "level": 3 }, { "title": "Gap 3 — Vector dimension regression guard (bug #1947)", "content": "\n`schema.sql:302-308` documents the exact bug: if `vector_indexes.dimensions` diverges from the ONNX model's 384-dim output, every `memory_store --vector` and `memory_search` call silently breaks.\n\n```javascript\n// tests/schema/vector-indexes.test.js\nconst config = require('../../claude-flow.config.json');\n\ntest('config vector dimensions match schema default 384 (bug #1947 guard)', () => {\n const configDims = config.memory?.vectorDimensions\n ?? config.memory?.hnsw?.dimensions\n ?? config.memory?.embeddings?.dimensions;\n // If config doesn't specify, the default is 384 per schema.sql:306-308\n if (configDims !== undefined) expect(configDims).toBe(384);\n});\n\ntest('default vector_index row has 384 dimensions', () => {\n const row = db.prepare(\"SELECT dimensions FROM vector_indexes WHERE name='default'\").get();\n expect(row?.dimensions).toBe(384);\n});\n\ntest.each(['cosine','euclidean','dot'])(\n 'vector_indexes accepts metric: %s', (m) =>\n db.prepare(\n `INSERT INTO vector_indexes(id,name,dimensions,metric) VALUES('v${m}','idx${m}',384,'${m}')`\n ).run()\n);\ntest('vector_indexes rejects unknown metric', () =>\n expect(() =>\n db.prepare(\"INSERT INTO vector_indexes(id,name,dimensions,metric) VALUES('vx','badmetric',384,'hamming')\").run()\n ).toThrow()\n);\ntest.each(['none','scalar','product'])(\n 'vector_indexes accepts quantization_type: %s', (q) =>\n db.prepare(\n `INSERT INTO vector_indexes(id,name,dimensions,quantization_type) VALUES('vq${q}','qi${q}',384,'${q}')`\n ).run()\n);\ntest('vector_indexes rejects invalid quantization_type', () =>\n expect(() =>\n db.prepare(\"INSERT INTO vector_indexes(id,name,dimensions,quantization_type) VALUES('vqx','qbad',384,'float16')\").run()\n ).toThrow()\n);\n```\n\n---\n\n", "level": 3 }, { "title": "Gap 4 — Foreign key enforcement requires explicit PRAGMA per connection", "content": "\n`schema.sql:8` sets `PRAGMA foreign_keys = ON` at schema creation time, but SQLite FKs are **per-connection** — a fresh connection without the pragma silently bypasses all FK constraints.\n\n```javascript\n// tests/schema/foreign-keys.test.js\ntest('FK violations silently pass without PRAGMA (footgun documentation)', () => {\n const fresh = new Database(':memory:');\n // No pragma set\n fresh.exec(fs.readFileSync('data/memory/schema.sql', 'utf8'));\n expect(() =>\n fresh.prepare(\n \"INSERT INTO trajectory_steps(trajectory_id,step_index,action) VALUES('nonexistent',0,'x')\"\n ).run()\n ).not.toThrow(); // passes — FKs disabled without pragma\n fresh.close();\n});\n\ntest('FK violations ARE rejected when PRAGMA foreign_keys = ON', () => {\n expect(() =>\n db.prepare(\n \"INSERT INTO trajectory_steps(trajectory_id,step_index,action) VALUES('nonexistent',0,'x')\"\n ).run()\n ).toThrow(/FOREIGN KEY/);\n});\n\ntest('trajectory_steps cascade-deletes when parent trajectory is deleted', () => {\n db.prepare(\"INSERT INTO trajectories(id) VALUES('t1')\").run();\n db.prepare(\"INSERT INTO trajectory_steps(trajectory_id,step_number,action) VALUES('t1',1,'act')\").run();\n db.prepare(\"DELETE FROM trajectories WHERE id='t1'\").run();\n const rows = db.prepare(\"SELECT * FROM trajectory_steps WHERE trajectory_id='t1'\").all();\n // Documents whether ON DELETE CASCADE is defined (it is NOT in current schema — orphans remain)\n expect(rows.length).toBe(1); // BUG: orphan row exists — schema missing ON DELETE CASCADE\n});\n```\n\n---\n\n", "level": 3 }, { "title": "Gap 5 — `sessions.status` CHECK uses `'expired'` not `'error'`", "content": "\n`schema.sql:235` — CHECK is `('active', 'paused', 'completed', 'expired')`. The prior automated analysis incorrectly listed `'error'` as valid.\n\n```javascript\n// tests/schema/sessions.test.js\nconst VALID = ['active', 'paused', 'completed', 'expired'];\nconst INVALID = ['error', 'running', 'done', 'ACTIVE', 'failed'];\n\ntest.each(VALID)('sessions accepts status: %s', (s) =>\n expect(() =>\n db.prepare(`INSERT INTO sessions(id,state,status) VALUES('s${s}','{}','${s}')`).run()\n ).not.toThrow()\n);\ntest.each(INVALID)('sessions rejects status: %s', (s) =>\n expect(() =>\n db.prepare(`INSERT INTO sessions(id,state,status) VALUES('sx','{}','${s}')`).run()\n ).toThrow()\n);\n```\n\n---\n\n", "level": 3 }, { "title": "Gap 6 — `migration_state.status` and `pattern_history.change_type` CHECKs untested", "content": "\n`schema.sql:195,129`\n\n```javascript\n// tests/schema/migration-state.test.js\nconst VALID_MIG = ['pending','in_progress','completed','failed','rolled_back'];\ntest.each(VALID_MIG)('migration_state accepts status: %s', (s) =>\n expect(() =>\n db.prepare(\n `INSERT INTO migration_state(id,migration_type,status) VALUES('m${s}','v2-to-v3','${s}')`\n ).run()\n ).not.toThrow()\n);\ntest('migration_state rejects invalid status', () =>\n expect(() =>\n db.prepare(\"INSERT INTO migration_state(id,migration_type,status) VALUES('mx','v2-to-v3','cancelled')\").run()\n ).toThrow()\n);\n\nconst VALID_CHANGE = ['created','updated','success','failure','decay','merged','split'];\ntest.each(VALID_CHANGE)('pattern_history accepts change_type: %s', (c) => {\n db.prepare(\n `INSERT INTO patterns(id,name,pattern_type,condition,action) VALUES('ph${c}','n','task-routing','c','a')`\n ).run();\n expect(() =>\n db.prepare(\n `INSERT INTO pattern_history(pattern_id,version,change_type) VALUES('ph${c}',1,'${c}')`\n ).run()\n ).not.toThrow();\n});\n```\n\n---\n\n", "level": 3 }, { "title": "Gap 7 — `trajectories` status/verdict CHECKs and self-referential `patterns.parent_id`", "content": "\n```javascript\n// tests/schema/trajectories.test.js\ntest.each(['active','completed','failed','abandoned'])(\n 'trajectories accepts status: %s', (s) =>\n db.prepare(`INSERT INTO trajectories(id,status) VALUES('t${s}','${s}')`).run()\n);\ntest.each(['success','failure','partial'])(\n 'trajectories accepts verdict: %s', (v) => {\n db.prepare(`INSERT INTO trajectories(id,status,verdict) VALUES('tv${v}','completed','${v}')`).run();\n }\n);\ntest('trajectories accepts NULL verdict', () =>\n db.prepare(\"INSERT INTO trajectories(id,status,verdict) VALUES('tnull','active',NULL)\").run()\n);\n\n// Self-referential FK: patterns.parent_id → patterns.id\ntest('patterns parent_id FK is enforced', () =>\n expect(() =>\n db.prepare(\n \"INSERT INTO patterns(id,name,pattern_type,condition,action,parent_id) VALUES('child','n','task-routing','c','a','nonexistent')\"\n ).run()\n ).toThrow(/FOREIGN KEY/)\n);\n```\n\n---\n\n", "level": 3 }, { "title": "To Wire These Up", "content": "\nAdd to `package.json`:\n\n```json\n{\n \"scripts\": {\n \"test\": \"jest --testPathPattern=tests/\"\n },\n \"devDependencies\": {\n \"jest\": \"^29\",\n \"better-sqlite3\": \"^9\"\n }\n}\n```\n\nRun with: `npm install --save-dev jest better-sqlite3 && npm test`\n\n---\n\n", "level": 2 }, { "title": "Summary Table", "content": "| Gap | File | Risk | Priority |\n|-----|------|------|----------|\n| `patterns.confidence` no bounds CHECK | `schema.sql:77` | Silent data corruption | **Critical** |\n| Vector dimension drift (bug #1947) | `schema.sql:302-308` | All vector search broken on fresh install | **Critical** |\n| FK pragma not enforced per connection | `schema.sql:8` | Orphan rows, broken referential integrity | **High** |\n| `trajectory_steps` missing `ON DELETE CASCADE` | `schema.sql:169` | Orphan steps after trajectory delete | **High** |\n| `sessions.status` CHECK (prior analysis wrong) | `schema.sql:235` | Tests built from old report will be incorrect | **Medium** |\n| All other enum CHECKs untested | multiple tables | Regression if constraints are dropped | **Medium** |\n| `claude-flow.config.json` no schema validation | root config | Bad config silently accepted | **Low** |", "level": 2 } ], "codeBlocks": [ { "language": "javascript", "code": "// tests/schema/memory-entries.test.js\nconst Database = require('better-sqlite3');\nconst fs = require('fs');\n\nlet db;\nbeforeEach(() => {\n db = new Database(':memory:');\n db.pragma('foreign_keys = ON');\n db.exec(fs.readFileSync('data/memory/schema.sql', 'utf8'));\n});\nafterEach(() => db.close());\n\nconst insert = (ns, key, type, status = 'active') =>\n db.prepare(\n `INSERT INTO memory_entries(id,key,namespace,content,type,status) VALUES('${ns}${key}','${key}','${ns}','v','${type}','${status}')`\n );\n\ntest.each(['semantic','episodic','procedural','working','pattern'])(\n 'memory_entries accepts type: %s', (t) => expect(() => insert('ns', 'k', t).run()).not.toThrow()\n);\ntest('memory_entries rejects invalid type', () =>\n expect(() => insert('ns', 'k', 'INVALID').run()).toThrow()\n);\ntest.each(['active','archived','deleted'])(\n 'memory_entries accepts status: %s', (s) => expect(() => insert('ns', `k${s}`, 'semantic', s).run()).not.toThrow()\n);\ntest('memory_entries rejects invalid status', () =>\n expect(() => insert('ns', 'k', 'semantic', 'gone').run()).toThrow()\n);\ntest('memory_entries enforces UNIQUE(namespace, key)', () => {\n insert('ns', 'dupe', 'semantic').run();\n expect(() => insert('ns', 'dupe', 'episodic').run()).toThrow(/UNIQUE/);\n});" }, { "language": "javascript", "code": "// tests/schema/patterns.test.js\nconst ins = (type, confidence) =>\n db.prepare(\n `INSERT INTO patterns(id,name,pattern_type,condition,action,confidence)\n VALUES('p1','n','${type}','c','a',${confidence})`\n );\n\n// BUG DOCUMENTATION: this test should throw but currently passes\ntest('patterns.confidence accepts 1.5 (missing CHECK — bug)', () =>\n expect(() => ins('task-routing', 1.5).run()).not.toThrow()\n);\ntest('patterns.confidence accepts -0.1 (missing CHECK — bug)', () =>\n expect(() => ins('task-routing', -0.1).run()).not.toThrow()\n);\n// Fix: ALTER TABLE patterns ADD CHECK(confidence BETWEEN 0.0 AND 1.0)\n\ntest.each(['task-routing','error-recovery','optimization','learning',\n 'coordination','prediction','code-pattern','workflow'])(\n 'patterns accepts pattern_type: %s', (t) =>\n expect(() => ins(t, 0.5).run()).not.toThrow()\n);\ntest('patterns rejects unknown pattern_type', () =>\n expect(() => ins('UNKNOWN', 0.5).run()).toThrow()\n);\ntest.each(['active','archived','deprecated','experimental'])(\n 'patterns accepts status: %s', (s) => {\n db.prepare(\n `INSERT INTO patterns(id,name,pattern_type,condition,action,status)\n VALUES('p${s}','n','task-routing','c','a','${s}')`\n ).run();\n }\n);" }, { "language": "javascript", "code": "// tests/schema/vector-indexes.test.js\nconst config = require('../../claude-flow.config.json');\n\ntest('config vector dimensions match schema default 384 (bug #1947 guard)', () => {\n const configDims = config.memory?.vectorDimensions\n ?? config.memory?.hnsw?.dimensions\n ?? config.memory?.embeddings?.dimensions;\n // If config doesn't specify, the default is 384 per schema.sql:306-308\n if (configDims !== undefined) expect(configDims).toBe(384);\n});\n\ntest('default vector_index row has 384 dimensions', () => {\n const row = db.prepare(\"SELECT dimensions FROM vector_indexes WHERE name='default'\").get();\n expect(row?.dimensions).toBe(384);\n});\n\ntest.each(['cosine','euclidean','dot'])(\n 'vector_indexes accepts metric: %s', (m) =>\n db.prepare(\n `INSERT INTO vector_indexes(id,name,dimensions,metric) VALUES('v${m}','idx${m}',384,'${m}')`\n ).run()\n);\ntest('vector_indexes rejects unknown metric', () =>\n expect(() =>\n db.prepare(\"INSERT INTO vector_indexes(id,name,dimensions,metric) VALUES('vx','badmetric',384,'hamming')\").run()\n ).toThrow()\n);\ntest.each(['none','scalar','product'])(\n 'vector_indexes accepts quantization_type: %s', (q) =>\n db.prepare(\n `INSERT INTO vector_indexes(id,name,dimensions,quantization_type) VALUES('vq${q}','qi${q}',384,'${q}')`\n ).run()\n);\ntest('vector_indexes rejects invalid quantization_type', () =>\n expect(() =>\n db.prepare(\"INSERT INTO vector_indexes(id,name,dimensions,quantization_type) VALUES('vqx','qbad',384,'float16')\").run()\n ).toThrow()\n);" }, { "language": "javascript", "code": "// tests/schema/foreign-keys.test.js\ntest('FK violations silently pass without PRAGMA (footgun documentation)', () => {\n const fresh = new Database(':memory:');\n // No pragma set\n fresh.exec(fs.readFileSync('data/memory/schema.sql', 'utf8'));\n expect(() =>\n fresh.prepare(\n \"INSERT INTO trajectory_steps(trajectory_id,step_index,action) VALUES('nonexistent',0,'x')\"\n ).run()\n ).not.toThrow(); // passes — FKs disabled without pragma\n fresh.close();\n});\n\ntest('FK violations ARE rejected when PRAGMA foreign_keys = ON', () => {\n expect(() =>\n db.prepare(\n \"INSERT INTO trajectory_steps(trajectory_id,step_index,action) VALUES('nonexistent',0,'x')\"\n ).run()\n ).toThrow(/FOREIGN KEY/);\n});\n\ntest('trajectory_steps cascade-deletes when parent trajectory is deleted', () => {\n db.prepare(\"INSERT INTO trajectories(id) VALUES('t1')\").run();\n db.prepare(\"INSERT INTO trajectory_steps(trajectory_id,step_number,action) VALUES('t1',1,'act')\").run();\n db.prepare(\"DELETE FROM trajectories WHERE id='t1'\").run();\n const rows = db.prepare(\"SELECT * FROM trajectory_steps WHERE trajectory_id='t1'\").all();\n // Documents whether ON DELETE CASCADE is defined (it is NOT in current schema — orphans remain)\n expect(rows.length).toBe(1); // BUG: orphan row exists — schema missing ON DELETE CASCADE\n});" }, { "language": "javascript", "code": "// tests/schema/sessions.test.js\nconst VALID = ['active', 'paused', 'completed', 'expired'];\nconst INVALID = ['error', 'running', 'done', 'ACTIVE', 'failed'];\n\ntest.each(VALID)('sessions accepts status: %s', (s) =>\n expect(() =>\n db.prepare(`INSERT INTO sessions(id,state,status) VALUES('s${s}','{}','${s}')`).run()\n ).not.toThrow()\n);\ntest.each(INVALID)('sessions rejects status: %s', (s) =>\n expect(() =>\n db.prepare(`INSERT INTO sessions(id,state,status) VALUES('sx','{}','${s}')`).run()\n ).toThrow()\n);" }, { "language": "javascript", "code": "// tests/schema/migration-state.test.js\nconst VALID_MIG = ['pending','in_progress','completed','failed','rolled_back'];\ntest.each(VALID_MIG)('migration_state accepts status: %s', (s) =>\n expect(() =>\n db.prepare(\n `INSERT INTO migration_state(id,migration_type,status) VALUES('m${s}','v2-to-v3','${s}')`\n ).run()\n ).not.toThrow()\n);\ntest('migration_state rejects invalid status', () =>\n expect(() =>\n db.prepare(\"INSERT INTO migration_state(id,migration_type,status) VALUES('mx','v2-to-v3','cancelled')\").run()\n ).toThrow()\n);\n\nconst VALID_CHANGE = ['created','updated','success','failure','decay','merged','split'];\ntest.each(VALID_CHANGE)('pattern_history accepts change_type: %s', (c) => {\n db.prepare(\n `INSERT INTO patterns(id,name,pattern_type,condition,action) VALUES('ph${c}','n','task-routing','c','a')`\n ).run();\n expect(() =>\n db.prepare(\n `INSERT INTO pattern_history(pattern_id,version,change_type) VALUES('ph${c}',1,'${c}')`\n ).run()\n ).not.toThrow();\n});" }, { "language": "javascript", "code": "// tests/schema/trajectories.test.js\ntest.each(['active','completed','failed','abandoned'])(\n 'trajectories accepts status: %s', (s) =>\n db.prepare(`INSERT INTO trajectories(id,status) VALUES('t${s}','${s}')`).run()\n);\ntest.each(['success','failure','partial'])(\n 'trajectories accepts verdict: %s', (v) => {\n db.prepare(`INSERT INTO trajectories(id,status,verdict) VALUES('tv${v}','completed','${v}')`).run();\n }\n);\ntest('trajectories accepts NULL verdict', () =>\n db.prepare(\"INSERT INTO trajectories(id,status,verdict) VALUES('tnull','active',NULL)\").run()\n);\n\n// Self-referential FK: patterns.parent_id → patterns.id\ntest('patterns parent_id FK is enforced', () =>\n expect(() =>\n db.prepare(\n \"INSERT INTO patterns(id,name,pattern_type,condition,action,parent_id) VALUES('child','n','task-routing','c','a','nonexistent')\"\n ).run()\n ).toThrow(/FOREIGN KEY/)\n);" }, { "language": "json", "code": "{\n \"scripts\": {\n \"test\": \"jest --testPathPattern=tests/\"\n },\n \"devDependencies\": {\n \"jest\": \"^29\",\n \"better-sqlite3\": \"^9\"\n }\n}" } ] }, "durationMs": 198055, "model": "sonnet", "sandboxMode": "permissive", "workerType": "testgaps", "timestamp": "2026-05-14T19:25:38.874Z", "executionId": "testgaps_1778786540819_03hvjy" }