From 48560319ae57f74dfdbeb8e5714781c26faf5138 Mon Sep 17 00:00:00 2001 From: "BenClawBot (AlphaOM3G1)" Date: Mon, 7 Sep 2026 14:25:04 +0200 Subject: [PATCH 01/16] fix: add base schema as migration 001 --- src/db/migrations/001_base_schema.sql | 78 +++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 src/db/migrations/001_base_schema.sql diff --git a/src/db/migrations/001_base_schema.sql b/src/db/migrations/001_base_schema.sql new file mode 100644 index 0000000..683b960 --- /dev/null +++ b/src/db/migrations/001_base_schema.sql @@ -0,0 +1,78 @@ +-- Open Brain Database Schema +-- PostgreSQL + pgvector + +-- Enable extensions +CREATE EXTENSION IF NOT EXISTS "uuid-ossp"; +CREATE EXTENSION IF NOT EXISTS "vector"; + +-- Core memory table +CREATE TABLE IF NOT EXISTS memory ( + id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), + source VARCHAR(50) NOT NULL, + source_id VARCHAR(255), + captured_by TEXT, + content TEXT NOT NULL, + raw_content TEXT, + embedding vector(768), + entities JSONB DEFAULT '{}', + tags TEXT[] DEFAULT '{}', + tag_sources JSONB DEFAULT '{}', + importance FLOAT DEFAULT 0.5, + created_at TIMESTAMP DEFAULT NOW(), + original_date TIMESTAMP, + language VARCHAR(10), + metadata JSONB DEFAULT '{}' +); + +-- Indexes for performance +CREATE INDEX IF NOT EXISTS idx_memory_embedding ON memory USING hnsw (embedding vector_cosine_ops); +CREATE INDEX IF NOT EXISTS idx_memory_entities ON memory USING gin (entities); +CREATE INDEX IF NOT EXISTS idx_memory_tags ON memory USING gin (tags); +CREATE INDEX IF NOT EXISTS idx_memory_source ON memory (source); +CREATE INDEX IF NOT EXISTS idx_memory_captured_by ON memory (captured_by); +CREATE INDEX IF NOT EXISTS idx_memory_created ON memory (created_at DESC); +CREATE INDEX IF NOT EXISTS idx_memory_original_date ON memory (original_date DESC); + +-- Full-text search index +CREATE INDEX IF NOT EXISTS idx_memory_content_fts ON memory USING GIN (to_tsvector('english', content)); + +-- Function to get memories from today +CREATE OR REPLACE FUNCTION get_today_memories(limit_count INTEGER DEFAULT 10) +RETURNS TABLE ( + id UUID, + source VARCHAR, + content TEXT, + tags TEXT[], + created_at TIMESTAMP, + importance FLOAT +) AS $$ +BEGIN + RETURN QUERY + SELECT m.id, m.source, m.content, m.tags, m.created_at, m.importance + FROM memory m + WHERE m.created_at >= CURRENT_DATE + ORDER BY m.importance DESC, m.created_at DESC + LIMIT limit_count; +END; +$$ LANGUAGE plpgsql; + +-- Function to get memory statistics +CREATE OR REPLACE FUNCTION get_memory_stats() +RETURNS TABLE ( + total_count BIGINT, + source_name VARCHAR, + source_count BIGINT, + tag_name TEXT, + tag_count BIGINT +) AS $$ +BEGIN + RETURN QUERY + SELECT + COUNT(*)::BIGINT as total_count, + NULL::VARCHAR as source_name, + NULL::BIGINT as source_count, + NULL::TEXT as tag_name, + NULL::BIGINT as tag_count + FROM memory; +END; +$$ LANGUAGE plpgsql; From 40f2a8d46e3bc254a8d2f6ac8ad44e1693eafbfd Mon Sep 17 00:00:00 2001 From: "BenClawBot (AlphaOM3G1)" Date: Mon, 7 Sep 2026 14:26:48 +0200 Subject: [PATCH 02/16] feat: expose packaged database migrations in CLI --- src/cli/__init__.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/cli/__init__.py b/src/cli/__init__.py index 2741d3f..386ca0d 100644 --- a/src/cli/__init__.py +++ b/src/cli/__init__.py @@ -23,6 +23,21 @@ def _run(command: list[str]) -> None: subprocess.run(command, check=True) +def migrate_cmd() -> int: + """Apply packaged database migrations, including the base schema.""" + try: + from src.db.migrate import apply_migrations + applied = apply_migrations() + except Exception as exc: + print(f"Database migration failed: {exc}", file=sys.stderr) + return 1 + if applied: + print("Applied migrations: " + ", ".join(applied)) + else: + print("Database schema is up to date.") + return 0 + + def update_cmd(skip_migrations: bool = False) -> int: """Upgrade a pipx-managed installation and apply additive migrations.""" try: @@ -106,6 +121,8 @@ def main() -> int: serve_parser.add_argument("--port", "-p", type=int, default=8000) serve_parser.add_argument("--reload", action="store_true") + subparsers.add_parser("migrate", help="Create or update the database schema") + update_parser = subparsers.add_parser("update", help="Upgrade Open Brain and apply additive migrations") update_parser.add_argument("--skip-migrations", action="store_true") @@ -157,6 +174,8 @@ def main() -> int: if args.command == "serve": from .serve import serve_cmd return serve_cmd(args) + if args.command == "migrate": + return migrate_cmd() if args.command == "update": return update_cmd(skip_migrations=args.skip_migrations) if args.command == "configure": From 537e02830a00bb6e0e3f9cbf86f94413c828c0e2 Mon Sep 17 00:00:00 2001 From: "BenClawBot (AlphaOM3G1)" Date: Mon, 7 Sep 2026 14:27:20 +0200 Subject: [PATCH 03/16] test: cover packaged schema bootstrap --- tests/test_schema_bootstrap.py | 38 ++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 tests/test_schema_bootstrap.py diff --git a/tests/test_schema_bootstrap.py b/tests/test_schema_bootstrap.py new file mode 100644 index 0000000..088cb5d --- /dev/null +++ b/tests/test_schema_bootstrap.py @@ -0,0 +1,38 @@ +from importlib.resources import files + + +def test_base_schema_is_packaged_as_first_migration() -> None: + migration = files("src.db.migrations").joinpath("001_base_schema.sql") + sql = migration.read_text(encoding="utf-8") + + assert "CREATE TABLE IF NOT EXISTS memory" in sql + assert "CREATE EXTENSION IF NOT EXISTS \"vector\"" in sql + + +def test_migrate_command_applies_packaged_migrations(monkeypatch, capsys) -> None: + from src.cli import migrate_cmd + from src.db import migrate + + monkeypatch.setattr( + migrate, + "apply_migrations", + lambda: ["001_base_schema.sql", "002_continuity_foundation.sql"], + ) + + assert migrate_cmd() == 0 + output = capsys.readouterr().out + assert "001_base_schema.sql" in output + assert "002_continuity_foundation.sql" in output + + +def test_migrate_command_reports_failure(monkeypatch, capsys) -> None: + from src.cli import migrate_cmd + from src.db import migrate + + def fail() -> list[str]: + raise RuntimeError("database unavailable") + + monkeypatch.setattr(migrate, "apply_migrations", fail) + + assert migrate_cmd() == 1 + assert "database unavailable" in capsys.readouterr().err From 28053628e0a1008883eee30e6ae746dd176bedd3 Mon Sep 17 00:00:00 2001 From: "BenClawBot (AlphaOM3G1)" Date: Mon, 7 Sep 2026 14:28:47 +0200 Subject: [PATCH 04/16] docs: explain standalone database bootstrap --- docs/INSTALLATION.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/docs/INSTALLATION.md b/docs/INSTALLATION.md index 403dc29..2296fa0 100644 --- a/docs/INSTALLATION.md +++ b/docs/INSTALLATION.md @@ -10,6 +10,14 @@ The installer verifies Python 3.11+, installs or upgrades Open Brain through `pi Set `OPENBRAIN_INSTALL_HERMES=0` to skip automatic Hermes wiring. Set `OPENBRAIN_REPO_URL` to install from a fork. +For a standalone CLI install backed by an existing PostgreSQL + pgvector server, configure the database connection through `DB_HOST`, `DB_PORT`, `DB_NAME`, `DB_USER`, and `DB_PASSWORD`, then bootstrap the schema from the installed package: + +```sh +openbrain migrate +``` + +The migration chain now begins with `001_base_schema.sql`, so an empty database can be initialized without cloning the repository or locating `src/db/schema.sql` manually. The command is idempotent and uses the same checksum-protected migration ledger as `openbrain update`. + ## Hermes Automatic installation copies the packaged provider into `${HERMES_HOME:-~/.hermes}/plugins/openbrain`. Manual repair is available with: @@ -38,7 +46,7 @@ openbrain version-check openbrain update ``` -`version-check` is offline-safe. `update` upgrades the pipx installation and applies additive migrations. When package upgrade succeeds but migration fails, it exits non-zero and reports that existing data was not deleted. +`version-check` is offline-safe. `update` upgrades the pipx installation and applies additive migrations. When package upgrade succeeds but migration fails, it exits non-zero and reports that existing data was not deleted. You can also run `openbrain migrate` directly whenever you need to initialize or reconcile the database schema without upgrading the package. ## Diagnostics From 7cf4ee7b8cd03351009b1df62c62a3409a2e1c8a Mon Sep 17 00:00:00 2001 From: "BenClawBot (AlphaOM3G1)" Date: Mon, 7 Sep 2026 14:30:05 +0200 Subject: [PATCH 05/16] docs: document standalone schema bootstrap --- README.md | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index aa1b51e..ac7e451 100644 --- a/README.md +++ b/README.md @@ -124,6 +124,14 @@ curl -fsSL https://raw.githubusercontent.com/benclawbot/open-brain/master/instal The installer uses `pipx`, keeping Open Brain isolated from system Python packages. It generates a private OpenBrain API key once in `~/.config/openbrain/.env`; upgrades reuse that key. This path is intended for users who already have a Postgres+pgvector instance elsewhere — the docker stack on this repo is the supported local-stack path. +Point `DB_HOST`, `DB_PORT`, `DB_NAME`, `DB_USER`, and `DB_PASSWORD` at that database, then initialize it directly from the installed package: + +```bash +openbrain migrate +``` + +The packaged migration chain starts at `001_base_schema.sql`, so a clean pipx install can create the core schema without cloning the repository or manually downloading `src/db/schema.sql`. + Verify: ```bash @@ -213,6 +221,7 @@ openbrain stats openbrain import file ./notes.md openbrain report --days 7 openbrain serve --host 127.0.0.1 --port 8000 +openbrain migrate openbrain install-hermes openbrain update ``` @@ -353,4 +362,4 @@ See [`CHANGELOG.md`](CHANGELOG.md) for release details. ## License -MIT. +MIT. \ No newline at end of file From 499111899742296c164e8f22635e352c916bee73 Mon Sep 17 00:00:00 2001 From: "BenClawBot (AlphaOM3G1)" Date: Mon, 7 Sep 2026 14:33:12 +0200 Subject: [PATCH 06/16] test: make e2e module collection-safe --- tests/e2e/conftest.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/e2e/conftest.py b/tests/e2e/conftest.py index 782d5c7..d2e62ab 100644 --- a/tests/e2e/conftest.py +++ b/tests/e2e/conftest.py @@ -16,6 +16,11 @@ import pytest +# test_api.py distinguishes pytest from direct script execution using this +# environment marker. Pytest normally sets it only while running a test, which +# is too late for module collection; set a harmless collection value here. +os.environ.setdefault("PYTEST_CURRENT_TEST", "collection") + def _api_url() -> str: return os.environ.get("OPENBRAIN_E2E_URL", "http://127.0.0.1:8765").rstrip("/") From 6b08496fcf3e8cbf6763d701fa1a92d278a92ef4 Mon Sep 17 00:00:00 2001 From: "BenClawBot (AlphaOM3G1)" Date: Mon, 7 Sep 2026 14:36:59 +0200 Subject: [PATCH 07/16] release: bump package version to 1.0.2 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index c62cab2..02e12bb 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "openbrain" -version = "1.0.0" +version = "1.0.2" description = "Shared personal memory, agent continuity, and actionable context" readme = "README.md" requires-python = ">=3.11" From 5bba9172739a7beeece24014d6469d800f4e1759 Mon Sep 17 00:00:00 2001 From: "BenClawBot (AlphaOM3G1)" Date: Mon, 7 Sep 2026 14:37:10 +0200 Subject: [PATCH 08/16] release: align Hermes plugin version --- src/openbrain_hermes_plugin/plugin.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/openbrain_hermes_plugin/plugin.yaml b/src/openbrain_hermes_plugin/plugin.yaml index 4e8f548..aa7f75a 100644 --- a/src/openbrain_hermes_plugin/plugin.yaml +++ b/src/openbrain_hermes_plugin/plugin.yaml @@ -1,5 +1,5 @@ name: openbrain -version: 1.0.0 +version: 1.0.2 description: Shared personal memory and cross-agent continuity through Open Brain hooks: - on_session_end From 264b24365d7cdfe376d7ae1574e951be1e4f5697 Mon Sep 17 00:00:00 2001 From: "BenClawBot (AlphaOM3G1)" Date: Mon, 7 Sep 2026 14:37:52 +0200 Subject: [PATCH 09/16] release: document 1.0.2 --- CHANGELOG.md | 43 +++++++++++++++++++++++++++++++------------ 1 file changed, 31 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 99dde31..1160c30 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,21 +4,38 @@ All notable changes to Open Brain are documented in this file. The project follo ## [Unreleased] +## [1.0.2] - 2026-09-07 + +### Fixed +- Capped the MCP Python SDK dependency to `mcp>=1.0.0,<2.0.0`, preventing fresh installs from resolving the incompatible 2.x API while Open Brain still uses the 1.x server decorators. +- Added bounded chunking, pooling, and validation for oversized embedding inputs so provider context limits no longer make memory storage fail outright. +- Added shared embedding regeneration for REST and MCP, with safe NULL-only replacement by default and an explicit force mode for embedding-model migrations. +- Made migration `015_embedding_dim_change.sql` safe on databases where the legacy `memory` table is not present yet. +- Added `001_base_schema.sql` to the packaged migration chain so an empty PostgreSQL + pgvector database can be initialized from a pip/pipx install. +- Added `openbrain migrate` as the supported installed-package database bootstrap/update command. +- Fixed e2e pytest collection so the standalone API runner skips cleanly when the API stack is unavailable instead of aborting the full test suite during module import. + ### Changed -- Default docker-compose stack now ships an `ollama` service running the `nomic-embed-text` model (768-dim). The api no longer requires an external embeddings API key for a working local stack. -- Default API host port changed from `8000` to `8765`. Port 8000 is held by the Windows IP Helper service (`iphlpsvc`) on many Windows hosts, which prevents the api container from binding. Override with `API_PORT` in `.env`. -- `scripts/setup_db.py` now honours `DB_HOST`/`DB_PORT`/`DB_NAME`/`DB_USER` environment variables, matching the pattern used by `check_db.py`. The api container's compose-injected values (e.g. `postgres:5432`) now apply correctly on first run. -- `scripts/startup.sh` now invokes `scripts/migrate.py` after `setup_db.py`. The README always claimed the API applied migrations on startup; the code now matches the documentation. -- `src/db/connection.py` registers `psycopg2.extras.UUID_adapter` at import time. Queries that bind `uuid.UUID` parameters (`get_memory_by_id`, etc.) no longer raise `ProgrammingError: can't adapt type 'UUID'`. +- Documented the standalone PostgreSQL + pgvector bootstrap flow in the README and installation guide. +- Aligned package and Hermes plugin release metadata on version `1.0.2`. +- Added release automation that publishes a tagged GitHub release with wheel and source-distribution artifacts only after `Verify` succeeds on `master`. -### Added -- `scripts/quickstart.sh` brings up the docker stack, waits for the api to become healthy, ensures the embedding model is available, and (with `--with-hermes`) wires Open Brain into a locally-installed Hermes as the active memory provider. Idempotent. -- Migration `015_embedding_dim_change.sql` alters `memory.embedding` from `vector(1536)` to `vector(768)` and rebuilds the HNSW index to match the new Ollama-backed default embedder. -- `.gitattributes` forces LF line endings for `*.sh`, `*.py`, `*.sql`, and similar source files so Windows checkouts no longer reintroduce CRLF into shell scripts. -- `.gitignore` now excludes `.env`; `.env.example` is the single source of truth for non-secret defaults. +## [1.0.1] - 2026-07-24 + +### Fixed +- Made the local Docker stack work from a fresh clone by enforcing LF shell-script line endings, honoring database environment variables, running migrations during API startup, and registering psycopg2 UUID adaptation. +- Updated NLTK resource handling for the 3.8.2+ resource-name changes and baked the required data into the API image. -### Security -- Removed the previously-checked-in `.env` from version control. Operators should `cp .env.example .env` (or let `openbrain configure --project-root .` generate the secrets) before first run. +### Changed +- Switched the default local embedding service to Ollama with `nomic-embed-text` (768 dimensions), removing the need for an external embeddings API key for the default stack. +- Changed the default host API port from `8000` to `8765` to avoid common Windows port conflicts. +- Added migration `015_embedding_dim_change.sql` to align `memory.embedding` with the 768-dimensional default model and rebuild the HNSW index. +- Removed the checked-in `.env`; `.env.example` is now the non-secret source of defaults and generated credentials remain private. +- Added `.gitattributes` so Windows checkouts do not reintroduce CRLF into scripts and source files. + +### Added +- Added `scripts/quickstart.sh` for idempotent local stack bootstrap and optional Hermes wiring. +- Added the API e2e suite covering health, memory, continuity, context, review workflows, maintenance, imports, and authentication behavior. ## [1.0.0] - 2026-07-22 @@ -51,4 +68,6 @@ All notable changes to Open Brain are documented in this file. The project follo A production deployment is ready only after automatic checks pass and operators explicitly attest that TLS, backups, restore drills, monitoring, and migration records have been verified. Open Brain deliberately does not infer these external controls from configuration alone. +[1.0.2]: https://github.com/benclawbot/open-brain/releases/tag/v1.0.2 +[1.0.1]: https://github.com/benclawbot/open-brain/releases/tag/v1.0.1 [1.0.0]: https://github.com/benclawbot/open-brain/releases/tag/v1.0.0 From 723eacfae7d65a36381c249088c336256a374500 Mon Sep 17 00:00:00 2001 From: "BenClawBot (AlphaOM3G1)" Date: Mon, 7 Sep 2026 14:38:21 +0200 Subject: [PATCH 10/16] release: publish verified package artifacts --- .github/workflows/release.yml | 79 +++++++++++++++++++++++++++++++++++ 1 file changed, 79 insertions(+) create mode 100644 .github/workflows/release.yml diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..a73b800 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,79 @@ +name: Release + +on: + workflow_run: + workflows: ["Verify"] + types: [completed] + +permissions: + contents: write + +jobs: + release: + if: >- + github.event.workflow_run.conclusion == 'success' && + github.event.workflow_run.event == 'push' && + github.event.workflow_run.head_branch == 'master' + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + ref: ${{ github.event.workflow_run.head_sha }} + fetch-depth: 0 + - uses: actions/setup-python@v5 + with: + python-version: "3.11" + - name: Install build tooling + run: python -m pip install --upgrade build + - name: Read package version + id: version + shell: bash + run: | + VERSION="$(python - <<'PY' + import tomllib + from pathlib import Path + + project = tomllib.loads(Path('pyproject.toml').read_text(encoding='utf-8'))['project'] + print(project['version']) + PY + )" + echo "version=$VERSION" >> "$GITHUB_OUTPUT" + echo "tag=v$VERSION" >> "$GITHUB_OUTPUT" + - name: Check whether release already exists + id: existing + env: + GH_TOKEN: ${{ github.token }} + TAG: ${{ steps.version.outputs.tag }} + shell: bash + run: | + if gh release view "$TAG" >/dev/null 2>&1; then + echo "exists=true" >> "$GITHUB_OUTPUT" + else + echo "exists=false" >> "$GITHUB_OUTPUT" + fi + - name: Build package + if: steps.existing.outputs.exists == 'false' + run: python -m build + - name: Smoke-test release wheel + if: steps.existing.outputs.exists == 'false' + env: + EXPECTED_VERSION: ${{ steps.version.outputs.version }} + shell: bash + run: | + python -m venv /tmp/openbrain-release-smoke + /tmp/openbrain-release-smoke/bin/pip install dist/*.whl + ACTUAL_VERSION="$(/tmp/openbrain-release-smoke/bin/openbrain --version | awk '{print $3}')" + test "$ACTUAL_VERSION" = "$EXPECTED_VERSION" + - name: Publish GitHub release + if: steps.existing.outputs.exists == 'false' + env: + GH_TOKEN: ${{ github.token }} + TAG: ${{ steps.version.outputs.tag }} + VERSION: ${{ steps.version.outputs.version }} + TARGET: ${{ github.event.workflow_run.head_sha }} + shell: bash + run: | + gh release create "$TAG" dist/*.whl dist/*.tar.gz \ + --target "$TARGET" \ + --title "Open Brain $VERSION" \ + --generate-notes From f989639328432e5b47b95aa6d414f8f18f8b0a88 Mon Sep 17 00:00:00 2001 From: "BenClawBot (AlphaOM3G1)" Date: Mon, 7 Sep 2026 14:39:49 +0200 Subject: [PATCH 11/16] release: align README with 1.0.2 --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index ac7e451..c3dd2b1 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ [![Verify](https://github.com/benclawbot/open-brain/actions/workflows/verify.yml/badge.svg)](https://github.com/benclawbot/open-brain/actions/workflows/verify.yml) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/license/mit) -[![Version](https://img.shields.io/badge/version-1.0.0-blue)](CHANGELOG.md) +[![Version](https://img.shields.io/badge/version-1.0.2-blue)](CHANGELOG.md) ## Overview @@ -354,7 +354,7 @@ src/ ## Release status -**Open Brain 1.0.0 is the first production/stable release.** The coordinated production-readiness train delivered deployment hardening, database resilience, real adapter host wiring, durable reconciliation, staged imports, unified proposal review workflows, and a machine-readable release gate. +**Open Brain 1.0.2 is the current production/stable release.** This patch includes embedding resilience and recovery, MCP SDK compatibility, packaged database bootstrap for pip/pipx installs, and release automation that publishes verified wheel and source-distribution artifacts. Production readiness still depends on the target environment. Operators must run the readiness gate and verify external controls such as TLS, backups, restore drills, and monitoring before serving real data. From 4059c8caa4fd9affa45e5ee08f9833062ccf91da Mon Sep 17 00:00:00 2001 From: "BenClawBot (AlphaOM3G1)" Date: Mon, 7 Sep 2026 14:41:52 +0200 Subject: [PATCH 12/16] fix: keep base migration safe for pre-attribution upgrades --- src/db/migrations/001_base_schema.sql | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/db/migrations/001_base_schema.sql b/src/db/migrations/001_base_schema.sql index 683b960..442e9d6 100644 --- a/src/db/migrations/001_base_schema.sql +++ b/src/db/migrations/001_base_schema.sql @@ -24,12 +24,13 @@ CREATE TABLE IF NOT EXISTS memory ( metadata JSONB DEFAULT '{}' ); --- Indexes for performance +-- Indexes for columns present in the original base schema. The captured_by +-- index is intentionally left to 014_agent_attribution.sql so an older +-- existing memory table can reach the migration that adds captured_by first. CREATE INDEX IF NOT EXISTS idx_memory_embedding ON memory USING hnsw (embedding vector_cosine_ops); CREATE INDEX IF NOT EXISTS idx_memory_entities ON memory USING gin (entities); CREATE INDEX IF NOT EXISTS idx_memory_tags ON memory USING gin (tags); CREATE INDEX IF NOT EXISTS idx_memory_source ON memory (source); -CREATE INDEX IF NOT EXISTS idx_memory_captured_by ON memory (captured_by); CREATE INDEX IF NOT EXISTS idx_memory_created ON memory (created_at DESC); CREATE INDEX IF NOT EXISTS idx_memory_original_date ON memory (original_date DESC); From 543319a302722149a0c0fd43b7d83a252d4c6697 Mon Sep 17 00:00:00 2001 From: "BenClawBot (AlphaOM3G1)" Date: Mon, 7 Sep 2026 14:42:20 +0200 Subject: [PATCH 13/16] test: guard pre-attribution upgrade path --- tests/test_schema_bootstrap.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/tests/test_schema_bootstrap.py b/tests/test_schema_bootstrap.py index 088cb5d..551274f 100644 --- a/tests/test_schema_bootstrap.py +++ b/tests/test_schema_bootstrap.py @@ -2,11 +2,15 @@ def test_base_schema_is_packaged_as_first_migration() -> None: - migration = files("src.db.migrations").joinpath("001_base_schema.sql") - sql = migration.read_text(encoding="utf-8") - - assert "CREATE TABLE IF NOT EXISTS memory" in sql - assert "CREATE EXTENSION IF NOT EXISTS \"vector\"" in sql + migration_root = files("src.db.migrations") + base_sql = migration_root.joinpath("001_base_schema.sql").read_text(encoding="utf-8") + attribution_sql = migration_root.joinpath("014_agent_attribution.sql").read_text(encoding="utf-8") + + assert "CREATE TABLE IF NOT EXISTS memory" in base_sql + assert "CREATE EXTENSION IF NOT EXISTS \"vector\"" in base_sql + assert "idx_memory_captured_by" not in base_sql + assert "ADD COLUMN IF NOT EXISTS captured_by" in attribution_sql + assert "idx_memory_captured_by" in attribution_sql def test_migrate_command_applies_packaged_migrations(monkeypatch, capsys) -> None: From 4e5071cad928a56fc59817a20fa1993494327d01 Mon Sep 17 00:00:00 2001 From: "BenClawBot (AlphaOM3G1)" Date: Mon, 7 Sep 2026 14:49:46 +0200 Subject: [PATCH 14/16] ci: publish release only after trusted master verification --- .github/workflows/verify.yml | 69 ++++++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) diff --git a/.github/workflows/verify.yml b/.github/workflows/verify.yml index 0dc0087..82ee750 100644 --- a/.github/workflows/verify.yml +++ b/.github/workflows/verify.yml @@ -8,6 +8,8 @@ on: jobs: test: runs-on: ubuntu-latest + permissions: + contents: read services: postgres: image: pgvector/pgvector:pg16 @@ -70,3 +72,70 @@ jobs: /tmp/openbrain-smoke/bin/openbrain install-hermes --hermes-home /tmp/hermes-smoke test -f /tmp/hermes-smoke/plugins/openbrain/plugin.yaml test -f /tmp/hermes-smoke/plugins/openbrain/__init__.py + + release: + needs: test + if: github.event_name == 'push' && github.ref == 'refs/heads/master' + runs-on: ubuntu-latest + permissions: + contents: write + steps: + - uses: actions/checkout@v4 + with: + persist-credentials: false + - uses: actions/setup-python@v5 + with: + python-version: "3.11" + - name: Install build tooling + run: python -m pip install --upgrade build + - name: Read package version + id: version + shell: bash + run: | + VERSION="$(python - <<'PY' + import tomllib + from pathlib import Path + + project = tomllib.loads(Path('pyproject.toml').read_text(encoding='utf-8'))['project'] + print(project['version']) + PY + )" + echo "version=$VERSION" >> "$GITHUB_OUTPUT" + echo "tag=v$VERSION" >> "$GITHUB_OUTPUT" + - name: Check whether release already exists + id: existing + env: + GH_TOKEN: ${{ github.token }} + TAG: ${{ steps.version.outputs.tag }} + shell: bash + run: | + if gh release view "$TAG" >/dev/null 2>&1; then + echo "exists=true" >> "$GITHUB_OUTPUT" + else + echo "exists=false" >> "$GITHUB_OUTPUT" + fi + - name: Build package + if: steps.existing.outputs.exists == 'false' + run: python -m build + - name: Smoke-test release wheel + if: steps.existing.outputs.exists == 'false' + env: + EXPECTED_VERSION: ${{ steps.version.outputs.version }} + shell: bash + run: | + python -m venv /tmp/openbrain-release-smoke + /tmp/openbrain-release-smoke/bin/pip install dist/*.whl + ACTUAL_VERSION="$(/tmp/openbrain-release-smoke/bin/openbrain --version | awk '{print $3}')" + test "$ACTUAL_VERSION" = "$EXPECTED_VERSION" + - name: Publish GitHub release + if: steps.existing.outputs.exists == 'false' + env: + GH_TOKEN: ${{ github.token }} + TAG: ${{ steps.version.outputs.tag }} + VERSION: ${{ steps.version.outputs.version }} + shell: bash + run: | + gh release create "$TAG" dist/*.whl dist/*.tar.gz \ + --target "$GITHUB_SHA" \ + --title "Open Brain $VERSION" \ + --generate-notes From a6789c6c58838eec9960dc6e5b411edeec5c7765 Mon Sep 17 00:00:00 2001 From: "BenClawBot (AlphaOM3G1)" Date: Mon, 7 Sep 2026 14:50:01 +0200 Subject: [PATCH 15/16] ci: remove privileged workflow-run release path --- .github/workflows/release.yml | 79 ----------------------------------- 1 file changed, 79 deletions(-) delete mode 100644 .github/workflows/release.yml diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml deleted file mode 100644 index a73b800..0000000 --- a/.github/workflows/release.yml +++ /dev/null @@ -1,79 +0,0 @@ -name: Release - -on: - workflow_run: - workflows: ["Verify"] - types: [completed] - -permissions: - contents: write - -jobs: - release: - if: >- - github.event.workflow_run.conclusion == 'success' && - github.event.workflow_run.event == 'push' && - github.event.workflow_run.head_branch == 'master' - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - with: - ref: ${{ github.event.workflow_run.head_sha }} - fetch-depth: 0 - - uses: actions/setup-python@v5 - with: - python-version: "3.11" - - name: Install build tooling - run: python -m pip install --upgrade build - - name: Read package version - id: version - shell: bash - run: | - VERSION="$(python - <<'PY' - import tomllib - from pathlib import Path - - project = tomllib.loads(Path('pyproject.toml').read_text(encoding='utf-8'))['project'] - print(project['version']) - PY - )" - echo "version=$VERSION" >> "$GITHUB_OUTPUT" - echo "tag=v$VERSION" >> "$GITHUB_OUTPUT" - - name: Check whether release already exists - id: existing - env: - GH_TOKEN: ${{ github.token }} - TAG: ${{ steps.version.outputs.tag }} - shell: bash - run: | - if gh release view "$TAG" >/dev/null 2>&1; then - echo "exists=true" >> "$GITHUB_OUTPUT" - else - echo "exists=false" >> "$GITHUB_OUTPUT" - fi - - name: Build package - if: steps.existing.outputs.exists == 'false' - run: python -m build - - name: Smoke-test release wheel - if: steps.existing.outputs.exists == 'false' - env: - EXPECTED_VERSION: ${{ steps.version.outputs.version }} - shell: bash - run: | - python -m venv /tmp/openbrain-release-smoke - /tmp/openbrain-release-smoke/bin/pip install dist/*.whl - ACTUAL_VERSION="$(/tmp/openbrain-release-smoke/bin/openbrain --version | awk '{print $3}')" - test "$ACTUAL_VERSION" = "$EXPECTED_VERSION" - - name: Publish GitHub release - if: steps.existing.outputs.exists == 'false' - env: - GH_TOKEN: ${{ github.token }} - TAG: ${{ steps.version.outputs.tag }} - VERSION: ${{ steps.version.outputs.version }} - TARGET: ${{ github.event.workflow_run.head_sha }} - shell: bash - run: | - gh release create "$TAG" dist/*.whl dist/*.tar.gz \ - --target "$TARGET" \ - --title "Open Brain $VERSION" \ - --generate-notes From 440757e102ef5b605f1e8424964e69d4713b3ee8 Mon Sep 17 00:00:00 2001 From: "BenClawBot (AlphaOM3G1)" Date: Mon, 7 Sep 2026 14:53:12 +0200 Subject: [PATCH 16/16] test: tolerate non-route FastAPI entries --- tests/test_embedding_resilience.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_embedding_resilience.py b/tests/test_embedding_resilience.py index f9aa491..e203d1d 100644 --- a/tests/test_embedding_resilience.py +++ b/tests/test_embedding_resilience.py @@ -161,7 +161,7 @@ def test_rest_and_mcp_expose_regeneration_controls(): assert EmbeddingRegenerationRequest(force=True).force is True assert any( - route.path == "/memories/{memory_id}/regenerate-embedding" + getattr(route, "path", None) == "/memories/{memory_id}/regenerate-embedding" for route in app.routes )