Files
physicalCombinatorics/src/physcom/db/schema.py
Andrew Simonson 434df718d7 close guardrail gaps and fix the scoring pipeline top to bottom
Constraint resolver: aggregate mass/footprint across a combo instead of
pairwise-only checks, treat medium/atmosphere as agreement not supply/demand,
reduce multi-provider checks by best/sum instead of AND-ing every provider,
fail closed on unrecognized mutex values, add a propulsion-viability
(thrust-to-weight) rule. Seed data updated to match (nuclear/solar-sail
footprint floors, water-medium exclusions, explicit ground/gravity providers).

Domain metric units were stored globally per metric name instead of
per-domain, silently corrupting cost_efficiency for every domain but the
first one seeded — fixed with a schema migration.

Stub estimator's cost_efficiency/safety/availability/reliability were a
backwards formula and flat constants; replaced with heuristics grounded in
each entity's thrust_profile/energy_form/infrastructure.

LLM estimate_physics() now receives each metric's unit and expected range
instead of a bare name, fixing wildly miscalibrated estimates traced back to
the prompt's own hardcoded example anchoring the model to the wrong order of
magnitude. Sharpened the safety-estimation and plausibility-review prompts.
Deduped provider parsing logic into llm/parsing.py.

Web pipeline form can now pick an LLM provider per run instead of only via
server env var.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-26 00:13:16 -05:00

189 lines
6.8 KiB
Python

"""DDL, table creation, and schema initialization."""
from __future__ import annotations
import sqlite3
from pathlib import Path
DDL = """
CREATE TABLE IF NOT EXISTS dimensions (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT UNIQUE NOT NULL,
description TEXT
);
CREATE TABLE IF NOT EXISTS entities (
id INTEGER PRIMARY KEY AUTOINCREMENT,
dimension_id INTEGER NOT NULL REFERENCES dimensions(id),
name TEXT NOT NULL,
description TEXT,
UNIQUE(dimension_id, name)
);
CREATE TABLE IF NOT EXISTS dependencies (
id INTEGER PRIMARY KEY AUTOINCREMENT,
entity_id INTEGER NOT NULL REFERENCES entities(id),
category TEXT NOT NULL,
key TEXT NOT NULL,
value TEXT NOT NULL,
unit TEXT,
constraint_type TEXT NOT NULL
);
CREATE TABLE IF NOT EXISTS domains (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT UNIQUE NOT NULL,
description TEXT
);
CREATE TABLE IF NOT EXISTS metrics (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT UNIQUE NOT NULL,
unit TEXT,
description TEXT
);
CREATE TABLE IF NOT EXISTS domain_metric_weights (
id INTEGER PRIMARY KEY AUTOINCREMENT,
domain_id INTEGER NOT NULL REFERENCES domains(id),
metric_id INTEGER NOT NULL REFERENCES metrics(id),
weight REAL NOT NULL,
norm_min REAL,
norm_max REAL,
lower_is_better INTEGER NOT NULL DEFAULT 0,
unit TEXT,
UNIQUE(domain_id, metric_id)
);
CREATE TABLE IF NOT EXISTS combinations (
id INTEGER PRIMARY KEY AUTOINCREMENT,
hash TEXT UNIQUE NOT NULL,
status TEXT NOT NULL DEFAULT 'pending',
block_reason TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE IF NOT EXISTS combination_entities (
combination_id INTEGER NOT NULL REFERENCES combinations(id),
entity_id INTEGER NOT NULL REFERENCES entities(id),
PRIMARY KEY (combination_id, entity_id)
);
CREATE TABLE IF NOT EXISTS combination_scores (
id INTEGER PRIMARY KEY AUTOINCREMENT,
combination_id INTEGER NOT NULL REFERENCES combinations(id),
domain_id INTEGER NOT NULL REFERENCES domains(id),
metric_id INTEGER NOT NULL REFERENCES metrics(id),
raw_value REAL,
normalized_score REAL,
estimation_method TEXT,
confidence REAL,
UNIQUE(combination_id, domain_id, metric_id)
);
CREATE TABLE IF NOT EXISTS combination_results (
id INTEGER PRIMARY KEY AUTOINCREMENT,
combination_id INTEGER NOT NULL REFERENCES combinations(id),
domain_id INTEGER NOT NULL REFERENCES domains(id),
composite_score REAL,
novelty_flag TEXT,
llm_review TEXT,
human_notes TEXT,
pass_reached INTEGER,
domain_block_reason TEXT,
UNIQUE(combination_id, domain_id)
);
CREATE TABLE IF NOT EXISTS pipeline_runs (
id INTEGER PRIMARY KEY AUTOINCREMENT,
domain_id INTEGER NOT NULL REFERENCES domains(id),
status TEXT NOT NULL DEFAULT 'pending',
config TEXT,
total_combos INTEGER DEFAULT 0,
combos_pass1 INTEGER DEFAULT 0,
combos_pass2 INTEGER DEFAULT 0,
combos_pass3 INTEGER DEFAULT 0,
combos_pass4 INTEGER DEFAULT 0,
current_pass INTEGER,
error_message TEXT,
started_at TIMESTAMP,
completed_at TIMESTAMP,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE IF NOT EXISTS domain_constraints (
id INTEGER PRIMARY KEY AUTOINCREMENT,
domain_id INTEGER NOT NULL REFERENCES domains(id),
key TEXT NOT NULL,
value TEXT NOT NULL,
UNIQUE(domain_id, key, value)
);
CREATE INDEX IF NOT EXISTS idx_deps_entity ON dependencies(entity_id);
CREATE INDEX IF NOT EXISTS idx_deps_category_key ON dependencies(category, key);
CREATE INDEX IF NOT EXISTS idx_combo_status ON combinations(status);
CREATE INDEX IF NOT EXISTS idx_scores_combo_domain ON combination_scores(combination_id, domain_id);
CREATE INDEX IF NOT EXISTS idx_results_domain_score ON combination_results(domain_id, composite_score DESC);
CREATE INDEX IF NOT EXISTS idx_pipeline_runs_domain ON pipeline_runs(domain_id);
"""
def _migrate(conn: sqlite3.Connection) -> None:
"""Apply incremental migrations for existing databases."""
cols = {r[1] for r in conn.execute("PRAGMA table_info(domain_metric_weights)").fetchall()}
if "lower_is_better" not in cols:
conn.execute(
"ALTER TABLE domain_metric_weights ADD COLUMN lower_is_better INTEGER NOT NULL DEFAULT 0"
)
if "unit" not in cols:
conn.execute("ALTER TABLE domain_metric_weights ADD COLUMN unit TEXT")
# Best-effort backfill from the old (metric-name-global) unit column —
# only correct for domains that happen to agree on that metric's unit.
# Seed data re-applies each domain's real per-domain unit on next load.
conn.execute(
"""UPDATE domain_metric_weights
SET unit = (SELECT m.unit FROM metrics m WHERE m.id = domain_metric_weights.metric_id)
WHERE unit IS NULL"""
)
# Create domain_constraints table if missing (added after initial schema)
tables = {r[0] for r in conn.execute(
"SELECT name FROM sqlite_master WHERE type='table'"
).fetchall()}
if "domain_constraints" not in tables:
conn.execute("""CREATE TABLE IF NOT EXISTS domain_constraints (
id INTEGER PRIMARY KEY AUTOINCREMENT,
domain_id INTEGER NOT NULL REFERENCES domains(id),
key TEXT NOT NULL,
value TEXT NOT NULL,
UNIQUE(domain_id, key, value)
)""")
# Add domain_block_reason to combination_results if missing
result_cols = {r[1] for r in conn.execute("PRAGMA table_info(combination_results)").fetchall()}
if "domain_block_reason" not in result_cols:
conn.execute(
"ALTER TABLE combination_results ADD COLUMN domain_block_reason TEXT"
)
# Backfill: cost_efficiency is lower-is-better in all domains
conn.execute(
"""UPDATE domain_metric_weights SET lower_is_better = 1
WHERE lower_is_better = 0
AND metric_id IN (SELECT id FROM metrics WHERE name = 'cost_efficiency')"""
)
conn.commit()
def init_db(db_path: str | Path) -> sqlite3.Connection:
"""Create/open the database and ensure all tables exist."""
db_path = Path(db_path)
db_path.parent.mkdir(parents=True, exist_ok=True)
conn = sqlite3.connect(str(db_path))
conn.execute("PRAGMA journal_mode=WAL")
conn.execute("PRAGMA foreign_keys=ON")
conn.executescript(DDL)
_migrate(conn)
conn.commit()
return conn