You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
aqe brain import matches qe_patterns rows by id only, but the table is unique on (name, qe_domain, pattern_type). Suppose the source holds a pattern with the same name, domain and type as one in the target, under a different id. The import then attempts a plain INSERT, fails with SqliteError: UNIQUE constraint failed: qe_patterns.name, qe_patterns.qe_domain, qe_patterns.pattern_type, rolls back the whole transaction, and exits 1. This happens with every --strategy (skip-conflicts, latest-wins, highest-confidence) and in both the JSONL and RVF formats. --dry-run only counts rows, so for the same pair it reports Imported: 2, Conflicts: 0.
Every store that seeded the starter patterns has this clash with every other such store, because QEReasoningBank gives each seeded pattern a fresh uuidv4(). So a store created in a subfolder (#735) cannot be merged into the project's root store with brain import.
~/.claude/plugins/installed_plugins.json; ~/.claude/plugins/cache/ruvnet-brain/ruvnet-brain/4.3.28/.claude-plugin/plugin.json (older cache dirs 4.3.21 and 4.3.26 also present)
The same code is on main at ffc0c6a51738 (2026-09-26).
Steps to reproduce
This block ran on 2026-09-26 as written, in a throwaway directory with a temporary HOME under env -i.
Two notes on the setup:
Why one row per store is seeded with SQL. In a fresh HOME on this machine the reasoning bank logs Loaded foundational patterns {"count":22}, yet no row reaches qe_patterns. aqe hooks learn fails with VECTOR_SPACE_UNVERIFIED: refusing to persist or index an embedding without runtime provenance. So I insert one row per store directly: the starter pattern AAA Unit Test (test-template / test-generation) with its own random id, as QEReasoningBank would. How the rows got there does not matter to the import.
Why aqe hooks stats runs once per project. The unique index idx_patterns_unique_name_domain_type is created when the SQLite pattern store first initializes, not by aqe init. In a real project the AQE hooks initialize it on the first tool call. Without that step the same import "succeeds" by inserting a second AAA Unit Test row next to the first one (observed; 3 rows in B afterwards).
T=$(mktemp -d); mkdir -p "$T/home";cd"$T"aq() { env -i PATH="$PATH" HOME="$T/home" TMPDIR="$T" TERM=dumb NO_COLOR=1 aqe "$@"; }
count() { sqlite3 "$1""select count(*) || ' patterns: ' || group_concat(name, ', ') from (select name from qe_patterns order by name);"; }
# The same starter pattern learned independently in two stores: same (name, qe_domain,# pattern_type), each with its own random id, as QEReasoningBank's uuidv4() gives it.seed() { sqlite3 "$1""INSERT INTO qe_patterns(id, pattern_type, qe_domain, domain, name, description) VALUES (lower(hex(randomblob(16))), 'test-template', 'test-generation', 'test-generation', '$2', 'seeded for the import repro');"; }
# three fresh projects; `hooks stats` initializes the pattern store once, as the AQE hooks doforPin A B C;do mkdir "$P"; (cd "$P"&& aq init --auto --skip-code-index --no-statusline >/dev/null && aq hooks stats >/dev/null);doneforPin A B C;doecho"$P: $(sqlite3 $P/.agentic-qe/memory.db "select name from sqlite_master where name='idx_patterns_unique_name_domain_type';")";done
seed A/.agentic-qe/memory.db 'AAA Unit Test'
seed A/.agentic-qe/memory.db 'repro-only-in-A'
seed B/.agentic-qe/memory.db 'AAA Unit Test'
count A/.agentic-qe/memory.db; count B/.agentic-qe/memory.db
aq brain export --db A/.agentic-qe/memory.db --format jsonl -o a-export
aq brain import -i a-export --db B/.agentic-qe/memory.db --dry-run
forSin skip-conflicts latest-wins highest-confidence;do
aq brain import -i a-export --db B/.agentic-qe/memory.db --strategy "$S";echo"exit=$?";done
count B/.agentic-qe/memory.db
# control 1: the same export back into A (same ids)
aq brain import -i a-export --db A/.agentic-qe/memory.db --strategy skip-conflicts;echo"exit=$?"# control 2: a fresh target
aq brain import -i a-export --db C/.agentic-qe/memory.db --strategy skip-conflicts;echo"exit=$?"
count C/.agentic-qe/memory.db
# the RVF format, same pair
aq brain export --db A/.agentic-qe/memory.db --format rvf -o a-export.rvf
aq brain import -i a-export.rvf --db B/.agentic-qe/memory.db --dry-run
aq brain import -i a-export.rvf --db B/.agentic-qe/memory.db --strategy latest-wins;echo"exit=$?"
count B/.agentic-qe/memory.db
Expected vs actual
Expected. A pattern that already exists in the target under its natural key counts as a conflict and is resolved by the chosen strategy: skipped, or the newer or more confident copy kept. The rest of the import goes through. --dry-run reports the same counts a real run would.
Actual (excerpt; [UnifiedMemory]-style init lines and stack frames removed; temporary path shown as <tmp>; # notes added):
A: idx_patterns_unique_name_domain_type
B: idx_patterns_unique_name_domain_type
C: idx_patterns_unique_name_domain_type
2 patterns: AAA Unit Test, repro-only-in-A
1 patterns: AAA Unit Test
Export complete.
Format: jsonl
Patterns: 2
Output: <tmp>/a-export
Dry-run mode — no data will be written.
Import complete.
Imported: 2
Skipped: 0
Conflicts: 0
Brain import failed: SqliteError: UNIQUE constraint failed: qe_patterns.name, qe_patterns.qe_domain, qe_patterns.pattern_type
exit=1 # skip-conflicts
Brain import failed: SqliteError: UNIQUE constraint failed: qe_patterns.name, qe_patterns.qe_domain, qe_patterns.pattern_type
exit=1 # latest-wins
Brain import failed: SqliteError: UNIQUE constraint failed: qe_patterns.name, qe_patterns.qe_domain, qe_patterns.pattern_type
exit=1 # highest-confidence
1 patterns: AAA Unit Test # B unchanged; repro-only-in-A was lost with the rollback
Imported: 0 # control 1 (same ids): conflicts are detected
Skipped: 2
Conflicts: 2
exit=0
Imported: 2 # control 2 (fresh target)
Skipped: 0
Conflicts: 0
exit=0
2 patterns: AAA Unit Test, repro-only-in-A
Format: rvf
Patterns: 2
Imported: 2 # rvf --dry-run
Skipped: 0
Conflicts: 0
Embeddings: 0
Brain import failed: SqliteError: UNIQUE constraint failed: qe_patterns.name, qe_patterns.qe_domain, qe_patterns.pattern_type
exit=1 # rvf latest-wins
1 patterns: AAA Unit Test
Stack excerpt for the JSONL failure: dist/cli/chunks/chunk-4XPPMADQ.js:168 inside sqliteTransaction (<npm-root-g>/agentic-qe/node_modules/better-sqlite3/lib/methods/transaction.js:65:24), called from dist/cli/chunks/brain-handler-H5OVZ22L.js, with code: 'SQLITE_CONSTRAINT_UNIQUE'.
Field observation. Earlier the same day the same failure occurred on copies of a real project's root store and one of its subfolder stores (#735). The direct import aborted with this error for every strategy, while --dry-run reported 0 conflicts. A workaround did go through. In a scratch copy of the subfolder store, every pattern whose (name, qe_domain, pattern_type) already existed in the root was deleted, and that copy was exported. Importing it into a copy of the root succeeded (314 → 370 patterns). Editing the export itself does not work, because the manifest checksum rejects it.
Evidence
Installed agentic-qe 3.14.3, dist/:
integrations/ruvector/brain-shared.js:24-26: PK_COLUMNS has no qe_patterns entry, so the key column is id. :44-45: the qe_patterns entry in TABLE_CONFIGS has no dedupColumns. :215-226: mergeGenericRow runs SELECT * FROM qe_patterns WHERE id = ? and, on a miss, calls dynamicInsert. The strategy is applied only when the id matches. :278-279: the legacy mergePattern also keys on id.
integrations/ruvector/brain-exporter.js:222-228: the dry-run returns { imported: total, skipped: 0, conflicts: 0 } after only counting JSONL rows; it never looks at the target. :235-258: one transaction for the whole import. :251-254: idCol = PK_COLUMNS[table] || 'id', then mergeGenericRow.
integrations/ruvector/brain-rvf-exporter.js:382-395: the same count-only dry-run. :417 and :443: the same transaction and merge.
learning/sqlite-persistence.js:211-213 and :293-296: CREATE UNIQUE INDEX IF NOT EXISTS idx_patterns_unique_name_domain_type ON qe_patterns(name, qe_domain, pattern_type).
learning/pattern-store.js:635: id: uuidv4() for every created pattern. learning/qe-reasoning-bank.js:236-249: the 22 PRETRAINED_PATTERNS are stored through that path whenever a store is empty.
cli/handlers/brain-handler.js:40-41: the --strategy and --dry-run options. :144: prints the raw error.
The RuvNet Brain corpus holds agentic-qe/src/integrations/ruvector/brain-exporter.ts and agentic-qe/src/integrations/ruvector/brain-shared.ts. agentic-qe/docs/brain-export-improvement-plan.md in the same corpus designs a dedupStrategy: 'id' | 'composite' with compositeColumns for exactly this case. qe_patterns ended up with id only.
--dry-run gives a green light for an import that is certain to fail.
The rollback also discards every pattern that did not clash, so nothing gets merged. The error is a raw SqliteError with a stack trace and does not name the clashing pattern.
Without the unique index (a store whose pattern store has not initialized yet), the same import inserts duplicates of the natural key instead.
Suggested direction
Also match qe_patterns on (name, qe_domain, pattern_type), for example through dedupColumns or a composite lookup, and apply the chosen strategy to the existing row.
Make --dry-run use the same lookups as a real run, for example by running the merge in a transaction that is always rolled back, so its counts match.
On a constraint error, name the table and the clashing key, and suggest a strategy.
Summary
aqe brain importmatchesqe_patternsrows byidonly, but the table is unique on(name, qe_domain, pattern_type). Suppose the source holds a pattern with the same name, domain and type as one in the target, under a different id. The import then attempts a plain INSERT, fails withSqliteError: UNIQUE constraint failed: qe_patterns.name, qe_patterns.qe_domain, qe_patterns.pattern_type, rolls back the whole transaction, and exits 1. This happens with every--strategy(skip-conflicts,latest-wins,highest-confidence) and in both the JSONL and RVF formats.--dry-runonly counts rows, so for the same pair it reportsImported: 2, Conflicts: 0.Every store that seeded the starter patterns has this clash with every other such store, because
QEReasoningBankgives each seeded pattern a freshuuidv4(). So a store created in a subfolder (#735) cannot be merged into the project's root store withbrain import.Environment
sw_vers,uname -runame -mnode --versionnpm --versionpnpm --versionlatest3.45.0)<npm-root-g>/ruflo/package.json;ruflo --version→ruflo v3.45.0latest3.45.0)<npm-root-g>/ruflo/node_modules/@claude-flow/cli/package.json(not installed globally on its own)latest3.0.0-alpha.26)<npm-root-g>/ruflo/node_modules/@claude-flow/memory/package.json; nested copy under@claude-flow/cliis also 3.0.0-alpha.25latest3.0.0-alpha.20)<npm-root-g>/ruflo/node_modules/agentdb/package.json; nested copies under@claude-flow/cliand@claude-flow/memoryare also 3.0.0-alpha.20<npm-root-g>/agentdb/package.json;agentdb --version→agentdb v3.0.0-alpha.17<npm-root-g>/ruflo/node_modules/ruvector/package.json(same version nested under@claude-flow/cli,@claude-flow/memory,agentdb)latest0.3.3)<npm-root-g>/ruvector/package.json;ruvector --version→0.3.3latest3.14.3)<npm-root-g>/agentic-qe/package.json;aqe --version→3.14.3~/.claude/plugins/installed_plugins.json;~/.claude/plugins/cache/ruvnet-brain/ruvnet-brain/4.3.28/.claude-plugin/plugin.json(older cache dirs 4.3.21 and 4.3.26 also present)~/.cache/ruvnet-brain/kb/SOURCE.jsonclaude --version;~/.local/bin/claude→~/.local/share/claude/versions/2.1.283codex --version;<npm-root-g>/@openai/codex/package.json<npm-root-g>/@pacphi/agentic-kit/package.json;ak --versiongit rev-parse HEAD<npm-root-g>=~/.local/share/mise/installs/node/26.4.0/lib/node_modules(npm root -g). npmlatestdist-tags read withnpm view <pkg> dist-tags. Captured 2026-09-26.The same code is on
mainatffc0c6a51738(2026-09-26).Steps to reproduce
This block ran on 2026-09-26 as written, in a throwaway directory with a temporary
HOMEunderenv -i.Two notes on the setup:
HOMEon this machine the reasoning bank logsLoaded foundational patterns {"count":22}, yet no row reachesqe_patterns.aqe hooks learnfails withVECTOR_SPACE_UNVERIFIED: refusing to persist or index an embedding without runtime provenance. So I insert one row per store directly: the starter patternAAA Unit Test(test-template/test-generation) with its own random id, asQEReasoningBankwould. How the rows got there does not matter to the import.aqe hooks statsruns once per project. The unique indexidx_patterns_unique_name_domain_typeis created when the SQLite pattern store first initializes, not byaqe init. In a real project the AQE hooks initialize it on the first tool call. Without that step the same import "succeeds" by inserting a secondAAA Unit Testrow next to the first one (observed; 3 rows in B afterwards).Expected vs actual
Expected. A pattern that already exists in the target under its natural key counts as a conflict and is resolved by the chosen strategy: skipped, or the newer or more confident copy kept. The rest of the import goes through.
--dry-runreports the same counts a real run would.Actual (excerpt;
[UnifiedMemory]-style init lines and stack frames removed; temporary path shown as<tmp>;#notes added):Stack excerpt for the JSONL failure:
dist/cli/chunks/chunk-4XPPMADQ.js:168insidesqliteTransaction (<npm-root-g>/agentic-qe/node_modules/better-sqlite3/lib/methods/transaction.js:65:24), called fromdist/cli/chunks/brain-handler-H5OVZ22L.js, withcode: 'SQLITE_CONSTRAINT_UNIQUE'.Field observation. Earlier the same day the same failure occurred on copies of a real project's root store and one of its subfolder stores (#735). The direct import aborted with this error for every strategy, while
--dry-runreported 0 conflicts. A workaround did go through. In a scratch copy of the subfolder store, every pattern whose(name, qe_domain, pattern_type)already existed in the root was deleted, and that copy was exported. Importing it into a copy of the root succeeded (314 → 370 patterns). Editing the export itself does not work, because the manifest checksum rejects it.Evidence
Installed
agentic-qe3.14.3,dist/:integrations/ruvector/brain-shared.js:24-26:PK_COLUMNShas noqe_patternsentry, so the key column isid.:44-45: theqe_patternsentry inTABLE_CONFIGShas nodedupColumns.:215-226:mergeGenericRowrunsSELECT * FROM qe_patterns WHERE id = ?and, on a miss, callsdynamicInsert. The strategy is applied only when the id matches.:278-279: the legacymergePatternalso keys onid.integrations/ruvector/brain-exporter.js:222-228: the dry-run returns{ imported: total, skipped: 0, conflicts: 0 }after only counting JSONL rows; it never looks at the target.:235-258: one transaction for the whole import.:251-254:idCol = PK_COLUMNS[table] || 'id', thenmergeGenericRow.integrations/ruvector/brain-rvf-exporter.js:382-395: the same count-only dry-run.:417and:443: the same transaction and merge.learning/sqlite-persistence.js:211-213and:293-296:CREATE UNIQUE INDEX IF NOT EXISTS idx_patterns_unique_name_domain_type ON qe_patterns(name, qe_domain, pattern_type).learning/pattern-store.js:635:id: uuidv4()for every created pattern.learning/qe-reasoning-bank.js:236-249: the 22PRETRAINED_PATTERNSare stored through that path whenever a store is empty.cli/handlers/brain-handler.js:40-41: the--strategyand--dry-runoptions.:144: prints the raw error.Upstream
main@ffc0c6a51738:src/integrations/ruvector/brain-shared.ts:118-120(PK_COLUMNS),:142-143(TABLE_CONFIGS),:357-370(mergeGenericRow)src/integrations/ruvector/brain-exporter.ts:337-343(dry-run),:353(transaction),:371-374src/integrations/ruvector/brain-rvf-exporter.ts:532-544(dry-run),:594src/learning/sqlite-persistence.ts:328,:416(unique index)src/learning/pattern-store.ts:1033;src/learning/qe-reasoning-bank.ts:297-302,:352src/cli/handlers/brain-handler.ts:66-67,:181The RuvNet Brain corpus holds
agentic-qe/src/integrations/ruvector/brain-exporter.tsandagentic-qe/src/integrations/ruvector/brain-shared.ts.agentic-qe/docs/brain-export-improvement-plan.mdin the same corpus designs adedupStrategy: 'id' | 'composite'withcompositeColumnsfor exactly this case.qe_patternsended up withidonly.Impact
brain importcannot merge any two stores that have ever seeded the starter patterns. That covers the common cases: subfolder stores (Running aqe from a subfolder creates and adopts a new .agentic-qe store (#516 follow-up) #735), an export from a teammate, and moving to another machine.skip-conflicts, the default, does not skip.--dry-rungives a green light for an import that is certain to fail.SqliteErrorwith a stack trace and does not name the clashing pattern.Suggested direction
qe_patternson(name, qe_domain, pattern_type), for example throughdedupColumnsor a composite lookup, and apply the chosen strategy to the existing row.--dry-runuse the same lookups as a real run, for example by running the merge in a transaction that is always rolled back, so its counts match.Related
aqefrom a subfolder creates and adopts a new.agentic-qestore. Merging such a store back into the root store is where this bites.brain import UNIQUE constraint,brain import,qe_patterns UNIQUE,import conflicts dry-run,UNIQUE constraint failed,skip-conflicts,brain export,merge patterns stores; PRsbrain import,qe_patterns unique,import conflict,brain.