SEP-1655: Move the remaining Nomad-specific vocabulary out of app/tasks/models.py - #1226
SEP-1655: Move the remaining Nomad-specific vocabulary out of app/tasks/models.py#1226peter-o-addo wants to merge 25 commits into
app/tasks/models.py#1226Conversation
There was a problem hiding this comment.
Pull request overview
This PR continues the executor-neutralization work in the Tasks service by relocating remaining Nomad-specific vocabulary/constants out of the tasks models layer into the Nomad executor package, renaming Nomad-named log cursor columns/accessors to executor-neutral terms, and regenerating the API/TS client contract (with a documented breaking change for the renamed TaskHistory field).
Changes:
- Break the Nomad executor import cycle by switching
app.tasks.execution.executors.nomadto a lazyNomadExecutorexport and deferringapp.tasks.configimports where needed. - Rename Nomad-vocabulary log cursor columns to executor-neutral names (
nomad_offset→producer_fetch_offset,allocation_epoch→producer_epoch,log_allocation_epoch→log_producer_epoch) with a reversible Alembic migration and updated CRUD/log-writer logic. - Regenerate OpenAPI specs + TS clients and add a breaking changelog fragment for the
TaskHistoryresponse field rename; update tests accordingly (including new regression tests for the import-cycle and migration rename).
Reviewed changes
Copilot reviewed 21 out of 23 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/app/tasks/test_routes.py | Updates test imports to pull RUN_SCRIPT_OUTPUT_FILES_PATH from Nomad constants. |
| tests/app/tasks/migrations/test_taskhistory_log_nomad_cursor.py | Updates existing migration test expectations to use executor-neutral cursor column names. |
| tests/app/tasks/migrations/test_rename_log_cursor_columns.py | Adds coverage for the rename migration upgrade/downgrade preserving values. |
| tests/app/tasks/logs/test_log_writer.py | Updates log-writer tests to use new cursor/epoch parameter and field names. |
| tests/app/tasks/logs/test_log_eviction.py | Updates eviction test to pass the renamed epoch argument. |
| tests/app/tasks/execution/executors/nomad/test_models.py | Updates Nomad executor tests for renamed cursor/epoch fields. |
| tests/app/tasks/execution/executors/nomad/test_import_cycle.py | Adds a subprocess-based regression test ensuring Nomad submodules import without config-first ordering. |
| tests/app/tasks/db/test_seed.py | Updates seed tests to import Nomad-only task name from Nomad constants. |
| tests/app/sep/apps/framework/test_spec.py | Updates framework spec tests to import RUN_SCRIPT_OUTPUT_FILES_PATH from Nomad constants. |
| frontend/packages/api/src/generated/tasks.ts | Regenerates TS types/docs including log_producer_epoch and doc wording adjustments. |
| frontend/packages/api/src/generated/sep.ts | Regenerates shared TS types/docs for the same doc wording adjustments. |
| frontend/packages/api/specs/tasks.json | Regenerates OpenAPI spec reflecting log_producer_epoch and doc wording adjustments. |
| frontend/packages/api/specs/sep.json | Regenerates OpenAPI spec reflecting doc wording adjustments. |
| changelog.d/SEP-1630.breaking.md | Documents the breaking rename from log_allocation_epoch to log_producer_epoch. |
| app/tasks/models.py | Removes RUN_SCRIPT_OUTPUT_FILES_PATH from tasks models; renames cursor/epoch columns and neutralizes related docs; updates internal task-name set. |
| app/tasks/migrations/versions/2026_07_30_1300-c8e4a2b91f70_rename_log_cursor_columns_executor_neutral.py | Adds reversible Alembic column renames for executor-neutral naming. |
| app/tasks/logs/log_writer.py | Renames log-writer cursor/epoch args and guards; defers config import to avoid import cycles. |
| app/tasks/execution/executors/nomad/models.py | Updates Nomad executor log fetching/persisting logic to use executor-neutral cursor/epoch naming. |
| app/tasks/execution/executors/nomad/constants.py | Introduces a dependency-free Nomad constants module for layout and Nomad-only system task names. |
| app/tasks/execution/executors/nomad/init.py | Implements lazy NomadExecutor export via __getattr__ to break import cycles. |
| app/tasks/db/seed.py | Switches seed-time Nomad constants to the new Nomad constants module to avoid heavy imports. |
| app/tasks/crud.py | Renames TaskHistory epoch accessor/mutator methods and updates state-frontier persistence APIs to new field names. |
| app/sep/apps/framework/spec.py | Updates framework spec code to import RUN_SCRIPT_OUTPUT_FILES_PATH from Nomad constants. |
1. DB schemasqlite3 tasks.db "PRAGMA table_info(taskhistory_log_state);" | rg 'producer_|nomad_|allocation_'
sqlite3 tasks.db "PRAGMA table_info(taskhistory);" | rg 'epoch'
sqlite3 tasks.db "SELECT * FROM alembic_version_tasks;"No 2. API contractcurl -sS -H "Authorization: Bearer $TOKEN" \
'http://127.0.0.1:8000/api/tasks/history/17'{
"id": 17,
"status": "success",
"log_capture": "complete",
"has_logs": true,
"has_log_allocation_epoch": false,
"has_log_producer_epoch": false
}Old name absent. ( 3. Live Nomad logsDispatch POST /api/tasks/execute/run-command
{"meta":{"target":"pbm-test-03","command":"ping","args":"-c\n30\n127.0.0.1","log_capture_hold_seconds":"5"}}{"id": 17, "status": "running"}While running → after finish (sync + DB) Logs curl -sS -H "Authorization: Bearer $TOKEN" \
'http://127.0.0.1:8000/api/tasks/history/17/logs/'{
"step": "run-script",
"type": "stdout",
"offset": 1931,
"msg": "PING 127.0.0.1 ...\n30 packets transmitted, 30 received, 0% packet loss..."
} |
Coverage reportClick to see where and how coverage changed
This report was generated by python-coverage-comment-action |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
…roducer vocabulary
Every other alter_column in the tree goes through batch_op; a bare call breaks the SQLite-backed paths. Also drops the both-columns-present branch, which dropped the old column without carrying its values over.
…utionEvent opener The annotation is the source of truth, so a :type:/:rtype: echoing it is redundant; the docstrings this branch rewrote had shed them only on their new lines. Opens ExecutionEvent with an imperative verb. Regenerates the OpenAPI specs and TS clients, which embed both docstrings verbatim.
…e docstrings The hand-maintained tuple covered three of the package's four submodules while the test claimed to cover any of them, so a submodule added later would join the package without joining the regression test. Deriving it from the package closes that, and an emptiness guard keeps the probe from passing vacuously. Also corrects three fixture docstrings that said Yield on a function that returns.
log_allocation_epoch never appeared in a tagged release -- the migration that added it is not reachable from v0.13.1 -- so there is no old field name for consumers to migrate off, and the fragment was filed under SEP-1630 rather than this PR's ticket.
…dels.py Covers the three spots outside the ticket's enumerated docstring list: TaskHistoryLog.source (the sibling of the already-neutralized TaskHistoryLogState.source), TaskHistoryStatusEnum.STALE, and the operator-facing stale-task alert summary. The alert text is released, so it ships with a changelog fragment; the enum docstring propagates into every app's OpenAPI snapshot.
|
Follow-up from review — release-notes coverage for the new Relative to The field itself was introduced by SEP-1630, which is still unreleased — the migration that added What is genuinely new to an operator upgrading from Suggested wording, filed under SEP-1630: the Resolved. Implemented in this PR as |
|
Follow-up from review — An AST sweep of Densest clusters:
The fix is mechanical — Resolved. Implemented in this PR (b05ade6): all 31 openers rewritten |
There was a problem hiding this comment.
@peter-o-addo — pushed fixes for everything this review could act on. Not approving yet, because two items are tracked separately rather than settled here:
- Relative to
v0.13.1,TaskHistoryresponses gain a requiredlog_producer_epochfield that no changelog fragment covers. The addition belongs to SEP-1630, which introduced the field and is still unreleased, so it is not this PR's fragment to add — see the follow-up comment above. - 31 fixture docstrings across
tests/open withYieldon functions thatreturn. The three inside this diff are fixed; the rest span files unrelated to this ticket and want their own PR.
Two notes on what changed under you, since neither corresponds to a review comment:
changelog.d/SEP-1630.breaking.mdwas removed. Its stated break — consumers migrating offlog_allocation_epoch— cannot have occurred: the migration that added that column is not reachable from thev0.13.1tag, so no published release ever returned the old field name. The rename is invisible from outside.- 40
:type:/:vartype:/:rtype:directives that restate their annotations were dropped from the docstrings this branch had already rewritten — 26 inapp/tasks/models.py, plus 9 inapp/tasks/logs/log_writer.py, 3 inapp/tasks/execution/executors/nomad/models.pyand 2 inapp/tasks/crud.py. Those were pre-existing on the base branch; editing a docstring is what obliges shedding them, so they had to go for the branch to pass the docstring checks.
Everything else — the batch_alter_table wrap on the rename migration, the executor-neutral alert summary and its fragment, and the derived submodule list in the import-cycle test — is in the pushed commits.
|
Follow-up from review —
It is out of scope here for a concrete reason rather than an arbitrary one: unlike the three columns this PR renamed, these are keys inside a persisted/serialized mapping, so neutralizing them is a data-shape change and needs its own migration story plus a sweep of every producer and consumer of Worth its own ticket in the same neutralization epic. |
|
Follow-up from review — the Nomad executor package now has two dependency-free constant homes.
Not raised against SEP-1655: its implementation notes named a new Resolved. Implemented in this PR (e6ef47c): |
|
Follow-up from review — four
Suggested replacement for the opening clause only, leaving each site's tail intact: "The path, relative to the executor's working directory, where output files …". Left for its own change rather than fixed inline, because it is not prose-only: this text reaches the published contract, so it needs a Resolved. Implemented in this PR (4ce4d57): the four docstrings now read "The path, relative to the executor's working directory, ...", and |
|
Follow-up from review — the deferred imports in This marker originally claimed two sites needed their import cycle named. That diagnosis was wrong on both, and the corrected finding is narrower but more interesting.
What the deferral actually does is keep those names late-bound, which is what lets six tests patch them at their source modules:
I confirmed this empirically: promoting the imports and deleting the block fails 4 tests in So the production structure is being shaped by test patch targets, and "so tests can patch the source module" is neither of the two sanctioned reasons for an inline import. A comment cannot fix it — the remedy is to promote the imports and retarget the patches to the binding site ( |
|
Follow-up from review — 18 SEP-1655 added
A child that hangs rather than exiting non-zero stalls the whole run with no diagnostic, and CI has no per-test time limit to fall back on. The fix is mechanical — add a bounded |
|
@copilot resolve the merge conflicts in this pull request |
Co-authored-by: yyyyyyyan <24644216+yyyyyyyan@users.noreply.github.com>
Resolved by merging |
Summary
Move remaining Nomad-specific vocabulary out of the tasks models layer into the Nomad executor package, with executor-neutral log-cursor columns and a regenerated API contract.
app/tasks/execution/executors/nomad/__init__.py,app/tasks/logs/log_writer.py: break the Nomad package import cycle via lazy export and deferred config importsapp/tasks/execution/executors/nomad/steps.py,app/tasks/execution/executors/nomad/constants.py,app/tasks/models.py,app/tasks/db/seed.py,app/sep/apps/framework/spec.py: relocate the Nomad-owned constants and keep internal task names without importing the executor package.RUN_SCRIPT_OUTPUT_FILES_PATHlives besideNomadStepand derives from it (f"{NomadStep.RUN_SCRIPT}/local/output_files") so renaming the step cannot leave the path behind;constants.pykeeps the seeded Nomad-only task nameapp/tasks/models.py,app/tasks/crud.py,app/tasks/logs/log_writer.py,app/tasks/execution/executors/nomad/models.py: rename Nomad-named log-cursor columns/accessors to executor-neutral names and neutralize related docs, including the fouroutput_files_pathdescriptionsapp/tasks/migrations/versions/2026_07_30_1300-c8e4a2b91f70_rename_log_cursor_columns_executor_neutral.py: rename those columns in place with a reversible Alembic migrationapp/tasks/models.py: the stale-task alert summary now readsskipped as stale (executor placement delayed past threshold)instead of naming Nomad — the one user-visible behaviour change, recorded inchangelog.d/SEP-1655.changed.mdapp/tasks/models.py: the alert-hook builder is imported at module scope; the deferral it replaced guarded no cycle, sincealert_hooksreachesapp.tasks.modelsonly underTYPE_CHECKINGchangelog.d/SEP-1630.added.md: record thatTaskHistoryresponses now carry a requiredlog_producer_epochfield. The field's existence is new relative tov0.13.1and belongs to SEP-1630, which introduced it and is already closedfrontend/packages/api/specs/{tasks,sep}.json,frontend/packages/api/src/generated/{tasks,sep}.ts,tests/app/sep/snapshots/openapi/*.json: regenerate the OpenAPI/TS client and snapshot fixtures for theTaskHistoryfield rename and the neutralized docstringsYieldon functions thatreturnwere corrected toReturnThe
changelog.d/SEP-1630.breaking.mdfragment that earlier revisions of this branch carried was dropped: the migration that addedlog_allocation_epochis not reachable from thev0.13.1tag, so no released version ever returned the old field name and the rename is invisible from outside.Tested
producer_fetch_offset/producer_epoch/log_producer_epochonly (nonomad_offset/allocation_epoch).GET /api/tasks/history/{id}has nolog_allocation_epochand reportslog_captureafter the task finishes.run-command(e.g. ping) and confirm logs grow while running, then finish complete with advancingproducer_fetch_offset.Operator note. Two of the renamed columns (
taskhistory_log_state.nomad_offset,taskhistory_log_state.allocation_epoch) shipped inv0.13.1, so rolling the image back to a build from before this release requiresalembic --name tasks downgrade a19da5cf0bcafirst.Checklist
make test)make run-pre-commit)make makemigrations)changelog.d/if the change is user-facing (make changelog-add), or confirmed N/A (internal-only change, or a same-release-cycle fix for an unreleased sibling ticket)