Skip to content

fix(mcp): db-backed branch resolution + reopen db created mid-session (2 PR-review P2s) - #2512

Merged
namastex888 merged 2 commits into
devfrom
fix/mcp-branch-resolve-and-db-reopen
Jul 3, 2026
Merged

namastex888 merged 2 commits into
devfrom
fix/mcp-branch-resolve-and-db-reopen

Conversation

@namastex888

Copy link
Copy Markdown
Contributor

Addresses the two P2 findings from the Codex review on #2510/#2509 (the HIGH was already fixed in #2511).

P2a — parseWishBranch mis-splits hyphenated wish slugs

wish/<slug>-<group> was split on the last -, so a top-level wish/genie-mcp branch resolved to a genie wish with an mcp group (returning resolved: true with the wrong wish + empty tasks). Real launch worktrees like wish/genie-mcp-g2 parsed correctly, so impact was limited to top-level wish branches (what the dogfood hit).

Fix: disambiguate against the known wish slugs in the db (new listWishSlugs, longest-first): exact match → top-level branch (group null); else the longest known slug that's a prefix followed by -<group>; the old last-dash heuristic only as a fallback when the wish has no tasks/groups yet. A top-level branch now lists all the wish's tasks (not group-filtered).

P2b — MCP server never picks up a db created mid-session

The read-only db handle was opened once at startup. If the repo had no genie.db then (null handle → empty board) and one was created during the session (a fresh genie init), the server served empty forever. Fix: reopen the read-only handle per tool call when it's null. (Existing dbs already saw new writes via WAL; only the absent→created case was stale.)

Tests

  • wish/genie-mcp → { wish: genie-mcp, group: null, resolved: true } (not the mis-split genie/mcp); wish/genie-mcp-g2 → genie-mcp/g2.
  • A db seeded after the server started is picked up (board no longer empty).

Both are load-bearing (fail on the old code). Gates: bun run check 631 pass; typecheck + build green.

Two P2s from the #2510/#2509 PR review (Codex):
- Worktree branch parsing split on the last '-', so a top-level 'wish/genie-mcp'
  mis-resolved to a 'genie' wish with an 'mcp' group. Now disambiguated against
  the KNOWN wish slugs (new listWishSlugs, longest-first): exact slug → top-level
  (group null); else longest known prefix + '-<group>'; heuristic only when the
  wish isn't in the db yet. A top-level branch now lists all the wish's tasks.
- The MCP server opened the db once at startup; a db created mid-session (fresh
  genie init) was never picked up (empty forever). Reopen the read-only handle
  per tool call when null. Existing dbs already saw writes via WAL; only the
  absent→created case was stale.
Tests: hyphenated-slug top-level + group branches resolve correctly; a db
created after the server started is picked up. check 631, build green.
@coderabbitai

coderabbitai Bot commented Jul 3, 2026 •

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 897018b1-612e-4094-a950-f13985bed677

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/mcp-branch-resolve-and-db-reopen

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code Review

This pull request improves the MCP server's branch resolution and database handling. It adds support for disambiguating hyphenated wish slugs against known wishes in the database and enables the server to dynamically pick up a database created mid-session. Feedback on these changes highlights a potential resource leak in src/term-commands/mcp.ts where dynamically opened database connections (ctx.db) are not closed by the server's shutdown handler. Additionally, it is recommended to simplify the SQL query in listWishSlugs by removing the redundant GROUP BY clause and wrapping the query in a try-catch block to handle uninitialized database states gracefully.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread src/term-commands/mcp.ts
// The db may have been absent when the server started (null handle → empty
// board). Re-attempt the read-only open per call so a db created mid-session
// (e.g. a fresh `genie init`) is picked up instead of serving empty forever.
if (!ctx.db) ctx.db = openReadonlyDb(cwd);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

high

When ctx.db is dynamically opened mid-session here, the local db variable (which was initialized to null at startup) is not updated. Consequently, the rl.on('close', ...) handler at the end of runMcpServer (which calls db?.close()) will not close this newly opened database connection, resulting in a resource/file descriptor leak.

To fix this, please update the close handler to close ctx.db instead of db:

rl.on('close', () => {
  ctx.db?.close();
  ...
});

Comment thread src/lib/v5/task-state.ts
Comment on lines +619 to +629
export function listWishSlugs(db: Database): string[] {
const rows = db
.query(
`SELECT wish FROM (
SELECT wish FROM tasks WHERE wish IS NOT NULL
UNION SELECT wish FROM wish_groups WHERE wish IS NOT NULL
) GROUP BY wish ORDER BY LENGTH(wish) DESC`,
)
.all() as Array<{ wish: string }>;
return rows.map((r) => r.wish);
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

The GROUP BY wish clause is redundant here because the UNION operator already implicitly removes duplicate rows. Removing it simplifies the query and avoids unnecessary grouping overhead.

Additionally, since this is a read-only MCP tool designed to degrade gracefully and never throw, wrapping the query in a try-catch block to return [] on error is highly recommended. This prevents potential unhandled exceptions (e.g., if the database is in an intermediate/uninitialized state where the tasks or wish_groups tables do not exist yet) from crashing the server.

Suggested change
export function listWishSlugs(db: Database): string[] {
const rows = db
.query(
`SELECT wish FROM (
SELECT wish FROM tasks WHERE wish IS NOT NULL
UNION SELECT wish FROM wish_groups WHERE wish IS NOT NULL
) GROUP BY wish ORDER BY LENGTH(wish) DESC`,
)
.all() as Array<{ wish: string }>;
return rows.map((r) => r.wish);
}
export function listWishSlugs(db: Database): string[] {
try {
const rows = db
.query(
"SELECT wish FROM (SELECT wish FROM tasks WHERE wish IS NOT NULL UNION SELECT wish FROM wish_groups WHERE wish IS NOT NULL) ORDER BY LENGTH(wish) DESC",
)
.all() as Array<{ wish: string }>;
return rows.map((r) => r.wish);
} catch {
return [];
}
}

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c60e1e39b6

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/lib/v5/mcp-tools.ts
const rest = branch.startsWith('wish/') ? branch.slice('wish/'.length) : null;
if (!rest) return null;
const known = db ? listWishSlugs(db) : []; // longest-first
if (known.includes(rest)) return { wish: rest, group: null };

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Check group-branch candidates before exact wish matches

When a launched group branch's <slug>-<group> string is also a known wish slug, this early exact match resolves the branch as a top-level wish instead of the group worktree. genie launch creates branches as wish/${slug}-${name} and both slugs and groups allow hyphens, so with wish foo-bar group baz plus a separate wish foo-bar-baz, the foo-bar/baz worktree now receives all tasks for foo-bar-baz rather than its own group context. Please validate known wish/group (or task group) candidates before treating the entire suffix as a top-level wish.

Useful? React with 👍 / 👎.

Comment thread src/term-commands/mcp.test.ts Outdated
Comment on lines +345 to +346
proc.stdin.write(`${JSON.stringify(INIT)}\n`);
seed(repo); // create .genie/genie.db + tasks AFTER the server opened (null) at startup

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Synchronize before seeding the mid-session DB

This test does not guarantee that the server has already opened a null DB handle before seed(repo) runs: the parent writes INIT and immediately creates the DB without waiting for the child to start and answer initialize. On a schedule where the child has not reached openReadonlyDb(cwd) yet, the DB exists at startup and the old one-open implementation would pass, so the regression test is not load-bearing. Wait for the initialize response first, then seed and send the tool call.

Useful? React with 👍 / 👎.

…lution

Gemini HIGH: the per-call db reopen wrote to ctx.db while close() closed a
separate startup 'db' local — a mid-session reopen leaked its handle. Drop the
stale local; ctx.db is the single source of truth (close() closes the current
handle). Codex P2: prefer a launch-worktree interpretation whose <group> is a
REAL group of the prefix wish over a same-named top-level slug (disambiguates
the genie-mcp / genie+mcp collision). Gemini MEDIUM: drop redundant GROUP BY
(UNION already dedups). Codex P2: the reopen test now waits for the initialize
reply (startup open ran) before seeding, so it can't pass without exercising
the reopen. + collision test. check 632.
@namastex888
namastex888 merged commit 3fe6f1c into dev Jul 3, 2026
11 checks passed
@automagik-genie
automagik-genie deleted the fix/mcp-branch-resolve-and-db-reopen branch September 25, 2026 04:50
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.

1 participant