fix: worker never actually ran end-to-end against compose #64

Merged
bart merged 2 commits from chore/fix-worker-compose-command into main 2026-09-11 11:24:52 +00:00
Collaborator

While verifying #6's last acceptance criterion ("worker starts against the compose stack and logs a heartbeat on schedule") end-to-end -- rather than just taking the CLI wiring in #63 on faith -- I found three real bugs, chained:

  1. infra/compose/docker-compose.yml: the worker service set command: ["polaris", "worker"], but backend/Dockerfile already sets ENTRYPOINT ["polaris"]. Compose command: replaces CMD, not ENTRYPOINT, so the container actually ran polaris polaris worker and exited immediately with an argparse error. Fixed to command: ["worker"].
  2. Same file: both api and worker set POLARIS_DB_URL, which is not a Settings field (it's POLARIS_DATABASE_URL) -- silently ignored (extra="ignore"), so both services actually connected to localhost:5432 inside their own container instead of the db service. This affected api too, not just worker -- nothing in the real compose stack could ever reach Postgres.
  3. backend/src/polaris/db/models/task.py: once the worker could actually reach a real, Alembic-migrated Postgres, the claim loop failed with invalid input value for enum task_status: "QUEUED". SQLAlchemy's Enum(TaskStatus, ...) persists the Python member name by default, not .value -- 0001's migration hand-writes the Postgres enum with lowercase labels (queued, ...) to match this project's StrEnum convention, but this column never set values_callable to match. Masked in tests because tests/conftest.py builds its schema with Base.metadata.create_all(), which derives the enum type straight from the same (buggy) column definition rather than the migration -- so tests and the real migrated database silently disagreed. #9 (schema 0002) hit and fixed this exact issue for its own enums with a shared native_enum() helper in db/models/_enum.py; task.py predates that helper (PH0.2, sealed) and was never retrofitted. This points task_status at the same helper.

Verified end-to-end this time, not just unit-tested: built the real worker image, ran alembic upgrade head against a real Postgres via the compose stack, started the worker service for real, and watched it log a heartbeat on schedule with no errors. Full backend suite still green (134 passed, 5 skipped needing live Keycloak) after the task.py fix.

Ticks the last acceptance-criteria box on #6.

🤖 Generated with Claude Code

https://claude.ai/code/session_013YioTVKBPoE6thZqbnTtnM

While verifying #6's last acceptance criterion ("worker starts against the compose stack and logs a heartbeat on schedule") end-to-end -- rather than just taking the CLI wiring in #63 on faith -- I found three real bugs, chained: 1. **`infra/compose/docker-compose.yml`**: the `worker` service set `command: ["polaris", "worker"]`, but `backend/Dockerfile` already sets `ENTRYPOINT ["polaris"]`. Compose `command:` replaces `CMD`, not `ENTRYPOINT`, so the container actually ran `polaris polaris worker` and exited immediately with an argparse error. Fixed to `command: ["worker"]`. 2. **Same file**: both `api` and `worker` set `POLARIS_DB_URL`, which is not a `Settings` field (it's `POLARIS_DATABASE_URL`) -- silently ignored (`extra="ignore"`), so both services actually connected to `localhost:5432` *inside their own container* instead of the `db` service. This affected `api` too, not just `worker` -- nothing in the real compose stack could ever reach Postgres. 3. **`backend/src/polaris/db/models/task.py`**: once the worker could actually reach a real, Alembic-migrated Postgres, the claim loop failed with `invalid input value for enum task_status: "QUEUED"`. SQLAlchemy's `Enum(TaskStatus, ...)` persists the Python member *name* by default, not `.value` -- `0001`'s migration hand-writes the Postgres enum with lowercase labels (`queued`, ...) to match this project's `StrEnum` convention, but this column never set `values_callable` to match. Masked in tests because `tests/conftest.py` builds its schema with `Base.metadata.create_all()`, which derives the enum type straight from the same (buggy) column definition rather than the migration -- so tests and the real migrated database silently disagreed. #9 (schema 0002) hit and fixed this exact issue for its own enums with a shared `native_enum()` helper in `db/models/_enum.py`; `task.py` predates that helper (PH0.2, sealed) and was never retrofitted. This points `task_status` at the same helper. Verified end-to-end this time, not just unit-tested: built the real `worker` image, ran `alembic upgrade head` against a real Postgres via the compose stack, started the `worker` service for real, and watched it log a heartbeat on schedule with no errors. Full backend suite still green (134 passed, 5 skipped needing live Keycloak) after the `task.py` fix. Ticks the last acceptance-criteria box on #6. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_013YioTVKBPoE6thZqbnTtnM
Two bugs, found while verifying #6's "worker starts against the
compose stack" acceptance criterion end-to-end:

- command: ["polaris", "worker"] duplicated the Dockerfile's own
  ENTRYPOINT ["polaris"], so the container actually ran
  `polaris polaris worker` and exited with an argparse error.
- POLARIS_DB_URL isn't a field Settings reads (it's
  POLARIS_DATABASE_URL); the value was silently ignored
  (extra="ignore") and both api and worker fell back to
  localhost:5432 inside their own container instead of the db
  service.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013YioTVKBPoE6thZqbnTtnM
SQLAlchemy's Enum(SomeEnum) persists the Python member *name*
("QUEUED") by default, not its .value ("queued") -- easy to miss
since both look right in isolation. 0001's migration hand-writes
the Postgres enum with lowercase labels to match this project's
enum.StrEnum convention, but task.py's column didn't set
values_callable to match, so every claim-loop query against a
real (Alembic-migrated) database failed with
"invalid input value for enum task_status: QUEUED" -- masked in
tests because conftest.py builds its schema with
Base.metadata.create_all(), which derives the enum type straight
from the (buggy) column definition instead of the migration.

#9 (schema 0002) hit the same issue for its own enums and fixed it
with a shared native_enum() helper in db/models/_enum.py -- task.py
predates that helper and was never retrofitted, since it's PH0.2's
sealed file, not PH1.1's. This just points task_status at the same
helper.

Found while verifying #6's "worker starts against the compose
stack" acceptance criterion end-to-end.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013YioTVKBPoE6thZqbnTtnM
bart merged commit c72e4feff0 into main 2026-09-11 11:24:52 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
patrick/Polaris!64
No description provided.