Skip to content

fix(base_settings): restore canonical pydantic assignment semantics#34

Merged
discreteds merged 7 commits into
developfrom
feat/profiles-promotion
Apr 18, 2026
Merged

fix(base_settings): restore canonical pydantic assignment semantics#34
discreteds merged 7 commits into
developfrom
feat/profiles-promotion

Conversation

@discreteds

Copy link
Copy Markdown
Member

Summary

  • Enables validate_assignment=True on MountainAshBaseSettings so declared field types (enums, SecretStr, AfterValidator transforms) are honoured on every post-construction mutation.
  • Removes the redundant update_settings_from_dict re-application in __init__ that was overwriting validated fields with raw kwargs.
  • Converts seven meta-field writes in __init__ to explicit object.__setattr__ to document intentional bypass for harness bookkeeping.

Why

Addresses the setattr-bypass-limitation backlog item:
`mountainash-central/01.principles/mountainash-data/f.backlog/setattr-bypass-limitation.md`

Root cause was `init` applying kwargs twice: once via `super().init(**kwargs)` (with validation) and then again via `update_settings_from_dict` (raw setattr, no validation) — discarding the validated values. Combined with `validate_assignment` being disabled, this forced per-class `setattr` overrides (e.g. `PySparkMode`) and defensive unwrap guards (e.g. `ConnectionProfile._default_driver_kwargs`) across consumer packages.

Commits (bisect-clean)

  1. `test(base_settings): add canonical assignment semantics tests (xfail)` — codifies the contract with 9 `xfail(strict=True)` tests.
  2. `feat(base_settings): enable validate_assignment for canonical semantics` — single flag flip; xfails flip to passing.
  3. `refactor(base_settings): remove redundant kwargs re-application in init` — removes the double-application; meta-field writes become `object.setattr`.
  4. `test(base_settings): add regression guard for validate_assignment` — two guards (base class + `DescriptorProfile`) + extended bookkeeping coverage.

Test plan

  • `hatch run test:test` green — 368 passed, 0 failed
  • `hatch run ruff:check` clean
  • `hatch run mypy:check` at parity with baseline (27 pre-existing errors, zero new)
  • New `TestCanonicalAssignmentSemantics` class (11 tests) passes
  • Existing `TestUpdateSettingsFromDict` still passes
  • Downstream packages (`mountainash-data`) still build against this branch — final reviewer confirmed `PySparkMode.setattr` and `ConnectionProfile` SecretStr guard remain correct (now dead code, not broken code).

Notes for reviewers

  • Test fixtures use `class _Mode(str, Enum)` instead of `StrEnum` because `pyrightconfig.json` pins `pythonVersion: "3.10"`. Behaviour is identical for the test assertions; `StrEnum` requires Python 3.11+.
  • `# type: ignore` comments on raw-str-to-typed-field assignments are pinpoint suppressions — the raw-str assignment IS the scenario under test (prove validate_assignment wraps/coerces on assignment).

Spec & Design

`docs/superpowers/specs/2026-04-18-setattr-bypass-fix-design.md`

Plan

`docs/superpowers/plans/2026-04-18-setattr-bypass-fix.md`

Follow-up (separate PRs, after this lands and consumers bump)

  • Remove `PySparkMode.setattr` override in `mountainash-data`
  • Remove `ConnectionProfile._default_driver_kwargs` SecretStr unwrap guard
  • Simplify adapter `str(enum_value)` defensive code
  • Close the backlog item

🤖 Generated with Claude Code

discreteds and others added 6 commits April 18, 2026 20:14
Restores canonical pydantic v2 assignment semantics on
MountainAshBaseSettings (validate_assignment=True), removes
the redundant update_settings_from_dict re-application in
__init__, and converts seven meta-field writes to
object.__setattr__.

Addresses the setattr-bypass-limitation backlog item promoted
from mountainash-data.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Four-task TDD plan: xfail tests → validate_assignment flip →
__init__ refactor → regression guard. Plus a final verify +
PR-open task targeting develop per the three-tier flow.

Spec: docs/superpowers/specs/2026-04-18-setattr-bypass-fix-design.md

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Codifies the contract to be restored by enabling
validate_assignment=True on MountainAshBaseSettings. Covers
SecretStr wrapping, StrEnum coercion, AfterValidator transform
on direct setattr, update_settings_from_dict, and the
DescriptorProfile integration path.

Marked xfail(strict=True) — will flip to expected-pass once
Change A lands.

Spec: docs/superpowers/specs/2026-04-18-setattr-bypass-fix-design.md
Sets model_config["validate_assignment"] = True on
MountainAshBaseSettings. Every setattr on an instance (including
via update_settings_from_dict, SettingsManager runtime overrides,
and apply_runtime_overrides) now runs the field's declared
validator pipeline — enum coercion, SecretStr wrapping,
AfterValidator transforms.

Addresses the setattr-bypass-limitation backlog item. Removes
the bypass mechanism that forced PySparkMode.__setattr__ and
ConnectionProfile SecretStr defensive code in consumer packages.

Flips the canonical-assignment test class out of xfail.

Spec: docs/superpowers/specs/2026-04-18-setattr-bypass-fix-design.md
…init__

super().__init__(**valid_attribute_kwargs) already applies kwargs
under full pydantic validation. The subsequent
update_settings_from_dict(valid_attribute_kwargs) call was
overwriting the validated values with raw input — the original
source of the setattr-bypass-limitation.

Replaces the call plus seven meta-field setattrs with explicit
object.__setattr__ writes. Meta-fields are harness bookkeeping,
not user config: bypassing validation is intentional and now
explicit. SETTINGS_SOURCE_KWARGS is stashed inline here;
update_settings_from_dict still stashes it for the
SettingsManager and apply_runtime_overrides callsites.

Adds a guard test for meta-field bookkeeping.

Spec: docs/superpowers/specs/2026-04-18-setattr-bypass-fix-design.md
Explicit assertion that model_config["validate_assignment"] is True
on MountainAshBaseSettings. Fails loudly if a future change turns
off the canonical assignment contract.

Also:
- Extends the Task-3 meta-field bookkeeping guard to cover a
  DescriptorProfile subclass, since descriptor profiles share the
  same __init__ path.
- Clarifies the __init__ comment block: exact spec filename, and
  explicit note that object.__setattr__ bypasses
  __pydantic_fields_set__ tracking (intentional, matches pre-Task-2
  behaviour — meta-fields are bookkeeping, not model state).

Spec: docs/superpowers/specs/2026-04-18-setattr-bypass-fix-design.md
@codecov

codecov Bot commented Apr 18, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 96.90%. Comparing base (37efeaa) to head (18de5ad).
⚠️ Report is 9 commits behind head on develop.
✅ All tests successful. No failed tests found.

Additional details and impacted files
@@             Coverage Diff             @@
##           develop      #34      +/-   ##
===========================================
+ Coverage    96.67%   96.90%   +0.23%     
===========================================
  Files           31       31              
  Lines          841      841              
  Branches       109      109              
===========================================
+ Hits           813      815       +2     
+ Misses          16       15       -1     
+ Partials        12       11       -1     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

- hatch.toml: set path = ".venv" under [envs.default] so the project
  env is non-ephemeral and predictable (per repo convention).
- pyrightconfig.json: new — pins venvPath/venv to ".venv", includes
  src + tests, basic type-checking mode, keyed to Python 3.10.
- .gitignore: ignore local .hiivmind/ (user-specific workspace
  symlinks to hiivmind-pulse-gh config outside the repo).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@sonarqubecloud

Copy link
Copy Markdown

Quality Gate Failed Quality Gate failed

Failed conditions
C Security Rating on New Code (required ≥ A)

See analysis details on SonarQube Cloud

Catch issues before they fail your Quality Gate with our IDE extension SonarQube for IDE

@discreteds discreteds merged commit 9eba5bb into develop Apr 18, 2026
5 of 6 checks passed
@discreteds discreteds deleted the feat/profiles-promotion branch April 18, 2026 13:39
@discreteds discreteds mentioned this pull request Apr 18, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant