feat(db): schema v15 — welcome_template singleton table

This commit is contained in:
ZdenekSrotyr 2026-04-30 18:35:18 +02:00
parent 96281f884c
commit 33e7107637
2 changed files with 66 additions and 1 deletions

View file

@ -39,7 +39,7 @@ def _maybe_instrument(con, db_tag: str):
_SAFE_IDENTIFIER = re.compile(r"^[a-zA-Z_][a-zA-Z0-9_]{0,63}$")
SCHEMA_VERSION = 20
SCHEMA_VERSION = 21
_SYSTEM_SCHEMA = """
CREATE TABLE IF NOT EXISTS schema_version (
@ -405,6 +405,18 @@ CREATE TABLE IF NOT EXISTS resource_grants (
assigned_by VARCHAR,
UNIQUE (group_id, resource_type, resource_id)
);
-- v21: customizable analyst-bootstrap welcome prompt.
-- Singleton row (id=1). NULL content means "use the default template
-- shipped at config/claude_md_template.txt"; admin-edited override
-- stores the raw Jinja2 source string.
CREATE TABLE IF NOT EXISTS welcome_template (
id INTEGER PRIMARY KEY DEFAULT 1,
content TEXT,
updated_at TIMESTAMP,
updated_by VARCHAR,
CONSTRAINT singleton CHECK (id = 1)
);
"""
@ -1614,6 +1626,17 @@ _V3_TO_V4_MIGRATIONS = [
""",
]
_V20_TO_V21_MIGRATIONS = [
"""CREATE TABLE IF NOT EXISTS welcome_template (
id INTEGER PRIMARY KEY DEFAULT 1,
content TEXT,
updated_at TIMESTAMP,
updated_by VARCHAR,
CONSTRAINT singleton CHECK (id = 1)
)""",
"INSERT INTO welcome_template (id, content) VALUES (1, NULL) ON CONFLICT (id) DO NOTHING",
]
def _ensure_schema(conn: duckdb.DuckDBPyConnection) -> None:
"""Create tables if they don't exist. Apply migrations if schema version changed.
@ -1672,6 +1695,10 @@ def _ensure_schema(conn: duckdb.DuckDBPyConnection) -> None:
"INSERT INTO schema_version (version) VALUES (?)",
[SCHEMA_VERSION],
)
conn.execute(
"INSERT INTO welcome_template (id, content) VALUES (1, NULL) "
"ON CONFLICT (id) DO NOTHING"
)
# Fresh-install seed is handled by the unconditional
# _seed_core_roles call at the bottom of _ensure_schema —
# left as a no-op branch here so the migration ladder still
@ -1749,6 +1776,9 @@ def _ensure_schema(conn: duckdb.DuckDBPyConnection) -> None:
if current < 20:
for sql in _V19_TO_V20_MIGRATIONS:
conn.execute(sql)
if current < 21:
for sql in _V20_TO_V21_MIGRATIONS:
conn.execute(sql)
conn.execute(
"UPDATE schema_version SET version = ?, applied_at = current_timestamp",
[SCHEMA_VERSION],

View file

@ -0,0 +1,35 @@
"""v20 → v21 migration: adds welcome_template singleton table."""
from pathlib import Path
import duckdb
import pytest
from src.db import SCHEMA_VERSION, _ensure_schema, get_schema_version
def _open(path: Path) -> duckdb.DuckDBPyConnection:
return duckdb.connect(str(path))
def test_v21_creates_welcome_template_table(tmp_path):
db_path = tmp_path / "system.duckdb"
conn = _open(db_path)
# Pretend we're on v20: write a v20-shaped DB by running schema then
# rolling the version row back.
_ensure_schema(conn)
conn.execute("UPDATE schema_version SET version = 20")
conn.execute("DROP TABLE IF EXISTS welcome_template")
conn.close()
# Re-open: migration ladder runs.
conn = _open(db_path)
_ensure_schema(conn)
assert get_schema_version(conn) == SCHEMA_VERSION
# Singleton row must exist with NULL content (= use shipped default).
rows = conn.execute(
"SELECT id, content, updated_at, updated_by FROM welcome_template"
).fetchall()
assert len(rows) == 1
assert rows[0][0] == 1 # singleton id
assert rows[0][1] is None # NULL = default