You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Most of the backend's shared type definitions sit in one central folder, even though each is used by a single module or a single package. That means a reader has to open a second place to see the shape a function returns, and the central folder imports from across the codebase, which makes import cycles more likely. This issue moves each type next to the code that uses it, keeps a shared home only for the one type that is used everywhere, and writes the placement rule into the contributor instructions.
Placement rule
Put each type at the narrowest scope that covers all of its users:
Used by one module: define it in that module.
Used by several modules in one package: put it in a module that all of them already import, or in a new module named for what the types describe (schema.py for external API payloads, as in lib/mapping/schema.py). Don't add a types.py to a package out of habit.
Used by unrelated packages: put it in a small shared module named for its domain.
about 20 modules in worker/, lib/logging/canonical.py, scripts
new lib/workflow/outcome.py
Stays shared:
UserData is used by about 27 modules across routers/ and lib/. Move it from lib/types/authentication.py to a new lib/principal.py, keeping its model imports under TYPE_CHECKING, then delete lib/types/.
Unchanged:
worker/lib/managers/types.py is already shared only within its own package.
Types already defined in the module that uses them, such as those in routers/score_sets.py, lib/mapping/schema.py and lib/validation/transform.py.
Steps:
Land this after the release branches merge into main. All of them touch these imports.
Move the types and update every import directly, with no re-exports left in the old locations.
Commit the JobExecutionOutcome rewrite separately so its diff reviews on its own.
Update the worker docs that point at lib/types/workflow.py: worker/README.md, worker/job_registry.md, worker/jobs_overview.md and .github/instructions/worker.instructions.md.
Add the placement rule above to .github/instructions/python.instructions.md.
Move tests/worker/lib/managers/test_types.py and other tests of the moved types so they match the new module paths.
Run ruff format only on the files you touch.
Acceptance criteria
Each symbol in the moves table is defined in its new home and nowhere else.
UserData is defined in lib/principal.py.
src/mavedb/lib/types/ no longer exists.
git grep "lib.types\|lib/types" -- src tests .github returns no matches.
No types.py module is added to any package.
lib/clingen/schema.py imports nothing from other lib/clingen modules.
lib/workflow/definitions.py imports nothing from job_factory or pipeline_factory.
.github/instructions/python.instructions.md states the placement rule.
Importing mavedb.lib.logging.canonical, mavedb.lib.permissions, mavedb.lib.principal and mavedb.lib.workflow in a fresh interpreter raises no circular-import error.
The test suite and mypy pass.
Background
Why not centralize every type. A central types folder groups code by kind instead of by feature, so each feature spreads across two directories. It also has to import models from across the codebase: lib/types/annotation.py and lib/types/permissions.py already do this at runtime, and lib/types/authentication.py needed a TYPE_CHECKING guard to avoid a cycle. The API isn't installed as a library by other applications, so sharing types outside the codebase isn't a reason to centralize them.
Why UserData doesn't go in lib/authentication.py. That module imports deps and orcid. Every permissions module imports UserData, so putting it there would make permissions load the request-dependency stack.
Why JobExecutionOutcome gets its own module. It's a dataclass with factory methods and to_dict(), a domain object rather than a type hint.
Logging and ORM models.lib/workflow/__init__.py eagerly imports JobFactory and PipelineFactory, which load ORM models. Importing lib/workflow/outcome.py runs that __init__, so lib/logging/canonical.py will load models. This isn't a cycle. If logging should stay model-free, make the import inside its isinstance check lazy.
Verification. Every usage in the moves table was checked on main, release-2026.3.0, the calibration-controls branch (Add calibration controls to support clinical confidence in variant interpretation #754) and the allele-centric mapping branch. SequenceFeature is used by lib/annotation/util.py on the first three and by lib/annotation/proposition.py on the allele-centric branch. The table uses the merged state.
Summary
Most of the backend's shared type definitions sit in one central folder, even though each is used by a single module or a single package. That means a reader has to open a second place to see the shape a function returns, and the central folder imports from across the codebase, which makes import cycles more likely. This issue moves each type next to the code that uses it, keeps a shared home only for the one type that is used everywhere, and writes the placement rule into the contributor instructions.
Placement rule
Put each type at the narrowest scope that covers all of its users:
schema.pyfor external API payloads, as inlib/mapping/schema.py). Don't add atypes.pyto a package out of habit.Scope
Moves:
ResourceWithCreationModificationDateslib/annotation/contribution.pylib/annotation/contribution.pyPublicationIdentifierAssociationslib/annotation/method.pylib/annotation/method.pySequenceFeaturelib/annotation/proposition.pylib/annotation/proposition.pyClassificationDictlib/score_calibrations.pylib/score_calibrations.pyMappingEntry,MappingEntrieslib/uniprot/id_mapping.pylib/uniprot/id_mapping.pyEntityTypelib/permissions/core.py,lib/permissions/utils.pylib/permissions/models.pylib/types/clingen.pylib/clingen/services.py,lib/clingen/content_constructors.pylib/clingen/schema.pyJobDefinition,PipelineDefinitionlib/workflow,worker/jobs/registry.py,scripts/run_job.py,scripts/run_score_set_pipelines.pylib/workflow/definitions.pyJobExecutionOutcomeworker/,lib/logging/canonical.py, scriptslib/workflow/outcome.pyStays shared:
UserDatais used by about 27 modules acrossrouters/andlib/. Move it fromlib/types/authentication.pyto a newlib/principal.py, keeping its model imports underTYPE_CHECKING, then deletelib/types/.Unchanged:
worker/lib/managers/types.pyis already shared only within its own package.routers/score_sets.py,lib/mapping/schema.pyandlib/validation/transform.py.Steps:
main. All of them touch these imports.JobExecutionOutcomerewrite separately so its diff reviews on its own.lib/types/workflow.py:worker/README.md,worker/job_registry.md,worker/jobs_overview.mdand.github/instructions/worker.instructions.md..github/instructions/python.instructions.md.tests/worker/lib/managers/test_types.pyand other tests of the moved types so they match the new module paths.ruff formatonly on the files you touch.Acceptance criteria
UserDatais defined inlib/principal.py.src/mavedb/lib/types/no longer exists.git grep "lib.types\|lib/types" -- src tests .githubreturns no matches.types.pymodule is added to any package.lib/clingen/schema.pyimports nothing from otherlib/clingenmodules.lib/workflow/definitions.pyimports nothing fromjob_factoryorpipeline_factory..github/instructions/python.instructions.mdstates the placement rule.mavedb.lib.logging.canonical,mavedb.lib.permissions,mavedb.lib.principalandmavedb.lib.workflowin a fresh interpreter raises no circular-import error.Background
lib/types/annotation.pyandlib/types/permissions.pyalready do this at runtime, andlib/types/authentication.pyneeded aTYPE_CHECKINGguard to avoid a cycle. The API isn't installed as a library by other applications, so sharing types outside the codebase isn't a reason to centralize them.UserDatadoesn't go inlib/authentication.py. That module importsdepsandorcid. Every permissions module importsUserData, so putting it there would make permissions load the request-dependency stack.JobExecutionOutcomegets its own module. It's a dataclass with factory methods andto_dict(), a domain object rather than a type hint.lib/workflow/__init__.pyeagerly importsJobFactoryandPipelineFactory, which load ORM models. Importinglib/workflow/outcome.pyruns that__init__, solib/logging/canonical.pywill load models. This isn't a cycle. If logging should stay model-free, make the import inside itsisinstancecheck lazy.main,release-2026.3.0, the calibration-controls branch (Add calibration controls to support clinical confidence in variant interpretation #754) and the allele-centric mapping branch.SequenceFeatureis used bylib/annotation/util.pyon the first three and bylib/annotation/proposition.pyon the allele-centric branch. The table uses the merged state.