Skip to content

ci(backend): type-check with ty and fix existing type errors - #116

Merged
klin2686 merged 24 commits into
mainfrom
refactor/ty-type-checking
Oct 13, 2026
Merged

klin2686 merged 24 commits into
mainfrom
refactor/ty-type-checking

Conversation

@klin2686

@klin2686 klin2686 commented Oct 13, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

  1. Adds the ty type checker to the backend, enforced in CI and in the pre-commit hook.
  2. Fixes the 99 type errors ty found, mostly by narrowing nullable values and correcting annotations.
  3. Makes the 24 auto-set timestamp columns NOT NULL, since the response schemas already promise them as non-null.

What changed

Backend

Models and migrations

alembic upgrade head required — 1 revision.

  • Makes every default=utcnow timestamp NOT NULL (created_at, updated_at, last_active_at, joined_at, submitted_at across 16 tables), filling any NULLs first. (63863039d4f1)
  • Annotates those columns as Mapped[datetime]; .claude/rules/models-and-migrations.md now says auto-set timestamps aren't nullable.
  • Declares TournamentRole.member_count = 0 as a plain class attribute (not a column), so the value with_member_counts() sets is typed.
  • Types down_revision as str | Sequence[str] | None in alembic/script.py.mako and the two existing merge revisions, which have tuple parents.

Schemas and routes

  • No endpoint or response shape changes.
  • Swaps the unpack order in TournamentSummary(...) and AdminTournamentRead(...) (**counts first) to avoid a ty false positive; no runtime effect.
  • Marks EventTrackDetailRead.needs narrowing list[EventStaffingNeed] to list[EventStaffingNeedRead] with # ty: ignore[invalid-attribute-override], an intended Pydantic subclass override.

Logic

  • get_scoped_or_404 fetches with db.get(model, id_) instead of db.query(model).filter(model.id == id_).first(), so a row already in the session is reused.
  • log_action and archive_tournament accept actor_id: int | None, which the nightly auto-archive passes for a tournament whose owner was deleted.
  • apply_join_code_update, deactivate_join_code and is_join_code_expired take JoinCode; the JoinCodeLike Protocol lacked the label it writes.
  • SessionLocal is always a sessionmaker (unbound without DATABASE_URL); get_db checks engine instead.
  • resolve_person_refs and _resolve_chapter_creators return dict[int | None, PersonRefResponse], matching the None key they already add for deleted accounts.
  • The onboarding add route reads the next order with func.max(TournamentForm.order); the reorder route sorts by the submitted orders.
  • seed_members.py passes membership.user to write-through instead of a FakeUser stub.
  • Narrows values that are never None in practice (form owner ids, email-change tokens, regex matches on reserved field keys) with asserts or explicit checks.

Tooling

  • Adds ty as a dev dependency (locked at 0.0.86), configured in backend/ty.toml with error-on-warning = true.
  • Excludes the three files of the deprecated Google Sheets import that fail type checking (routes/sheets.py, sheets_validation.py, sync_service.py); sync_sheet still writes membership columns dropped in 2df371e5.
  • Adds a "Type check" step (uv run ty check) to the backend workflow.
  • Renames that workflow from pytest.yml / "Backend Tests" to backend.yml / "Backend CI" (job test → checks), since it now lints and type-checks too.
  • The workflow also runs when only its own file changes.
  • Upgrades actions/checkout to v7 in backend.yml and release.yml, and actions/setup-node to v7 in release.yml, which run on Node 24 instead of the deprecated Node 20.
  • .githooks/pre-commit type-checks a copy of the staged backend, so unstaged edits don't affect the result.

Editor

  • Adds .vscode/settings.json, pointing the ty and Ruff extensions at backend/.venv, setting ty.configurationFile, reporting ty diagnostics for the whole workspace and making Ruff the Python formatter.
  • Adds .vscode/extensions.json recommending both extensions; .gitignore now ignores .vscode/* except these two files.

Out of scope

  • Fixing or removing the Google Sheets import; it stays deprecated and unchecked.

Test plan

  • pytest passes locally
  • Migration included for every model change
  • Clicked through the UI change in a browser (no UI change)

Automated

  • tests/core/test_form_branching.py uses string field ids, matching FormField.id.
  • tests/test_migrations.py covers the new revision against the models.
  • uv run ty check and Ruff pass across backend/.

Manual

  • Upgraded and downgraded the dev database through 63863039d4f1, and ran alembic check.
  • Pre-commit hook fails on a type error that's staged, and ignores one that's only in the working tree.
  • With the repo root as the workspace, ty's language server reports nothing for the excluded sheets files.
  • release.yml is untested with the new action versions; it first runs at the next manual release.

@vercel

vercel Bot commented Oct 13, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
nexus Ready Ready Preview Oct 13, 2026 10:52pm UTC

@railway-app

railway-app Bot commented Oct 13, 2026

Copy link
Copy Markdown

This PR was not deployed automatically as @klin2686 does not have access to the Railway project.

In order to get automatic PR deploys, please add @klin2686 to your workspace on Railway.

@klin2686
klin2686 merged commit 724b472 into main Oct 13, 2026
3 checks passed

This branch was successfully deployed

1 active deployment
Preview — 2f1f8e21 Deployed Oct 13, 2026 by vercel[bot]
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