fix: render registry workspaces for harness
This commit is contained in:
@@ -6,6 +6,7 @@ from tht.config import (
|
||||
PostgresDwhConfig,
|
||||
ThothRestDwhConfig,
|
||||
ThothVectorHttpConfig,
|
||||
workspace_id_for_config,
|
||||
load_config,
|
||||
)
|
||||
from tht.adapters.evidence import FilesystemEvidenceSource, HttpManifestEvidenceSource
|
||||
@@ -86,6 +87,65 @@ roots:
|
||||
assert cfg.roots.sessions.as_posix() == "build/sessions"
|
||||
|
||||
|
||||
def test_runtime_handoff_preserves_canonical_identity_and_durable_roots(monkeypatch, tmp_path):
|
||||
data_root = tmp_path / "data"
|
||||
runtime_root = data_root / "sessions" / "psd-clinical"
|
||||
workspace = tmp_path / "runtime-random-uuid.yaml"
|
||||
workspace.write_text(f"""
|
||||
runtime_identity:
|
||||
workspace_id: psd-clinical
|
||||
workspace_revision: {'a' * 40}
|
||||
dwh:
|
||||
type: postgres_direct
|
||||
connection: {{database: analytics, schema: mart, user: reader, password: secret}}
|
||||
vectors:
|
||||
type: pgvector_direct
|
||||
connection: {{database: analytics, schema: vectors, user: vector, password: secret}}
|
||||
roots:
|
||||
sessions: {runtime_root / 'sessions'}
|
||||
artifacts: {runtime_root / 'artifacts'}
|
||||
indexes: {runtime_root / 'indexes'}
|
||||
embeddings: {{base_url: http://embedding.invalid, model: embed, dim: 768}}
|
||||
""")
|
||||
monkeypatch.setenv("THT_DATA_ROOT", str(data_root))
|
||||
|
||||
cfg = load_config(workspace)
|
||||
|
||||
assert cfg._workspace_id == "psd-clinical"
|
||||
assert cfg._workspace_revision == "a" * 40
|
||||
assert cfg.paths.sessions == runtime_root / "sessions"
|
||||
assert cfg.paths.artifacts == runtime_root / "artifacts"
|
||||
assert cfg.paths.indexes == runtime_root / "indexes"
|
||||
|
||||
|
||||
def test_runtime_identity_is_authoritative_over_runtime_filename(tmp_path):
|
||||
workspace = tmp_path / "runtime-random-uuid.yaml"
|
||||
workspace.write_text(f"""
|
||||
runtime_identity:
|
||||
workspace_id: psd-clinical
|
||||
workspace_revision: {'a' * 40}
|
||||
dwh:
|
||||
type: postgres_direct
|
||||
connection: {{database: analytics, schema: mart, user: reader, password: secret}}
|
||||
roots: {{sessions: sessions, artifacts: artifacts, indexes: indexes}}
|
||||
""")
|
||||
|
||||
cfg = load_config(workspace)
|
||||
|
||||
assert workspace_id_for_config(cfg, workspace) == "psd-clinical"
|
||||
|
||||
|
||||
def test_load_config_rejects_executable_yaml_tags_without_running_them(tmp_path):
|
||||
marker = tmp_path / "must-not-exist"
|
||||
workspace = tmp_path / "workspace.yaml"
|
||||
workspace.write_text(f"dwh: !command touch {marker}\n")
|
||||
|
||||
with pytest.raises(ConfigError, match="YAML|configurazione"):
|
||||
load_config(workspace)
|
||||
|
||||
assert not marker.exists()
|
||||
|
||||
|
||||
def test_loads_direct_discriminated_resources(tmp_path):
|
||||
workspace = tmp_path / "workspace.yaml"
|
||||
workspace.write_text(
|
||||
|
||||
@@ -5,7 +5,7 @@ import typer
|
||||
|
||||
from tht.cli.config_cmd import CONFIG_OPT
|
||||
from tht.cli.schema_cmd import _load_config_or_exit
|
||||
from tht.config import workspace_id_from_path
|
||||
from tht.config import workspace_id_for_config
|
||||
|
||||
KIND_MAP = {
|
||||
"evidence": ["evidence"],
|
||||
@@ -60,7 +60,7 @@ def search_cmd(
|
||||
cfg = _load_config_or_exit(config)
|
||||
from tht.search.evidence import validate_corpus_workspace
|
||||
|
||||
workspace_id = workspace_id_from_path(config)
|
||||
workspace_id = workspace_id_for_config(cfg, config)
|
||||
validate_corpus_workspace(cfg, workspace_id)
|
||||
dwh_snapshot = _leased_dwh_snapshot(cfg, ctx)
|
||||
require_vector_cfg(cfg)
|
||||
@@ -264,7 +264,7 @@ def pack_cmd(
|
||||
cfg = _load_config_or_exit(config)
|
||||
from tht.search.evidence import validate_corpus_workspace
|
||||
|
||||
workspace_id = workspace_id_from_path(config)
|
||||
workspace_id = workspace_id_for_config(cfg, config)
|
||||
validate_corpus_workspace(cfg, workspace_id)
|
||||
dwh_snapshot = _leased_dwh_snapshot(cfg, ctx)
|
||||
require_vector_cfg(cfg)
|
||||
|
||||
+24
-2
@@ -162,6 +162,11 @@ class PathsConfig(BaseModel):
|
||||
sessions: Path = Path("sessions")
|
||||
|
||||
|
||||
class RuntimeIdentityConfig(BaseModel):
|
||||
workspace_id: str = Field(pattern=r"^[a-z][a-z0-9-]{2,62}$")
|
||||
workspace_revision: str = Field(pattern=r"^[0-9a-f]{40}$")
|
||||
|
||||
|
||||
class WorkspaceRoots(PathsConfig):
|
||||
pass
|
||||
|
||||
@@ -305,7 +310,9 @@ class ExecutionConfig(BaseModel):
|
||||
|
||||
class Config(BaseModel):
|
||||
_workspace_id: str = PrivateAttr(default="default")
|
||||
_workspace_revision: str | None = PrivateAttr(default=None)
|
||||
_config_source: str = PrivateAttr(default="direct")
|
||||
runtime_identity: RuntimeIdentityConfig | None = None
|
||||
dwh: DwhResourceConfig
|
||||
vectors: VectorResourceConfig | None = None
|
||||
session_storage: SessionStorageConfig | None = None
|
||||
@@ -354,10 +361,20 @@ def workspace_id_from_path(path: Path) -> str:
|
||||
return path.resolve().stem.lower().replace(".", "-").replace("_", "-")
|
||||
|
||||
|
||||
def workspace_id_for_config(config: Config, path: Path) -> str:
|
||||
"""Return the canonical runtime identity, falling back for legacy configs."""
|
||||
if config.runtime_identity is not None:
|
||||
return config.runtime_identity.workspace_id
|
||||
return workspace_id_from_path(path)
|
||||
|
||||
|
||||
def load_config(path: Path) -> Config:
|
||||
if not path.exists():
|
||||
raise ConfigError(f"File di configurazione non trovato: {path}")
|
||||
raw = yaml.safe_load(path.read_text())
|
||||
try:
|
||||
raw = yaml.safe_load(path.read_text())
|
||||
except yaml.YAMLError as exc:
|
||||
raise ConfigError(f"Configurazione YAML non valida: {path}") from exc
|
||||
if not isinstance(raw, dict):
|
||||
raise ConfigError(f"Configurazione non valida (atteso un mapping YAML): {path}")
|
||||
expanded = _resolve_secret_files(_expand_env(raw))
|
||||
@@ -407,7 +424,12 @@ def load_config(path: Path) -> Config:
|
||||
FutureWarning,
|
||||
stacklevel=2,
|
||||
)
|
||||
cfg._workspace_id = workspace_id_from_path(path)
|
||||
cfg._workspace_id = workspace_id_for_config(cfg, path)
|
||||
cfg._workspace_revision = (
|
||||
cfg.runtime_identity.workspace_revision
|
||||
if cfg.runtime_identity is not None
|
||||
else None
|
||||
)
|
||||
cfg._config_source = path.resolve().as_posix()
|
||||
return cfg
|
||||
|
||||
|
||||
Reference in New Issue
Block a user