Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .data/snapshots/state_AgentA.json.meta
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"created_at": "2025-10-10T08:23:58Z", "schema_version": "v1"}
{"created_at": "2025-10-10T10:21:34Z", "schema_version": "v1"}
2 changes: 1 addition & 1 deletion .data/snapshots/state_Ambrose.json.meta
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"created_at": "2025-10-10T08:23:57Z", "schema_version": "v1"}
{"created_at": "2025-10-10T10:21:34Z", "schema_version": "v1"}
2 changes: 1 addition & 1 deletion .data/snapshots/state_agent.json.meta
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"created_at": "2025-10-10T08:23:55Z", "schema_version": "v1"}
{"created_at": "2025-10-10T10:21:31Z", "schema_version": "v1"}
2 changes: 1 addition & 1 deletion .data/snapshots/state_smoke.json.meta
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"created_at": "2025-10-10T08:23:58Z", "schema_version": "v1"}
{"created_at": "2025-10-10T10:21:34Z", "schema_version": "v1"}
2 changes: 1 addition & 1 deletion .logs/apply.jsonl
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"turn": "demo-1", "agent": "AgentA", "applied": 0, "clamps": 0, "version_etag": "46", "snapshot": "./.data/snapshots/state_AgentA.json", "cache_invalidations": 0, "ms": 0.777}
{"turn": "demo-1", "agent": "AgentA", "applied": 0, "clamps": 0, "version_etag": "46", "snapshot": "./.data/snapshots/state_AgentA.json", "cache_invalidations": 0, "ms": 0.88}
2 changes: 1 addition & 1 deletion .logs/t1.jsonl
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"turn": "demo-1", "agent": "AgentA", "pops": 5, "iters": 1, "propagations": 3, "radius_cap_hits": 0, "layer_cap_hits": 0, "node_budget_hits": 0, "max_delta": 1.0, "graphs_touched": 1, "cache_hits": 0, "cache_misses": 1, "cache_used": false, "cache_enabled": true, "ms": 0.278, "now": "2025-10-10T08:23:58.151083+00:00"}
{"turn": "demo-1", "agent": "AgentA", "pops": 5, "iters": 1, "propagations": 3, "radius_cap_hits": 0, "layer_cap_hits": 0, "node_budget_hits": 0, "max_delta": 1.0, "graphs_touched": 1, "cache_hits": 0, "cache_misses": 1, "cache_used": false, "cache_enabled": true, "ms": 0.381, "now": "2025-10-10T10:21:34.762491+00:00"}
2 changes: 1 addition & 1 deletion .logs/t2.jsonl
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"turn": "demo-1", "agent": "AgentA", "tier_sequence": ["exact_semantic", "cluster_semantic", "archive"], "k_returned": 0, "k_used": 0, "k_residual": 0, "sim_stats": {"mean": 0.0, "max": 0.0}, "score_stats": {"mean": 0.0, "max": 0.0}, "owner_scope": "any", "caps": {"residual_cap": 32}, "cache_enabled": true, "cache_used": true, "cache_hits": 0, "cache_misses": 2, "backend": "inmemory", "backend_fallback": false, "hybrid_used": false, "cache_hit": false, "cache_size": 1, "ms": 0.145, "now": "2025-10-10T08:23:58.151083+00:00"}
{"turn": "demo-1", "agent": "AgentA", "tier_sequence": ["exact_semantic", "cluster_semantic", "archive"], "k_returned": 0, "k_used": 0, "k_residual": 0, "sim_stats": {"mean": 0.0, "max": 0.0}, "score_stats": {"mean": 0.0, "max": 0.0}, "owner_scope": "any", "caps": {"residual_cap": 32}, "cache_enabled": true, "cache_used": true, "cache_hits": 0, "cache_misses": 2, "backend": "inmemory", "backend_fallback": false, "hybrid_used": false, "cache_hit": false, "cache_size": 1, "ms": 0.126, "now": "2025-10-10T10:21:34.762491+00:00"}
2 changes: 1 addition & 1 deletion .logs/t3.jsonl
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"turn": "demo-1", "agent": "AgentA", "backend": "rulebased", "ops_counts": {"Speak": 1, "RequestRetrieve": 1}, "requested_retrieve": true, "rag_used": true, "ms_plan": 0.032, "ms_rag": 0.055, "ms_speak": 0.036, "now": "2025-10-10T08:23:58.151083+00:00"}
{"turn": "demo-1", "agent": "AgentA", "backend": "rulebased", "ops_counts": {"Speak": 1, "RequestRetrieve": 1}, "requested_retrieve": true, "rag_used": true, "ms_plan": 0.029, "ms_rag": 0.053, "ms_speak": 0.038, "now": "2025-10-10T10:21:34.762491+00:00"}
2 changes: 1 addition & 1 deletion .logs/t3_dialogue.jsonl
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"turn": "demo-1", "agent": "AgentA", "tokens": 7, "truncated": false, "style_prefix_used": false, "snippet_count": 0, "ms": 0.036, "backend": "rulebased", "now": "2025-10-10T08:23:58.151083+00:00"}
{"turn": "demo-1", "agent": "AgentA", "tokens": 7, "truncated": false, "style_prefix_used": false, "snippet_count": 0, "ms": 0.038, "backend": "rulebased", "now": "2025-10-10T10:21:34.762491+00:00"}
2 changes: 1 addition & 1 deletion .logs/t3_plan.jsonl
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"turn": "demo-1", "agent": "AgentA", "policy_backend": "rulebased", "backend": "rulebased", "ops_counts": {"Speak": 1, "RequestRetrieve": 1}, "requested_retrieve": true, "rag_used": true, "reflection": false, "ms_deliberate": 0.032, "ms_rag": 0.055, "now": "2025-10-10T08:23:58.151083+00:00"}
{"turn": "demo-1", "agent": "AgentA", "policy_backend": "rulebased", "backend": "rulebased", "ops_counts": {"Speak": 1, "RequestRetrieve": 1}, "requested_retrieve": true, "rag_used": true, "reflection": false, "ms_deliberate": 0.029, "ms_rag": 0.053, "now": "2025-10-10T10:21:34.762491+00:00"}
2 changes: 1 addition & 1 deletion .logs/t4.jsonl
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"turn": "demo-1", "agent": "AgentA", "counts": {"input": 0, "after_cooldown": 0, "after_novelty": 0, "after_l2": 0, "approved": 0, "dropped_tail": 0}, "clamps": {"novelty_clamped": 0, "l2_scale": 1.0}, "cooldowns": {"blocked_ops": 0}, "caps": {"delta_norm_cap_l2": 1.5, "novelty_cap_per_node": 0.3, "churn_cap_edges": 64}, "approved": 0, "rejected": 0, "reasons": [], "ms": 0.006, "now": "2025-10-10T08:23:58.151083+00:00"}
{"turn": "demo-1", "agent": "AgentA", "counts": {"input": 0, "after_cooldown": 0, "after_novelty": 0, "after_l2": 0, "approved": 0, "dropped_tail": 0}, "clamps": {"novelty_clamped": 0, "l2_scale": 1.0}, "cooldowns": {"blocked_ops": 0}, "caps": {"delta_norm_cap_l2": 1.5, "novelty_cap_per_node": 0.3, "churn_cap_edges": 64}, "approved": 0, "rejected": 0, "reasons": [], "ms": 0.006, "now": "2025-10-10T10:21:34.762491+00:00"}
2 changes: 1 addition & 1 deletion .logs/turn.jsonl
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"turn": "demo-1", "agent": "AgentA", "durations_ms": {"t1": 0.278, "t2": 0.145, "t4": 0.006, "apply": 0.777, "total": 2.955}, "t1": {"pops": 5, "iters": 1, "graphs_touched": 1}, "t2": {"k_returned": 0, "k_used": 0, "cache_hit": false}, "t4": {"approved": 0, "rejected": 0}, "now": "2025-10-10T08:23:58.151083+00:00"}
{"turn": "demo-1", "agent": "AgentA", "durations_ms": {"t1": 0.381, "t2": 0.126, "t4": 0.006, "apply": 0.88, "total": 3.376}, "t1": {"pops": 5, "iters": 1, "graphs_touched": 1}, "t2": {"k_returned": 0, "k_used": 0, "cache_hit": false}, "t4": {"approved": 0, "rejected": 0}, "now": "2025-10-10T10:21:34.762491+00:00"}
2 changes: 1 addition & 1 deletion clematis/VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
0.10.2
0.10.3
2 changes: 1 addition & 1 deletion clematis/engine/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -289,7 +289,7 @@ class Config:
}
)
flags: Dict[str, Any] = field(
default_factory=lambda: {"enable_world_memory": True, "allow_reflection": True}
default_factory=lambda: {"enable_world_memory": False, "allow_reflection": False}
)

# M5: scheduler config (feature-flagged; defaults merged by validate.py)
Expand Down
11 changes: 3 additions & 8 deletions clematis/errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,18 +28,13 @@ class ClematisError(Exception):



# NOTE(backcompat): For v3 we also inherit from ValueError so older tests/consumers
# that catch ValueError keep working.
# TODO(v4): Drop ValueError parent and migrate callers to catch ConfigError/ClematisError.
class ConfigError(ClematisError, ValueError):
# Typed errors no longer inherit from ValueError; callers should catch the specific subclasses.
class ConfigError(ClematisError):
"""Configuration invalid, unknown keys, wrong version, etc."""
pass



# NOTE(backcompat): For v3 we also inherit from ValueError for legacy callers/tests.
# TODO(v4): Drop ValueError parent and migrate callers to catch SnapshotError/ClematisError.
class SnapshotError(ClematisError, ValueError):
class SnapshotError(ClematisError):
"""Snapshot missing schema, mismatched version, corrupted header, etc."""
pass

Expand Down
13 changes: 11 additions & 2 deletions clematis/scripts/validate.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,15 @@
except Exception: # PyYAML optional
yaml = None # type: ignore

try:
from clematis.errors import ConfigError
except ModuleNotFoundError:
HERE = os.path.abspath(os.path.dirname(__file__))
ROOT = os.path.abspath(os.path.join(HERE, ".."))
if ROOT not in sys.path:
sys.path.insert(0, ROOT)
from clematis.errors import ConfigError

# Ensure the project root (parent of scripts/) is importable when run directly
try:
from configs.validate import validate_config_verbose, validate_config # type: ignore
Expand Down Expand Up @@ -127,8 +136,8 @@ def main(argv: list[str]) -> int:
else:
normalized = validate_config(cfg)
warnings = []
except ValueError as ve:
print("CONFIG INVALID\n" + str(ve))
except ConfigError as err:
print("CONFIG INVALID\n" + str(err))
return 1

# --strict: treat warnings as errors
Expand Down
17 changes: 10 additions & 7 deletions configs/config.yaml
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
version: "v1"
k_surface: 32
surface_method: PCA
t1:
Expand Down Expand Up @@ -48,10 +49,10 @@ t3:
tokens: 256
temp: 0.2
max_ops_per_turn: 3
allow_reflection: true
backend: llm
allow_reflection: false
backend: rulebased
reflection:
backend: llm # deterministic summariser; "llm" requires fixtures (see PR84)
backend: rulebased # deterministic summariser; switch to "llm" via overlay
summary_tokens: 128 # whitespace-tokenised cap
embed: true # embed the reflection summary deterministically
log: true # write t3_reflection.jsonl (not part of identity logs)
Expand All @@ -65,15 +66,15 @@ t3:
epsilon_edit: 0.10
llm:
# M3-07 LLM adapter scaffolding — defaults OFF and fixture-driven in CI
provider: ollama # "fixture" | "ollama"
provider: fixture # "fixture" | "ollama"
model: qwen3:4b-instruct
endpoint: http://localhost:11434/api/generate
max_tokens: 256
temp: 0.2
timeout_ms: 10000
fixtures:
enabled: true # fixtures-only mode for deterministic LLM reflection
path: tests/fixtures/llm_cassettes/reflection.jsonl
enabled: false # enable in overlays/examples; requires non-empty path
path: null
t4:
enabled: true
delta_norm_cap_l2: 1.5
Expand All @@ -94,7 +95,9 @@ t4:
ttl_sec: 600
# budgets: ...
budgets: {time_ms: 1000, ops: 1000, tokens: 1024, time_ms_reflection: 6000}
flags: {enable_world_memory: true, allow_reflection: true}
flags:
enable_world_memory: false
allow_reflection: false

# -----------------------------------------------------------------------------
# M9 Deterministic Parallelism (defaults OFF)
Expand Down
Binary file added dist_local/clematis-0.10.3-py3-none-any.whl
Binary file not shown.
Binary file not shown.
Binary file not shown.
1 change: 1 addition & 0 deletions docs/m13/error_taxonomy.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,3 +44,4 @@ When an error has an empty message, `format_error(e)` renders only the class nam
**Notes**
- Error messages are deterministic and concise; avoid stack traces in operator paths.
- Identity is preserved in v3; enabling quality/perf/reflection features may change behavior and should remain off by default.
- Typed errors inherit only from `ClematisError`; update callers to catch the specific subclasses instead of `ValueError`.
2 changes: 1 addition & 1 deletion docs/m3/llm_adapter.md
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ pytest -q
Ensure each line is valid JSON; no trailing commas; UTF‑8 encoding.
- **`LLMAdapterError: No fixture for prompt hash`**
The prompt string used at runtime must match the one hashed in the JSONL (after newline normalization).
- **Config validation failures (ValueError)**
- **Config validation failures (ConfigError)**
- `t3.llm.provider` must be one of `{fixture, ollama}`
- `t3.llm.temp` in `[0, 1]`
- `t3.llm.max_tokens ≥ 1`
Expand Down
31 changes: 31 additions & 0 deletions healthcheck.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# Clematis v3 Healthcheck

## Repository Reality & Dossier Update
- Version metadata now consistently reports **0.10.3** across `pyproject.toml`, `clematis/VERSION`, `clematis.egg-info/PKG-INFO`, CLI man pages, and help goldens (`tests/cli/goldens/help/top.txt`).
- Deterministic turn loop (T1→T4) and staging remain intact; identity CI continues to rely on fixed env vars and normalized LF logs. GEL (`graph.enabled`) and deterministic parallelism (`perf.parallel`) are present but default **OFF**.
- Config schema v1 is enforced via `configs/validate.py`; snapshots remain schema v1 with strict inspector semantics. CLI (`python -m clematis …`) still emits one-line typed errors.
- Default config (`configs/config.yaml`) now mirrors the identity baseline: `t3.allow_reflection=false`, `t3.backend=rulebased`, LLM fixtures disabled, and `flags.allow_reflection=false`. Optional features stay opt-in through overlays/examples.

## Observed Gaps (carried into v4 planning)
- Native kernels, GEL-informed nudge planner, retrieval quality defaults, and CLI/frontend evolution remain unimplemented; existing scaffolding is deterministic but dormant.

## Hardening & Maintenance Steps Completed
- Audited defaults and flipped identity gates OFF by editing `configs/config.yaml` (reflection + LLM fixtures now opt-in; added explicit `version: "v1"` header).
- Updated `clematis/engine/types.Config.flags` to default both `enable_world_memory` and `allow_reflection` to `False` to keep in-process defaults consistent with config files and docs.
- Validated the adjusted config with `python3 -m clematis validate -- --config configs/config.yaml` (passes with reflection/quality gates reported as disabled).
- Bumped project version markers to 0.10.3 and refreshed CLI artifacts: updated `pyproject.toml`, `clematis/VERSION`, `clematis.egg-info/PKG-INFO`, `man/*.1`, and `tests/cli/goldens/help/top.txt`.
- Identity marker suite verified (`pytest -q -m identity`), and full test suite passes locally (`pytest -q`).
- Built wheels into `dist_local/` and captured deterministic hashes via `shasum -a 256 dist_local/* | sort` as part of the packaging parity check.
- Removed `ValueError` inheritance from typed errors, updated validators/scripts/tests to catch `ConfigError`/`SnapshotError`, and refreshed docs to match the cleaned taxonomy.

## Recommended Next Actions
- Re-run the identity and packaging matrices once the version/doc alignment is settled, ensuring the refreshed defaults keep byte-identical outputs.
- Keep overlays/examples (e.g., `examples/reflection/*.yaml`, GEL demos) up to date with the new defaults so operators enable gates explicitly.
- For v4 kickoff, formalize the migration doc covering schema v2, error taxonomy cleanup, native acceleration strategy, and GEL-driven planner roadmap.

## Suggested Verification Commands
- Run in order: `python3 -m clematis validate -- --config configs/config.yaml`
- `pytest -q -m identity` *(sequential vs parallel/disabled-path matches)*
- `pytest -q tests/config/test_validate_reflection.py` *(reflection gate defaults + fixtures contract)*
- `python3 -m pip wheel . -w dist_local` **then** `shasum -a 256 dist_local/* | sort`
- `python3 -m clematis --help > /tmp/help.txt` and compare against `tests/cli/goldens/help/top.txt`
2 changes: 1 addition & 1 deletion man/clematis-bench-t4.1
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
.TH clematis-bench-t4 1 "2024-01-01" "Clematis 0.10.2" "User Commands"
.TH clematis-bench-t4 1 "2024-01-01" "Clematis 0.10.3" "User Commands"
.SH NAME
clematis\-bench\-t4 \\\- Delegates to scripts/ for 'bench\-t4'
.SH SYNOPSIS
Expand Down
2 changes: 1 addition & 1 deletion man/clematis-chat.1
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
.TH clematis-chat 1 "2024-01-01" "Clematis 0.10.2" "User Commands"
.TH clematis-chat 1 "2024-01-01" "Clematis 0.10.3" "User Commands"
.SH NAME
clematis\-chat \\\- Delegates to scripts/ for 'chat'
.SH SYNOPSIS
Expand Down
2 changes: 1 addition & 1 deletion man/clematis-console.1
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
.TH clematis-console 1 "2024-01-01" "Clematis 0.10.2" "User Commands"
.TH clematis-console 1 "2024-01-01" "Clematis 0.10.3" "User Commands"
.SH NAME
clematis\-console \\\- Delegates to scripts/ for 'console'
.SH SYNOPSIS
Expand Down
2 changes: 1 addition & 1 deletion man/clematis-demo.1
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
.TH clematis-demo 1 "2024-01-01" "Clematis 0.10.2" "User Commands"
.TH clematis-demo 1 "2024-01-01" "Clematis 0.10.3" "User Commands"
.SH NAME
clematis\-demo \\\- Delegates to scripts/ for 'demo'
.SH SYNOPSIS
Expand Down
2 changes: 1 addition & 1 deletion man/clematis-export-logs.1
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
.TH clematis-export-logs 1 "2024-01-01" "Clematis 0.10.2" "User Commands"
.TH clematis-export-logs 1 "2024-01-01" "Clematis 0.10.3" "User Commands"
.SH NAME
clematis\-export\-logs \\\- Delegates to scripts/ for 'export\-logs'
.SH SYNOPSIS
Expand Down
2 changes: 1 addition & 1 deletion man/clematis-inspect-snapshot.1
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
.TH clematis-inspect-snapshot 1 "2024-01-01" "Clematis 0.10.2" "User Commands"
.TH clematis-inspect-snapshot 1 "2024-01-01" "Clematis 0.10.3" "User Commands"
.SH NAME
clematis\-inspect\-snapshot \\\- Delegates to scripts/ for 'inspect\-snapshot'
.SH SYNOPSIS
Expand Down
2 changes: 1 addition & 1 deletion man/clematis-rotate-logs.1
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
.TH clematis-rotate-logs 1 "2024-01-01" "Clematis 0.10.2" "User Commands"
.TH clematis-rotate-logs 1 "2024-01-01" "Clematis 0.10.3" "User Commands"
.SH NAME
clematis\-rotate\-logs \\\- Delegates to scripts/ for 'rotate\-logs'
.SH SYNOPSIS
Expand Down
2 changes: 1 addition & 1 deletion man/clematis-seed-lance-demo.1
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
.TH clematis-seed-lance-demo 1 "2024-01-01" "Clematis 0.10.2" "User Commands"
.TH clematis-seed-lance-demo 1 "2024-01-01" "Clematis 0.10.3" "User Commands"
.SH NAME
clematis\-seed\-lance\-demo \\\- Delegates to scripts/ for 'seed\-lance\-demo'
.SH SYNOPSIS
Expand Down
2 changes: 1 addition & 1 deletion man/clematis-validate.1
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
.TH clematis-validate 1 "2024-01-01" "Clematis 0.10.2" "User Commands"
.TH clematis-validate 1 "2024-01-01" "Clematis 0.10.3" "User Commands"
.SH NAME
clematis\-validate \\\- Delegates to scripts/ for 'validate'
.SH SYNOPSIS
Expand Down
4 changes: 2 additions & 2 deletions man/clematis.1
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
.TH clematis 1 "2024-01-01" "Clematis 0.10.2" "User Commands"
.TH clematis 1 "2024-01-01" "Clematis 0.10.3" "User Commands"
.SH NAME
clematis \\\- umbrella CLI for Clematis
.SH SYNOPSIS
Expand All @@ -9,7 +9,7 @@ umbrella CLI for Clematis
.nf
usage: clematis [\-h] [\-\-version] <cmd> ...

Clematis umbrella CLI (v0.10.2)
Clematis umbrella CLI (v0.10.3)

options:
\-h, \-\-help show this help message and exit
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "clematis"
version = "0.10.2"
version = "0.10.3"
description = "Clematis — deterministic mind-like simulation engine (M1–M8 baseline)"
readme = "README.md"
requires-python = ">=3.11,<3.14"
Expand Down
4 changes: 3 additions & 1 deletion scripts/examples_smoke.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@
except Exception:
# Best-effort; fall back to user-provided PYTHONPATH or editable install
pass

from clematis.errors import ConfigError
from typing import Any, Dict, Iterable, List, Mapping, Optional, Sequence, Tuple

# ------------------------------ imports ------------------------------------
Expand Down Expand Up @@ -138,7 +140,7 @@ def _run_example(
cfg = _load_yaml(path)
try:
normalized, warnings = validate_config_verbose(cfg)
except ValueError as e:
except ConfigError as e:
return False, f"validation errors: {str(e)}"
cfg = normalized

Expand Down
6 changes: 4 additions & 2 deletions scripts/validate_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@
if ROOT not in sys.path:
sys.path.insert(0, ROOT)

from clematis.errors import ConfigError


def _load_validator():
"""Import the configs.validate module lazily to avoid circular imports.
Expand Down Expand Up @@ -135,8 +137,8 @@ def main(argv: list[str]) -> int:
else:
normalized = validate_config_func(cfg)
warnings = []
except ValueError as ve:
print("CONFIG INVALID\n" + str(ve))
except ConfigError as err:
print("CONFIG INVALID\n" + str(err))
return 1

# --strict: treat warnings as errors
Expand Down
2 changes: 1 addition & 1 deletion tests/cli/goldens/help/top.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
usage: clematis [-h] [--version]

Clematis umbrella CLI (v0.10.2)
Clematis umbrella CLI (v0.10.3)

options:
-h, --help show this help message and exit
Expand Down
6 changes: 3 additions & 3 deletions tests/config/test_config_version_lock.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@


from __future__ import annotations

import pytest

from configs.validate import validate_config, CONFIG_VERSION
from clematis.errors import ConfigError


def _base_cfg() -> dict:
Expand All @@ -28,7 +28,7 @@ def test_accepts_v1_version():
def test_rejects_wrong_version():
cfg = _base_cfg()
cfg["version"] = "v999"
with pytest.raises(ValueError) as e:
with pytest.raises(ConfigError) as e:
validate_config(cfg)
# Message should be explicit but avoid overfitting exact text
assert "must be" in str(e.value)
Expand All @@ -37,5 +37,5 @@ def test_rejects_wrong_version():
def test_rejects_unknown_top_level_keys():
cfg = _base_cfg()
cfg["whaaat"] = {}
with pytest.raises(ValueError):
with pytest.raises(ConfigError):
validate_config(cfg)
3 changes: 2 additions & 1 deletion tests/config/test_validate_perf_snapshots.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import pytest
from clematis.errors import ConfigError

try:
from configs.validate import validate_config, validate_config_verbose
Expand Down Expand Up @@ -34,7 +35,7 @@ def test_rejects_bad_level_and_codec():
},
}
}
with pytest.raises(ValueError) as e:
with pytest.raises(ConfigError) as e:
validate_config(cfg)
msg = str(e.value).lower()
assert "perf.snapshots.compression" in msg or "compression" in msg
Expand Down
Loading