Repository navigation
Conversation
|
ZETETIC-REVIEW: REQUEST_CHANGES Reviewed against #744's acceptance and the loop's standard. The machinery is complete and measured (pinned asset + sha256, one flock-supervised server per install, TCP 127.0.0.1, generated password, schema through setup_db, loud failure, zero pg0 writes outside
The fix for 1–2 is an owner decision (relink and re-sign a third-party binary against the uv.lock-pinned OpenSSL that psycopg-binary already ships, wait for a fixed upstream release, or accept a documented Homebrew openssl@3 requirement); options and costs are in the PR body. Not mergeable as a closure of #744 until that decision is made and implemented. |
|
ZETETIC-REVIEW: REQUEST_CHANGES
|
… binaries; pg0 dropped (#744, #740) pg0 0.15.2's macOS PostgreSQL loads Homebrew's OpenSSL by absolute path and dies in dyld on a Mac without Homebrew (reproduced under sandbox-exec). theseus-rs 18.6.0 bundles OpenSSL (@loader_path); Setup now downloads it and pgvector_compiled v0.18.320 (sha256-pinned), runs initdb --pwfile and pg_ctl itself, creates vector, pg_trgm and the schema, and only then writes the `managed` backend marker (ADR-1110, amends ADR-1104 decision 1). - A configured database always wins (CLAUDE_PLUGIN_OPTION_DATABASE_URL mapped for hooks and Setup); a failed provision leaves SQLite selected. - Stale postmaster.pid classified by identity (EPERM is not ours; executable or, under emulation, the recorded loopback port); removed before a start, never on stop. - No migration: only a store that does not exist yet becomes managed; eligibility is rechecked right before the marker is written. - setup_db._init_schema fails on any failed DDL statement. - Linux glibc needs the distribution's libxml2.so.2/OpenSSL 3 (else SQLite, cause in setup.log); Windows and musl keep SQLite. - CI: managed-pg job (macOS under no_homebrew.sb, Linux x86_64/aarch64); install-e2e checks the first session names the PostgreSQL store. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BYyqzx4FVMHasVPZV3uBYq
f33db02 to
6214187
Compare
|
ZETETIC-REVIEW: REQUEST_CHANGES Revue adversariale PR #748 (round 3) — ADR-1110, theseus + pgvector_compiledRevue en lecture seule d'un worktree détaché 1. Statut des bloquants du verdict précédent (2026-10-16)
2. Table de mutation (une garde à la fois, tests ciblés, chaque mutation annulée ensuite ;
|
| Mutation | Fichier | Test qui la tue | Résultat |
|---|---|---|---|
| M1 marker écrit malgré provision échouée | launcher_pg_provision.py | test_a_server_that_does_not_start_selects_nothing | TUÉE |
| M2a éligibilité Setup sans mapping de l'option | launcher_pg_select.py | test_setup_with_the_hook_option_is_not_eligible | TUÉE |
M2b apply() sans mapping de l'option |
launcher_backend.py | test_the_hook_option_wins_over_a_managed_marker_and_starts_nothing | TUÉE |
| M3 PermissionError => vivant | launcher_pg_server.py | aucun (9 passed) | SURVIT (le test « pid d'un autre utilisateur » passe par le repli sur le port, qui masque la mutation) |
| M4 sha256 non comparé | launcher_pg_assets.py | test_a_digest_mismatch_is_refused_and_nothing_installed | TUÉE |
M5 _init_schema « ready » malgré erreurs DDL |
setup_db.py | test_one_failed_statement_fails_and_names_it_after_running_all | TUÉE |
| M6a identité loopback supprimée | launcher_pg_server.py | test_an_emulated_postmaster_is_recognised_by_its_listening_port | TUÉE |
| M6b identité = tout pid vivant | launcher_pg_server.py | test_a_reused_pid_of_this_user_is_not_a_postmaster | TUÉE (cas sans écouteur seulement ; le cas avec écouteur n'est pas testé, cf. B2) |
| M7 contrôle des membres tar désactivé | launcher_pg_assets.py | test_a_link_leaving_the_tree_is_refused | TUÉE |
| M8 éligibilité ignore un store SQLite existant | launcher_pg_select.py | test_an_existing_sqlite_store_is_never_switched | TUÉE |
Suite exécutée : 89 passed, 1 skipped (le skip est le e2e opt-in CORTEX_MANAGED_PG_E2E, remplacé par mon exécution manuelle ci-dessus). python scripts/check_craftsmanship.py : OK. Digests : les 8 sha256 épinglés (4 triples x 2 archives) sont égaux aux digests que GitHub publie aujourd'hui pour ces assets (gh api .../releases/tags/...).
3. Constats
BLOQUANTS
B1 — Setup avec marker managed démarre le serveur managed même quand database_url est configuré (viole ADR-1110 §4a).
launcher_pg_provision._target renvoie (Path(marker["root"]), True, "") dès que le marker est managed, avant tout appel à map_database_option / environment_decides. Contre-exemple (e1.py) : marker managed + CLAUDE_PLUGIN_OPTION_DATABASE_URL=postgresql://u:p@remote/db => provision est appelée ; sur erreur, setup_step renvoie une cause non vide, donc Setup échoue (_fail). Conséquence : l'échappatoire que le message d'erreur du PR recommande (« or set DATABASE_URL ») marche pour les lancements mais pas pour le Setup suivant (mise à jour du plugin, job de maintenance) : l'utilisateur qui a configuré sa propre base voit son Setup échouer, ou télécharger et démarrer un PG qu'il a refusé. Correction : appliquer map_database_option + environment_decides dans _target avant la branche managed (un store configuré gagne, rien de managed ne démarre, message explicite) + test « marker managed + option => aucun provision ». Effet exact d'un Setup failed sur les hooks suivants : non mesuré.
B2 — L'identité par port loopback fait d'un processus étranger « le nôtre » (l'affirmation « cannot make a foreign process ours » est fausse).
live_postmaster : pid vivant de cet utilisateur dont l'exécutable n'est pas bin/postgres + n'importe quel port en écoute => couple accepté ; le port n'est pas lié au pid. Contre-exemples exécutés : (foreign.py) un pid quelconque + un écouteur quelconque => live_postmaster renvoie le couple ; (stopprobe.py, faux pg_ctl qui enregistre ses arguments, un sleep 300 que j'ai lancé, un écouteur) => stop() invoque pg_ctl -D <cluster> -m fast -w stop, et start() renvoie le port étranger sans retirer le postmaster.pid. Un vrai pg_ctl stop signale le pid du fichier (comportement de PostgreSQL, non mesuré ici : je n'ai rien voulu tuer) : un processus étranger de l'utilisateur serait interrompu ; l'adoption est permanente tant que fichier et écouteur existent (status running sur un port étranger, authentification en échec à chaque lancement : verrouillage jusqu'à suppression manuelle). Probabilité faible (réutilisation de pid ET port éphémère en écoute, typiquement après un reboot), mais c'est la famille du bloquant 4 de la revue précédente et la correction est peu coûteuse. Correction : prouver l'identité par le protocole (SSLRequest PostgreSQL, réponse S ou N exigée) ou lier le port au pid (répertoire de données de la ligne 2 de postmaster.pid, écouteur appartenant à ce pid) + test avec écouteur étranger.
B3 — La commande livrée /preflight (python -m mcp_server.doctor) plante avec un traceback pour tout utilisateur d'une installation fraîche managed (régression vs main).
doctor.active_checks appelle effective_backend(os.environ), qui lève BackendMarkerError sur un marker managed sans DATABASE_URL (reproduit : HOME de scratch avec marker managed => traceback « the backend marker selects the plugin's managed PostgreSQL server ... or set DATABASE_URL »). Sur main une installation fraîche reçoit un marker sqlite et le doctor tourne. Le marker managed devenant le cas par défaut sur macOS, l'outil de diagnostic casse précisément chez l'utilisateur qui en a besoin. Le commentaire de doctor (« BackendMarkerError propagates ») visait un marker corrompu, pas un marker valide. Correction : le doctor connaît managed (résolution via le launcher ou rapport de l'état du serveur managed) + test.
NON BLOQUANTS (classés)
- N1. Repli SQLite bruyant seulement dans
setup.log(et dans le journal quescripts/init_plugin.pyaffiche en direct).setup_steprenvoie"": l'outcome resteready,init_pluginsort 0, et aucun lecteur deprovisioned.jsonn'existe dansmcp_server/(grep). Un utilisateur marketplace ouclaude --init-onlynu voit « SQLite » sans cause ; le docstring de launcher_setup dit lui-même que l'hôte n'affiche rien de Setup. Conforme à la lettre de la décision d'owner (repli SQLite, cause dans setup.log), pas à l'esprit « pas de dégradation silencieuse ». Recommandé : champ dédié dans<key>.setup.json+ une ligne SessionStart. Pèse surtout sur le point (b). - N2.
pg_ctl start -w/stop -wsans-tniPGCTLTIMEOUT(grep : aucun « timeout » dans scripts/launcher_pg*.py) => l'horloge de 60 s par défaut de pg_ctl décide de l'échec. Un démarrage lent (récupération après crash, qemu) est déclaré en échec alors que le postmaster finit de monter ; le superviseur meurt, le suivant adopte. Le docstring « no timer of ours decides anything » est exact mot pour mot mais trompeur. Décision d'owner (règle « pas de timeout comme vérification »). - N3. Le mutant M3 survit : aucun test ne distingue PermissionError=>mort de PermissionError=>vivant, le repli sur le port masque la différence. Test à ajouter avec un écouteur.
- N4. TOCTOU :
_selectrevérifie l'existence du fichier SQLite, mais un lancement écrit son marker avant que le moteur créememory.db; un serveur MCP déjà parti en SQLite continuerait d'écrire dans le fichier après le basculement du marker (store scindé). Fenêtre étroite, non reproduit. Le test existant pré-crée le fichier. - N5. Tar :
_insidevalide noms et cibles de liens sans modéliser les chaînes de liens symboliques internes à l'archive ; la protection réelle estfilter="data"(3.10.12+, 3.11.4+, 3.12+), et sur les Python plus anciens le sha256 épinglé (vérifié avant toute extraction : M4) est la seule barrière. Raisonné, pas exécuté. Extraction toujours dans un scratch sousdist/des données du plugin. - N6. Provenance : l'archive pgvector vient du dépôt personnel
nicoloboschi/pgvector_compiled(release du 2026-01-31, mutable, sans attestation). Les digests épinglés protègent contre une substitution ultérieure, pas contre le contenu initial ; PRIVACY/README le divulguent. - N7. Un marker global (
~/.claude/methodology) pointe vers un store qui vit dans les données d'un plugin : désinstaller supprime les mémoires (divulgué dans PRIVACY) et laisse un marker pendant ;attachdit « root is missing » sans indiquerclaude --init-only. Le Setup suivant reprovisionne un store vide à la même racine. - N8. Non mesurés : latence d'un hook lancé sans serveur MCP vivant (start + stop de PG par hook) ; accessibilité du store managed pour cortex-viz,
mcp_server.migrate, benchmarks (port aléatoire par start, mot de passe en fichier, arrêt au dernier client). - N9.
free_portpuispg_ctl -p: course sur le port, mais l'échec est bruyant et le start suivant retire un nouveau port. Accepté. - Vérifié sain : flock (ordre start/supervisor/clients, aucune attente chronométrée de notre côté), état écrit seulement après succès, sha256 avant extraction, mot de passe jamais en argv, droits 0600/0700, extraction confinée aux données du plugin, éligibilité qui protège un store SQLite existant, aucune migration dans cette PR (le marker
sqlitedu launcher + fichier présent reste inéligible : l'importeur futur peut le basculer).
4. Jugement sur les points ouverts de l'auteur
(a) Marker global lu sans le launcher (mcp_server/__main__.py, hooks/entry.py, Codex runtime.py qui exécute hooks.entry). Pas une régression pour un utilisateur existant : le marker managed n'est écrit que pour un store sans état antérieur (why_not_eligible, M8) après provisionnement réussi. C'est une régression pour un nouvel utilisateur Claude + Codex sur le même HOME (sur main ils partageaient le SQLite créé au premier lancement ; maintenant Codex et hypermnesia-mcp direct échouent bruyamment avec BackendMarkerError tant que DATABASE_URL n'est pas posé). Bruyant, pas de scission de données : NE BLOQUE PAS en soi, sous réserve que CHANGELOG/README Codex le disent (aujourd'hui seul l'ADR le dit). En revanche /preflight (B3) bloque. Décision d'owner : Codex doit-il s'attacher au serveur managed ?
(b) Linux glibc non autonome => SQLite : acceptable pour un déploiement par étapes, non bloquant, mais seulement avec N1 corrigé ; sinon l'utilisateur Ubuntu 25.10 ne saura jamais pourquoi.
(c) Anciens dist/ non nettoyés : non bloquant (croissance disque à chaque déplacement de pin) ; à suivre avec la règle d'hygiène disque de l'owner.
(d) Installation fraîche sans claude --init-only => SQLite : acceptable (cohérent avec l'éligibilité : un store existant n'est jamais basculé) ; l'importeur devra basculer le marker sqlite écrit par le launcher.
5. Décisions d'owner nécessaires
- Codex / lancements directs : s'attachent-ils au serveur managed, ou erreur documentée (état actuel) ?
pg_ctl -wavec son délai de 60 s par défaut : accepté, ou-t/PGCTLTIMEOUTtrès grand + attente événementielle ?- Repli SQLite : suffit-il de
setup.log, ou faut-il une ligne visible (outcome Setup + SessionStart) ? - Confiance dans
nicoloboschi/pgvector_compiled(binaire d'un dépôt personnel, digest épinglé) ou compilation par la CI de Cortex. - Durée de vie du store managed = celle des données du plugin (désinstallation = perte) alors que le marker est global : acceptable avant l'importeur ?
6. Non vérifié
Signal réel envoyé par pg_ctl stop à un pid étranger (faux pg_ctl utilisé) ; les exécutions Linux (glibc, qemu) et Rosetta x86_64 (preuves de l'auteur et jobs CI seulement ; j'ai rejoué macOS arm64 natif) ; effet d'un Setup failed sur les hooks suivants ; course N4 ; chaînes de liens tar ; Python < 3.10.12 ; latence des hooks ; cortex-viz.
Artefacts : mut.log, mut.py, e1.py, foreign.py, stopprobe.py dans ~/.claude/fleet/evidence/cortex-748-round3/.
… theseus tree (ADR-1110, D4) Builds on a runner of each of the four triples, proves the library loads (postgres --single), checks the glibc floor on Linux, attests build provenance and publishes the archives once as a prerelease of this repo. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BYyqzx4FVMHasVPZV3uBYq
…ed lifecycle, every surface attaches (ADR-1110)
Blockers of the round-3 review:
- B1: Setup applies database_url / CORTEX_* before reading a managed marker;
nothing managed is installed or started and Setup says so.
- B2: a postmaster is ours only when postmaster.pid names this data
directory and the {pid, kernel start time, port} recorded at spawn; only
then is the port dialled, and the server must complete SCRAM-SHA-256 with
the cluster password (server signature checked). A foreign listener is
never adopted, dialled or signalled.
- B3: the doctor attaches to the managed server and reports it as a check.
- M3: PermissionError test with a listener present.
Owner decisions:
- D1: the attach code moves into the wheel (mcp_server/infrastructure/
managed_pg*.py); apply_backend_resolution attaches on a managed marker, so
hypermnesia-mcp, the Codex hook entry and cortex-doctor share the server.
- D2: postgres is the supervisor's child; ready = postmaster.pid "ready"
(kqueue/inotify), failure = end of file on an inherited pipe, stop =
SIGINT then flock on postmaster.lock held by the whole group. No pg_ctl -w,
no timer; zombies read as gone; works under qemu (no pidfd).
- D3: sqlite_fallback recorded in <key>.setup.json, shown at SessionStart.
- D4: pins point to the pgvector 0.8.7 archives Cortex CI built and attested
(release pgvector-v0.8.7-pg18.6.0, run 39017072528).
- D5: cluster root ~/.claude/methodology/managed-pg; binaries stay in the
plugin data; uninstalling never deletes memories.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BYyqzx4FVMHasVPZV3uBYq
|
ZETETIC-REVIEW: REQUEST_CHANGES Revue adversariale PR #748 (round 4) — ADR-1110, PostgreSQL géréRevue en lecture seule, worktree détaché Un seul bloquant (B4, numéroté à la suite de B1-B3 du round 3), peu coûteux. Les trois bloquants du round 3 (B1, B2, B3) et M3 sont résolus, avec contre-exemples rejoués ; les cinq décisions d'owner sont implémentées. Ce qui bloque, c'est la règle d'owner « aucun timeout comme vérification » : le mutant D2 n'est tué que par le timeout pytest de 300 s, alors qu'un test événementiel le tue immédiatement (exécuté). 1. Bloquants du round 3, rejoués sur ce head
e2e rejoué par moi (macOS arm64, 2. BLOQUANTB4 — Le mutant D2 n'est tué que par une horloge (pytest-timeout 300 s), contre la règle d'owner « aucun timeout/attente/budget de temps comme vérification, tests et CI compris ».
3. NON BLOQUANTS (classés)N1 — Signature serveur et nonce SCRAM : code correct, gardes non testées. N2 — Superviseur tué entre spawn et enregistrement : récupération automatique, mais par accident, et l'ADR dit le contraire (point 4).
N3 — Un postmaster mort sous un superviseur vivant n'est jamais remplacé tant qu'un client vit. N4 — D4 : chaîne d'approvisionnement saine. La provenance ne nomme qu'une branche, et une branche peut réserver une release. N5 — D1 vérifié, avec deux restes. N6 — Point 7 : N7 — Statut Vérifié sain, exécuté : chemin de readiness, où PG réécrit 4. Réponses aux points de l'orchestrateur
5. Table de mutation (exécutée par moi sur 52c3156, une garde à la fois,
|
| Mutation | Test(s) lancé(s) | Résultat |
|---|---|---|
| Base, head non modifié | test_managed_pg_server.py | 14 passed |
| M3 PermissionError => vivant | test_permission_error_is_not_ours_even_with_a_listener | TUÉE |
B2-stop stop() lit read_lockfile au lieu de live_postmaster |
test_stop_never_signals_a_foreign_process | TUÉE |
| SIG signature serveur SCRAM non vérifiée | test_managed_pg_server.py + test_managed_pg_attach.py | SURVIT (19 passed) |
| NONCE nonce serveur non vérifié | idem | SURVIT (19 passed) |
| TRUST client qui attend AuthOK sans SCRAM | idem | tuée, mais trivialement : fake_postgres n'envoie jamais AuthOK directement, donc le refus du mode trust n'est pas testé non plus |
D2 (mutant de l'auteur) avec le test événementiel proposé (d2_event.py, hors pytest) |
— | TUÉE immédiatement ; le head passe |
Les mutants de l'auteur que je n'ai pas rejoués (M1, M2a/b, M4 à M8, B1, B2-start, B3, D1, D3, D5) : sa table sur 8df17473 / 52c3156, non revérifiée par moi.
6. Non vérifié
Linux (glibc, qemu) et Rosetta x86_64 (preuves de l'auteur seulement ; j'ai rejoué macOS arm64 natif) ; latence des hooks sans serveur vivant ; désaccord de versions plugin/roue Codex ; attestation depuis main (après merge) ; sous-cas N2 où l'orphelin n'a pas encore écrit postmaster.pid ; N7 ; suite complète (CI).
7. Nettoyage
- Worktree détaché
.claude/worktrees/review-748-r4supprimé (git worktree remove, status propre, absent degit worktree listet du disque).register-worktreel'avait refusé (worktree détaché) : aucune entrée de ledger, aucun hook ne le reprendra. - Répertoire temporaire
disk-hygiene-xygw9lpe(185 Mo : venv de scratch, archives téléchargées, cluster et HOME de scratch, sondes) enregistré parcreate-temp, marquéevidence-preserved, puis supprimé pardispose. - Ref locale
refs/remotes/pr/748supprimée. - Processus : tous ceux que j'ai lancés sont terminés (orphelin postgres 12925 arrêté par PG lui-même, superviseurs et postmasters de scratch arrêtés au dernier client ;
ps: 0 restant). Je n'ai tué aucun processus que je n'avais pas lancé. - Conservé : ce verdict et
~/.claude/fleet/evidence/cortex-748-round4-review/(80 Ko : sondes, sorties, rapport e2e, table de mutation). Le worktree de l'auteurfeat-744-pg0-launchern'a pas été touché (sa suppression revient à l'orchestrateur).
Closes #744 and #740 (reproducing the locked-down Mac).
Three commits:
6214187b(round 3: theseus binaries replace pg0, ADR-1110),d6f869d8(D4: Cortex CI compiles pgvector; the release assets the pins name were attested from this exact commit), and the round-4 fix on top. ADR-1110 (wiki page and its mirror) is rewritten for every decision below.Round 4: the round-3 blockers, red then green
managedmarker ignoreddatabase_urle1.py:provision called: ['…/data/pg'], Setup failse1.py:provision called: [],said: managed PostgreSQL not selected: database_url … selects the store; the managed server is not started; testtest_a_configured_database_wins_over_a_managed_marker_in_setupstop()ranpg_ctl stopon itforeign.py:live_postmaster -> (31490, 54222);stopprobe.py:stop() invoked pg_ctl: -D … -m fast -w stoptest_start_never_adopts_or_dials_a_foreign_listener,test_stop_never_signals_a_foreign_process(the foreign process is neither signalled nor dialled),test_a_reused_pid_with_a_listener_is_not_ours,test_an_unrecorded_pid_with_a_listener_is_not_ourspython -m mcp_server.doctorcrashed on a managed markerBackendMarkerError: the backend marker selects the plugin's managed PostgreSQL server …(traceback)[FAIL] Managed PostgreSQL server … run \claude --init-only`with a fix line, no traceback;test_the_doctor_reports_the_managed_server_instead_of_crashing`mut.py M3rerun on 6214187:rc 0 ['9 passed']test_permission_error_is_not_ours_even_with_a_listenerkills it (table below){"status": "ready", "error": ""}{"status": "ready", "error": "", "sqlite_fallback": "managed PostgreSQL did not start: …"}+[cortex-setup] SQLite fallback: …; SessionStart adds the lineSQLite fallback: <cause>Owner decisions
bin/postgres -D <cluster> -p <port>as its own child and records{pid, started, port},startedbeing the kernel's start time of that PID (proc_pidinfo,/proc/<pid>/stat; a zombie has none). Only after that proof is the port dialled, and the server must finish SCRAM-SHA-256 with the cluster's password, server signature included (managed_pg_protocol, stdlib). Nopg_ctlany more: readiness ispostmaster.pidreportingready(kqueue / inotify events); failure is end of file on a pipe whose write end only the postmaster's process group holds; stop is SIGINT then aflockonpostmaster.lock, granted once the postmaster and every child it forked are gone. No sleep, timeout or attempt cap anywhere; themanaged-pgCI job lost itstimeout-minutes. No pidfd: it is ENOSYS under qemu-user (measured).mcp_server/infrastructure/managed_pg*.py, renames ofscripts/launcher_pg*.py).apply_backend_resolutionattaches on amanagedmarker, sohypermnesia-mcp, the Codex hook entry (mcp_server.hooks.entry, whatruntime.pyexecutes —runtime.pyitself needed no change) andcortex-doctoruse the same locks and supervisor as the launcher.effective_backendstays pure (postgresql, starts nothing). Tests:test_a_codex_hook_attaches_to_the_managed_server,test_claude_and_codex_on_one_home_share_one_server; the e2e checksdirect_launch_same_url.sqlite_fallbackin<key>.setup.json(kept if a later step fails) and the SessionStart line, only while the store is SQLite..github/workflows/pgvector.yml+scripts/pgvector_build.py: pgvector v0.8.7 (commitf37c13f6verified; 0.8.5 → 0.8.7 for the IVFFlat build buffer overflow fixed upstream), built with PGXS against the pinned theseus 18.6 tree on macos-15, macos-15-intel, ubuntu-22.04 and ubuntu-22.04-arm,OPTFLAGS="", load-proved withpostgres --single, glibc ≤ 2.34 checked, deterministic archive,actions/attest-build-provenance, published once as prereleasepgvector-v0.8.7-pg18.6.0(run 39017072528; an existing release is never modified). The four pinned digests are those of the published assets;gh attestation verify <archive> --repo cdeust/Cortexpasses for each (subject digest = pin, source reffeat/744-pg0-launcher, commitd6f869d8). The workflow also runs whenlauncher_pg_assets.pychanges: a version bump pushed on any branch publishes before review.~/.claude/methodology/managed-pg(data, password, state, locks, logs); binaries stay in<plugin data>/pg/dist. Uninstalling the plugin never deletes memories;has_earlier_stateignoresmanaged-pg.Mutation table (one guard at a time, targeted tests, restored after)
test_a_server_that_does_not_start_selects_nothingtest_setup_with_the_hook_option_is_not_eligiblelauncher_backend.applyskips the option mappingtest_the_hook_option_wins_over_a_managed_marker_and_starts_nothingtest_permission_error_is_not_ours_even_with_a_listenertest_a_digest_mismatch_is_refused_and_nothing_installed_init_schemaready despite DDL errorstest_one_failed_statement_fails_and_names_it_after_running_alltest_a_recorded_live_postmaster_is_ourstest_a_reused_pid_with_a_listener_is_not_ourstest_a_link_leaving_the_tree_is_refusedtest_an_existing_sqlite_store_is_never_switchedtest_a_configured_database_wins_over_a_managed_marker_in_setuppostmaster.pidnamestest_start_never_adopts_or_dials_a_foreign_listenerpostmaster.pidnamestest_stop_never_signals_a_foreign_processtest_the_doctor_reports_the_managed_server_instead_of_crashingtest_a_codex_hook_attaches_to_the_managed_serverwaitpid, not the pipetest_the_exit_is_read_from_the_pipe_not_from_waitpidpytest-timeoutguard ends it (the failure mode this mutant is about, not an assertion)test_an_eligible_store_left_on_sqlite_records_whytest_the_managed_root_is_not_earlier_cortex_stateM3, M6a, M6b, B2-start, B2-stop, B3 and D1 were rerun on this head (their tests changed in the last amend); the others on
8df17473, whose product code is this head's. On the first pass the B2 anchors no longer matched the reworked code (PATTERN NOT FOUND), and once re-anchored B2-stop survived because a SIGINT inherited as ignored (the suite ran as a background job) is invisible to a plainsleep-like process; the foreign process now records every SIGINT with an explicit handler, and B2-stop is killed.Measured on this head
managed_pg_e2e.py,sandbox-exec no_homebrew.sb,env -i, fresh HOME, uv Python outside /opt/homebrew): macOS arm64 and x86_64 (Rosetta)passed: true,cluster_root = $HOME/.claude/methodology/managed-pg, binaries underdata/pg/dist,direct_launch_same_url: true, vector 0.8.7, pg_trgm 1.6, status after last clientstopped, nopostmaster.pidleft. Ubuntu 24.04 arm64 container:passed: true, same checks.test_managed_pg_server.pyon Linux arm64 and Linux x86_64 under qemu, non-root, in a container without init: all passed.ruff check,ruff format --check,check_craftsmanship.py --base origin/main,check_doc_claims,check_version_surfaces,check_ci_gate_complete,check_project_wiki,check_no_deps_invariant: OK. pyright 1.1.414 with theuv.locktypecheck env, Linux and Darwin platforms: 0 errors.709 passed, 1 skippedon8df17473(51 min); this head differs only intest_managed_pg_server.pyandtest_managed_pg_attach.py, rerun here:19 passed.poll()could still say "running" because descriptors close before a process becomes reapable — the exit is now decided by the pipe's end of file (product code, regression test); (b) the fallback test leakedCORTEX_SQLITE_FALLBACKinto later hook tests (_ensure_runtimewritesos.environ); (c) the test postmaster installed its SIGINT handler late. Plus two test races fixed (port read fromstatus.jsonafter the supervisor stopped; SIGINT inherited as ignored).Known limits (unchanged unless stated)
Linux distributions without
libxml2.so.2, OpenSSL 3 orlibgssapi_krb5.so.2stay on SQLite, now with the cause shown. Native Windows and musl stay on SQLite. A fresh install that skipsclaude --init-onlykeeps SQLite until the importer exists. Olddist/trees are not collected. A supervisor killed between spawning the postmaster and recording it leaves a group holdingpostmaster.lock; the next start then fails loudly naming it.🤖 Generated with Claude Code
https://claude.ai/code/session_01BYyqzx4FVMHasVPZV3uBYq