Repository navigation
ci: enforce backend import layers - #87
Merged
Merged
Conversation
* docs: point the deploy README's env table at app/features * ci: deploy only what the box runs * test: check the deploy filter ships every path the units and the job use * docs: record the layered backend (#89) * docs: record the layered backend * fix: address the backend migration review (#91) * fix: configure logging for the api process * fix: let a publisher's link claim a platform subdomain * fix: fail a link loudly on a non-numeric jev answer * docs: point stale docstrings at where the code moved --------- Co-authored-by: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com>
dimslaev
added a commit
that referenced
this pull request
Oct 19, 2026
* test: colocate backend tests beside their modules * test: cover the curation tools, sign-in refusals, publisher lookups and the known-URL filter * fix: fail the daily report when its mail does not send * test: pin that the report reaches the send, and clean up the filter's publisher * ci: enforce backend import layers (#87) * fix: run mypy and ty in pre-commit with the backend config * ci: enforce backend import layers * fix: name the exact claude-code-action tag in its pin comments * ci: deploy only what the box runs (#88) * docs: point the deploy README's env table at app/features * ci: deploy only what the box runs * test: check the deploy filter ships every path the units and the job use * docs: record the layered backend (#89) * docs: record the layered backend * fix: address the backend migration review (#91) * fix: configure logging for the api process * fix: let a publisher's link claim a platform subdomain * fix: fail a link loudly on a non-numeric jev answer * docs: point stale docstrings at where the code moved --------- Co-authored-by: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com>
dimslaev
added a commit
that referenced
this pull request
Oct 19, 2026
* refactor: expose what entry needs through each feature's service * refactor: split the mcp tools into app/entry/mcp and fold the router into main * refactor: move scripts and the timer entrypoints into app/entry/cli * test: cover the mcp token gate end to end over http * test: cover the cli entrypoints' targets and exit codes * fix: fail the backup when pg_dump writes nothing * refactor: make app/entry pass strict mypy * fix: keep sentry off in development * chore: name the seed module in restore-db's hint without backticks * docs: say what tests-start.sh does with its argument * test: return the tool result's error flag and text from the gate tests' helper * fix: stop importing the email module from configuring the root logger * fix: require the alembic that reads its config from pyproject * test: check the collect run leaves the root logger alone * docs: say where command-line entrypoints live * refactor: name the newsletter service alike in every mcp tool * test: colocate backend tests beside their modules (#86) * test: colocate backend tests beside their modules * test: cover the curation tools, sign-in refusals, publisher lookups and the known-URL filter * fix: fail the daily report when its mail does not send * test: pin that the report reaches the send, and clean up the filter's publisher * ci: enforce backend import layers (#87) * fix: run mypy and ty in pre-commit with the backend config * ci: enforce backend import layers * fix: name the exact claude-code-action tag in its pin comments * ci: deploy only what the box runs (#88) * docs: point the deploy README's env table at app/features * ci: deploy only what the box runs * test: check the deploy filter ships every path the units and the job use * docs: record the layered backend (#89) * docs: record the layered backend * fix: address the backend migration review (#91) * fix: configure logging for the api process * fix: let a publisher's link claim a platform subdomain * fix: fail a link loudly on a non-numeric jev answer * docs: point stale docstrings at where the code moved --------- Co-authored-by: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com>
dimslaev
added a commit
that referenced
this pull request
Oct 19, 2026
* refactor: split backend domains into app/features * refactor: type app/features for mypy strict * fix: keep links on a shared platform's subdomains with the aggregator * fix: drop a newsletter product whose picked link is past the search results * test: cover approve and reject against the schema, and publisher resolution edges * chore: point the end-of-file exclude at the moved email templates * refactor: tidy comments and test helpers from the features split * refactor: move mcp tools and scripts into app/entry (#85) * refactor: expose what entry needs through each feature's service * refactor: split the mcp tools into app/entry/mcp and fold the router into main * refactor: move scripts and the timer entrypoints into app/entry/cli * test: cover the mcp token gate end to end over http * test: cover the cli entrypoints' targets and exit codes * fix: fail the backup when pg_dump writes nothing * refactor: make app/entry pass strict mypy * fix: keep sentry off in development * chore: name the seed module in restore-db's hint without backticks * docs: say what tests-start.sh does with its argument * test: return the tool result's error flag and text from the gate tests' helper * fix: stop importing the email module from configuring the root logger * fix: require the alembic that reads its config from pyproject * test: check the collect run leaves the root logger alone * docs: say where command-line entrypoints live * refactor: name the newsletter service alike in every mcp tool * test: colocate backend tests beside their modules (#86) * test: colocate backend tests beside their modules * test: cover the curation tools, sign-in refusals, publisher lookups and the known-URL filter * fix: fail the daily report when its mail does not send * test: pin that the report reaches the send, and clean up the filter's publisher * ci: enforce backend import layers (#87) * fix: run mypy and ty in pre-commit with the backend config * ci: enforce backend import layers * fix: name the exact claude-code-action tag in its pin comments * ci: deploy only what the box runs (#88) * docs: point the deploy README's env table at app/features * ci: deploy only what the box runs * test: check the deploy filter ships every path the units and the job use * docs: record the layered backend (#89) * docs: record the layered backend * fix: address the backend migration review (#91) * fix: configure logging for the api process * fix: let a publisher's link claim a platform subdomain * fix: fail a link loudly on a non-numeric jev answer * docs: point stale docstrings at where the code moved --------- Co-authored-by: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com>
dimslaev
added a commit
that referenced
this pull request
Oct 19, 2026
* fix: percent-encode database credentials in the dsn * fix: declare the indexes and column types the migrations built * refactor: move tables into app/core/db * test: cover the schema against migrations, the engine and ledger writes * refactor: split backend domains into app/features (#84) * refactor: split backend domains into app/features * refactor: type app/features for mypy strict * fix: keep links on a shared platform's subdomains with the aggregator * fix: drop a newsletter product whose picked link is past the search results * test: cover approve and reject against the schema, and publisher resolution edges * chore: point the end-of-file exclude at the moved email templates * refactor: tidy comments and test helpers from the features split * refactor: move mcp tools and scripts into app/entry (#85) * refactor: expose what entry needs through each feature's service * refactor: split the mcp tools into app/entry/mcp and fold the router into main * refactor: move scripts and the timer entrypoints into app/entry/cli * test: cover the mcp token gate end to end over http * test: cover the cli entrypoints' targets and exit codes * fix: fail the backup when pg_dump writes nothing * refactor: make app/entry pass strict mypy * fix: keep sentry off in development * chore: name the seed module in restore-db's hint without backticks * docs: say what tests-start.sh does with its argument * test: return the tool result's error flag and text from the gate tests' helper * fix: stop importing the email module from configuring the root logger * fix: require the alembic that reads its config from pyproject * test: check the collect run leaves the root logger alone * docs: say where command-line entrypoints live * refactor: name the newsletter service alike in every mcp tool * test: colocate backend tests beside their modules (#86) * test: colocate backend tests beside their modules * test: cover the curation tools, sign-in refusals, publisher lookups and the known-URL filter * fix: fail the daily report when its mail does not send * test: pin that the report reaches the send, and clean up the filter's publisher * ci: enforce backend import layers (#87) * fix: run mypy and ty in pre-commit with the backend config * ci: enforce backend import layers * fix: name the exact claude-code-action tag in its pin comments * ci: deploy only what the box runs (#88) * docs: point the deploy README's env table at app/features * ci: deploy only what the box runs * test: check the deploy filter ships every path the units and the job use * docs: record the layered backend (#89) * docs: record the layered backend * fix: address the backend migration review (#91) * fix: configure logging for the api process * fix: let a publisher's link claim a platform subdomain * fix: fail a link loudly on a non-numeric jev answer * docs: point stale docstrings at where the code moved --------- Co-authored-by: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com>
dimslaev
added a commit
that referenced
this pull request
Oct 19, 2026
* refactor: move shared backend mechanism into app/core * refactor: reword the check_link module docstring * fix: read a feed date with no timezone as utc * test: cover token rejection, the daily mail send and github lookups * test: spell unparsable the way typos expects * refactor: move tables into app/core/db (#83) * fix: percent-encode database credentials in the dsn * fix: declare the indexes and column types the migrations built * refactor: move tables into app/core/db * test: cover the schema against migrations, the engine and ledger writes * refactor: split backend domains into app/features (#84) * refactor: split backend domains into app/features * refactor: type app/features for mypy strict * fix: keep links on a shared platform's subdomains with the aggregator * fix: drop a newsletter product whose picked link is past the search results * test: cover approve and reject against the schema, and publisher resolution edges * chore: point the end-of-file exclude at the moved email templates * refactor: tidy comments and test helpers from the features split * refactor: move mcp tools and scripts into app/entry (#85) * refactor: expose what entry needs through each feature's service * refactor: split the mcp tools into app/entry/mcp and fold the router into main * refactor: move scripts and the timer entrypoints into app/entry/cli * test: cover the mcp token gate end to end over http * test: cover the cli entrypoints' targets and exit codes * fix: fail the backup when pg_dump writes nothing * refactor: make app/entry pass strict mypy * fix: keep sentry off in development * chore: name the seed module in restore-db's hint without backticks * docs: say what tests-start.sh does with its argument * test: return the tool result's error flag and text from the gate tests' helper * fix: stop importing the email module from configuring the root logger * fix: require the alembic that reads its config from pyproject * test: check the collect run leaves the root logger alone * docs: say where command-line entrypoints live * refactor: name the newsletter service alike in every mcp tool * test: colocate backend tests beside their modules (#86) * test: colocate backend tests beside their modules * test: cover the curation tools, sign-in refusals, publisher lookups and the known-URL filter * fix: fail the daily report when its mail does not send * test: pin that the report reaches the send, and clean up the filter's publisher * ci: enforce backend import layers (#87) * fix: run mypy and ty in pre-commit with the backend config * ci: enforce backend import layers * fix: name the exact claude-code-action tag in its pin comments * ci: deploy only what the box runs (#88) * docs: point the deploy README's env table at app/features * ci: deploy only what the box runs * test: check the deploy filter ships every path the units and the job use * docs: record the layered backend (#89) * docs: record the layered backend * fix: address the backend migration review (#91) * fix: configure logging for the api process * fix: let a publisher's link claim a platform subdomain * fix: fail a link loudly on a non-numeric jev answer * docs: point stale docstrings at where the code moved --------- Co-authored-by: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com>
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.
Step 6 of the backend migration. Stacked on #86.
What changed
>=2.15,<3.0.0), and uv.lock is updated.backend/pyproject.tomlunder[tool.importlinter].import-linterhook (uv run lint-imports --config backend/pyproject.toml). It runs on every commit, like mypy, so a deleted file or a lock bump can't skip it.Check import layersstep before the db comes up.backend/conftest.pysits outside theapppackage, so it isn't linted.Contracts
main > entry > features > core: layers, withcontainers = ["app"]andexhaustive = true. A new top-level module underapp/must join a layer.app/test_main.pyis the one exception.Features never import each other: independence overapp.features.*.api, mcp and cli never import each other: independence overapp.entry.api,app.entry.mcpandapp.entry.cli.Flat core modules never import core.db: forbidden, fromapp.core.*toapp.core.db. Indirect chains count too.Entry reaches a feature only through its service and model: forbidden, fromapp.entrytoapp.features.*.*, withallow_indirect_imports = true. The directapp.entry.** -> app.features.*.serviceand-> app.features.*.modeledges are ignored, so no list has to be kept by hand. The one exception isapp.entry.api.test_articles -> app.features.catalog.semantic_search, because the search tests stubembedwhere it is called.Proof each contract fails
For each one I added a violating import, ran
lint-imports, and then removed it.app/core/dates.py:from app.features.catalog import modelA new empty
app/probe.pyalso breaks it:app/features/stories/service.py:from app.features.catalog import modelapp/entry/cli/report.py:from app.entry.api import depsapp/core/urls.py:from app.core.db import articleIt also reports every flat module that reaches it, for example
app.core.extract -> app.core.urls -> app.core.db.article.app/entry/api/articles.py:from app.features.catalog import facetsWith the
test_articlesignore line removed:mypy and ty in pre-commit
uv run mypy backend/appanduv run ty check backend/appfrom the repo root. Neither one readbackend/pyproject.toml.def _probe(x): return xgot no error from the hook, butcd backend && uv run mypy appgaveno-untyped-def.[tool.ty.terminal]. Witherror-on-warning = false, the root run still exited 1 and the backend run exited 0.uv run mypy --config-file backend/pyproject.toml backend/appanduv run ty check --project backend backend/app. Both pass, with the strict config applied.Bug fixes
fix: run mypy and ty in pre-commit with the backend config, as described above.fix: name the exact claude-code-action tag in its pin comments.claude.ymlandclaude-code-review.ymlpinf27be6bwith the comment# v1. Thev1tag moved to5eb090f(v1.0.253), so zizmor's onlineref-version-mismatchaudit started failing on every PR. The comment now says# v1.0.252, which is the tag onf27be6b. The pinned commit is the same.Checks
--offline: clean. Online zizmor can't reach the GitHub API from the container./search?q=agents, because the HF model download is blocked here. It passes in CI.bash scripts/generate-client.sh: no diff.prek run --all-files: typos fails on.claude/skills/curate/regression.md, which is already like that on the base and isn't touched here.Review findings (code-review, high)
Fixed:
filesfilter. A commit that only deleted a.pyfile or bumped uv.lock could skip it. The filter is gone.app/was in no layer. It is nowexhaustive.Not changed:
__init__.pywould get past contract 5. Every feature__init__.pyis empty today.app.mainisn't a source in contract 5. It imports only entry and core.app.core.*also matchesapp.core.db, and import-linter skips that overlap. A comment in the config says so.🤖 Generated with Claude Code
https://claude.ai/code/session_01Lv3GURL2oLiS9Rff2o5vQU