Skip to content

docs: layered-architecture guideline + import-linter pilot#380

Merged
gbrlcustodio merged 5 commits into
devfrom
docs/architecture-guidelines
Jul 9, 2026
Merged

docs: layered-architecture guideline + import-linter pilot#380
gbrlcustodio merged 5 commits into
devfrom
docs/architecture-guidelines

Conversation

@gbrlcustodio

@gbrlcustodio gbrlcustodio commented Jul 9, 2026

Copy link
Copy Markdown
Member

Summary

Encode project-level architecture rules as a written guideline and start enforcing intra-package layering.

The repo already documents and enforces the direction between packages (docs/dependencies.md + ruff TID251), and states boundary parsing / self-guaranteeing types / the per-app runtime (AGENTS.md, coding principles). What was missing is the layer model within a package: which module is domain, which is an adapter, which is the composition root, and what each may import. This PR fills that gap and reinforces the existing rules rather than replacing them.

What changed

  • New docs/architecture.md: layer model (hexagonal, thin core), inward-only dependency rule, type ownership at boundaries, ports and dependency inversion, per-app composition root, an alternative-constructor decision table, testing at boundaries, enforcement, and a reconciliation note. Cites real precedents (StartupIdentity.from_configured_credential, McpRuntime.for_profile, require_request_bearer, JwtTokenVerifier, build_pipefy_mcp_server).
  • import-linter pilot on packages/mcp: a layers contract in packages/mcp/pyproject.toml locking the inward-only spine server > tools > core > auth > settings, run in CI (cd packages/mcp && uv run lint-imports) beside the existing banned-import steps. The stricter framework-free-domain contract is recorded disabled until the domain has a dedicated module.
  • Doc index links from AGENTS.md, docs/README.md, and packages/mcp/AGENTS.md.

Notes

  • core/__init__.py added so pipefy_mcp.core is a discoverable package like its auth//tools/ siblings (the layers contract needs the intermediate node). One-line docstring, behavior-neutral.
  • The lock holds rich at 14.0.0: adding import-linter forced a lock refresh that would otherwise have bumped rich 14 to 15 (constraint is rich>=13, the lock was stale), which breaks the CLI help golden test. Lock diff is import-linter + grimp only.
  • Enforcement is a pilot on pipefy_mcp; other packages keep their TID251 inter-package guards. Extending intra-package contracts to them is a later step.

Testing

  • cd packages/mcp && uv run lint-imports: 1 kept, 0 broken (55 files, 155 deps).
  • uv run ruff check and ruff format --check on packages/mcp/src: clean.
  • uv run python -m pytest -m "not integration": 3261 passed, 5 skipped.

@adriannoes adriannoes left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Summary

Solid docs and import-linter pilot: the guideline fills the intra-package gap cleanly, the physical spine is honest about not matching hexagonal role names 1:1, and the next framework-free ratchet is recorded disabled instead of over-claimed. Local verification was checkout plus lint-imports on the PR head (KEPT) and on a merge with current dev (BROKEN, matching CI).

What worked well

  • Clear separation from docs/dependencies.md / TID251 (inter-package) versus this intra-package model
  • Lock holds rich at 14.0.0 so the import-linter refresh does not drag an unrelated bump
  • Same author already opened #381 to clear the back-edge this gate detects

Required before merge

F1. Merging this branch into current dev breaks the new lint-imports gate. The PR head alone keeps the layers contract, but GitHub CI runs the merge commit, and that tree still has pipefy_mcp.core.tool_middleware importing pipefy_mcp.tools.tool_error_envelope from #378.

Please land #381 first (or an equivalent move of the envelope into core), rebase this branch, and confirm cd packages/mcp && uv run lint-imports is green on the merge with dev. The Testing section numbers look correct for the head alone; they do not describe merge CI until that rebase lands.

Verification

cd packages/mcp && uv run lint-imports

(After rebase onto #381 / updated dev, the merge tree should report 1 kept, 0 broken.)

State the layer model within a package (domain, adapter, composition root),
the inward-only dependency rule, type ownership at boundaries, the port and
inversion seam, the per-app composition root, and a decision guide for
alternative constructors. Fills the intra-package gap left by docs/dependencies.md
(inter-package direction) and AGENTS.md (boundary parsing, per-app runtime), and
reconciles with those existing rules rather than replacing them.
Add import-linter (root dev group) and a layers contract in
packages/mcp/pyproject.toml that locks the inward-only spine
server > tools > core > auth > settings, run in CI beside the ruff
banned-import steps. The stricter framework-free-domain contract is recorded
disabled until the scattered domain helpers get a dedicated module.

core/ gains an __init__.py so pipefy_mcp.core is a discoverable package like its
auth/ and tools/ siblings; the layers contract needs the intermediate node.
The lock holds rich at 14.0.0 so adding import-linter does not drag in an
unrelated rich bump.
Add docs/architecture.md to the AGENTS.md documentation map and docs/README.md
index, a pointer from the AGENTS.md composition section, and a link from
packages/mcp/AGENTS.md.
…ple citations

The guideline read as mcp-specific: rules were stated once and illustrated
only with pipefy_mcp, and phrases like "the two ports of this package" sat
inside universal-rule sections. Add an explicit repo-wide scope paragraph and
relabel the pipefy_mcp specifics as the first enforced instance, not the
definition. Also drop citations to the numbered coding principles, which live
only in a user-global file and are unresolvable for anyone cloning the repo;
the principle content is now stated inline.
PR #381 relocated tool_error_envelope.py from tools/ to core/. Refresh
the two spots that still described it under tools/: the domain mapping in
docs/architecture.md and the disabled-ratchet comment in pyproject.toml.
Also name core/tool_middleware.py alongside fastmcp_tool_lifecycle.py as
adapter code that currently lives under core/.
@gbrlcustodio
gbrlcustodio force-pushed the docs/architecture-guidelines branch from 808b36f to b754d85 Compare July 9, 2026 19:17
@gbrlcustodio
gbrlcustodio marked this pull request as ready for review July 9, 2026 19:21
@gbrlcustodio
gbrlcustodio requested a review from adriannoes July 9, 2026 19:21

@adriannoes adriannoes left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

F1 is cleared: #381 is on dev, this branch is rebased, and lint-imports keeps the contract on the merge tree.

Thanks for addressing the feedback so cleanly, Gabriel :)

@gbrlcustodio
gbrlcustodio merged commit 8663408 into dev Jul 9, 2026
2 checks passed
@gbrlcustodio
gbrlcustodio deleted the docs/architecture-guidelines branch July 9, 2026 19:33
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.

2 participants