Repository navigation
fix(storage): no default PostgreSQL URL in code; auto without a URL is SQLite (#704, slice 2) - #937
Conversation
…s SQLite (#704) MemorySettings.DATABASE_URL defaulted to postgresql://127.0.0.1:5432/cortex, so library callers with no URL and no marker (CLI-runtime auto, cortex-migrate, the agent-briefing raw connection) reached whatever local cortex database answered. The default is now empty; auto without a named URL is SQLite in every runtime; an explicit postgresql backend with no URL raises; _get_database_url refuses an empty conninfo and cortex-migrate reports it on its exit-1 contract. Refs #704 Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_018kyZYtyFK1iv4S9wZDjGoP
…a URL is SQLite (#704) fixup! fix(storage): no default PostgreSQL URL in code; auto without a URL is SQLite (#704) Review B1 (PR #865, comment 6658250021): portability, migrate and wiki_migrate run as standalone commands and never read the backend marker. With no default URL, a PostgreSQL marker and no URL in the shell gave an empty SQLite store, silently. They now call backend_resolution.settle_for_command first, as mcp_server/__main__.py does. Also: migrate and memory_store docstrings, and the PRIVACY/CHANGELOG wording on which backends use the legacy URL. Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_018kyZYtyFK1iv4S9wZDjGoP
…a URL is SQLite (#704) fixup! fix(storage): no default PostgreSQL URL in code; auto without a URL is SQLite (#704) Codex review on 749bf05 (comment 4480636738): the nine scripts/ maintenance commands that open the shared store (memory_staleness_revalidate, memory_reheat, memory_dedup_exact, and six more) now read the backend marker first through settle_for_command, like portability and migrate. Without it, a PostgreSQL marker with no URL gave them an empty SQLite store. Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_018kyZYtyFK1iv4S9wZDjGoP
…a URL is SQLite (#704) fixup! fix(storage): no default PostgreSQL URL in code; auto without a URL is SQLite (#704) unit failed on 25c27d2 (run 42031311038, job 127258608806): test_migrate_reports_a_missing_url_on_its_exit_contract. migrate now settles the backend first. The fixture left CORTEX_MEMORY_STORE_BACKEND=postgresql in place: scripts/ci_unit.sh exports it, and tests_py/conftest.py replaces it with sqlite only when PostgreSQL is unreachable, which is why the test passed locally. settle_backend then supplied the legacy URL. The fixture now removes the backend variable, points the marker at an empty temporary methodology directory, and restores the environment whole. Reproduced locally by re-exporting the variable after conftest; passes with the fix. Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_018kyZYtyFK1iv4S9wZDjGoP
…a URL is SQLite (#704) fixup! fix(storage): no default PostgreSQL URL in code; auto without a URL is SQLite (#704) Mutation gate (ADR-1122) on cc3804d, run 42034218395: kill the survivors in the functions this PR changes. Adds tests for settle_for_command, portability.main, migrate._run, _get_database_url and agent_briefing._connect. settle_for_command moves above the first function: when it was appended after resolve_auto_backend, the diff's trailing blank lines put that untouched function in scope. Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_018kyZYtyFK1iv4S9wZDjGoP
…a URL is SQLite (#704) fixup! fix(storage): no default PostgreSQL URL in code; auto without a URL is SQLite (#704) Mutation gate (ADR-1122) on cc3804d, run 42034218395: for memory_store.py the stats run died on tests_py/hooks/test_entry.py. The hook subprocess inherits MUTANT_UNDER_TEST and runs mutmut's trampoline in a temp cwd with no [tool.mutmut] config ("Could not figure out where the code to mutate is"). hermetic_hook_env now drops the variable, so the hook process runs the original code and the in-process tests judge the mutants. Any PR that touches a module these hook tests import hits the same failure; the change is also proposed to main separately. Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_018kyZYtyFK1iv4S9wZDjGoP
…a URL is SQLite (#704) fixup! fix(storage): no default PostgreSQL URL in code; auto without a URL is SQLite (#704) unit on f944b6f (run 42042293324, attempt 2): 35 failed, 204 errors, most of them 'no PostgreSQL URL configured' or KeyError 'DATABASE_URL' in later PostgreSQL tests. The no_named_url fixture pops DATABASE_URL, a test then monkeypatch.setenv's it, and monkeypatch's own teardown (which runs after the fixture's full restore) deletes the key it recorded as absent. CI's DATABASE_URL was gone for the rest of the session. Both fixtures now call monkeypatch.undo() before the restore. Reproduced with a probe test after these files (failed before, passes after); a full before/after environment comparison now differs only in PYTEST_CURRENT_TEST. The tree_sitter_language_pack DownloadError failures in the same run are GitHub fetches, not this PR. Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_018kyZYtyFK1iv4S9wZDjGoP
…(300-line cap) They test backend_resolution.settle_for_command, not a standalone command; the move brings test_standalone_commands_settle_backend.py from 301 to 263 lines.
…a URL is SQLite (#704) Check unit, run 42204224811 (32ad0ff): the ADR-1122 mutation gate left 122 survivors in the changed functions of memory_store.py (_construct_store 102, _resolve_backend_url 16, _database_url_is_explicit 4). No test pinned the selection's outcomes: which store, path and dimension, which URL is tried, and the exact operator-facing error and warning texts. test_construct_store_selection.py pins every branch with PostgreSQL and SQLite replaced by recorders. Two code changes remove mutants no test can kill: the strict-failure message no longer carries the dead `url or '<unset>'` (url is non-empty there since #704 raises first), and os.environ.get("DATABASE_URL") drops a "" default that `or` made equivalent to None. The two remaining CORTEX_ALLOW_SQLITE_FALLBACK default mutants are registered equivalents. Local gate (mutation_check.sh, main's gate scripts, forkserver, only this test file): 187 in-scope mutants, 185 killed, 2 registered equivalents. Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_018kyZYtrWzv6PGpJnXKSYPs
…tabase-url # Conflicts: # memory/mutation-equivalents.json
…tabase-url # Conflicts: # memory/mutation-equivalents.json
…ore a handler opens the store (#704) PR #865 review 4 (B2): scripts/groomer.py, scripts/cortex_ingest.py and scripts/dev_run_ingest.py reach get_shared_store through handlers (get_grooming_health, curate_distill, ingest_codebase) and never called settle_for_command. With a PostgreSQL marker and no DATABASE_URL (the documented launchd groomer job), they used an empty SQLite store, silently. Each entry point now settles first and exits 1 when it cannot. The earlier sweep relied on a list kept by hand. A new test scans scripts/ and requires settle_for_command in every script that imports a handler or the store, with the two exemptions explained in the test. Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_018kyZYtyFK1iv4S9wZDjGoP
Codex Review Summary: ✅ CompletedThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
|
Claude finished @cdeust's task in 4m 13s —— View job Review 1 of 3 (ADR-1119): complete
Blocker: I did not run any tests. I made no code changes. Verdict: block |
| is no default URL in code: with neither `DATABASE_URL` nor a PostgreSQL | ||
| backend marker, Cortex uses SQLite and opens no PostgreSQL connection | ||
| (a PostgreSQL backend selected by the marker or by | ||
| `CORTEX_MEMORY_STORE_BACKEND=postgresql`, with no URL, uses | ||
| `postgresql://localhost:5432/cortex`, ADR-1104; issue #704). The opt-in setup script installs |
There was a problem hiding this comment.
Blocker: this statement about behaviour is false. "With neither DATABASE_URL nor a PostgreSQL backend marker, Cortex uses SQLite and opens no PostgreSQL connection" only holds on a fresh install.
Here is the case that breaks it: a HOME that already has Cortex state in ~/.claude/methodology but no marker.
settle_backend→persist_first_decision→decide(mcp_server/infrastructure/backend_resolution.py:116-128) runslegacy_store_answer, which opens TCP sockets tolocalhost:5432(lines 86-113).- If anything answers, or the result is unclear (for example a timeout or a
PG*variable), it writes apostgresqlmarker. settle_backendthen setsDATABASE_URL=postgresql://localhost:5432/cortex(line 173).
So a user with no URL and no marker can end up connected to a local cortex database they never configured. That is the case this paragraph says cannot happen. The parenthetical also leaves out CORTEX_BACKEND=postgres and CORTEX_MEMORY_DATABASE_URL, both of which select PostgreSQL.
Suggested fix: limit the claim to a fresh install. Then say that on a HOME with earlier Cortex state and no marker, the first launch probes localhost:5432 once and may record and use PostgreSQL there (ADR-1104).
|
Review 1 of 3 (ADR-1119) All five reviews ran: code quality, performance, test coverage, documentation accuracy and security. Blockers
Notes, no action before merge
Verdict: block |
…a URL is SQLite (#704) Review 1 of #937, blocker: PRIVACY.md said that with neither DATABASE_URL nor a marker Cortex opens no PostgreSQL connection. That holds on a fresh install only: on a HOME with earlier Cortex state and no marker, settle_backend -> persist_first_decision -> decide probes localhost:5432 once and selects PostgreSQL when something answers or the answer is unclear, then writes the marker (ADR-1104). The text now says both.
|
Claude finished @cdeust's task in 1m 39s —— View job Review 2 of 3 (ADR-1119): complete
Blockers: none. The new commit introduces no false security or behaviour statement. The review lists three wording notes that need no action before merge:
I did not run any tests. I made no code changes. Verdict: approve |
|
Review 2 of 3 (ADR-1119) Scope: this review checks the review 1 Blocker, then reviews only the one commit pushed after Review 1 Blocker (
Blockers None. I found no false security or behaviour statement introduced by Notes, no action before merge
Verdict: approve |
Summary
Library residual of #704, named out of scope by #804 (
docs/verification/draft-relevance-20261027.md§ 770):MemorySettings.DATABASE_URLstill defaulted topostgresql://127.0.0.1:5432/cortex. Every library path that reads the setting without a named URL could therefore reach a localcortexdatabase nobody configured:_construct_storewith thecliruntime mappedautotopostgresqland tried the default URL;python -m mcp_server.migrateconnected to it (RED reproduced:connection to server at "127.0.0.1", port 5432 failed);agent_briefing_query._connectpassed it to rawpg_connectwhen no backend was decided.Root cause: the default URL in
memory_config.py. It is now empty, and the callers follow an explicit contract:autowith no named URL → SQLite in every runtime, with no connection attempted (_resolve_backend_urlfollows the same rule, so the cache key stays consistent);postgresqlbackend with no URL →RuntimeError(no silent SQLite store, no guessed server; the oldallow_fallback = not urlbranch disappears);pg_store_ddl._get_database_urlrefuses an empty conninfo (ValueError), because libpq would then connect to its local defaults;cortex-migratereports it on its exit-1 contract;postgresqlwithout a URL, nothing is opened.PRIVACY.md is corrected: it promised a "default
postgresql://127.0.0.1:5432/cortex". The marker path keeps ADR-1104's legacy URL insettle_backend, and the text says so.Refs #704 (not
Closes: see "Breaking changes / remaining work").Type of change
Test plan
tests_py/infrastructure/test_no_default_database_url.py, 10 tests: 9 fail onorigin/main(1de07f9) and 10 pass with the fix. Every_try_pg_verbose/pg_connectcall fails the test, so any connection attempt is caught.test_sqlite_backend.py::test_reachable_postgresql_is_preferred_over_sqliteasserted exactly the behaviour A profile with no configuration connects to a local PostgreSQL it was never configured for #704 rejects (a default URL that answers wins). It now names its URL throughCORTEX_MEMORY_DATABASE_URL.test_no_default_database_url,test_sqlite_backend,test_backend_resolution,hooks/test_entry_fresh_backend,test_shared_backend_bootstrap,hooks/test_agent_briefing*,test_migrate→ 122 passed, 8 skipped (PostgreSQL absent).pytest tests_py: 10904 passed, 624 skipped, 4 failed, none caused by this PR: 3test_embedding_engine(torchnot installable here, its download host is blocked) andtest_wiki_coverage_dashboard::test_unreadable_page_is_skipped_not_fatal, which also fails onorigin/main(the session runs as root, so "unreadable" files can be read).ruff check/ruff format --check(0.17.3, fromuv.lock) pass on the touched files (main has 15 E501/E741 errors elsewhere).scripts/check_craftsmanship.py: OK.Audit notes
Breaking changes
auto, migrate, briefing) connected topostgresql://127.0.0.1:5432/cortex.auto), or an explicit error (postgresqlbackend, migrate). Migration: setDATABASE_URLorCORTEX_MEMORY_DATABASE_URL.scripts/install-plugin.sh(lines 20–21, 87) still selects PostgreSQL at install time when it finds a reachable localcortexdatabase ("never downgrade"). Removing that choice changes upgrades for existing PostgreSQL installs, so it is a product decision, not part of this PR.Reviewer checklist
Generated by Claude Code