Repository navigation
refactor: move mcp tools and scripts into app/entry - #85
Merged
Merged
Conversation
This was referenced Oct 18, 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: 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 4 of the backend architecture plan. Stacked on #84.
The pipeline, report and backup units now start
-m app.entry.cli.collect,-m app.entry.cli.reportandapp/entry/cli/backup_db.py.pipeline/andbackend/scripts/are gone, so the old units fail with ModuleNotFoundError or a missing file. Right after the deploy, on the box:Do it before 03:30 (backup) and 04:00 (collect).
What moved
app/router.pyis folded intoapp/main.py. Router order is unchanged.app/mcp/becameapp/entry/mcp/:server.py(FastMCP instance, instructions, tool list),auth.py(SharedSecret,WRITE_SCOPE,require_write), and one file per tool. Each tool checks the scope, calls one feature verb and shapes the result.sql_query.pykeeps the read-only DSN, the plan check and the blocked tables.app/entry/cli/holdscollect.py(waspipeline/run.py),report.py(waspipeline/report.py),backup_db.py,prestart.sh,wait_for_db.py,create_superuser.py,seed_tags.py+tags.json,seed_articles.py,seed_stories.py,backfill_publishers.pyandfind_missing_tags.py. Run them withpython -m app.entry.cli.<name>frombackend/.test.shandtests-start.shmoved to rootscripts/.pipeline/andbackend/scripts/are deleted.serviceandmodel. To make that possible:origin_conditionmoved fromservice.pyto a newrows.py, soservice.pycan re-exportsearchand the facet functions without a cycle.service.pyre-exports the sign-in and welcome mail functions.service.pyre-exportsdraft.service.pyre-exportscheck_link, andLinkErrormoved tomodel.py.service.pyre-exportsAGGREGATOR_TYPESandPublisherResolver.deploy-production.ymlandtest-backend.yml, the tests-start path intest-backend.yml, the mypy/ty paths in pre-commit (nowbackend/apponly), and the T201 ignore (nowapp/entry/cli/**). The CLAUDE.md MCP path and the paths indocs/backend.mdanddocs/style.mdare updated too.app/mcp/testsmoved toapp/entry/mcp/tests.test_tools.pywas split intotest_sql_query.pyandtest_web_tools.py.tests/scripts/test_wait_for_db.pymoved toapp/entry/cli/tests/. Thetests/subfolders are left for step 5.Behaviour changes
source = ["app"]). The total is 80%; CI requires 65%.app.core.emailno longer callslogging.basicConfigon import (see bug fixes). The backend's INFO line "send email result" no longer prints unless a handler is configured.ENVIRONMENT=development(see bug fixes).Bug fixes
dump_dbchecked the size of the gzip file, which is never 0 bytes because of the gzip header. It now checks the uncompressed byte count.parents[2]. After the move that would have pointed atbackend/, so it is nowparents[4]. A test pins it to the repo'scompose.yml.main.pycomparedENVIRONMENTwith"local", which is not one of its values, so the check was always true. It now compares with"development". Strict mypy flagged it.app.core.emailset the root logger to INFO. The collect and report runs import it throughcollect.service. httpx then logged a line for every request of the night into the journal.[tool.alembic], which older alembic versions don't read. The floor went from>=1.12.1to>=1.18.0. The lock already had 1.18.5.tests-start.shpasses its args to pytest.test.shuses its args as the coverage report's title, and CI relies on that. The doc now says so.Tests added
MCP_WRITE_TOKENunset, its old value gets a 401 and the read token still can't write. These sit alongside the existing tests for no token, a wrong token and an unsetMCP_TOKEN.python -minprestart.sh/tests-start.shresolves to a real module or file.collectandreportexit with their verb's code (0 and 1). The backup fails on a failed or empty pg_dump. The seeds refuse production. The collect run leaves the root logger at WARNING.Types
cd backend && uv run mypy --strict app/entry app/features app/corereports 0 errors, tests included. That meant removing the type ignores infind_missing_tags.py(nowcol()and anNDArray[float32]alias) and annotating the test functions.Review findings
The Claude Code Review workflow can't read diffs right now, so I ran the
code-reviewskill at high effort. It compared against master, so some findings belong to #82–#84:docs/style.mdscripts paragraph, the root logger set at import, and the alembic floor.send_mailreadingos.environbesidesettings(ADR 7 question). The collect run buildingSettings()at import (the plan accepts this under ADR 7). The Jev non-number answer counting as 0, which refactor: split backend domains into app/features #84 lists as intended. Stale paths in the glossary and thedeploy/README.mdenv table (step 8).How I verified
backend/), ty, typos (one pre-existing hit in.claude/skills/curate/regression.md, not touched here), the banned-names hook, and the other pre-commit hooks over all files.prestart.shand../scripts/tests-start.sh: 658 passed, 1 failed. The failure is/search, which needs the Hugging Face model download this sandbox blocks. It passes in CI.bash scripts/generate-client.shleavesfrontend/src/clientunchanged. The OpenAPI JSON is byte-identical, path order included.tools/listdump (names, descriptions, input schemas, in order) and the server instructions diff empty against the base.Generated by Claude Code