feat(worktree): query plumbing surfaces merged rows under parent project
ObservationCompiler.queryObservationsMulti and querySummariesMulti WHERE clause extended with OR merged_into_project IN (...), so a parent-project read pulls in rows originally written under any child worktree's composite name once merged. SearchManager wraps the Chroma project filter in \$or so semantic search behaves identically. ChromaSync baseMetadata now carries merged_into_project on new embeddings; existing rows are patched retroactively by the adoption engine. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -141,7 +141,8 @@ export function queryObservationsMulti(
|
|||||||
o.project
|
o.project
|
||||||
FROM observations o
|
FROM observations o
|
||||||
LEFT JOIN sdk_sessions s ON o.memory_session_id = s.memory_session_id
|
LEFT JOIN sdk_sessions s ON o.memory_session_id = s.memory_session_id
|
||||||
WHERE o.project IN (${projectPlaceholders})
|
WHERE (o.project IN (${projectPlaceholders})
|
||||||
|
OR o.merged_into_project IN (${projectPlaceholders}))
|
||||||
AND type IN (${typePlaceholders})
|
AND type IN (${typePlaceholders})
|
||||||
AND EXISTS (
|
AND EXISTS (
|
||||||
SELECT 1 FROM json_each(o.concepts)
|
SELECT 1 FROM json_each(o.concepts)
|
||||||
@@ -151,6 +152,7 @@ export function queryObservationsMulti(
|
|||||||
ORDER BY o.created_at_epoch DESC
|
ORDER BY o.created_at_epoch DESC
|
||||||
LIMIT ?
|
LIMIT ?
|
||||||
`).all(
|
`).all(
|
||||||
|
...projects,
|
||||||
...projects,
|
...projects,
|
||||||
...typeArray,
|
...typeArray,
|
||||||
...conceptArray,
|
...conceptArray,
|
||||||
@@ -189,11 +191,12 @@ export function querySummariesMulti(
|
|||||||
ss.project
|
ss.project
|
||||||
FROM session_summaries ss
|
FROM session_summaries ss
|
||||||
LEFT JOIN sdk_sessions s ON ss.memory_session_id = s.memory_session_id
|
LEFT JOIN sdk_sessions s ON ss.memory_session_id = s.memory_session_id
|
||||||
WHERE ss.project IN (${projectPlaceholders})
|
WHERE (ss.project IN (${projectPlaceholders})
|
||||||
|
OR ss.merged_into_project IN (${projectPlaceholders}))
|
||||||
${platformSource ? "AND COALESCE(s.platform_source, 'claude') = ?" : ''}
|
${platformSource ? "AND COALESCE(s.platform_source, 'claude') = ?" : ''}
|
||||||
ORDER BY ss.created_at_epoch DESC
|
ORDER BY ss.created_at_epoch DESC
|
||||||
LIMIT ?
|
LIMIT ?
|
||||||
`).all(...projects, ...(platformSource ? [platformSource] : []), config.sessionCount + SUMMARY_LOOKAHEAD) as SessionSummary[];
|
`).all(...projects, ...projects, ...(platformSource ? [platformSource] : []), config.sessionCount + SUMMARY_LOOKAHEAD) as SessionSummary[];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ interface StoredObservation {
|
|||||||
id: number;
|
id: number;
|
||||||
memory_session_id: string;
|
memory_session_id: string;
|
||||||
project: string;
|
project: string;
|
||||||
|
merged_into_project: string | null;
|
||||||
text: string | null;
|
text: string | null;
|
||||||
type: string;
|
type: string;
|
||||||
title: string | null;
|
title: string | null;
|
||||||
@@ -47,6 +48,7 @@ interface StoredSummary {
|
|||||||
id: number;
|
id: number;
|
||||||
memory_session_id: string;
|
memory_session_id: string;
|
||||||
project: string;
|
project: string;
|
||||||
|
merged_into_project: string | null;
|
||||||
request: string | null;
|
request: string | null;
|
||||||
investigated: string | null;
|
investigated: string | null;
|
||||||
learned: string | null;
|
learned: string | null;
|
||||||
@@ -129,11 +131,12 @@ export class ChromaSync {
|
|||||||
const files_read = parseFileList(obs.files_read);
|
const files_read = parseFileList(obs.files_read);
|
||||||
const files_modified = parseFileList(obs.files_modified);
|
const files_modified = parseFileList(obs.files_modified);
|
||||||
|
|
||||||
const baseMetadata: Record<string, string | number> = {
|
const baseMetadata: Record<string, string | number | null> = {
|
||||||
sqlite_id: obs.id,
|
sqlite_id: obs.id,
|
||||||
doc_type: 'observation',
|
doc_type: 'observation',
|
||||||
memory_session_id: obs.memory_session_id,
|
memory_session_id: obs.memory_session_id,
|
||||||
project: obs.project,
|
project: obs.project,
|
||||||
|
merged_into_project: obs.merged_into_project ?? null,
|
||||||
created_at_epoch: obs.created_at_epoch,
|
created_at_epoch: obs.created_at_epoch,
|
||||||
type: obs.type || 'discovery',
|
type: obs.type || 'discovery',
|
||||||
title: obs.title || 'Untitled'
|
title: obs.title || 'Untitled'
|
||||||
@@ -190,11 +193,12 @@ export class ChromaSync {
|
|||||||
private formatSummaryDocs(summary: StoredSummary): ChromaDocument[] {
|
private formatSummaryDocs(summary: StoredSummary): ChromaDocument[] {
|
||||||
const documents: ChromaDocument[] = [];
|
const documents: ChromaDocument[] = [];
|
||||||
|
|
||||||
const baseMetadata: Record<string, string | number> = {
|
const baseMetadata: Record<string, string | number | null> = {
|
||||||
sqlite_id: summary.id,
|
sqlite_id: summary.id,
|
||||||
doc_type: 'session_summary',
|
doc_type: 'session_summary',
|
||||||
memory_session_id: summary.memory_session_id,
|
memory_session_id: summary.memory_session_id,
|
||||||
project: summary.project,
|
project: summary.project,
|
||||||
|
merged_into_project: summary.merged_into_project ?? null,
|
||||||
created_at_epoch: summary.created_at_epoch,
|
created_at_epoch: summary.created_at_epoch,
|
||||||
prompt_number: summary.prompt_number || 0
|
prompt_number: summary.prompt_number || 0
|
||||||
};
|
};
|
||||||
@@ -346,6 +350,7 @@ export class ChromaSync {
|
|||||||
id: observationId,
|
id: observationId,
|
||||||
memory_session_id: memorySessionId,
|
memory_session_id: memorySessionId,
|
||||||
project: project,
|
project: project,
|
||||||
|
merged_into_project: null,
|
||||||
text: null, // Legacy field, not used
|
text: null, // Legacy field, not used
|
||||||
type: obs.type,
|
type: obs.type,
|
||||||
title: obs.title,
|
title: obs.title,
|
||||||
@@ -390,6 +395,7 @@ export class ChromaSync {
|
|||||||
id: summaryId,
|
id: summaryId,
|
||||||
memory_session_id: memorySessionId,
|
memory_session_id: memorySessionId,
|
||||||
project: project,
|
project: project,
|
||||||
|
merged_into_project: null,
|
||||||
request: summary.request,
|
request: summary.request,
|
||||||
investigated: summary.investigated,
|
investigated: summary.investigated,
|
||||||
learned: summary.learned,
|
learned: summary.learned,
|
||||||
|
|||||||
@@ -170,8 +170,16 @@ export class SearchManager {
|
|||||||
// Include project in the Chroma where clause to scope vector search.
|
// Include project in the Chroma where clause to scope vector search.
|
||||||
// Without this, larger projects dominate the top-N results and smaller
|
// Without this, larger projects dominate the top-N results and smaller
|
||||||
// projects get crowded out before the post-hoc SQLite filter.
|
// projects get crowded out before the post-hoc SQLite filter.
|
||||||
|
// Match both native-provenance rows (project) and adopted merged-worktree
|
||||||
|
// rows (merged_into_project) so a parent-project query surfaces its
|
||||||
|
// merged children's observations too.
|
||||||
if (options.project) {
|
if (options.project) {
|
||||||
const projectFilter = { project: options.project };
|
const projectFilter = {
|
||||||
|
$or: [
|
||||||
|
{ project: options.project },
|
||||||
|
{ merged_into_project: options.project }
|
||||||
|
]
|
||||||
|
};
|
||||||
whereFilter = whereFilter
|
whereFilter = whereFilter
|
||||||
? { $and: [whereFilter, projectFilter] }
|
? { $and: [whereFilter, projectFilter] }
|
||||||
: projectFilter;
|
: projectFilter;
|
||||||
|
|||||||
Reference in New Issue
Block a user