feat: add typed CoreConfig dataclass with validation (CIV-009) - #609
Merged
Conversation
There was a problem hiding this comment.
Hey - I've found 4 issues
Prompt for AI Agents
Please address the comments from this code review:
## Individual Comments
### Comment 1
<location path="core/config.py" line_range="30-50" />
<code_context>
+ test_suites: list[str] | None = None
+ include_markers: str | None = None
+
+ def __post_init__(self) -> None:
+ self.validate()
+
+ def validate(self) -> None:
+ for name in _REQUIRED_FIELDS:
+ value = getattr(self, name)
+ if value is None or (isinstance(value, str) and not value.strip()):
+ raise ValueError(
+ f"'{name}' is required and must be a non-empty string"
+ )
+
+ if self.environment not in _VALID_ENVIRONMENTS:
</code_context>
<issue_to_address>
**issue (bug_risk):** `validate` does not enforce the declared types for most fields: non-string required paths are accepted, arbitrary values are accepted for `debug`, `parallel`, `stop_cleanup`, and path fields, and `tags` values or `test_suites` elements are not checked. Invalid configurations therefore pass construction and fail later at consumers such as file opening or `join`, instead of raising the promised `ValueError`.
**Triggers:** When a YAML or legacy dictionary contains a wrong scalar type, a non-string tag value, or a non-string test-suite entry.
**Suggested fix:** Validate every field explicitly, including string elements and tag key/value types, and reject non-boolean values for boolean fields.
</issue_to_address>
### Comment 2
<location path="core/config.py" line_range="41-45" />
<code_context>
+ f"'{name}' is required and must be a non-empty string"
+ )
+
+ if self.environment not in _VALID_ENVIRONMENTS:
+ raise ValueError(
+ f"'environment' must be one of {sorted(_VALID_ENVIRONMENTS)}, "
+ f"got '{self.environment}'"
+ )
+
+ if self.tags is not None and not isinstance(self.tags, dict):
</code_context>
<issue_to_address>
**issue (bug_risk):** An unhashable invalid environment value, such as a YAML list, makes `self.environment not in _VALID_ENVIRONMENTS` raise `TypeError` rather than the documented `ValueError` for wrong types.
**Triggers:** When the environment field is supplied as a YAML sequence or another unhashable object.
**Suggested fix:** Check that `environment` is a string before membership testing and raise the configured `ValueError` for all invalid types.
```suggestion
if not isinstance(self.environment, str) or self.environment not in _VALID_ENVIRONMENTS:
raise ValueError(
f"'environment' must be one of {sorted(_VALID_ENVIRONMENTS)}, "
f"got '{self.environment}'"
)
```
</issue_to_address>
### Comment 3
<location path="core/config.py" line_range="22-25" />
<code_context>
+ debug: bool = False
+ parallel: bool = False
+ stop_cleanup: bool = False
+ instances_json: str = "/tmp/civ-instances.json"
+ ssh_identity_file: str = "/tmp/civ-ssh-key"
+ ssh_pub_key_file: str = "/tmp/civ-ssh-key.pub"
+ ssh_config_file: str = "/tmp/civ-ssh-config"
+ test_filter: str | None = None
</code_context>
<issue_to_address>
**issue (broader_impact):** The new default runtime paths differ from the defaults in the existing `CIVConfig`: instances and SSH files move from `/tmp/instances.json`, `/tmp/ssh_key`, `/tmp/ssh_key.pub`, and `/tmp/ssh_config` to different names. Using `CoreConfig` as the replacement therefore makes existing consumers, attach workflows, and external tooling look at different files.
**Triggers:** When callers migrate from `CIVConfig.get_default_config()` to `CoreConfig` without explicitly setting these paths.
**Suggested fix:** Preserve the legacy default paths or provide an explicit compatibility/migration policy for the changed defaults.
```suggestion
instances_json: str = "/tmp/instances.json"
ssh_identity_file: str = "/tmp/ssh_key"
ssh_pub_key_file: str = "/tmp/ssh_key.pub"
ssh_config_file: str = "/tmp/ssh_config"
```
</issue_to_address>
### Comment 4
<location path="core/config.py" line_range="61" />
<code_context>
+ return asdict(self)
+
+ @classmethod
+ def from_dict(cls, data: dict[str, object]) -> CoreConfig:
+ for name in _REQUIRED_FIELDS:
+ if name not in data or data[name] is None:
+ raise ValueError(
+ f"'{name}' is required and must be a non-empty string"
+ )
+
+ known = {f.name for f in fields(cls)}
+ filtered: dict[str, object] = {}
+ for key, value in data.items():
+ if key not in known:
+ continue
+ if key == "stop_cleanup" and value is None:
+ value = False
+ filtered[key] = value
+
+ return cls(**filtered)
+
+ @classmethod
</code_context>
<issue_to_address>
**issue (bug_risk):** `from_dict` assumes its argument is a mapping and calls membership and `.items()` directly, so a wrong input type raises `TypeError` or `AttributeError` instead of the promised `ValueError` for invalid configuration input.
**Triggers:** When a caller passes a list, scalar, or other non-mapping object to `CoreConfig.from_dict`.
**Suggested fix:** Check `isinstance(data, dict)` at the start of `from_dict` and raise a descriptive `ValueError` otherwise.
```suggestion
def from_dict(cls, data: dict[str, object]) -> CoreConfig:
if not isinstance(data, dict):
raise ValueError(
f"Configuration must be a dict, got {type(data).__name__}"
)
```
</issue_to_address>Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
F-X64
force-pushed
the
civ-009-add-typed-config-mgmt
branch
from
August 31, 2026 08:24
4eb48ee to
13d8e76
Compare
sshmulev
force-pushed
the
refactor/civ-core
branch
from
August 31, 2026 10:42
2caf2ab to
7dc74cc
Compare
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
F-X64
force-pushed
the
civ-009-add-typed-config-mgmt
branch
from
September 1, 2026 12:30
13d8e76 to
f708c13
Compare
sshmulev
approved these changes
Sep 3, 2026
sshmulev
left a comment
Collaborator
There was a problem hiding this comment.
The PR looks good, didn't find anything important to raise.
LGTM+1
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary