Skip to content

[codex] Harden deployment config startup#347

Merged
michaelmwu merged 10 commits into
mainfrom
michaelmwu/no-true-var-default
Jun 29, 2026
Merged

[codex] Harden deployment config startup#347
michaelmwu merged 10 commits into
mainfrom
michaelmwu/no-true-var-default

Conversation

@michaelmwu

@michaelmwu michaelmwu commented Jun 29, 2026

Copy link
Copy Markdown
Member

Summary

  • keep deployment settings construction from failing eagerly when optional or runtime-only env vars are missing
  • keep the API process alive when startup Postgres migrations/connection fail so /health can report degraded status, including preserved migration-failure state
  • keep CRM sync enabled by default, but skip scheduler startup unless ESPO credentials are configured; newsletter sync remains opt-in
  • keep DISCORD_BOT_TOKEN required at bot runtime without breaking config imports
  • add ClamAV sidecar-backed resume scanning for production Compose and report missing scanner config through /health
  • add a Docker smoke script plus GitHub workflow for merge-queue and labeled/on-demand API container startup checks

Why

Coolify cleans up containers after startup failures, which makes env/config crashes hard to debug. This shifts failures from import/startup validation into health/runtime reporting where possible, while preserving required runtime behavior for the services that truly need credentials.

Validation

  • uv run ruff check packages/shared/src/five08/settings.py apps/worker/src/five08/worker/config.py apps/api/src/five08/backend/api.py apps/discord_bot/src/five08/discord_bot/config.py apps/discord_bot/src/five08/discord_bot/main.py tests/unit/test_shared_settings.py tests/unit/test_worker_config.py tests/unit/test_bot.py tests/unit/test_backend_api.py
  • uv run pytest tests/ -v --tb=short (1821 passed, 20 skipped)
  • uv run pytest tests/unit/test_backend_api.py tests/unit/test_worker_config.py tests/unit/test_shared_settings.py -v --tb=short (313 passed, 1 warning)
  • uv run pytest tests/unit/test_worker_config.py tests/unit/test_intake_form_processor.py -v --tb=short (62 passed, 1 warning)
  • uv run pytest tests/unit/test_backend_api.py tests/unit/test_worker_config.py tests/unit/test_intake_form_processor.py -v --tb=short (329 passed, 1 warning)
  • docker compose -f compose.yaml config
  • git diff --check
  • local ./scripts/docker-smoke.sh passed on 4e632b1f, including Redis/Postgres/ClamAV startup, /health, and a real clamdscan --stream check inside the API container
  • GitHub Docker Smoke / docker-smoke passed on latest commit 4e632b1f

Summary by CodeRabbit

  • New Features
    • Added a Docker “smoke” workflow to automatically build/run a temporary stack and verify /health is healthy.
    • Added a ClamAV service to the default compose setup and default resume virus scanning command with health gating.
  • Bug Fixes
    • Improved API startup resilience and enhanced /health to reflect Postgres migrations status and effective resume-scan configuration.
    • Hardened CRM sync scheduling and resume-scan execution/config validation based on required settings.
  • Documentation
    • Updated environment-variable and configuration guidance, including newsletter sync defaulting to off.
  • Tests
    • Expanded unit coverage for health, scheduler gating, bot token handling, and resume scanning error cases.

Copilot AI review requested due to automatic review settings June 29, 2026 02:10
@coderabbitai

coderabbitai Bot commented Jun 29, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@michaelmwu, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 47 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 14172b41-d692-45ba-944c-5ee3bc01d6ba

📥 Commits

Reviewing files that changed from the base of the PR and between 2024957 and 7cfb7ea.

📒 Files selected for processing (4)
  • apps/api/src/five08/backend/api.py
  • compose.yaml
  • tests/unit/test_backend_api.py
  • tests/unit/test_intake_form_processor.py
📝 Walkthrough

Walkthrough

The PR updates runtime configuration defaults and validation, adds resume scan handling and health reporting, changes Discord bot startup token handling, and introduces a Docker smoke stack with workflow and documentation support.

Changes

Runtime config, startup, and smoke validation

Layer / File(s) Summary
Shared settings and documented defaults
packages/shared/src/five08/settings.py, .env.example, ENVIRONMENT.md, docs/configuration.md, tests/unit/test_shared_settings.py
SharedSettings defaults change for Postgres and newsletter sync, the eager secret validator is removed, and environment/configuration docs and tests are updated to match the new runtime requirements and defaults.
Worker CRM and resume-scan settings
apps/worker/src/five08/worker/config.py, apps/worker/src/five08/worker/crm/intake_form_processor.py, compose.yaml, tests/unit/test_worker_config.py, tests/unit/test_intake_form_processor.py
WorkerSettings now derives EspoCRM and virus-scan configuration state from helper properties, resume processing uses those derived flags to reject missing scan configuration and raise scan execution errors, and the compose/test coverage is updated around those behaviors.
API startup and health reporting
apps/api/src/five08/backend/api.py, tests/unit/test_backend_api.py
CRM scheduler startup is gated on Espo configuration, Postgres startup and migrations are tracked separately during lifespan startup, and /health reports the new Postgres and intake resume scan state.
Discord bot token validation
apps/discord_bot/src/five08/discord_bot/config.py, apps/discord_bot/src/five08/discord_bot/main.py, tests/unit/test_bot.py
The Discord bot settings default token becomes empty, and startup now trims and validates the configured token before starting the bot.
Docker smoke stack and workflow
scripts/docker-smoke.sh, .github/workflows/docker-smoke.yml, apps/api/Dockerfile, apps/worker/Dockerfile, DEVELOPMENT.md
A temporary Docker Compose smoke stack is generated and validated with /health, the images gain clamdscan setup, and a GitHub Actions workflow plus development notes are added for running the smoke script.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • 508-dev/508-workflows#335: Shares the same resume malware-scan gating path in apps/worker/src/five08/worker/crm/intake_form_processor.py.
  • 508-dev/508-workflows#346: Touches the same resume-processing flow that this PR extends with scan configuration checks and error handling.

Poem

🐇 I sniff the configs, hop through the smoke,
The healthy stack purrs as the red tests awoke.
ClamAV hums in a Compose-side den,
And the bot checks its token before starting again.
With health signs bright, I give a little cheer —
“This bunny says: the path is clear!”

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 31.88% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title is concise and accurately reflects the main change: hardening startup/config behavior across deployment components.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch michaelmwu/no-true-var-default

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Comment thread .github/workflows/docker-smoke.yml Fixed

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: dfc49fa338

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +9099 to +9102
try:
await asyncio.to_thread(run_job_migrations)
except Exception:
logger.exception("Failed to run job migrations during startup")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Don't mark the API healthy after skipped migrations

When run_job_migrations() fails here, startup now continues and the only later Postgres health check is SELECT 1. In a deploy where the migration fails because the DB is temporarily unavailable, permissions are wrong, or a migration SQL error occurs, the service can later return /health 200 once a bare connection succeeds even though Alembic never reran and required tables/columns may be missing, causing API routes and queued jobs to fail against a stale schema. Preserve the migration failure in health or retry migrations before allowing Postgres to be considered healthy.

Useful? React with 👍 / 👎.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adjusts runtime configuration and startup behavior across the API/worker/bot so deployments are less likely to crash on import/startup when optional or runtime-only environment variables are missing, shifting failures into /health and runtime reporting instead.

Changes:

  • Relaxed settings validation defaults (notably Postgres/MinIO secrets and newsletter sync default) and updated corresponding unit tests.
  • Made API startup resilient to Postgres migration/connection failures and gated CRM sync scheduler startup on ESPO credentials.
  • Added a Docker Compose-based smoke script and a merge-queue workflow to validate API container startup + /health.

Reviewed changes

Copilot reviewed 15 out of 15 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
tests/unit/test_worker_config.py Updates worker settings tests for ESPO-optional behavior and non-local virus-scan defaults.
tests/unit/test_shared_settings.py Updates shared settings tests to allow non-local construction even when runtime deps are unset.
tests/unit/test_bot.py Adds coverage for bot token being optional for config import but required at runtime start.
tests/unit/test_backend_api.py Adds coverage for CRM scheduler gating + degraded health when Postgres startup fails.
scripts/docker-smoke.sh New script to build/run an isolated API+Redis+Postgres stack and poll /health.
packages/shared/src/five08/settings.py Changes shared defaults (newsletter sync off; Postgres default constant) and removes eager non-local secret validation.
ENVIRONMENT.md Updates env var guidance for Postgres/ESPO/newsletter sync defaults.
docs/configuration.md Updates configuration docs for virus-scan requirement removal and newsletter sync default.
DEVELOPMENT.md Documents the new Docker smoke script for deployment-style startup checks.
apps/worker/src/five08/worker/config.py Removes non-local ESPO requirement validation and adds espo_configured helper.
apps/discord_bot/src/five08/discord_bot/main.py Enforces non-empty bot token at runtime start (after stripping).
apps/discord_bot/src/five08/discord_bot/config.py Makes bot token default blank to avoid config import failures.
apps/api/src/five08/backend/api.py Makes Postgres startup best-effort, adds CRM scheduler gating, and supports None postgres connection checks.
.github/workflows/docker-smoke.yml Adds merge-group/manual workflow that runs the Docker smoke script.
.env.example Updates the commented newsletter sync default to false.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

if _should_start_crm_sync_scheduler():
app.state.crm_sync_task = asyncio.create_task(_crm_sync_scheduler(app))
else:
logger.info("CRM sync scheduler disabled by config")
Comment thread apps/api/src/five08/backend/api.py Outdated
Comment on lines +974 to +982
def _should_start_crm_sync_scheduler() -> bool:
if not settings.crm_sync_enabled:
return False
if settings.espo_configured:
return True
logger.warning(
"CRM sync scheduler enabled but ESPO_BASE_URL/ESPO_API_KEY are not configured; skipping scheduler startup"
)
return False
Comment thread ENVIRONMENT.md
Comment on lines 13 to 16
- `API_SHARED_SECRET` (required for protected endpoints)
- `POSTGRES_URL` (required in non-local environments)
- `MINIO_ROOT_PASSWORD` (required in non-local environments)
- `DISCORD_BOT_TOKEN` (Discord bot runtime)
Comment thread ENVIRONMENT.md Outdated
## Postgres + Compose Exposure

- `Optional`: `POSTGRES_URL` (default: `postgresql://postgres:postgres@127.0.0.1:5432/workflows`; `./scripts/dev.sh` overrides it to a deterministic per-worktree localhost port, Compose injects a Docker-network URL)
- `Required in non-local environments`: `POSTGRES_URL` (local default: `postgresql://postgres:postgres@127.0.0.1:5432/workflows`; `./scripts/dev.sh` overrides it to a deterministic per-worktree localhost port, Compose injects a Docker-network URL)
Copilot AI review requested due to automatic review settings June 29, 2026 02:15

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (3)
tests/unit/test_backend_api.py (1)

64-65: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Add a migration-only degraded-health case.

This patches both migrations and connection setup to fail, so it doesn’t catch the case where migrations fail but get_postgres_connection() succeeds. Add that isolated case to lock the intended startup-health contract.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/unit/test_backend_api.py` around lines 64 - 65, The degraded-health
coverage in the backend API tests currently only exercises the case where both
run_job_migrations and get_postgres_connection fail, so it misses the startup
path where migrations fail but the database connection still succeeds. Add a
separate test case in the test_backend_api suite that monkeypatches only
run_job_migrations to fail while leaving get_postgres_connection successful,
then assert the health state matches the intended migration-only degraded
behavior.
.github/workflows/docker-smoke.yml (1)

11-11: 🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick win

Disable checkout credential persistence.

The next step runs a repository shell script, so storing the workflow token in local git config is unnecessary exposure here. Set persist-credentials: false on checkout.

Proposed fix
-      - uses: actions/checkout@v6
+      - uses: actions/checkout@v6
+        with:
+          persist-credentials: false
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/docker-smoke.yml at line 11, The checkout step in the
docker-smoke workflow is persisting credentials unnecessarily, which exposes the
workflow token to the repository config. Update the existing actions/checkout
configuration to set persist-credentials to false so the later shell script step
can run without leaving the token in local git settings.

Source: Linters/SAST tools

scripts/docker-smoke.sh (1)

99-100: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Match the health payload semantically, not by exact JSON formatting.

grep -q '"status":"healthy"' will false-fail if the response serializer adds whitespace while keeping the same /health contract. A whitespace-tolerant match is safer here.

Proposed fix
-  if [ "$last_code" = "200" ] && grep -q '"status":"healthy"' "$body_file"; then
+  if [ "$last_code" = "200" ] && grep -Eq '"status"[[:space:]]*:[[:space:]]*"healthy"' "$body_file"; then
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/docker-smoke.sh` around lines 99 - 100, The health check in the
docker smoke test is matching the /health response by exact JSON text, which is
too brittle. Update the check in scripts/docker-smoke.sh near the curl/grep
logic to validate the payload semantically or with a whitespace-tolerant pattern
so it still passes when the serializer changes formatting. Keep the status-code
gate and adjust the grep/match around the health response body to use a more
flexible check tied to the health contract.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/docker-smoke.yml:
- Around line 3-9: The docker-smoke workflow currently relies on the default
GITHUB_TOKEN scope, which is broader than needed for this job. Add an explicit
minimal permissions block to the docker-smoke job in the workflow so it only has
repository read access, and keep the change localized to the existing workflow
job definition.

In `@apps/api/src/five08/backend/api.py`:
- Around line 9099-9102: The startup migration failure in the
`run_job_migrations` block is only logged, so `/health` can still look healthy
even after migrations fail. Add a persistent startup state in `api.py` (set it
when `run_job_migrations()` raises in the `asyncio.to_thread` call) and have the
health check path that currently relies on `get_postgres_connection()`/`SELECT
1` also read that state. Make the health calculation return degraded/unhealthy
whenever the migration-failed flag is set, while keeping the existing connection
check behavior.

---

Nitpick comments:
In @.github/workflows/docker-smoke.yml:
- Line 11: The checkout step in the docker-smoke workflow is persisting
credentials unnecessarily, which exposes the workflow token to the repository
config. Update the existing actions/checkout configuration to set
persist-credentials to false so the later shell script step can run without
leaving the token in local git settings.

In `@scripts/docker-smoke.sh`:
- Around line 99-100: The health check in the docker smoke test is matching the
/health response by exact JSON text, which is too brittle. Update the check in
scripts/docker-smoke.sh near the curl/grep logic to validate the payload
semantically or with a whitespace-tolerant pattern so it still passes when the
serializer changes formatting. Keep the status-code gate and adjust the
grep/match around the health response body to use a more flexible check tied to
the health contract.

In `@tests/unit/test_backend_api.py`:
- Around line 64-65: The degraded-health coverage in the backend API tests
currently only exercises the case where both run_job_migrations and
get_postgres_connection fail, so it misses the startup path where migrations
fail but the database connection still succeeds. Add a separate test case in the
test_backend_api suite that monkeypatches only run_job_migrations to fail while
leaving get_postgres_connection successful, then assert the health state matches
the intended migration-only degraded behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: cbcff1fe-e224-421f-96aa-581d3b6a1d77

📥 Commits

Reviewing files that changed from the base of the PR and between 80f1c8e and dfc49fa.

📒 Files selected for processing (15)
  • .env.example
  • .github/workflows/docker-smoke.yml
  • DEVELOPMENT.md
  • ENVIRONMENT.md
  • apps/api/src/five08/backend/api.py
  • apps/discord_bot/src/five08/discord_bot/config.py
  • apps/discord_bot/src/five08/discord_bot/main.py
  • apps/worker/src/five08/worker/config.py
  • docs/configuration.md
  • packages/shared/src/five08/settings.py
  • scripts/docker-smoke.sh
  • tests/unit/test_backend_api.py
  • tests/unit/test_bot.py
  • tests/unit/test_shared_settings.py
  • tests/unit/test_worker_config.py

Comment thread .github/workflows/docker-smoke.yml
Comment thread apps/api/src/five08/backend/api.py

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 15 out of 15 changed files in this pull request and generated 5 comments.

Comment thread scripts/docker-smoke.sh Outdated
Comment on lines 34 to +38
redis_queue_name: str = "jobs.default"
redis_key_prefix: str = "jobs"
redis_socket_connect_timeout: float | None = 5.0
redis_socket_timeout: float | None = 5.0
postgres_url: str = "postgresql://postgres:postgres@127.0.0.1:5432/workflows"
postgres_url: str = DEFAULT_POSTGRES_URL
Comment on lines +3 to +7
on:
merge_group:
pull_request:
types: [labeled]
workflow_dispatch:
Comment thread apps/api/src/five08/backend/api.py Outdated
Comment on lines +974 to +978
def _should_start_crm_sync_scheduler() -> bool:
if not settings.crm_sync_enabled:
return False
if settings.espo_configured:
return True
Comment thread ENVIRONMENT.md Outdated
Comment on lines 14 to 15
- `POSTGRES_URL` (required in non-local environments)
- `MINIO_ROOT_PASSWORD` (required in non-local environments)
@michaelmwu michaelmwu added docker-smoke Run Docker smoke check on this PR and removed docker-smoke Run Docker smoke check on this PR labels Jun 29, 2026

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 3aea425c6f

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines 188 to 190
if (
self.intake_resume_require_virus_scan
and not (self.intake_resume_virus_scan_command or "").strip()

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Restore production resume scan enforcement

In production with intake enabled and INTAKE_RESUME_REQUIRE_VIRUS_SCAN left unset/false, this validator now accepts the config, so IntakeFormProcessor._scan_resume_content() returns True immediately and document_processor.extract_text() parses the downloaded resume without a malware scan. This regresses the explicit AGENTS.md guidance: "Resume files are untrusted. Keep scanning required in production and configure a scanner command"; keep the non-local requirement or fail/degrade health instead of allowing production to process unscanned resumes.

Useful? React with 👍 / 👎.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
apps/api/src/five08/backend/api.py (1)

9110-9119: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Bound the Postgres startup calls. run_job_migrations() and get_postgres_connection() still have no timeout here, so a stalled Postgres or locked migration can block lifespan indefinitely and keep /health from ever coming up.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/api/src/five08/backend/api.py` around lines 9110 - 9119, The startup
path in api.py still lets run_job_migrations and get_postgres_connection block
indefinitely, which can stall lifespan and prevent /health from becoming ready.
Add explicit timeouts around both asyncio.to_thread calls in the startup flow,
handling timeout failures the same way other startup errors are handled by
logging and setting the relevant app.state flag. Use the existing startup block
near run_job_migrations and get_postgres_connection so the Postgres bootstrap
work cannot hang forever.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@compose.yaml`:
- Around line 137-138: The `depends_on` entries for `web` and `worker` are
making `clamav` a hard startup dependency in all Compose runs, which conflicts
with the optional/degraded behavior introduced by
`effective_intake_resume_require_virus_scan`. Update the Compose configuration
so the `clamav: condition: service_healthy` dependency is only applied in
prod/smoke contexts, using a profile or override file, and make the same
adjustment for the `worker` service as well.

---

Outside diff comments:
In `@apps/api/src/five08/backend/api.py`:
- Around line 9110-9119: The startup path in api.py still lets
run_job_migrations and get_postgres_connection block indefinitely, which can
stall lifespan and prevent /health from becoming ready. Add explicit timeouts
around both asyncio.to_thread calls in the startup flow, handling timeout
failures the same way other startup errors are handled by logging and setting
the relevant app.state flag. Use the existing startup block near
run_job_migrations and get_postgres_connection so the Postgres bootstrap work
cannot hang forever.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 58491cf8-bf72-4841-9b2f-74d42b87b2b0

📥 Commits

Reviewing files that changed from the base of the PR and between dfc49fa and 4e632b1.

📒 Files selected for processing (13)
  • .github/workflows/docker-smoke.yml
  • ENVIRONMENT.md
  • apps/api/Dockerfile
  • apps/api/src/five08/backend/api.py
  • apps/worker/Dockerfile
  • apps/worker/src/five08/worker/config.py
  • apps/worker/src/five08/worker/crm/intake_form_processor.py
  • compose.yaml
  • docs/configuration.md
  • scripts/docker-smoke.sh
  • tests/unit/test_backend_api.py
  • tests/unit/test_intake_form_processor.py
  • tests/unit/test_worker_config.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • tests/unit/test_worker_config.py

Comment thread compose.yaml Outdated

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 20 out of 20 changed files in this pull request and generated 3 comments.

Comment on lines +1030 to 1034
def _check_postgres_connection(connection: Connection | None) -> bool:
if connection is None:
return False
try:
with connection.cursor() as cursor:
Comment thread compose.yaml Outdated
Comment on lines +118 to +119
MINIO_INTERNAL_BUCKET: ${MINIO_INTERNAL_BUCKET:-internal-transfers}
INTAKE_RESUME_VIRUS_SCAN_COMMAND: ${INTAKE_RESUME_VIRUS_SCAN_COMMAND-clamdscan --stream --no-summary --config-file=/etc/clamav/clamdscan.conf {path}}
Comment thread compose.yaml Outdated
Comment on lines +165 to +166
MINIO_INTERNAL_BUCKET: ${MINIO_INTERNAL_BUCKET:-internal-transfers}
INTAKE_RESUME_VIRUS_SCAN_COMMAND: ${INTAKE_RESUME_VIRUS_SCAN_COMMAND-clamdscan --stream --no-summary --config-file=/etc/clamav/clamdscan.conf {path}}

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 4a4486479b

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread compose.yaml Outdated
MINIO_INTERNAL_BUCKET: ${MINIO_INTERNAL_BUCKET:-internal-transfers}
# Use '-' intentionally: an explicitly blank value means "scanner not configured",
# allowing startup while /health reports degraded in non-local runtimes.
INTAKE_RESUME_VIRUS_SCAN_COMMAND: ${INTAKE_RESUME_VIRUS_SCAN_COMMAND-clamdscan --stream --no-summary --config-file=/etc/clamav/clamdscan.conf {path}}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Don't let copied env files suppress ClamAV default

Docker Compose interpolation reads the project .env file, and the Docker docs distinguish ${VAR-default} (default only when unset) from ${VAR:-default} (also when empty). Because .env.example:251 defines INTAKE_RESUME_VIRUS_SCAN_COMMAND= blank, a production .env copied from it makes this new Compose default resolve to an empty string, so /health is degraded instead of using the documented ClamAV sidecar command. Use :- here, or remove/comment the blank sample variable if an empty value should not opt out of the default. See the Compose interpolation docs: https://docs.docker.com/compose/how-tos/environment-variables/variable-interpolation/

Useful? React with 👍 / 👎.

Comment thread compose.yaml Outdated
MINIO_INTERNAL_BUCKET: ${MINIO_INTERNAL_BUCKET:-internal-transfers}
# Use '-' intentionally: an explicitly blank value means "scanner not configured",
# allowing startup while /health reports degraded in non-local runtimes.
INTAKE_RESUME_VIRUS_SCAN_COMMAND: ${INTAKE_RESUME_VIRUS_SCAN_COMMAND-clamdscan --stream --no-summary --config-file=/etc/clamav/clamdscan.conf {path}}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Wait for ClamAV before starting resume workers

In the canonical Compose stack, this command points the worker at the clamav sidecar, but the worker depends_on below only waits for web/redis/postgres/minio; Compose only waits for healthchecks on dependencies declared with condition: service_healthy (as the smoke compose does for ClamAV in scripts/docker-smoke.sh:79-85; Docker docs: https://docs.docker.com/compose/how-tos/startup-order/). On deploys where ClamAV is still initializing and an intake job arrives, _scan_resume_content() treats the connection failure as a failed scan and the intake can complete with resume_uploaded: false, so add a clamav: service_healthy dependency for the worker.

Useful? React with 👍 / 👎.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 20 out of 20 changed files in this pull request and generated 1 comment.

Comment on lines +3442 to 3446
postgres_migrations_ok = getattr(request.app.state, "postgres_migrations_ok", True)
if not postgres_migrations_ok:
postgres_ok = False
elif hasattr(request.app.state, "postgres_conn"):
postgres_ok = await _is_postgres_connection_healthy(request.app)

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 202495710c

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread compose.yaml Outdated
MINIO_INTERNAL_BUCKET: ${MINIO_INTERNAL_BUCKET:-internal-transfers}
# Use '-' intentionally: an explicitly blank value means "scanner not configured",
# allowing startup while /health reports degraded in non-local runtimes.
INTAKE_RESUME_VIRUS_SCAN_COMMAND: ${INTAKE_RESUME_VIRUS_SCAN_COMMAND-clamdscan --stream --no-summary --config-file=/etc/clamav/clamdscan.conf {path}}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Avoid braces in the Compose scanner default

When deployers override INTAKE_RESUME_VIRUS_SCAN_COMMAND as the docs suggest, e.g. clamdscan --fdpass {path}, this Compose expression still parses the braced ${VAR-default} value up to the } in the default and leaves the final } as a literal; Docker documents that unquoted values are interpolated as braced expressions (https://docs.docker.com/compose/how-tos/environment-variables/variable-interpolation/). The worker then receives ... {path}}, _scan_resume_content() replaces it with a temp filename ending in }, and clamdscan scans a nonexistent path, causing resume intake jobs to fail. Since the code already appends the path when the placeholder is omitted, keep {path} out of the Compose interpolation default.

Useful? React with 👍 / 👎.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@compose.yaml`:
- Around line 119-121: The default scanner command interpolation is malformed
because the inline Compose fallback for INTAKE_RESUME_VIRUS_SCAN_COMMAND
includes {path}, which conflicts with Compose’s ${...} parsing. Move the default
ClamAV command out of the compose interpolation or source it from a separate env
value, and keep the command text in a place where {path} is not parsed by
Compose. Update the affected INTAKE_RESUME_VIRUS_SCAN_COMMAND entry so the
default is preserved without breaking either Compose configuration.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 42185892-34d8-4fc8-96c2-24ab273ff26d

📥 Commits

Reviewing files that changed from the base of the PR and between 4e632b1 and 2024957.

📒 Files selected for processing (6)
  • .env.example
  • apps/api/src/five08/backend/api.py
  • apps/worker/src/five08/worker/crm/intake_form_processor.py
  • compose.yaml
  • tests/unit/test_backend_api.py
  • tests/unit/test_intake_form_processor.py
🚧 Files skipped from review as they are similar to previous changes (3)
  • apps/worker/src/five08/worker/crm/intake_form_processor.py
  • tests/unit/test_backend_api.py
  • apps/api/src/five08/backend/api.py

Comment thread compose.yaml Outdated
@michaelmwu michaelmwu added this pull request to the merge queue Jun 29, 2026
Merged via the queue into main with commit 60b08c3 Jun 29, 2026
12 checks passed
@michaelmwu michaelmwu deleted the michaelmwu/no-true-var-default branch June 29, 2026 07:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

docker-smoke Run Docker smoke check on this PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants