Skip to content

fix(cli): seed config projects into the database on CLI-only installs - #1354

Merged
phernandez merged 10 commits into
mainfrom
fix/1334-cli-reconcile-projects
Aug 29, 2026
Merged

fix(cli): seed config projects into the database on CLI-only installs#1354
phernandez merged 10 commits into
mainfrom
fix/1334-cli-reconcile-projects

Conversation

@phernandez

Copy link
Copy Markdown
Member

Fixes #1334 (regression of #974).

Problem

A fresh install seeds main into config.json, but only initialize_app() — run by the API/MCP server lifespans and a handful of CLI commands — reconciles config projects into the project table. cli/app.py skips it for project, status, tool, sync, reindex, … i.e. nearly every first command a new user runs. Result on a clean profile (reproduced locally before this change):

bm project add main ~/basic-memory   # Error: Project 'main' already exists   (config check)
bm status                            # Project not found: 'main'. No projects are set up yet…   (DB check)

#974's fixes (#985/#987/#990) promote an existing DB project when the config default is missing, but a fresh DB has none to promote, and #990's message points at the one command that can't work.

Fix

get_engine_factory's non-lifespan fallback — the path every CLI/MCP local ASGI request takes to open the database — now calls reconcile_projects_with_config_once(), a once-per-process wrapper around the same reconcile_projects_with_config() the servers run, gated on skip_local_initialization exactly like initialize_app() so cloud/stateless deployments are untouched. This is what the comment in cli/app.py ("API-using commands handle initialization via deps.py") already claimed happened.

Tests

  • tests/cli/test_fresh_install.py — pristine HOME, subprocess bm status must exit 0, not say "Project not found", and list main.
  • tests/services/test_initialization_reconcile_once.py — reconciles once per database per process; skipped under BASIC_MEMORY_CLOUD_MODE.

ruff / ty clean; tests/cli/test_cli_exit.py (import-lightness + exit guards) still pass.

🤖 Generated with Claude Code

https://claude.ai/code/session_017STCpbNsYjZgUdftxgEAZ4

A fresh install writes `main` into config.json, but only the API/MCP
server lifespan (initialize_app) reconciled config projects into the
projects table. The CLI skips that initialization for `project`,
`status`, `tool`, and most other commands, so a CLI-only flow hit
"Project not found: 'main'" on every default-project command while
`project add main` refused with "already exists" — the #974 wedge again.

Run the same reconciliation once per process from the API dependency
that opens the local database outside a server lifespan
(get_engine_factory's CLI/MCP fallback), gated on
skip_local_initialization like initialize_app so cloud/stateless
deployments are untouched.

Regression tests: a pristine-HOME subprocess `bm status` must succeed
and see `main`; the once-guard runs reconciliation a single time and
skips cloud mode.

Fixes #1334

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017STCpbNsYjZgUdftxgEAZ4
Signed-off-by: phernandez <paul@basicmachines.co>
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Aug 28, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-08-28T23:52:03.621222Z 33805d6 Manual request
ℹ️ 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" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@phernandez

Copy link
Copy Markdown
Member Author

@codex review

@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: da5424fb55

ℹ️ 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/basic_memory/deps/db.py
phernandez and others added 2 commits August 28, 2026 17:06
Two tests used "project validation fails against the empty local DB" to
prove --local routing, and faked get_or_create_db with stand-ins. The
CLI/MCP local ASGI client now seeds config projects into the database
(#1334), so validation succeeds: assert routing directly (no workspace
lookup, ASGI transport) and skip reconciliation where the engine is a
stand-in.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017STCpbNsYjZgUdftxgEAZ4
Signed-off-by: phernandez <paul@basicmachines.co>
synchronize_projects() created a local projects row for every config
entry regardless of mode, and let the database default overwrite the
configured one. With reconciliation now running on every CLI process
(#1334) that undid `set-cloud` on each run: the row set-cloud had
deliberately deleted came back, and a cloud default could be flipped to
a local project. Skip cloud-mode entries when seeding rows and leave a
cloud-mode default alone; the database default is only the local
fallback.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017STCpbNsYjZgUdftxgEAZ4
Signed-off-by: phernandez <paul@basicmachines.co>
@phernandez

Copy link
Copy Markdown
Member Author

@codex review

@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: cc8b54a97e

ℹ️ 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 tests/mcp/test_project_context.py
Reconciliation now skips cloud-mode entries, so the routing test's
cloud project has no local row and validation 404s. That miss — not a
workspace error — is the signal that routing stayed local; assert it
while keeping the workspace-lookup guard.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017STCpbNsYjZgUdftxgEAZ4
Signed-off-by: phernandez <paul@basicmachines.co>
@phernandez

Copy link
Copy Markdown
Member Author

@codex review

@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: e78be5000a

ℹ️ 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/basic_memory/services/project_service.py Outdated
…copy

Codex round two on #1354: `add --cloud --local-path` writes a
cloud-mode entry that still owns a local sync directory, and local-side
commands (`project ls --local`, startup watching) look it up in the
projects table. Skip only cloud-only entries — cloud mode with no
local_sync_path — when seeding rows and when protecting the configured
default.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017STCpbNsYjZgUdftxgEAZ4
Signed-off-by: phernandez <paul@basicmachines.co>
@phernandez

Copy link
Copy Markdown
Member Author

@codex review

@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: c78d8fbf6a

ℹ️ 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/basic_memory/services/project_service.py Outdated
Comment thread src/basic_memory/services/initialization.py Outdated
…after failure

Codex round three on #1354:

- set-cloud blanked `path` but left `local_sync_path` (and bisync flags)
  behind, so an entry created with `add --cloud --local-path` and then
  cut over still read as a local copy and reconciliation recreated the
  row set-cloud had just dropped. The cutover now clears the sync
  metadata too, which is what "purely cloud" already promised.
- The once-per-process guard recorded the database path before
  reconciliation ran, and reconcile_projects_with_config() swallows
  synchronize failures, so a transient error on the first request pinned
  a long-lived MCP process to an unseeded database. It now reports
  success, and the path is only retired after a completed run.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017STCpbNsYjZgUdftxgEAZ4
Signed-off-by: phernandez <paul@basicmachines.co>
@phernandez

Copy link
Copy Markdown
Member Author

@codex review

@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: a143d25836

ℹ️ 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/basic_memory/services/project_service.py Outdated
Comment thread src/basic_memory/services/project_service.py Outdated
…alized cloud default

Codex round four on #1354: a local row an earlier reconciliation had
recreated for a cut-over project never converged away, because the
deletion pass treated every config key as a local claim; and a default
written in display form (`Research Cloud`) stopped matching its
normalized key, so config validation reset it to the first local
project. Cloud-only entries are now absent from the local-row set for
both passes, and the default is renamed together with its key.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017STCpbNsYjZgUdftxgEAZ4
Signed-off-by: phernandez <paul@basicmachines.co>
@phernandez

Copy link
Copy Markdown
Member Author

@codex review

@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: 662964b71e

ℹ️ 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/basic_memory/services/project_service.py Outdated
Codex round five on #1354: older cloud-mode entries record the local
sync copy only in `path`, and `_require_local_sync_path` still honors
that fallback. Treating them as cloud-only would drop their local row
and make local-side commands report the project missing. A cloud entry
is cloud-only only when it has neither local_sync_path nor path — the
state set-cloud and `add --cloud` without --local-path produce.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017STCpbNsYjZgUdftxgEAZ4
Signed-off-by: phernandez <paul@basicmachines.co>
@phernandez

Copy link
Copy Markdown
Member Author

@codex review

@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: 00d5206e23

ℹ️ 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/basic_memory/services/project_service.py Outdated
Codex round six on #1354: a legacy cloud-only entry can carry the
remote slug (`research`) in `path`. Treating any non-empty path as a
local copy would seed a row whose base resolves against the process
cwd. Mirror is_locally_syncable and _require_local_sync_path: the
fallback path must be absolute.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017STCpbNsYjZgUdftxgEAZ4
Signed-off-by: phernandez <paul@basicmachines.co>
@phernandez

Copy link
Copy Markdown
Member Author

@codex review

@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: f057cd46b8

ℹ️ 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/basic_memory/services/project_service.py Outdated
Codex round seven on #1354: ProjectEntry accepts a relative
local_sync_path, and _require_local_sync_path rejects it, so
reconciliation must not seed a row for it either. Apply that function's
rule as written — local_sync_path, falling back to path, absolute only.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017STCpbNsYjZgUdftxgEAZ4
Signed-off-by: phernandez <paul@basicmachines.co>
@phernandez

Copy link
Copy Markdown
Member Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. What shall we delve into next?

Reviewed commit: 33805d6dc8

ℹ️ 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".

@phernandez
phernandez merged commit f47e26c into main Aug 29, 2026
27 checks passed
phernandez added a commit that referenced this pull request Aug 29, 2026
…copy

Codex round two on #1354: `add --cloud --local-path` writes a
cloud-mode entry that still owns a local sync directory, and local-side
commands (`project ls --local`, startup watching) look it up in the
projects table. Skip only cloud-only entries — cloud mode with no
local_sync_path — when seeding rows and when protecting the configured
default.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017STCpbNsYjZgUdftxgEAZ4
Signed-off-by: phernandez <paul@basicmachines.co>
@phernandez
phernandez deleted the fix/1334-cli-reconcile-projects branch August 29, 2026 00:10
phernandez added a commit that referenced this pull request Aug 29, 2026
…after failure

Codex round three on #1354:

- set-cloud blanked `path` but left `local_sync_path` (and bisync flags)
  behind, so an entry created with `add --cloud --local-path` and then
  cut over still read as a local copy and reconciliation recreated the
  row set-cloud had just dropped. The cutover now clears the sync
  metadata too, which is what "purely cloud" already promised.
- The once-per-process guard recorded the database path before
  reconciliation ran, and reconcile_projects_with_config() swallows
  synchronize failures, so a transient error on the first request pinned
  a long-lived MCP process to an unseeded database. It now reports
  success, and the path is only retired after a completed run.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017STCpbNsYjZgUdftxgEAZ4
Signed-off-by: phernandez <paul@basicmachines.co>
phernandez added a commit that referenced this pull request Aug 29, 2026
…alized cloud default

Codex round four on #1354: a local row an earlier reconciliation had
recreated for a cut-over project never converged away, because the
deletion pass treated every config key as a local claim; and a default
written in display form (`Research Cloud`) stopped matching its
normalized key, so config validation reset it to the first local
project. Cloud-only entries are now absent from the local-row set for
both passes, and the default is renamed together with its key.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017STCpbNsYjZgUdftxgEAZ4
Signed-off-by: phernandez <paul@basicmachines.co>
phernandez added a commit that referenced this pull request Aug 29, 2026
Codex round five on #1354: older cloud-mode entries record the local
sync copy only in `path`, and `_require_local_sync_path` still honors
that fallback. Treating them as cloud-only would drop their local row
and make local-side commands report the project missing. A cloud entry
is cloud-only only when it has neither local_sync_path nor path — the
state set-cloud and `add --cloud` without --local-path produce.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017STCpbNsYjZgUdftxgEAZ4
Signed-off-by: phernandez <paul@basicmachines.co>
phernandez added a commit that referenced this pull request Aug 29, 2026
Codex round six on #1354: a legacy cloud-only entry can carry the
remote slug (`research`) in `path`. Treating any non-empty path as a
local copy would seed a row whose base resolves against the process
cwd. Mirror is_locally_syncable and _require_local_sync_path: the
fallback path must be absolute.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017STCpbNsYjZgUdftxgEAZ4
Signed-off-by: phernandez <paul@basicmachines.co>
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.

[BUG] Fresh CLI-only install wedges on auto-created 'main': config says it exists, DB says it doesn't (regression of #974)

1 participant