Skip to content

feat(memory): add selective branch pick support#193

Open
gouhongshen wants to merge 14 commits intomatrixorigin:mainfrom
gouhongshen:feature/memory-pick
Open

feat(memory): add selective branch pick support#193
gouhongshen wants to merge 14 commits intomatrixorigin:mainfrom
gouhongshen:feature/memory-pick

Conversation

@gouhongshen
Copy link
Copy Markdown
Contributor

@gouhongshen gouhongshen commented Apr 21, 2026

Summary

  • add selective branch pick support across git, MCP remote, and REST API
  • support key_list, snapshot_range, and retrieve selectors, optional target branch, and fail|skip|accept conflict strategies
  • add retrieve.min_score and dry_run preview/output shaping for memory_pick
  • add MCP/API/remote e2e coverage for success, validation, conflicts, target branches, retrieve ranking, and dry-run previews

Interface

MCP

Tool: memory_pick

Inputs:

  • source: source branch name
  • target: optional target branch, defaults to main
  • strategy: optional fail | skip | accept, defaults to fail
  • selector:
    • {"type":"key_list","keys":["", "..."]}
    • {"type":"snapshot_range","from_snapshot":"snap_a","to_snapshot":"snap_b"}
    • {"type":"retrieve","query":"alpha query","top_k":5,"min_score":0.82} (top_k and min_score only apply to retrieve)
  • dry_run (optional preview mode; no database mutation):
    • {"limit":10,"offset":0,"include_content_preview":true,"include_scores":true}
    • limit / offset only shape preview output; they do not change the selected candidate set
    • preview output never includes embeddings

Outputs:

  • normal execution returns plain text in content[0].text
  • dry_run returns preview JSON rendered in content[0].text, including:
    • result
    • summary
    • page
    • candidates
  • v1 intentionally does not add session_id

REST API

POST /v1/branches/:source/pick

Request body:

  • target: optional target branch, defaults to main
  • strategy: optional fail | skip | accept, defaults to fail
  • selector: same shape as MCP
  • dry_run: same shape as MCP; when present, returns a structured preview instead of applying changes

Success response:

  • normal execution:
{"result":"Picked ..."}
  • dry_run preview:
{
  "dry_run": true,
  "result": "Previewed ...",
  "summary": {
    "candidate_count": 2,
    "shown_count": 1,
    "would_apply": 2,
    "would_skip": 0,
    "conflict_count": 0,
    "would_abort": false
  },
  "page": {
    "limit": 1,
    "offset": 0,
    "has_more": true
  },
  "candidates": [
    {
      "memory_id": "019...",
      "content_preview": "alpha branch memory",
      "score": 0.93,
      "status": "would_apply",
      "reason": "new_or_updated_row"
    }
  ]
}

Error mapping:

  • 422: validation error
  • 404: source/target branch or snapshot not found
  • 409: pick conflict when strategy=fail

Testing

  • cargo check -p memoria-mcp -p memoria-api
  • cargo test -p memoria-mcp --test branch_e2e --no-run
  • cargo test -p memoria-api --test api_e2e --no-run
  • local runtime pick e2e currently still depends on a ready MatrixOne test DB; on this machine the existing multi-db harness times out before tests start

Copilot AI review requested due to automatic review settings April 21, 2026 02:36
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds a new selective “branch pick” capability that applies a subset of changes from one memory branch into another (defaulting to main), wiring it through MCP tooling, the RemoteClient, and the REST API, with E2E coverage for success and conflict cases.

Changes:

  • Introduces memory_pick MCP tool + GitForDataService support for picking by key_list, snapshot_range, or retrieve selectors with conflict strategies.
  • Adds REST endpoint POST /v1/branches/:name/pick and RemoteClient forwarding for memory_pick.
  • Adds MCP/API/remote E2E tests covering happy paths, validation, and conflict handling.

Reviewed changes

Copilot reviewed 9 out of 9 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
memoria/crates/memoria-mcp/tests/branch_e2e.rs Adds MCP E2E coverage for key_list/snapshot_range/retrieve pick and conflict strategies
memoria/crates/memoria-mcp/src/remote.rs Forwards memory_pick to the new REST pick endpoint
memoria/crates/memoria-mcp/src/git_tools.rs Implements memory_pick tool parsing, selection logic, and pick execution
memoria/crates/memoria-git/src/service.rs Adds MatrixOne DDL helpers for data branch pick (keys + snapshot range)
memoria/crates/memoria-api/tests/api_e2e.rs Adds REST + remote E2E tests for pick, validation, and conflicts
memoria/crates/memoria-api/src/routes/snapshots.rs Adds REST handler that calls MCP git_tools pick and maps conflicts to HTTP 409
memoria/crates/memoria-api/src/routes/mcp.rs Marks memory_pick as dirtying FULL metrics state
memoria/crates/memoria-api/src/models.rs Adds PickRequest and PickSelector request models
memoria/crates/memoria-api/src/lib.rs Adds /v1/branches/:name/pick route and metrics dirty-path handling

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread memoria/crates/memoria-mcp/src/git_tools.rs
Comment thread memoria/crates/memoria-mcp/src/git_tools.rs Outdated
Comment thread memoria/crates/memoria-mcp/src/git_tools.rs Outdated
Comment thread memoria/crates/memoria-git/src/service.rs
gouhongshen and others added 2 commits April 21, 2026 11:10
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@gouhongshen gouhongshen force-pushed the feature/memory-pick branch from dc5812a to a8ecfdf Compare April 21, 2026 03:15
@gouhongshen gouhongshen changed the title Add selective memory_pick branch apply feat(memory): add selective branch pick support Apr 21, 2026
gouhongshen and others added 4 commits April 21, 2026 11:26
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@gouhongshen gouhongshen requested a review from Copilot April 21, 2026 09:01
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 9 out of 9 changed files in this pull request and generated 2 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +1903 to +1921
let diff_clause = pickable_diff_clause("s_to", "s_from");
let mut qb: QueryBuilder<MySql> = QueryBuilder::new(format!(
"SELECT s_to.memory_id, COALESCE(s_to.content, '') AS content, \
COALESCE(s_to.memory_type, 'semantic') AS memory_type, \
CASE WHEN t.memory_id IS NULL THEN 0 ELSE 1 END AS target_exists \
FROM {source_table} {{SNAPSHOT = '"
));
qb.push(to_snapshot);
qb.push("'}} s_to ");
qb.push(format!(
"LEFT JOIN {source_table} {{SNAPSHOT = '{from_snapshot}'}} s_from ON s_from.memory_id = s_to.memory_id "
));
qb.push(format!(
"LEFT JOIN {target_table} t ON t.memory_id = s_to.memory_id WHERE s_to.user_id = "
));
qb.push_bind(user_id);
qb.push(" AND (");
qb.push(&diff_clause);
qb.push(") ORDER BY s_to.updated_at DESC, s_to.created_at DESC");
Copy link

Copilot AI Apr 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

snapshot_range dry-run candidate query only checks differences between to_snapshot and from_snapshot (s_to vs s_from) and does not filter out rows that are already identical in the target branch. This can inflate candidate_count and mark target_exists rows as conflicts even when picking would be a no-op for that memory_id in the target. Consider also applying the same “pickable vs target” diff predicate (s_to vs target alias) so the preview reflects what would actually change in the target branch.

Copilot uses AI. Check for mistakes.
Comment on lines +2155 to +2163
let message = err.to_string();
if message.to_lowercase().contains("conflict") {
let selection_detail = selected
.map(|count| format!(" for {count} selected change(s)"))
.unwrap_or_default();
return Err(MemoriaError::Validation(format!(
"Conflict: pick from branch '{source}' into '{target}' aborted{selection_detail}: {message}"
)));
}
Copy link

Copilot AI Apr 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

map_pick_conflict treats any error whose message contains the substring "conflict" as a pick conflict. Because the SQL statement itself contains "when conflict ...", unrelated failures (notably SQL parser errors on older MatrixOne versions) will be misclassified and returned as MemoriaError::Validation("Conflict: ..."), which the REST layer maps to HTTP 409. Suggest tightening the detection to only match real pick-conflict errors (e.g., by checking specific MatrixOne error codes / a more specific phrase) and letting parser/DDL errors surface as internal errors instead of 409.

Copilot uses AI. Check for mistakes.
gouhongshen and others added 8 commits April 21, 2026 17:49
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants