Skip to content

feat(arch): adoption guide + Python import-linter preset - #81

Merged
CMaintz merged 4 commits into
mainfrom
feat/arch-adopt-guide
Oct 9, 2026
Merged

CMaintz merged 4 commits into
mainfrom
feat/arch-adopt-guide

Conversation

@CMaintz

@CMaintz CMaintz commented Oct 9, 2026

Copy link
Copy Markdown
Owner

Adds a dual-audience (human + agent) walkthrough for adopting architecture fitness, fills the Python stack gap, and wires the arch configs into the ruleset-guard so the guide's CI claims are real.

What's here

  • presets/arch/SETUP.md — adoption guide for all three stacks. Deep ArchUnit section (dependency, test placement, freeze store + archunit.properties, .gitattributes LF, the allowStoreCreation/allowStoreUpdate/refreeze semantics, changed-scope exclusion); TS and Python walkthroughs; a "For agents" block with exact commands and the safe-vs-label matrix.
  • presets/arch/importlinter.ini — Python preset: hexagonal default + the same four example maps as the TS/Java presets. Verified live against import-linter 2.15 (clean→pass, violation→fail, +ignore_imports→pass, fixed-but-ignore-kept→fail on unmatched, framework-freedom fires on pydantic in domain).
  • mise/python.toml — opt-in tier-c arch task; import-linter pinned (Renovate-tracked) and installed into the project .venv by setup:pytools only when arch is adopted.
  • ruleset-guard wiring — .dependency-cruiser.cjs, .dependency-cruiser-known-violations.json, .importlinter, archunit_store/ added to the default ruleset_paths; dep-cruiser baseline reuses the snooze kind and the ArchUnit store the lines kind, so pruning stays label-free while adding an entry / editing a rule config needs ruleset-change.

Reviewer disclosures (scope grew past "a guide")

  • Touches two shared reusable workflows (_guards.yml, security.yml default ruleset_paths). Consumers who pass their own ruleset_paths override the default and won't get the arch paths unless they re-list them — documented in SETUP.md.
  • setup:pytools stamp format changed (adds a no-arch/version token) → every existing Python consumer rebuilds its .venv once on the next mise install. Benign.
  • The Java guard path has never run live (no JVM here; design doc already notes this). As a precaution, ruleset_guard.py's lines kind now skips # comment lines so ArchUnit's stored.rules Properties timestamp can't read as an added line — but the first real Java adopter should confirm the store contents against the lines multiset.
  • Bumped the ArchUnit template/doc pin 1.3.0 → 1.5.1. ruleset_guard.py tests: 21 pass.

Design: docs/designs/arch-fitness.md (Implemented/Not-yet + Python ratchet note updated).

Add presets/arch/SETUP.md, a dual-audience (human + agent) walkthrough for
adopting architecture fitness on all three stacks, with a deep ArchUnit section
(dependency, test placement, freeze store + archunit.properties, .gitattributes
LF, refreeze escape hatch, changed-scope exclusion).

Add the Python stack: presets/arch/importlinter.ini (hexagonal default + the
four example maps) and an opt-in tier-c arch task in mise/python.toml, with
import-linter pinned into the project .venv via setup:pytools (version + stamp).
import-linter keeps its baseline inline, so pruning rides a ruleset-change PR;
unmatched_ignore_imports_alerting=error (the default) prevents stale-ignore rot.

Fix pre-restructure designs/ -> docs/designs/ path drift in the arch preset
headers and the ts arch task comment; point the dep-cruiser baseline example at
the root .dependency-cruiser.cjs the task actually uses; update the design doc's
Implemented/Not-yet with the Python preset and the ratchet divergence.
Address review of the adoption guide:

- setup:pytools installs import-linter only when arch is adopted (.importlinter /
  [tool.importlinter] present), the same three-way test the arch task uses, folded
  into the version stamp so adding config triggers a reinstall. Non-adopters no
  longer install or pip-audit it. (import-linter 2.15 + grimp verified clean under
  pip-audit; the preset's four acceptance scenarios verified live against 2.15.)
- SETUP.md Java: seed the freeze store with allowStoreCreation=true, then remove it
  and commit, so a deleted store fails the test loudly (ruleset-guard's lines kind
  cannot see a deletion). allowStoreUpdate stays default-true, so new rules still
  freeze and fixed violations still prune. Spell out refreeze/delete as ruleset-change.
- Bump the ArchUnit template + doc pin 1.3.0 -> 1.5.1 (current).

CHANGELOG is release-please-generated from conventional commits (no Unreleased
section), so these feat/fix messages feed it; no manual edit.
The adoption guide claims .importlinter / dep-cruiser / ArchUnit configs ride the
ruleset-file watch; make that true. Extend the default ruleset_paths regex in
_guards.yml and security.yml to cover .dependency-cruiser.cjs,
.dependency-cruiser-known-violations.json, .importlinter and archunit_store/, and
add two shrink-check cases to the guard loop: the dep-cruiser baseline reuses the
snooze kind and the ArchUnit store the lines kind, so pruning either stays
label-free while adding an entry (or editing a rule config) needs ruleset-change.
Consumer-specific ArchUnit rule-class paths and monorepo-prefixed baselines are
documented in SETUP.md as a ruleset_paths extension. Regex + YAML verified.
… .importlinter

- ruleset_guard.py lines kind now skips '#' comment lines: ArchUnit's stored.rules
  is a Java Properties file with a changing '#<timestamp>' comment that would
  otherwise read as an added line on every legitimate prune. Add a test (21 pass).
- Steer adopters to the standalone .importlinter in the preset header and SETUP.md:
  only that file is in the guard's default ruleset_paths, so an inlined
  [tool.importlinter]/[importlinter] config is unguarded unless added explicitly.
- SETUP.md: note that a ruleset_paths override REPLACES the default (must re-list
  the arch paths), and that adding a new rule freezes new debt so expects the label.
@CMaintz
CMaintz merged commit 3751592 into main Oct 9, 2026
6 checks passed
@CMaintz
CMaintz deleted the feat/arch-adopt-guide branch October 9, 2026 16:15
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