refactor(db): split import_economics.py; single generator-source registry (T-1067)

import_economics.py 2,620 → 309 lines — a thin orchestrator keeping the
exact CLI, single-transaction/rollback contract, and exit codes. The 16
import steps, MIGRATION_SQL, brands shell-out, validators, and stamp
write now live in tooling/economy-db/economy_import/ (db, migration,
economy, corporations, brands, bodies, atlas, specialization, traits,
validators, stamp, paths, errors). Full type hints throughout.

tooling/generator_sources.py replaces the triplicated source registry
(importer / stamp checker / pr-process watch list — the skill now derives
its list via --list). The registry stamps itself, and economy_import/
modules are globbed fail-closed, so a future module is stamped the moment
it exists — closing the silently-weakened-stamp failure mode.

Rider: connector config helpers centralized in tooling/db/common.py.

Byte-identical behavior proven: full-import table dump diff EMPTY over
107,843 lines / 37 tables (volatile timestamp fields excluded); dry-run
output parity; generated_brands.toml sha unchanged. make test-tooling
PASS; ruff clean.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-06-12 20:13:28 +02:00
co-authored by Claude Fable 5
parent 5b3f18da65
commit 4f73624eff
25 changed files with 2785 additions and 2472 deletions
+3 -9
View File
@@ -15,7 +15,7 @@ from pathlib import Path
sys.path.insert(0, str(Path(__file__).resolve().parent))
from common import ensure_venv # noqa: E402
from common import ensure_venv, get_base_url as _get_base_url # noqa: E402
ensure_venv()
@@ -27,15 +27,9 @@ import time
import urllib.error
import urllib.request
CONFIG_PATH = os.path.join(os.path.dirname(__file__), "config.json")
def load_config():
with open(CONFIG_PATH) as f:
return json.load(f)
def get_base_url():
config = load_config()
return config.get("stable_audio_url", "http://tower-of-joy:11500")
def get_base_url() -> str:
return _get_base_url("stable_audio_url", "http://tower-of-joy:11500")
def health():
"""Check if the Stable Audio API is reachable."""
+46 -3
View File
@@ -3,11 +3,14 @@
Provides `ensure_venv`, used by the asset connectors (audio_connector,
audio_batch, image_connector, trellis_connector) to re-exec into the project
.venv before their third-party imports. The former settledreach.db connection
helpers were removed when the ticket/decision tooling was retired (pql migration
Phase 6); planning now lives in pql (`.pql/`).
.venv before their third-party imports, plus the connector config helpers
(`load_config`, `get_base_url`, `get_api_key`) that were formerly copy-pasted
across the connectors (T-1067 rider, S-25). The former settledreach.db
connection helpers were removed when the ticket/decision tooling was retired
(pql migration Phase 6); planning now lives in pql (`.pql/`).
"""
import json
import os
import sys
from pathlib import Path
@@ -18,6 +21,7 @@ from pathlib import Path
SCRIPT_DIR = Path(__file__).resolve().parent
WORKTREE_ROOT = (SCRIPT_DIR / ".." / "..").resolve()
CONFIG_PATH = SCRIPT_DIR / "config.json"
# ---------------------------------------------------------------------------
@@ -53,3 +57,42 @@ def ensure_venv() -> None:
# Re-exec into the venv Python, preserving all arguments.
os.execv(str(venv_python), [str(venv_python)] + sys.argv)
def load_config() -> dict:
"""Load the shared endpoint configuration from tooling/db/config.json."""
with open(CONFIG_PATH) as f:
return json.load(f)
def get_base_url(key: str, default: str) -> str:
"""Resolve a service base URL from config.json, with a fallback default.
Usage::
base = get_base_url("trellis_url", "http://tower-of-joy:11510")
"""
return load_config().get(key, default)
def get_api_key(env_var: str, config_key: str) -> str:
"""Get an API key from the environment or config.json.
Checks the ``env_var`` environment variable first, then ``config_key`` in
config.json. Prints a JSON error and exits 1 if neither is set — connector
scripts emit machine-readable JSON on all paths.
"""
key = os.environ.get(env_var)
if key:
return key
try:
with open(CONFIG_PATH) as f:
config = json.load(f)
return config.get(config_key, "")
except Exception:
pass
print(json.dumps({
"ok": False,
"error": f"No {env_var} found in environment or config.json"
}, indent=2))
sys.exit(1)
+3 -17
View File
@@ -15,7 +15,7 @@ from pathlib import Path
sys.path.insert(0, str(Path(__file__).resolve().parent))
from common import ensure_venv # noqa: E402
from common import ensure_venv, get_api_key as _get_api_key # noqa: E402
ensure_venv()
@@ -25,26 +25,12 @@ import os
import urllib.error
import urllib.request
CONFIG_PATH = os.path.join(os.path.dirname(__file__), "config.json")
DEFAULT_OUTPUT_DIR = os.path.expanduser("~/Pictures/mcp-images")
def get_api_key():
def get_api_key() -> str:
"""Get Gemini API key from env or config."""
key = os.environ.get("GEMINI_API_KEY")
if key:
return key
try:
with open(CONFIG_PATH) as f:
config = json.load(f)
return config.get("gemini_api_key", "")
except Exception:
pass
print(json.dumps({
"ok": False,
"error": "No GEMINI_API_KEY found in environment or config.json"
}, indent=2))
sys.exit(1)
return _get_api_key("GEMINI_API_KEY", "gemini_api_key")
def health():
+3 -11
View File
@@ -41,7 +41,7 @@ from pathlib import Path
sys.path.insert(0, str(Path(__file__).resolve().parent))
from common import ensure_venv # noqa: E402
from common import ensure_venv, get_base_url as _get_base_url # noqa: E402
ensure_venv()
@@ -53,17 +53,9 @@ import urllib.error
import urllib.parse
import urllib.request
CONFIG_PATH = os.path.join(os.path.dirname(__file__), "config.json")
def load_config():
with open(CONFIG_PATH) as f:
return json.load(f)
def get_base_url():
config = load_config()
return config.get("trellis_url", "http://tower-of-joy:11510")
def get_base_url() -> str:
return _get_base_url("trellis_url", "http://tower-of-joy:11510")
def health():