Skip to content

feat: establish forward engineering plan authority - #834

Open
seonghobae wants to merge 143 commits into
mainfrom
feat/forward-engineering-workflow
Open

feat: establish forward engineering plan authority#834
seonghobae wants to merge 143 commits into
mainfrom
feat/forward-engineering-workflow

Conversation

@seonghobae

@seonghobae seonghobae commented Aug 9, 2026

Copy link
Copy Markdown
Collaborator

Latest vertical slice

Exact head be1bd78dfa1b4392ef51935d521856e808b8b988 adds an execution-free scheduled dispatch lifecycle:

  • explicit opt-in configuration starts one application-owned relay task only when the Valkey signal backend is configured;
  • each due identifier-only outbox claim owns a fresh metadata transaction and commits only after exact-attempt acknowledgement;
  • UUID-only ready signals now move atomically into an isolated processing set under a bounded exact lease-token; expiry reclaim, stale-ack rejection, acknowledgement, and scheduled retry release remain execution-free;
  • publication/database failures leave the claim pending through transaction rollback, emit only a fixed non-secret log code, and retry after a bounded 0–60 second interval;
  • empty polls wait rather than busy-loop, and startup/shutdown validates, cancels, and awaits every background task;
  • the lifecycle publishes only migration_run_uuid; it does not consume signals, load plans or credentials, create a sandbox, access a target, or execute SQL;
  • canonical Architecture, TRD, ADR-0004, Data Model, threat model, v1 contract, standards, test strategy, runbook, audit, environment example, and CHANGELOG now distinguish the Implemented scheduled publisher from the Planned consumer/worker/failover boundary.

Test-first and local evidence:

  • RED: lifecycle imports/configuration failed before the scheduler and settings existed; documentation contracts failed while canonical docs still classified the scheduler as Planned;
  • GREEN: 41 focused queue/lease/documentation tests passed locally (the real-Valkey case is environment-gated locally and runs in CI);
  • exact app.jobs.migration_dispatch_relay statement/branch coverage: 100%;
  • full backend: 685 passed, 3 environment-gated skips;
  • mypy: success in 78 source files;
  • compileall and git diff --check: passed;
  • the first local full-suite attempt exposed only the automation host's SOCKS proxy without the optional socksio package; removing that host-only proxy variable produced the complete green run. No product code changed for that environment artifact.

Exact-head CI, Security Scan, SAST, real-Valkey acceptance, and CodeRabbit review must be re-established after the signal-lease commit; queued or predecessor evidence is not passing. Qualifying independent non-author approval also remains required. Queue consumption, worker execution, isolated PostgreSQL dry-run, live read-only preflight, apply, recovery, and convergence remain explicitly Planned.

Summary

This PR establishes the Phase 1 forward-engineering control plane:

  • canonical PostgreSQL schema-model validation and deterministic digests
  • immutable schema-model revisions with strong ETag / If-Match concurrency control
  • immutable, bounded migration-plan compilation bound to an exact connection, revision, and succeeded snapshot
  • fail-closed snapshot adaptation against a versioned introspection contract
  • complete plan review inventory via executable statements, review-only proposed statements, and explicit blockers
  • deployer role separation and strong-consistency authorization for persistent SQL apply
  • repeatable-read, read-only catalog introspection
  • ADR, PRD, TRD, architecture, UML, data model/ERD, threat model, runbook, test strategy, standards, v1 contract, and documentation audit

Safety invariants

  • The server, not the browser, is authoritative for SQL planning.
  • Unsupported or lossy snapshot semantics fail closed.
  • A blocked plan has no executable statements; supported deltas remain visible as review-only proposals.
  • Type changes are conservatively classified as destructive/data-loss risk.
  • Object and dependency authority uses structured references, not delimiter-joined display strings.
  • Browser revision writes use a strong revision ETag; stale and weak validators are rejected.
  • Cross-origin clients can send If-Match and read ETag.
  • Snapshot reads are captured in one read-only repeatable-read transaction.
  • Persistent apply authorization reads membership from the primary metadata session.

Current boundary

Implemented in this PR: schema models, revisions, snapshot capability checks, immutable migration plans, durable migration-run/event persistence, atomic identifier-only dry-run dispatch outbox persistence, bounded UUID-only Valkey publication with exact-attempt acknowledgement, public exact-digest/idempotent dry-run intent creation, atomic transition/cancellation CAS writers, editor-authorized cancellation intent, integrity-checked run polling, and safety/authority foundations.

Still planned and explicitly documented as not implemented: queue consumption and migration workers, isolated PostgreSQL dry-run, live preflight/drift checks, public apply creation, structured executor, locks/timeouts, durable recovery and post-apply convergence, the forward-engineering UI, and production E2E/fault-injection coverage.

The documentation audit concludes that the repository is sufficient for Phase 1 review and sequenced implementation, but not sufficient to claim production apply readiness.

Documentation

  • Root architecture: ARCHITECTURE.md
  • Product and technical requirements: docs/PRD.md, docs/TRD.md
  • UML and data model/ERD: docs/UML.md, docs/DATA_MODEL.md
  • Decisions: docs/adr/ADR-0001... through ADR-0005...
  • Normative contract: docs/contracts/forward-engineering-v1.md
  • Audit: docs/DOCUMENTATION_AUDIT.md
  • Threat model and runbook: docs/security/forward-engineering-threat-model.md, docs/runbooks/forward-engineering.md
  • Supporting FigJam: https://www.figma.com/board/MLWimuWoOWhatQ239QihfP

Figma is supporting context only; repository documents remain authoritative.

Verification

  • Backend: 685 passed, 3 skipped
  • Mypy: Success: no issues found in 81 source files
  • Frontend Vitest: 27 files / 197 tests passed
  • Frontend typecheck: passed
  • Frontend production build: passed
  • Focused relay/Valkey/documentation boundary: 32 tests passed
  • app.jobs.migration_dispatch_relay and app.jobs.valkey_queue statement/branch coverage: 100%
  • Review remediation: CPU-heavy compilation/serialization is thread-offloaded; duplicate immutable plan identities are serialized and reused; optional Citus metadata failures are contained; typed OpenAPI plan payloads and ETag/base-snapshot regressions are covered
  • Alembic: one head (0010_migration_run); offline full upgrade and migration downgrade contract passed
  • git diff --check: clean

Local frontend verification ran successfully on Node 24.14; the package declares Node 26, so CI remains authoritative for the supported runtime.

Review / merge gates

Summary by CodeRabbit

  • 새 기능

    • PostgreSQL 스키마 모델 버전 관리와 ETag 기반 동시성 제어를 지원합니다.
    • 검토 가능한 불변 마이그레이션 계획과 위험·권한·사전조건·차단 사유를 제공합니다.
    • 마이그레이션 실행 기록, 상태, 취소 및 무결성 이력을 조회할 수 있습니다.
    • deployer 역할과 멱등성 기반 드라이런 요청을 지원합니다.
    • 실행 신호 전달과 요청 ID 추적을 강화했습니다.
  • 버그 수정

    • 지원하지 않거나 불확실한 변경은 실행 가능한 SQL에서 제외합니다.
    • 작업 실패 시 민감한 오류 정보가 노출되지 않습니다.
  • 문서

    • 지원 범위, 보안 경계, 제한사항 및 향후 계획을 문서화했습니다.

@coderabbitai

coderabbitai Bot commented Aug 9, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Safe Forward Engineering 제어면을 확장했습니다. 서버 권위형 스키마 모델과 불변 리비전을 저장합니다. PostgreSQL 스냅샷에서 구조화된 migration plan을 생성합니다. durable run, 취소 intent, outbox, Valkey relay와 이벤트 무결성 검증을 추가합니다. 실제 SQL 실행과 worker는 아직 계획 상태입니다.

Changes

Safe Forward Engineering

Layer / File(s) Summary
모델·스냅샷·계획 계약
backend/app/forward/*, backend/app/pg_introspect/*, backend/app/schemas.py, backend/app/models.py, backend/alembic/versions/0008_schema_model_revision.py, backend/alembic/versions/0009_migration_plan.py, backend/tests/test_forward_*
정규화된 스키마 모델, snapshot 변환, canonical digest, immutable revision과 migration plan 저장을 추가했습니다. 지원하지 않는 PostgreSQL 의미는 blocker 또는 검증 오류로 처리합니다.
모델·계획 API와 동시성 제어
backend/app/api/schema_models.py, backend/app/api/migration_plans.py, backend/app/main.py, backend/tests/test_api_schema_models.py, backend/tests/test_api_migration_plans.py
모델과 계획 API를 추가했습니다. ETagIf-Match로 stale revision을 거부합니다. 계획 입력 바인딩, digest 무결성, 만료, 크기 제한, IDOR masking과 idempotency를 검증합니다.
내구성 실행과 이벤트 조회
backend/app/forward/migration_run.py, backend/app/api/migration_runs.py, backend/alembic/versions/0010_migration_run.py, backend/tests/test_forward_migration_run.py, backend/tests/test_api_migration_runs.py
durable run과 append-only 이벤트 체인을 추가했습니다. 상태 전이는 CAS로 수행합니다. 멱등성, 취소 intent, 증거 제한과 digest 검증을 적용합니다.
Dispatch relay와 통합 검증
backend/app/jobs/migration_dispatch_relay.py, backend/app/jobs/valkey_queue.py, backend/app/main.py, backend/tests/test_migration_dispatch_*, backend/tests/test_postgres_migration_run_integration.py, backend/tests/test_valkey_queue*, .github/workflows/ci.yml
기한이 된 dispatch를 claim하고 실행 UUID만 Valkey에 발행합니다. 정확한 claim만 published 상태로 변경합니다. PostgreSQL 및 Valkey 통합 테스트와 수명주기 검증을 추가합니다.
권한·introspection·오류 경계
backend/app/permissions.py, backend/app/api/connections.py, backend/app/pg_introspect/*, backend/app/jobs/worker.py, 관련 테스트
deployer 역할과 적용 권한을 추가했습니다. introspection은 read-only repeatable_read 트랜잭션에서 수행합니다. 워커 오류는 고정 코드로 저장합니다. CORS는 If-MatchETag를 지원합니다.
설계·보안·운영 계약
ARCHITECTURE.md, README.md, SECURITY.md, docs/*, CHANGELOG.md, CLAUDE.md, backend/tests/test_documentation_contract.py
현재 구현 범위와 예정된 sandbox, preflight, apply, convergence 및 frontend 흐름을 문서화했습니다. 문서 상태, Mermaid 계약, CI 기준과 보안 경계를 검증합니다.

Estimated code review effort: 5 (Critical) | ~120 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Browser
  participant SchemaModelsAPI
  participant MetadataDB
  participant SnapshotAdapter
  participant MigrationPlansAPI
  participant MigrationPlanCompiler
  participant MigrationRunAPI
  participant DispatchRelay
  participant Valkey

  Browser->>SchemaModelsAPI: Submit canonical schema model
  SchemaModelsAPI->>MetadataDB: Store immutable model revision
  Browser->>MigrationPlansAPI: Request migration plan
  MigrationPlansAPI->>SnapshotAdapter: Convert validated snapshot
  SnapshotAdapter-->>MigrationPlansAPI: Return canonical base model
  MigrationPlansAPI->>MigrationPlanCompiler: Compile model difference
  MigrationPlanCompiler-->>MigrationPlansAPI: Return statements, risks, and blockers
  MigrationPlansAPI->>MetadataDB: Store immutable migration plan
  Browser->>MigrationRunAPI: Create or cancel dry-run
  MigrationRunAPI->>MetadataDB: Store run, event, and identifier-only outbox
  DispatchRelay->>MetadataDB: Claim due dispatch
  DispatchRelay->>Valkey: Publish migration run UUID
  DispatchRelay->>MetadataDB: Mark exact dispatch attempt published
  Browser->>MigrationRunAPI: Retrieve validated run history
  MigrationRunAPI->>MetadataDB: Verify event digest chain
  MigrationRunAPI-->>Browser: Return validated state and events
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 48.20% 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
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.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed 제목은 정규 스키마 모델과 불변 마이그레이션 계획을 서버 권위형으로 수립하는 PR의 핵심 변경을 명확하게 요약합니다.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/forward-engineering-workflow

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 backend/app/forward/snapshot_adapter.py Fixed

Copy link
Copy Markdown
Collaborator Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 9, 2026

Copy link
Copy Markdown

Rate Limit Exceeded

@seonghobae have exceeded the limit for the number of chat messages per hour. Please wait 0 minutes and 39 seconds before sending another message.

@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: 12

🧹 Nitpick comments (14)
backend/app/schemas.py (1)

170-184: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

계획 페이로드에 구조화된 모델을 도입하는 것을 검토하십시오.

statements, proposed_statements, blockers, risk_summary는 형식이 없는 dict입니다. 이 페이로드는 파괴적 변경을 검토하는 주요 산출물입니다. 전용 Pydantic 모델을 정의하면 OpenAPI 문서와 검증이 강화됩니다. 후속 단계에서 처리해도 됩니다.

🤖 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 `@backend/app/schemas.py` around lines 170 - 184, Define dedicated Pydantic
models for the structured payload fields in MigrationPlanOut, then replace the
untyped list[dict] and dict annotations for statements, proposed_statements,
blockers, and risk_summary with those models. Preserve the existing response
shape while ensuring OpenAPI schemas and validation describe each field
explicitly.
backend/app/api/migration_plans.py (1)

114-116: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

plan_json 키 접근 방식을 통일하십시오.

Line 114는 proposed_statements.get(..., [])로 읽습니다. 그러나 Line 116, 130-133, 147-154는 statements, compiler_version, blockers, risk_summary를 직접 인덱싱합니다. compile_migration_plan의 출력 계약이 proposed_statements를 항상 포함한다면 직접 인덱싱하십시오. 포함을 보장하지 않는다면 나머지 키도 방어적으로 읽어야 합니다. 근본 원인은 컴파일러 출력 계약이 명시되지 않은 점입니다. compile_migration_plan에 TypedDict 반환 타입을 도입하면 두 방식의 불일치가 사라집니다.

🤖 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 `@backend/app/api/migration_plans.py` around lines 114 - 116, Unify plan_json
access in compile_migration_plan by defining a TypedDict return contract for the
compiler output, including proposed_statements, statements, compiler_version,
blockers, and risk_summary. Then update the surrounding accesses to consistently
follow that contract, using direct indexing when fields are guaranteed or
defensive defaults when they are optional.
backend/tests/test_pg_introspect_connection.py (1)

28-31: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

type: ignore 대신 반환 타입을 정확히 선언하십시오.

fetchvalSELECT EXISTS 조회에 False를 반환하고 그 밖에는 "16.0"을 반환합니다. 근본 원인은 반환 애노테이션이 str로 좁게 선언된 점입니다. 억제 주석을 추가하는 대신 애노테이션을 넓히십시오.

♻️ 제안 변경
-    async def fetchval(self, *_args: object) -> str:
+    async def fetchval(self, *_args: object) -> str | bool:
         if _args and "SELECT EXISTS" in str(_args[0]):
-            return False  # type: ignore[return-value]
+            return False
         return "16.0"
As per coding guidelines: "Keep backend Python code strictly typed; public definitions require docstrings, and mypy plus interrogate checks must continue to pass."
🤖 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 `@backend/tests/test_pg_introspect_connection.py` around lines 28 - 31, Update
the fetchval method’s return annotation to accurately allow both the boolean
False result for SELECT EXISTS queries and the string version result, then
remove the type: ignore suppression while preserving the existing return
behavior.

Source: Coding guidelines

backend/tests/test_api_schema_models.py (1)

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

_validate_base_snapshot 분기에 대한 커버리지를 추가하십시오.

FakeWriteSessionget을 제공하지 않습니다. 모든 테스트가 base_schema_snapshot_uuid를 생략하므로 _validate_base_snapshot이 즉시 반환하고, session.get은 호출되지 않습니다. 따라서 다음 분기가 검증되지 않습니다.

  • 스냅샷이 존재하지 않는 경우
  • 스냅샷이 다른 프로젝트에 속한 경우
  • 스냅샷 status"succeeded"가 아닌 경우

이 분기는 프로젝트 경계를 강제합니다. 422 응답을 확인하는 테스트를 추가하십시오. 제가 테스트 코드를 작성해 드릴까요?

🤖 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 `@backend/tests/test_api_schema_models.py` around lines 25 - 33,
FakeWriteSession에 비동기 get 모킹을 추가하고, base_schema_snapshot_uuid를 전달해
_validate_base_snapshot 분기를 실행하는 API 테스트를 보강하십시오. 스냅샷이 없거나 다른 프로젝트에 속하거나 status가
"succeeded"가 아닌 각각의 경우에 대해 422 응답을 검증하고, 유효한 프로젝트 스냅샷 경로의 기존 동작은 유지하십시오.
backend/tests/test_forward_snapshot_adapter.py (1)

311-311: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

mutate 매개변수에 타입을 지정하십시오.

이 파일의 다른 테스트는 모두 매개변수와 반환값에 타입을 지정합니다. mutate만 타입이 없습니다. strict mypy 설정에서는 인자 하나가 미주석이면 함수 전체가 untyped로 처리되어 검사가 실패할 수 있습니다.

♻️ 제안 변경
+from collections.abc import Callable
+from typing import Any
...
-def test_snapshot_adapter_fails_closed_for_uncompiled_features(mutate, message: str) -> None:
+def test_snapshot_adapter_fails_closed_for_uncompiled_features(
+    mutate: Callable[[dict[str, Any]], object], message: str
+) -> None:
As per coding guidelines: "Keep backend Python code strictly typed; public definitions require docstrings, and mypy plus interrogate checks must continue to pass."
🤖 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 `@backend/tests/test_forward_snapshot_adapter.py` at line 311,
test_snapshot_adapter_fails_closed_for_uncompiled_features의 mutate 매개변수에 해당
테스트에서 사용하는 변이 함수의 정확한 타입을 지정하고, 기존 message 타입과 반환 타입은 유지하십시오. 인라인 람다나 호출 가능한 객체를
받는다면 저장소의 기존 테스트 타입 별칭을 재사용해 strict mypy 검사를 통과하게 하십시오.

Source: Coding guidelines

backend/app/forward/schema_model.py (1)

257-264: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

선택 필드 처리 규칙을 통일하십시오.

unsupported_features는 Line 238에서 기본값 []을 허용합니다. 그러나 unique_constraints, foreign_keys, indexes는 키가 없으면 _list(None, ...)가 "must be a list" 오류를 발생시킵니다. 결과 canonical JSON은 항상 세 필드를 빈 리스트로 포함하므로, 입력에서도 생략을 허용하면 계약이 일관됩니다.

♻️ 제안 변경
     for field in ("unique_constraints", "foreign_keys", "indexes"):
-        entries = _list(table.get(field), f"{path}.{field}")
+        entries = _list(table.get(field, []), f"{path}.{field}")
         if entries:
🤖 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 `@backend/app/forward/schema_model.py` around lines 257 - 264, Update the
validation loop for unique_constraints, foreign_keys, and indexes to default
missing table fields to empty lists before calling _list, matching the existing
unsupported_features optional-field behavior. Preserve validation of explicitly
provided values and ensure canonical output continues to include all three
fields as empty lists when omitted.
backend/app/forward/snapshot_adapter.py (1)

183-190: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

table_oid 폴백은 키가 없을 때만 동작합니다.

dict.get(key, default)는 키가 없을 때만 기본값을 반환합니다. 스냅샷 행이 relation_oid: None을 포함하면 table_oid 폴백이 적용되지 않습니다. 현재는 뒤이어 예외가 발생하므로 fail-closed입니다. 의도를 명확히 하려면 명시적으로 처리하십시오.

♻️ 제안 변경
-        relation_oid = index_row.get("relation_oid", index_row.get("table_oid"))
+        relation_oid = index_row.get("relation_oid")
+        if relation_oid is None:
+            relation_oid = index_row.get("table_oid")
🤖 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 `@backend/app/forward/snapshot_adapter.py` around lines 183 - 190, Update the
relation_oid resolution in the index loop so table_oid is used when relation_oid
is absent or explicitly None, while preserving a valid relation_oid when
present. Keep the existing primary-key backing-index validation in place.
backend/alembic/versions/0009_migration_plan.py (1)

62-74: 🧹 Nitpick | 🔵 Trivial

만료 계획 조회용 인덱스를 고려하십시오.

expires_at은 만료 검사와 정리 작업의 조건 컬럼이 됩니다. 현재 인덱스는 project_space_uuidschema_model_revision_uuid뿐입니다. 계획 수가 늘어나면 만료 정리 쿼리가 전체 테이블 스캔을 수행합니다.

🤖 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 `@backend/alembic/versions/0009_migration_plan.py` around lines 62 - 74, Add an
index on the expires_at column in the migration_plan table alongside the
existing indexes, so expiration checks and cleanup queries can efficiently
filter plans by expiry time.
backend/app/pg_introspect/introspect.py (1)

164-164: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

citus_distributed_tables에 명시적 타입 주석을 추가하십시오.

빈 리스트 리터럴은 mypy strict 모드에서 var-annotated 오류를 유발할 수 있습니다. 백엔드 Python 코드는 mypy 검사를 통과해야 합니다.

♻️ 제안 수정
-            citus_distributed_tables = []
+            citus_distributed_tables: list[asyncpg.Record] = []

As per coding guidelines: "Keep backend Python code strictly typed; public definitions require docstrings, and mypy plus interrogate checks must continue to pass."

🤖 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 `@backend/app/pg_introspect/introspect.py` at line 164, 변수
citus_distributed_tables에 명시적 타입 주석을 추가하여 빈 리스트의 요소 타입을 선언하고 mypy strict 검사를
통과하도록 수정하십시오.

Source: Coding guidelines

backend/app/forward/migration_plan.py (1)

428-432: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

계획 정체성에 스냅샷 계약 버전을 포함하는 방안을 고려하십시오.

계획 digest는 compiler_version, 모델 digest, 문장 목록으로 계산됩니다. 기반 스냅샷을 모델로 변환하는 계약(CURRENT_POSTGRES_SNAPSHOT_CONTRACT_VERSION)은 포함되지 않습니다. 어댑터 의미가 바뀌면 동일한 digest가 서로 다른 의미의 계획을 가리킬 수 있습니다.

snapshot_contract_version을 계획 본문에 추가하면 정체성이 명확해집니다.

Also applies to: 563-576

🤖 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 `@backend/app/forward/migration_plan.py` around lines 428 - 432, Update the
plan construction flow so each plan includes snapshot_contract_version set from
CURRENT_POSTGRES_SNAPSHOT_CONTRACT_VERSION before _digest_plan computes its
digest. Ensure the field is part of the serialized plan body, so changes to the
snapshot adapter contract produce a distinct plan identity while preserving the
existing digest inputs.
backend/tests/test_forward_schema_model.py (2)

139-139: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

정규식 패턴에 raw string을 사용하세요.

match=에 전달된 패턴은 정규식으로 처리됩니다. primary_key.*not nullable에는 메타문자 .*가 있습니다. 의도가 정규식이면 raw string으로 표시하고, 리터럴 매칭이면 re.escape()를 사용하세요. Ruff RUF043 경고와 일치합니다.

♻️ 제안 수정
-    with pytest.raises(SchemaModelValidationError, match="primary_key.*not nullable"):
+    with pytest.raises(SchemaModelValidationError, match=r"primary_key.*not nullable"):
🤖 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 `@backend/tests/test_forward_schema_model.py` at line 139, Update the
pytest.raises call around the primary_key validation assertion to express its
regex pattern as a raw string, preserving the existing matching behavior and
resolving Ruff RUF043.

Source: Linters/SAST tools


199-204: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

mypy 설정에서 backend/tests만 제외하지 않았습니다. 테스트 함수의 변수 인자에 mutate: Callable[[dict[str, Any]], object]value: object 주석을 추가하세요. 또한 setup.cfg의 mypy 설정도 함께 확인해 적용 범위를 최종 확실히 하세요.

🤖 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 `@backend/tests/test_forward_schema_model.py` around lines 199 - 204, Update
test_model_validation_fails_closed to annotate mutate as Callable[[dict[str,
Any]], object] and value as object wherever the test’s variable arguments are
declared. Also inspect setup.cfg’s mypy configuration and ensure the intended
backend/tests exclusion or coverage is correctly applied.

Source: Coding guidelines

backend/tests/test_api_migration_plans.py (1)

122-130: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

유니크 인덱스 경로도 함께 검사하세요.

이 테스트는 __table__.constraintsUniqueConstraint만 확인합니다. SQLAlchemy에서 Index(..., unique=True)로 선언한 유니크 제약은 __table__.indexes에 들어가며 constraints에는 나타나지 않습니다. 현재 형태로는 유니크 인덱스로 추가된 idempotency key를 감지하지 못합니다.

💚 제안 수정
     unique_column_sets = {
         tuple(column.name for column in constraint.columns)
         for constraint in MigrationPlan.__table__.constraints
         if isinstance(constraint, UniqueConstraint)
     }
+    unique_column_sets |= {
+        tuple(column.name for column in index.columns)
+        for index in MigrationPlan.__table__.indexes
+        if index.unique
+    }
 
     assert ("project_space_uuid", "statement_digest") not in unique_column_sets
🤖 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 `@backend/tests/test_api_migration_plans.py` around lines 122 - 130, Extend
test_migration_plans_do_not_use_plan_digest_as_database_idempotency_key to also
inspect MigrationPlan.__table__.indexes for unique indexes, and assert that no
unique index covers (“project_space_uuid”, “statement_digest”). Keep the
existing UniqueConstraint check intact.
backend/tests/test_forward_migration_plan.py (1)

71-89: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

변수 이름이 인자 위치와 반대입니다.

target이라는 변수가 compile_migration_plan의 첫 번째 인자, 즉 base 모델로 전달됩니다. 동작은 맞습니다. 이름만 혼동을 유발합니다. base로 바꾸면 drop 방향이 명확해집니다.

♻️ 제안 수정
-    target = _table_model()
-    target["schemas"][0]["tables"][0]["columns"].append(
+    base = _table_model()
+    base["schemas"][0]["tables"][0]["columns"].append(
         {
             "column_name": "Legacy Value",
             "data_type": "text",
             "nullable": True,
             "ordinal_position": 2,
         }
     )
-    plan = compile_migration_plan(target, _table_model())
+    plan = compile_migration_plan(base, _table_model())
🤖 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 `@backend/tests/test_forward_migration_plan.py` around lines 71 - 89, Rename
the local variable target to base in
test_destructive_drop_has_explicit_risk_and_recovery_boundary, and pass base as
the first argument to compile_migration_plan while preserving the existing drop
assertions and behavior.
🤖 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 `@backend/app/api/migration_plans.py`:
- Around line 106-120: In the async handler around snapshot_to_schema_model and
compile_migration_plan, offload the CPU-intensive compilation and json.dumps
work with anyio.to_thread.run_sync so the event loop remains responsive. Keep
the existing SchemaModelValidationError-to-422 behavior and perform the
MAX_PLAN_STATEMENTS/MAX_PLAN_BYTES validation on the resulting plan and
serialized payload.

In `@backend/app/api/schema_models.py`:
- Around line 90-93: Align the ETag documentation and concurrency tests with
_revision_etag using the revision UUID. In backend/app/api/schema_models.py
lines 90-93, update revise_schema_model’s docstring and _revision_etag
documentation to describe the UUID-based ETag. In
backend/tests/test_api_schema_models.py lines 132-170, use the quoted current
revision UUID for if_match and assert that changing only the base snapshot
creates a new revision; in lines 174-201, use the weak UUID ETag and assert its
rejection.
- Around line 90-93: Update the docstrings for _revision_etag and
revise_schema_model to state that the strong ETag and If-Match value identify
the current revision via schema_model_revision_uuid, not revision_digest or a
digest. Ensure all related documentation, including the additionally referenced
text, consistently describes the UUID-based ETag contract.

In `@backend/app/forward/migration_plan.py`:
- Around line 87-91: _column_sql에서 모델의 column["default"]를 누락하지 않도록 DEFAULT 절을 생성
SQL에 반영하고, CREATE TABLE 및 ADD COLUMN 경로에서 동일한 의미가 유지되게 하세요. 기본값 표현을 안전하게 SQL로
변환하는 기존 유틸리티가 있으면 재사용하고, 지원할 수 없는 default 형식은 계획을 safe로 표시하지 말고 blocker로 처리하여
fail-closed 동작을 유지하세요.
- Around line 197-238: Update the ordinal baseline used by the added-column
validation in the migration-plan logic so deleted-column gaps are not treated as
required positions. Derive the expected ordinals from the current existing
columns’ ranks, then validate each sorted added column as contiguous after that
current sequence while preserving the existing blocker structure.

In `@backend/app/models.py`:
- Around line 236-275: Enforce uniqueness for the immutable plan identity
`(schema_model_revision_uuid, db_connection_uuid, base_schema_snapshot_uuid,
statement_digest)` on `MigrationPlan`, adding an `expires_at` index if expiry
cleanup is planned, and create the required database migration. Update
`create_migration_plan` to look up and reuse an existing valid plan for the same
identity instead of inserting duplicates, while preserving server-authoritative
deterministic behavior.

In `@backend/app/pg_introspect/introspect.py`:
- Around line 169-182: Update the Citus metadata query handling around
CITUS_DISTRIBUTED_TABLES_SQL to catch InsufficientPrivilegeError,
UndefinedColumnError, and UndefinedFunctionError alongside UndefinedTableError;
roll back the savepoint and set citus_distributed_tables to an empty list for
all of these optional Citus failures.

In `@backend/tests/test_api_apply_sql.py`:
- Around line 96-116: Update
test_live_apply_requires_deployer_role_while_dry_run_requires_editor to also
invoke apply_sql with dry_run=True and assert that require_project_member is
called with minimum_role="editor"; retain the existing dry_run=False assertion
for "deployer" so both authorization paths are covered.

In `@backend/tests/test_documentation_contract.py`:
- Around line 68-81: Add concise docstrings to every public test function in
backend/tests/test_documentation_contract.py, including
test_canonical_forward_engineering_documents_exist_and_are_nonempty and the
additional public tests referenced by the comment. Each docstring should briefly
state the test’s contract while preserving the existing test logic.

In `@backend/tests/test_forward_snapshot_adapter.py`:
- Line 60: Update the pytest.raises match patterns at the shown locations to use
raw string literals, preserving the existing “recapture|required” alternation
and resolving Ruff RUF043.

In `@docs/superpowers/specs/2026-08-09-forward-engineering-design.md`:
- Line 8: Adjust the “Implementation snapshot” heading hierarchy so it follows
the preceding top-level heading: change `### Implementation snapshot` to `##
Implementation snapshot`, unless an appropriate intermediate `##` section is
intentionally added.

In `@docs/TEST_STRATEGY.md`:
- Around line 197-215: Add PR workflow security gates for osv-scan,
dependency-review, and trivy-fs under .github/workflows, including database
refresh before trivy-fs and scanning the merge ref rather than the PR head.
Update docs/TEST_STRATEGY.md to document these checks as active PR requirements
instead of deferring them to the release workflow.

---

Nitpick comments:
In `@backend/alembic/versions/0009_migration_plan.py`:
- Around line 62-74: Add an index on the expires_at column in the migration_plan
table alongside the existing indexes, so expiration checks and cleanup queries
can efficiently filter plans by expiry time.

In `@backend/app/api/migration_plans.py`:
- Around line 114-116: Unify plan_json access in compile_migration_plan by
defining a TypedDict return contract for the compiler output, including
proposed_statements, statements, compiler_version, blockers, and risk_summary.
Then update the surrounding accesses to consistently follow that contract, using
direct indexing when fields are guaranteed or defensive defaults when they are
optional.

In `@backend/app/forward/migration_plan.py`:
- Around line 428-432: Update the plan construction flow so each plan includes
snapshot_contract_version set from CURRENT_POSTGRES_SNAPSHOT_CONTRACT_VERSION
before _digest_plan computes its digest. Ensure the field is part of the
serialized plan body, so changes to the snapshot adapter contract produce a
distinct plan identity while preserving the existing digest inputs.

In `@backend/app/forward/schema_model.py`:
- Around line 257-264: Update the validation loop for unique_constraints,
foreign_keys, and indexes to default missing table fields to empty lists before
calling _list, matching the existing unsupported_features optional-field
behavior. Preserve validation of explicitly provided values and ensure canonical
output continues to include all three fields as empty lists when omitted.

In `@backend/app/forward/snapshot_adapter.py`:
- Around line 183-190: Update the relation_oid resolution in the index loop so
table_oid is used when relation_oid is absent or explicitly None, while
preserving a valid relation_oid when present. Keep the existing primary-key
backing-index validation in place.

In `@backend/app/pg_introspect/introspect.py`:
- Line 164: 변수 citus_distributed_tables에 명시적 타입 주석을 추가하여 빈 리스트의 요소 타입을 선언하고 mypy
strict 검사를 통과하도록 수정하십시오.

In `@backend/app/schemas.py`:
- Around line 170-184: Define dedicated Pydantic models for the structured
payload fields in MigrationPlanOut, then replace the untyped list[dict] and dict
annotations for statements, proposed_statements, blockers, and risk_summary with
those models. Preserve the existing response shape while ensuring OpenAPI
schemas and validation describe each field explicitly.

In `@backend/tests/test_api_migration_plans.py`:
- Around line 122-130: Extend
test_migration_plans_do_not_use_plan_digest_as_database_idempotency_key to also
inspect MigrationPlan.__table__.indexes for unique indexes, and assert that no
unique index covers (“project_space_uuid”, “statement_digest”). Keep the
existing UniqueConstraint check intact.

In `@backend/tests/test_api_schema_models.py`:
- Around line 25-33: FakeWriteSession에 비동기 get 모킹을 추가하고,
base_schema_snapshot_uuid를 전달해 _validate_base_snapshot 분기를 실행하는 API 테스트를 보강하십시오.
스냅샷이 없거나 다른 프로젝트에 속하거나 status가 "succeeded"가 아닌 각각의 경우에 대해 422 응답을 검증하고, 유효한 프로젝트
스냅샷 경로의 기존 동작은 유지하십시오.

In `@backend/tests/test_forward_migration_plan.py`:
- Around line 71-89: Rename the local variable target to base in
test_destructive_drop_has_explicit_risk_and_recovery_boundary, and pass base as
the first argument to compile_migration_plan while preserving the existing drop
assertions and behavior.

In `@backend/tests/test_forward_schema_model.py`:
- Line 139: Update the pytest.raises call around the primary_key validation
assertion to express its regex pattern as a raw string, preserving the existing
matching behavior and resolving Ruff RUF043.
- Around line 199-204: Update test_model_validation_fails_closed to annotate
mutate as Callable[[dict[str, Any]], object] and value as object wherever the
test’s variable arguments are declared. Also inspect setup.cfg’s mypy
configuration and ensure the intended backend/tests exclusion or coverage is
correctly applied.

In `@backend/tests/test_forward_snapshot_adapter.py`:
- Line 311: test_snapshot_adapter_fails_closed_for_uncompiled_features의 mutate
매개변수에 해당 테스트에서 사용하는 변이 함수의 정확한 타입을 지정하고, 기존 message 타입과 반환 타입은 유지하십시오. 인라인 람다나
호출 가능한 객체를 받는다면 저장소의 기존 테스트 타입 별칭을 재사용해 strict mypy 검사를 통과하게 하십시오.

In `@backend/tests/test_pg_introspect_connection.py`:
- Around line 28-31: Update the fetchval method’s return annotation to
accurately allow both the boolean False result for SELECT EXISTS queries and the
string version result, then remove the type: ignore suppression while preserving
the existing return behavior.
🪄 Autofix

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: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 4a9abd63-20cb-44a7-ab15-e459756ada5d

📥 Commits

Reviewing files that changed from the base of the PR and between 72afe6d and 68df39b.

📒 Files selected for processing (50)
  • ARCHITECTURE.md
  • CHANGELOG.md
  • CLAUDE.md
  • README.md
  • SECURITY.md
  • backend/alembic/versions/0008_schema_model_revision.py
  • backend/alembic/versions/0009_migration_plan.py
  • backend/app/api/connections.py
  • backend/app/api/migration_plans.py
  • backend/app/api/schema_models.py
  • backend/app/forward/__init__.py
  • backend/app/forward/migration_plan.py
  • backend/app/forward/schema_model.py
  • backend/app/forward/snapshot_adapter.py
  • backend/app/main.py
  • backend/app/models.py
  • backend/app/permissions.py
  • backend/app/pg_introspect/introspect.py
  • backend/app/pg_introspect/queries.py
  • backend/app/pg_introspect/snapshot_contract.py
  • backend/app/schemas.py
  • backend/tests/test_api_apply_sql.py
  • backend/tests/test_api_migration_plans.py
  • backend/tests/test_api_schema_models.py
  • backend/tests/test_documentation_contract.py
  • backend/tests/test_forward_migration_plan.py
  • backend/tests/test_forward_schema_model.py
  • backend/tests/test_forward_snapshot_adapter.py
  • backend/tests/test_permissions.py
  • backend/tests/test_pg_introspect_connection.py
  • backend/tests/test_pg_introspect_queries.py
  • backend/tests/test_schema_validation.py
  • backend/tests/test_security_headers.py
  • docs/DATA_MODEL.md
  • docs/DOCUMENTATION_AUDIT.md
  • docs/PRD.md
  • docs/STANDARDS.md
  • docs/TEST_STRATEGY.md
  • docs/TRD.md
  • docs/UML.md
  • docs/adr/ADR-0001-server-authoritative-planning.md
  • docs/adr/ADR-0002-isolated-dry-run-and-preflight.md
  • docs/adr/ADR-0003-plan-execution-segmentation.md
  • docs/adr/ADR-0004-durable-runs-and-recovery.md
  • docs/adr/ADR-0005-authority-approvals-and-convergence.md
  • docs/adr/README.md
  • docs/contracts/forward-engineering-v1.md
  • docs/runbooks/forward-engineering.md
  • docs/security/forward-engineering-threat-model.md
  • docs/superpowers/specs/2026-08-09-forward-engineering-design.md

Comment thread backend/app/api/migration_plans.py
Comment thread backend/app/api/schema_models.py
Comment thread backend/app/forward/migration_plan.py
Comment thread backend/app/forward/migration_plan.py
Comment thread backend/app/models.py
Comment thread backend/tests/test_api_apply_sql.py Outdated
Comment thread backend/tests/test_documentation_contract.py
Comment thread backend/tests/test_forward_snapshot_adapter.py Outdated
Comment thread docs/superpowers/specs/2026-08-09-forward-engineering-design.md Outdated
Comment thread docs/TEST_STRATEGY.md Outdated

Copy link
Copy Markdown
Collaborator Author

@coderabbitai review

Please review exact head 7bb7cb28d1ec8262ab18584bf54104bdc6a0d695. The previous PostgreSQL matrix failure was an integration-fixture FK ordering defect; this head explicitly flushes prerequisite rows in dependency order.

@coderabbitai

coderabbitai Bot commented Aug 11, 2026

Copy link
Copy Markdown

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

head="7bb7cb28d1ec8262ab18584bf54104bdc6a0d695"
prior="90e35045c1a6dd7ad19531bc3bee25bc178d5228"

echo '== Exact head and PR identity =='
git cat-file -e "${head}^{commit}"
git show -s --format='commit=%H%nparents=%P%nsubject=%s' "$head"
git rev-parse HEAD
gh pr view 834 --json headRefOid,url --jq '{headRefOid,url}'
git merge-base --is-ancestor "$prior" "$head"
echo "prior_head_is_ancestor=$?"

echo '== Delta from prior reviewed head =='
git log --oneline "${prior}..${head}"
git diff --check "${prior}..${head}"
git diff --name-status "${prior}..${head}"
git diff --find-renames --find-copies "${prior}..${head}" -- \
  backend/tests/test_postgres_migration_run_integration.py \
  .github/workflows/ci.yml \
  backend/alembic/versions/0010_migration_run.py \
  backend/app/forward/migration_run.py

echo '== Integration test structure =='
ast-grep outline backend/tests/test_postgres_migration_run_integration.py --items all
nl -ba backend/tests/test_postgres_migration_run_integration.py | sed -n '1,260p'

echo '== ORM foreign-key definitions for fixture entities =='
rg -n -C 4 --glob '*.py' \
  'class (UserAccount|ProjectSpace|DbConnection|SchemaSnapshot|SchemaModel|SchemaModelRevision|MigrationPlan|MigrationRun|MigrationRunDispatch)\b|ForeignKey\(' \
  backend/app/models.py

echo '== Integration workflow PostgreSQL jobs =='
nl -ba .github/workflows/ci.yml | sed -n '1,180p'

echo '== Exact-head checks and PostgreSQL matrix results =='
git diff --check "${head}^" "$head"
gh pr checks 834 --json name,state,workflow,link \
  --jq '.[] | select((.name | test("PostgreSQL|ci|Security Scan|SAST Semgrep"; "i")) or (.workflow | test("ci|Security Scan|SAST Semgrep"; "i"))) | {name,state,workflow,link}'

Length of output: 7826


🏁 Script executed:

#!/bin/bash
set -euo pipefail

head="7bb7cb28d1ec8262ab18584bf54104bdc6a0d695"

echo '== Complete integration fixture at exact head =='
git show "${head}:backend/tests/test_postgres_migration_run_integration.py" |
  awk '{printf "%6d  %s\n", NR, $0}' |
  sed -n '1,280p'

echo '== Relevant foreign-key declarations =='
sed -n '/class UserAccount\b/,/class MigrationRunDispatch\b/p' backend/app/models.py |
  rg -n -C 3 'class (UserAccount|ProjectSpace|DbConnection|SchemaSnapshot|SchemaModel|SchemaModelRevision|MigrationPlan|MigrationRun|MigrationRunDispatch)\b|ForeignKey\('

echo '== Exact-head whitespace check =='
if git diff --check "${head}^" "${head}"; then
  echo 'exact_head_diff_check=passed'
else
  echo 'exact_head_diff_check=failed'
fi

echo '== PostgreSQL matrix and required workflow status =='
gh pr checks 834 --json name,state,workflow,link \
  --jq '.[] |
    select(
      (.name | test("PostgreSQL|ci|Security Scan|SAST Semgrep"; "i")) or
      (.workflow | test("ci|Security Scan|SAST Semgrep"; "i"))
    ) |
    {name,state,workflow,link}'

Length of output: 21217


@seonghobae I reviewed exact head 7bb7cb28d1ec8262ab18584bf54104bdc6a0d695.

The commit changes only backend/tests/test_postgres_migration_run_integration.py.

The fixture now flushes rows in a valid dependency order:

UserAccountProjectSpaceDbConnectionSchemaSnapshot and SchemaModelSchemaModelRevisionMigrationPlan.

This corrects the prior fk_db_connection__project_space failure. SchemaModelRevision is also flushed only after both referenced rows exist.

git diff --check passes. I found no defect in this exact-head change.

The PostgreSQL 14–18 matrix jobs were still IN_PROGRESS. I cannot yet verify real PostgreSQL execution, idempotency, dispatch creation, and rollback from completed CI evidence.

You are interacting with an AI system.

Copy link
Copy Markdown
Collaborator Author

@coderabbitai full review

Please review exact head 7c70695cfb6aaa8ef8df653ef5e3b2e3381fff43, focusing on the lock-scoped FOR UPDATE SKIP LOCKED dispatch claim, attempt-bound publish-state CAS, transaction ownership/rollback semantics, identifier-only authority, and PostgreSQL 14–18 integration acceptance.

@coderabbitai

coderabbitai Bot commented Aug 11, 2026

Copy link
Copy Markdown

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

head="7c70695cfb6aaa8ef8df653ef5e3b2e3381fff43"
prior="7bb7cb28d1ec8262ab18584bf54104bdc6a0d695"

echo '== Exact head, ancestry, and PR =='
git cat-file -e "${head}^{commit}"
git show -s --format='commit=%H%nparents=%P%nsubject=%s' "$head"
git rev-parse HEAD
git merge-base --is-ancestor "$prior" "$head"
echo "prior_is_ancestor=$?"
gh pr view 834 --json headRefOid,baseRefOid,url --jq '{headRefOid,baseRefOid,url}'

echo '== Delta since prior reviewed head =='
git log --oneline "${prior}..${head}"
git diff --check "${prior}..${head}"
git diff --name-status "${prior}..${head}"
git diff --find-renames --find-copies "${prior}..${head}"

echo '== Dispatch-related implementation map =='
rg -n -C 4 --glob '*.py' \
  '\b(MigrationRunDispatch|claim.*dispatch|publish.*dispatch|SKIP LOCKED|with_for_update|attempt_count|attempt|published_at|dispatch)\b' \
  backend/app backend/tests

echo '== Changed Python structure =='
git diff --name-only "${prior}..${head}" -- '*.py' |
while IFS= read -r file; do
  [ -n "$file" ] || continue
  echo "-- $file"
  ast-grep outline "$file" --items all || true
done

echo '== Transaction/session ownership =='
fd -a '^db\.py$' backend/app -x sh -c 'echo "-- $1"; sed -n "1,240p" "$1"' sh {}
rg -n -C 5 --glob '*.py' \
  '\b(begin|begin_nested|commit|rollback|flush|create_migration_run|claim.*dispatch|publish.*dispatch)\s*\(' \
  backend/app backend/tests

echo '== ORM and Alembic dispatch contracts =='
rg -n -C 6 --glob '*.py' \
  '^class MigrationRunDispatch\b|migration_run_dispatch|CheckConstraint|UniqueConstraint|ForeignKey\(' \
  backend/app/models.py backend/alembic/versions

echo '== Focused integration tests and workflow =='
fd -a '^test_postgres_migration_run_integration\.py$' backend/tests -x sh -c 'echo "-- $1"; nl -ba "$1" | sed -n "1,360p"' sh {}
fd -a '^ci\.yml$|^ci\.yaml$' .github -x sh -c 'echo "-- $1"; nl -ba "$1" | sed -n "1,240p"' sh {}

echo '== Exact-head check results =='
gh pr checks 834 --json name,state,workflow,link \
  --jq '.[] | select((.name | test("PostgreSQL|ci|Security Scan|SAST Semgrep"; "i")) or (.workflow | test("ci|Security Scan|SAST Semgrep"; "i"))) | {name,state,workflow,link}'

Length of output: 215640


🏁 Script executed:

#!/bin/bash
set -euo pipefail

head="7c70695cfb6aaa8ef8df653ef5e3b2e3381fff43"

show_file() {
  local file="$1" start="$2" end="$3"
  echo "-- ${file}:${start}-${end}"
  awk -v start="$start" -v end="$end" \
    'NR >= start && NR <= end { printf "%6d  %s\n", NR, $0 }' "$file"
}

echo '== Complete exact integration acceptance test =='
show_file backend/tests/test_postgres_migration_run_integration.py 1 280

echo '== Unit tests for claim, CAS, and transaction behavior =='
show_file backend/tests/test_forward_migration_run.py 340 490
show_file backend/tests/test_forward_migration_run.py 1140 1190

echo '== Complete dispatch functions =='
show_file backend/app/forward/migration_run.py 110 225

echo '== Existing patterns for lock ownership and external publish =='
show_file backend/app/jobs/worker.py 55 190
rg -n -C 5 --glob '*.py' \
  '\b(enqueue_job_signal|publish.*signal|pop_due_job_signal|valkey_queue_enabled)\s*\(' \
  backend/app

echo '== All dispatch usage sites and persisted dispatch fields =='
rg -n -C 4 --glob '*.py' \
  '\b(claim_one_migration_dispatch|mark_migration_dispatch_published|MigrationDispatchClaim)\s*\(' \
  backend
show_file backend/app/models.py 391 442

echo '== CI workflow PostgreSQL acceptance definition =='
fd -a '^ci\.yml$|^ci\.yaml$' .github -x sh -c \
  'echo "-- $1"; awk "NR >= 1 && NR <= 220 { printf \"%6d  %s\\n\", NR, \$0 }" "$1"' sh {}

echo '== Current required GitHub Actions state =='
gh pr checks 834 --json name,state,workflow,link \
  --jq '.[] | select((.name | test("PostgreSQL (14|15|16|17|18) migration-run/outbox|backend|frontend"; "i")) or (.workflow | test("Security Scan|SAST Semgrep"; "i"))) | {name,state,workflow,link}'

echo '== Exact-head full-PR whitespace check =='
git diff --check "72afe6db712b145baaba084f64a1ff4fb36d9fd0..${head}"

Length of output: 59055


@seonghobae I reviewed exact head 7c70695cfb6aaa8ef8df653ef5e3b2e3381fff43.

The dispatch claim uses due-order selection with FOR UPDATE SKIP LOCKED. The claim remains in the caller-owned transaction. The publish update binds the dispatch UUID, run UUID, dispatch kind, pending state, exact attempt count, and unset published_at. It does not commit.

The dispatch schema contains identifiers, fixed state, attempt metadata, and timestamps only. It contains no DSNs, SQL, plan payload, or queue payload. The real PostgreSQL test covers migration application, idempotent run reuse, one dispatch row, claim/publish state, and rollback.

I found one issue:

  1. P2 — Remove trailing whitespace that fails the full PR diff check.
    git diff --check 72afe6db712b145baaba084f64a1ff4fb36d9fd0..7c70695cfb6aaa8ef8df653ef5e3b2e3381fff43 fails in docs/superpowers/specs/2026-08-09-forward-engineering-design.md, Lines 3–4. Remove the trailing spaces so the full PR passes git diff --check.

The PostgreSQL 14–18 acceptance jobs were still IN_PROGRESS. The backend, frontend, Semgrep, and Strix jobs were also still in progress. I could not verify final CI acceptance for this head.

You are interacting with an AI system.

Comment thread backend/tests/test_forward_migration_run.py Fixed
Comment thread backend/tests/test_forward_migration_run.py Fixed

Copy link
Copy Markdown
Collaborator Author

@coderabbitai review

Exact head de5554d5dcd1fec6bbb46ebb1fe5e512df65a86f removes the two trailing-space defects reported against the full PR range. git diff --check 72afe6db712b145baaba084f64a1ff4fb36d9fd0..de5554d5dcd1fec6bbb46ebb1fe5e512df65a86f is clean; documentation contract remains 10 passed.

@coderabbitai

coderabbitai Bot commented Aug 11, 2026

Copy link
Copy Markdown

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

head="de5554d5dcd1fec6bbb46ebb1fe5e512df65a86f"
base="72afe6db712b145baaba084f64a1ff4fb36d9fd0"
prior="7c70695cfb6aaa8ef8df653ef5e3b2e3381fff43"

echo '== Exact head and PR identity =='
git cat-file -e "${head}^{commit}"
git show -s --format='commit=%H%nparents=%P%nsubject=%s' "$head"
git rev-parse HEAD
git status --short
gh pr view 834 --json headRefOid,baseRefOid,url --jq '{headRefOid,baseRefOid,url}'

echo '== Ancestry and exact-head delta =='
git merge-base --is-ancestor "$prior" "$head"
echo "prior_is_ancestor=$?"
git diff --name-status "${prior}..${head}"
git diff --check "${head}^" "$head"
git diff --find-renames --find-copies "${head}^" "$head"

echo '== Full PR whitespace check =='
git diff --check "${base}..${head}"

echo '== Affected documentation lines at the exact head =='
git show "${head}:docs/superpowers/specs/2026-08-09-forward-engineering-design.md" |
  awk 'NR >= 1 && NR <= 8 { printf "%6d  %s\n", NR, $0 }'

echo '== Documentation contract test map =='
ast-grep outline backend/tests/test_documentation_contract.py --items all
rg -n -C 3 --glob '*.py' \
  'test_.*documentation|CANONICAL_DOCUMENTS|DOCUMENTATION_AUDIT|forward-engineering-design' \
  backend/tests/test_documentation_contract.py

echo '== Full PR remaining whitespace errors, if any =='
git diff --check "${base}..${head}" 2>&1 || true

Length of output: 5342


@seonghobae I verified exact head de5554d5dcd1fec6bbb46ebb1fe5e512df65a86f.

The commit removes the trailing whitespace from docs/superpowers/specs/2026-08-09-forward-engineering-design.md, Lines 3-4.

git diff --check passes for:

  • The exact-head change.
  • The full PR range: 72afe6db712b145baaba084f64a1ff4fb36d9fd0..de5554d5dcd1fec6bbb46ebb1fe5e512df65a86f.

I found no defect in this change. The documentation-contract suite contains ten tests. I did not execute the suite in this sandbox.

You are interacting with an AI system.

Copy link
Copy Markdown
Collaborator Author

@coderabbitai full review

Please review exact head 519c2b339f1f693d6121e3d845aacbd2cab7673d, including the identifier-only migration signal boundary and the current-head CodeQL remediation. Do not treat earlier-head findings or status-only checks as exact-head evidence.

Comment thread backend/tests/test_valkey_queue.py Fixed
@coderabbitai

coderabbitai Bot commented Aug 11, 2026

Copy link
Copy Markdown
🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

head="519c2b339f1f693d6121e3d845aacbd2cab7673d"
prior="de5554d5dcd1fec6bbb46ebb1fe5e512df65a86f"

echo '== Exact head and PR identity =='
git cat-file -e "${head}^{commit}"
git show -s --format='commit=%H%nparents=%P%nsubject=%s' "$head"
printf 'checked_out='; git rev-parse HEAD
git status --short
gh pr view 834 --json headRefOid,baseRefOid,url --jq '{headRefOid,baseRefOid,url}'
git merge-base --is-ancestor "$prior" "$head"
echo "prior_is_ancestor=$?"

echo '== Exact-head delta and whitespace =='
git log --oneline "${prior}..${head}"
git diff --name-status "${prior}..${head}"
git diff --check "${prior}..${head}"
git diff --find-renames --find-copies "${prior}..${head}"

echo '== Locate signal and CodeQL remediation paths =='
git diff --name-only "${prior}..${head}"
rg -n -i -C 4 --glob '*.py' --glob '*.yml' --glob '*.yaml' --glob '*.md' \
  'migration.{0,20}signal|signal.{0,20}migration|enqueue.*migration|publish.*migration|dispatch|CodeQL|codeql|cleartext|secret|credential|dsn|connection string' \
  backend .github docs || true

echo '== Structure of changed Python files =='
git diff --name-only "${prior}..${head}" -- '*.py' |
while IFS= read -r file; do
  [ -n "$file" ] || continue
  echo "-- $file"
  ast-grep outline "$file" --items all || true
done

echo '== Current signal boundary call sites and models =='
rg -n -C 5 --glob '*.py' \
  '\b(MigrationRunDispatch|Migration.*Signal|claim_one_migration_dispatch|mark_migration_dispatch_published|enqueue.*migration|publish.*migration|migration_run_uuid|dispatch_uuid)\b' \
  backend/app backend/tests || true

echo '== Exact-head GitHub checks and workflows =='
gh pr checks 834 --json name,state,workflow,link,startedAt,completedAt \
  --jq '.[] | {name,state,workflow,startedAt,completedAt,link}' || true

echo '== Code-scanning alerts associated with this PR or repository =='
gh api "repos/ContextualWisdomLab/pg-erd-cloud/code-scanning/alerts?state=open&pr=834&per_page=100" \
  --jq '.[] | {number,state,rule:.rule.id,most_recent_instance:.most_recent_instance.location.path,ref:.most_recent_instance.ref}' || true

Length of output: 195370


🏁 Script executed:

#!/bin/bash
set -euo pipefail

head="519c2b339f1f693d6121e3d845aacbd2cab7673d"
prior="de5554d5dcd1fec6bbb46ebb1fe5e512df65a86f"

echo '== Commit identity and changed paths =='
git show -s --format='commit=%H%nparent=%P%nsubject=%s' "$head"
git diff --name-status "${prior}..${head}"
git diff --find-renames --find-copies "${prior}..${head}" -- \
  backend/app/jobs/migration_dispatch_relay.py \
  backend/app/jobs/valkey_queue.py \
  backend/app/settings.py \
  backend/tests/test_migration_dispatch_relay.py \
  backend/tests/test_valkey_queue.py \
  backend/tests/test_documentation_contract.py \
  .github/workflows

echo '== Full changed production implementations =='
for file in \
  backend/app/jobs/migration_dispatch_relay.py \
  backend/app/jobs/valkey_queue.py \
  backend/app/settings.py
do
  echo "-- $file"
  nl -ba "$file" | sed -n '1,280p'
done

echo '== Full signal-boundary tests =='
for file in \
  backend/tests/test_migration_dispatch_relay.py \
  backend/tests/test_valkey_queue.py
do
  echo "-- $file"
  nl -ba "$file" | sed -n '1,420p'
done

echo '== Migration signal consumers and worker integration =='
rg -n -C 6 --glob '*.py' \
  '\b(pop_due_job_signal|valkey_migration_run_queue_key|enqueue_migration_run_signal|publish_one_migration_dispatch|migration dispatch|migration_run_uuid)\b' \
  backend/app backend/tests

echo '== CodeQL remediation diff only =='
git diff -U80 "${prior}..${head}" -- \
  backend/app/jobs/valkey_queue.py \
  backend/app/settings.py \
  backend/tests/test_valkey_queue.py \
  .github/workflows \
  | sed -n '1,1400p'

echo '== CodeQL exact-head run metadata =='
for run in 31455828452; do
  gh run view "$run" --json headSha,event,status,conclusion,workflowName,createdAt,updatedAt,jobs \
    --jq '{headSha,event,status,conclusion,workflowName,createdAt,updatedAt,jobs:[.jobs[]|{name,conclusion,startedAt,completedAt,steps:[.steps[]|{name,conclusion}]}]}'
done

echo '== CodeQL failed logs and completed job logs =='
gh run view 31455828452 --log-failed || true
for job in 93669268620 93669268629; do
  echo "-- job $job"
  gh run view 31455828452 --job "$job" --log 2>&1 | tail -n 300
done

Length of output: 27524


🏁 Script executed:

#!/bin/bash
set -euo pipefail

head="519c2b339f1f693d6121e3d845aacbd2cab7673d"
base="72afe6db712b145baaba084f64a1ff4fb36d9fd0"

show_file() {
  local file="$1" start="$2" end="$3"
  echo "-- ${file}:${start}-${end}"
  awk -v start="$start" -v end="$end" \
    'NR >= start && NR <= end { printf "%6d  %s\n", NR, $0 }' "$file"
}

echo '== Relay and Valkey implementations =='
show_file backend/app/jobs/migration_dispatch_relay.py 1 180
show_file backend/app/jobs/valkey_queue.py 1 260
show_file backend/app/settings.py 80 150

echo '== Boundary tests =='
show_file backend/tests/test_migration_dispatch_relay.py 1 180
show_file backend/tests/test_valkey_queue.py 140 370

echo '== All signal consumers and relay callers =='
rg -n -C 8 --glob '*.py' \
  '\b(pop_due_job_signal|enqueue_migration_run_signal|publish_one_migration_dispatch|migration_run_queue_key|migration-run-queue)\b' \
  backend/app backend/tests

echo '== Documentation claims for this boundary =='
rg -n -C 3 --glob '*.md' \
  'identifier-only|Valkey|migration signal|queue consumer|one-attempt publisher|bounded.*publisher|relay' \
  docs ARCHITECTURE.md CHANGELOG.md README.md SECURITY.md

echo '== Exact-head and full-PR whitespace =='
git diff --check "${head}^" "$head"
git diff --check "${base}..${head}"

echo '== CodeQL workflow bound to exact head =='
gh run view 31455828452 --json headSha,event,status,conclusion,workflowName,createdAt,updatedAt,jobs \
  --jq '{headSha,event,status,conclusion,workflowName,createdAt,updatedAt,jobs:[.jobs[]|{name,conclusion,startedAt,completedAt,steps:[.steps[]|{name,conclusion}]}]}'

echo '== CodeQL log evidence =='
for job in 93669268620 93669268629; do
  echo "-- CodeQL job ${job}"
  gh run view 31455828452 --job "$job" --log 2>&1 | \
    rg -n -i -C 2 'analy|alert|result|warning|error|database|upload|success|fail' || true
done

Length of output: 202374


🏁 Script executed:

#!/bin/bash
set -euo pipefail

head="519c2b339f1f693d6121e3d845aacbd2cab7673d"
run_id="31455828452"

echo '== Exact CodeQL workflow binding =='
gh run view "$run_id" --json headSha,status,conclusion,workflowName \
  --jq '{headSha,status,conclusion,workflowName}'
printf 'requested_head=%s\n' "$head"

echo '== CodeQL result upload completion =='
for job in 93669268620 93669268629; do
  echo "-- job ${job}"
  gh run view "$run_id" --job "$job" --log 2>&1 |
    rg -n -i \
      'Successfully uploaded results|CodeQL job status was success|Uploading .*results|Post-processing sarif' |
    tail -n 12
done

echo '== Current exact-head source checks =='
git rev-parse HEAD
git diff --check "${head}^" "$head"

Length of output: 2468


@seonghobae I reviewed exact head 519c2b339f1f693d6121e3d845aacbd2cab7673d.

I found no actionable defect.

The migration relay publishes only migration_run_uuid to the dedicated Valkey sorted-set key. It does not load a migration plan, start a worker, or execute SQL. The relay keeps commit and rollback ownership with its caller. A failed signal raises before the outbox claim is marked published.

The Valkey tests cover the isolated key, UUID-only member value, disabled queue behavior, timezone validation, client-close behavior, and signal failure behavior.

The CodeQL workflow ran on the requested head. Both Python and JavaScript/TypeScript analysis jobs completed successfully and uploaded their SARIF results. The pop_due_job_signal() change closes the Valkey client in an inner finally after eval() succeeds or fails.

git diff --check passed for the exact-head change and the full PR range.

GitHub denied this integration access to the code-scanning alert inventory. I therefore cannot independently list or verify individual alert records. The exact-head CodeQL workflow evidence is available and successful.

⚠️ Action not completed

Review rate limited.


Your included review limit is currently reached under our Fair Usage Limits Policy. This review may still proceed through usage-based billing if eligible. Your next included review will be available in 57 minutes.

@coderabbitai

coderabbitai Bot commented Aug 11, 2026

Copy link
Copy Markdown
🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

head="519c2b339f1f693d6121e3d845aacbd2cab7673d"
prior="de5554d5dcd1fec6bbb46ebb1fe5e512df65a86f"

echo '== Exact head and PR identity =='
git cat-file -e "${head}^{commit}"
git show -s --format='commit=%H%nparents=%P%nsubject=%s' "$head"
printf 'checked_out='; git rev-parse HEAD
git status --short
gh pr view 834 --json headRefOid,baseRefOid,url --jq '{headRefOid,baseRefOid,url}'
git merge-base --is-ancestor "$prior" "$head"
echo "prior_is_ancestor=$?"

echo '== Exact-head delta and whitespace =='
git log --oneline "${prior}..${head}"
git diff --name-status "${prior}..${head}"
git diff --check "${prior}..${head}"
git diff --find-renames --find-copies "${prior}..${head}"

echo '== Locate signal and CodeQL remediation paths =='
git diff --name-only "${prior}..${head}"
rg -n -i -C 4 --glob '*.py' --glob '*.yml' --glob '*.yaml' --glob '*.md' \
  'migration.{0,20}signal|signal.{0,20}migration|enqueue.*migration|publish.*migration|dispatch|CodeQL|codeql|cleartext|secret|credential|dsn|connection string' \
  backend .github docs || true

echo '== Structure of changed Python files =='
git diff --name-only "${prior}..${head}" -- '*.py' |
while IFS= read -r file; do
  [ -n "$file" ] || continue
  echo "-- $file"
  ast-grep outline "$file" --items all || true
done

echo '== Current signal boundary call sites and models =='
rg -n -C 5 --glob '*.py' \
  '\b(MigrationRunDispatch|Migration.*Signal|claim_one_migration_dispatch|mark_migration_dispatch_published|enqueue.*migration|publish.*migration|migration_run_uuid|dispatch_uuid)\b' \
  backend/app backend/tests || true

echo '== Exact-head GitHub checks and workflows =='
gh pr checks 834 --json name,state,workflow,link,startedAt,completedAt \
  --jq '.[] | {name,state,workflow,startedAt,completedAt,link}' || true

echo '== Code-scanning alerts associated with this PR or repository =='
gh api "repos/ContextualWisdomLab/pg-erd-cloud/code-scanning/alerts?state=open&pr=834&per_page=100" \
  --jq '.[] | {number,state,rule:.rule.id,most_recent_instance:.most_recent_instance.location.path,ref:.most_recent_instance.ref}' || true

Length of output: 195370


🏁 Script executed:

#!/bin/bash
set -euo pipefail

head="519c2b339f1f693d6121e3d845aacbd2cab7673d"
prior="de5554d5dcd1fec6bbb46ebb1fe5e512df65a86f"

echo '== Commit identity and changed paths =='
git show -s --format='commit=%H%nparent=%P%nsubject=%s' "$head"
git diff --name-status "${prior}..${head}"
git diff --find-renames --find-copies "${prior}..${head}" -- \
  backend/app/jobs/migration_dispatch_relay.py \
  backend/app/jobs/valkey_queue.py \
  backend/app/settings.py \
  backend/tests/test_migration_dispatch_relay.py \
  backend/tests/test_valkey_queue.py \
  backend/tests/test_documentation_contract.py \
  .github/workflows

echo '== Full changed production implementations =='
for file in \
  backend/app/jobs/migration_dispatch_relay.py \
  backend/app/jobs/valkey_queue.py \
  backend/app/settings.py
do
  echo "-- $file"
  nl -ba "$file" | sed -n '1,280p'
done

echo '== Full signal-boundary tests =='
for file in \
  backend/tests/test_migration_dispatch_relay.py \
  backend/tests/test_valkey_queue.py
do
  echo "-- $file"
  nl -ba "$file" | sed -n '1,420p'
done

echo '== Migration signal consumers and worker integration =='
rg -n -C 6 --glob '*.py' \
  '\b(pop_due_job_signal|valkey_migration_run_queue_key|enqueue_migration_run_signal|publish_one_migration_dispatch|migration dispatch|migration_run_uuid)\b' \
  backend/app backend/tests

echo '== CodeQL remediation diff only =='
git diff -U80 "${prior}..${head}" -- \
  backend/app/jobs/valkey_queue.py \
  backend/app/settings.py \
  backend/tests/test_valkey_queue.py \
  .github/workflows \
  | sed -n '1,1400p'

echo '== CodeQL exact-head run metadata =='
for run in 31455828452; do
  gh run view "$run" --json headSha,event,status,conclusion,workflowName,createdAt,updatedAt,jobs \
    --jq '{headSha,event,status,conclusion,workflowName,createdAt,updatedAt,jobs:[.jobs[]|{name,conclusion,startedAt,completedAt,steps:[.steps[]|{name,conclusion}]}]}'
done

echo '== CodeQL failed logs and completed job logs =='
gh run view 31455828452 --log-failed || true
for job in 93669268620 93669268629; do
  echo "-- job $job"
  gh run view 31455828452 --job "$job" --log 2>&1 | tail -n 300
done

Length of output: 27524


🏁 Script executed:

#!/bin/bash
set -euo pipefail

head="519c2b339f1f693d6121e3d845aacbd2cab7673d"
base="72afe6db712b145baaba084f64a1ff4fb36d9fd0"

show_file() {
  local file="$1" start="$2" end="$3"
  echo "-- ${file}:${start}-${end}"
  awk -v start="$start" -v end="$end" \
    'NR >= start && NR <= end { printf "%6d  %s\n", NR, $0 }' "$file"
}

echo '== Relay and Valkey implementations =='
show_file backend/app/jobs/migration_dispatch_relay.py 1 180
show_file backend/app/jobs/valkey_queue.py 1 260
show_file backend/app/settings.py 80 150

echo '== Boundary tests =='
show_file backend/tests/test_migration_dispatch_relay.py 1 180
show_file backend/tests/test_valkey_queue.py 140 370

echo '== All signal consumers and relay callers =='
rg -n -C 8 --glob '*.py' \
  '\b(pop_due_job_signal|enqueue_migration_run_signal|publish_one_migration_dispatch|migration_run_queue_key|migration-run-queue)\b' \
  backend/app backend/tests

echo '== Documentation claims for this boundary =='
rg -n -C 3 --glob '*.md' \
  'identifier-only|Valkey|migration signal|queue consumer|one-attempt publisher|bounded.*publisher|relay' \
  docs ARCHITECTURE.md CHANGELOG.md README.md SECURITY.md

echo '== Exact-head and full-PR whitespace =='
git diff --check "${head}^" "$head"
git diff --check "${base}..${head}"

echo '== CodeQL workflow bound to exact head =='
gh run view 31455828452 --json headSha,event,status,conclusion,workflowName,createdAt,updatedAt,jobs \
  --jq '{headSha,event,status,conclusion,workflowName,createdAt,updatedAt,jobs:[.jobs[]|{name,conclusion,startedAt,completedAt,steps:[.steps[]|{name,conclusion}]}]}'

echo '== CodeQL log evidence =='
for job in 93669268620 93669268629; do
  echo "-- CodeQL job ${job}"
  gh run view 31455828452 --job "$job" --log 2>&1 | \
    rg -n -i -C 2 'analy|alert|result|warning|error|database|upload|success|fail' || true
done

Length of output: 202374


🏁 Script executed:

#!/bin/bash
set -euo pipefail

head="519c2b339f1f693d6121e3d845aacbd2cab7673d"
run_id="31455828452"

echo '== Exact CodeQL workflow binding =='
gh run view "$run_id" --json headSha,status,conclusion,workflowName \
  --jq '{headSha,status,conclusion,workflowName}'
printf 'requested_head=%s\n' "$head"

echo '== CodeQL result upload completion =='
for job in 93669268620 93669268629; do
  echo "-- job ${job}"
  gh run view "$run_id" --job "$job" --log 2>&1 |
    rg -n -i \
      'Successfully uploaded results|CodeQL job status was success|Uploading .*results|Post-processing sarif' |
    tail -n 12
done

echo '== Current exact-head source checks =='
git rev-parse HEAD
git diff --check "${head}^" "$head"

Length of output: 2468


@seonghobae I reviewed exact head 519c2b339f1f693d6121e3d845aacbd2cab7673d.

I found no actionable defect.

The migration relay publishes only migration_run_uuid to the dedicated Valkey sorted-set key. It does not load a migration plan, start a worker, or execute SQL. The relay keeps commit and rollback ownership with its caller. A failed signal raises before the outbox claim is marked published.

The Valkey tests cover the isolated key, UUID-only member value, disabled queue behavior, timezone validation, client-close behavior, and signal failure behavior.

The CodeQL workflow ran on the requested head. Both Python and JavaScript/TypeScript analysis jobs completed successfully and uploaded their SARIF results. The pop_due_job_signal() change closes the Valkey client in an inner finally after eval() succeeds or fails.

git diff --check passed for the exact-head change and the full PR range.

GitHub denied this integration access to the code-scanning alert inventory. I therefore cannot independently list or verify individual alert records. The exact-head CodeQL workflow evidence is available and successful.

Action performed

Full review triggered.


Your included review limit is currently reached under our Fair Usage Limits Policy. This review may still proceed through usage-based billing if eligible. Your next included review will be available in 57 minutes.

Copy link
Copy Markdown
Collaborator Author

@coderabbitai full review

Please review exact head 160f9eb3a4f2e7925e1503463c35f85f770ef946, including the pinned real-Valkey signal-isolation acceptance. Queue membership must remain UUID-only and separate from generic jobs; scheduled relay, consumer, worker, and SQL authority remain absent.

@coderabbitai

coderabbitai Bot commented Aug 11, 2026

Copy link
Copy Markdown

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

head="160f9eb3a4f2e7925e1503463c35f85f770ef946"
prior="519c2b339f1f693d6121e3d845aacbd2cab7673d"

echo '== Exact head and PR identity =='
git cat-file -e "${head}^{commit}"
git show -s --format='commit=%H%nparents=%P%nsubject=%s' "$head"
printf 'checked_out='; git rev-parse HEAD
git status --short
git merge-base --is-ancestor "$prior" "$head"
echo "prior_is_ancestor=$?"
gh pr view 834 --json headRefOid,baseRefOid,url --jq '{headRefOid,baseRefOid,url}'

echo '== Exact-head delta =='
git log --oneline "${prior}..${head}"
git diff --name-status "${prior}..${head}"
git diff --check "${prior}..${head}"
git diff --find-renames --find-copies "${prior}..${head}"

echo '== Candidate Valkey workflow and acceptance-test paths =='
git ls-tree -r --name-only "$head" |
  rg -i '(^|/)(workflows/.*\.(yml|yaml)|.*(valkey|migration_dispatch|integration).*\.(py|yml|yaml))$' || true

echo '== Changed Python structure =='
git diff --name-only "${prior}..${head}" -- '*.py' |
while IFS= read -r file; do
  [ -n "$file" ] || continue
  echo "-- $file"
  ast-grep outline "$file" --items all || true
done

echo '== All migration signal and generic queue call sites =='
rg -n -C 5 --glob '*.py' \
  '\b(enqueue_migration_run_signal|pop_due_job_signal|publish_one_migration_dispatch|claim_one_migration_dispatch|MigrationRunDispatch|migration_run_uuid|valkey_migration_run_queue_key|valkey_job_queue_key|JobQueue|enqueue_job_signal)\b' \
  backend/app backend/tests || true

echo '== Potential execution-authority paths =='
rg -n -i -C 4 --glob '*.py' \
  'migration.{0,30}(worker|consumer|schedule|relay|execute|sql|plan)|\
(worker|consumer|schedule).{0,30}migration|\
(create_task|asyncio\.create_task|cron|apscheduler|celery|rq|arq)' \
  backend/app backend/tests || true

echo '== Exact-head GitHub checks =='
gh pr checks 834 --json name,state,workflow,link,startedAt,completedAt \
  --jq '.[] | select((.name | test("Valkey|PostgreSQL|ci|Security Scan|SAST Semgrep|CodeQL"; "i")) or (.workflow | test("Valkey|ci|Security Scan|SAST Semgrep|CodeQL"; "i"))) | {name,state,workflow,startedAt,completedAt,link}' || true

Length of output: 179263


🏁 Script executed:

#!/bin/bash
set -euo pipefail

head="160f9eb3a4f2e7925e1503463c35f85f770ef946"
run_id="31456149087"
job_id="93670222617"

show() {
  local file="$1" start="$2" end="$3"
  echo "-- ${file}:${start}-${end}"
  git show "${head}:${file}" |
    awk -v start="$start" -v end="$end" \
      'NR >= start && NR <= end { printf "%6d  %s\n", NR, $0 }'
}

echo '== Exact workflow job definition =='
show .github/workflows/ci.yml 100 165

echo '== Full real-Valkey acceptance test =='
show backend/tests/test_valkey_queue_integration.py 1 140

echo '== Queue adapter implementation =='
show backend/app/jobs/valkey_queue.py 1 260

echo '== Migration relay implementation and all production callers =='
show backend/app/jobs/migration_dispatch_relay.py 1 140
rg -n -C 5 --glob '*.py' \
  '\bpublish_one_migration_dispatch\s*\(|\benqueue_migration_run_signal\s*\(' \
  backend/app

echo '== Migration signal consumers and scheduler libraries =='
rg -n -i -C 3 --glob '*.py' \
  '\b(pop_due_migration|dequeue_migration|consume_migration|migration.*consumer|migration.*worker|worker.*migration|schedule.*migration|migration.*schedule)\b' \
  backend/app backend/tests || true
rg -n -i -C 2 --glob '*.py' \
  '\b(apscheduler|celery|rq|arq|cron|create_task)\b' \
  backend/app || true

echo '== SQL execution references in migration-specific modules =='
rg -n -i -C 3 --glob '*.py' \
  '\b(execute|connection|connect|sql|ddl|asyncpg|psycopg)\b' \
  backend/app/jobs/migration_dispatch_relay.py backend/app/forward/migration_run.py || true

echo '== Exact job metadata and log =='
gh run view "$run_id" --json headSha,status,conclusion,workflowName,jobs \
  --jq '{headSha,status,conclusion,workflowName,jobs:[.jobs[] | select(.databaseId == '"$job_id"') | {name,conclusion,startedAt,completedAt,steps:[.steps[]|{name,conclusion}]}]}'
gh run view "$run_id" --job "$job_id" --log | tail -n 220

echo '== Full PR whitespace check =='
git diff --check "72afe6db712b145baaba084f64a1ff4fb36d9fd0..${head}"

Length of output: 27177


@seonghobae I reviewed exact head 160f9eb3a4f2e7925e1503463c35f85f770ef946.

I found no actionable defect.

The new CI job uses a digest-pinned Valkey image. The exact-head Valkey 8 queue signal boundary job completed successfully. Its real-Valkey acceptance test uses distinct random sorted-set keys. It verifies UUID-only generic and migration members. It also verifies that pop_due_job_signal() removes only the generic UUID and leaves the migration UUID unchanged.

enqueue_migration_run_signal() writes only migration_run_uuid to valkey_migration_run_queue_key. enqueue_job_signal() and pop_due_job_signal() use valkey_queue_key. The keys remain separate.

backend/app/jobs/migration_dispatch_relay.py is the only production migration signal publisher. It does not load a plan, start a worker, or execute SQL. The generic worker registers only the snapshot handler. I found no migration signal consumer, scheduled migration relay invocation, migration worker, or migration SQL execution path.

git diff --check passed for the exact-head delta.

The overall CI workflow was still in progress during inspection. PostgreSQL 16/18, frontend, Semgrep, CodeQL, and Strix had not completed. I cannot confirm full current-head CI acceptance yet.

You are interacting with an AI system.

@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: 8

🧹 Nitpick comments (10)
backend/app/jobs/migration_dispatch_relay.py (2)

41-44: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

now 분기가 불필요합니다.

mark_migration_dispatch_publishednow 기본값은 None입니다. 따라서 now=now를 항상 전달해도 동작이 같습니다. Line 34의 claim_one_migration_dispatch 호출은 이미 now=now를 그대로 전달하므로, 두 호출의 형태도 일치하게 됩니다. 이 변경은 backend/tests/test_migration_dispatch_relay.py Line 73의 단언 수정도 필요합니다.

♻️ 제안 변경
-    if now is None:
-        await mark_migration_dispatch_published(session, claim=claim)
-    else:
-        await mark_migration_dispatch_published(session, claim=claim, now=now)
+    await mark_migration_dispatch_published(session, claim=claim, now=now)
🤖 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 `@backend/app/jobs/migration_dispatch_relay.py` around lines 41 - 44, Remove
the unnecessary now conditional around mark_migration_dispatch_published and
always pass now=now, matching the existing claim_one_migration_dispatch call.
Update the related assertion in test_migration_dispatch_relay.py to reflect the
explicit now argument.

34-40: 🩺 Stability & Availability | 🔵 Trivial

실패한 claim에 대한 백오프 계획을 확인하십시오.

publish 실패 시 트랜잭션이 롤백되므로 attempt_count 증가와 not_before 값이 모두 그대로 유지됩니다. 따라서 동일 행이 즉시 다시 claim 대상이 됩니다. 아직 relay loop가 구현되지 않아 현재 위험은 없습니다. loop를 추가할 때 not_before 지수 백오프와 최대 시도 한도를 함께 커밋하는 경로를 준비하십시오.

🤖 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 `@backend/app/jobs/migration_dispatch_relay.py` around lines 34 - 40, Update
the migration dispatch failure path around claim_one_migration_dispatch and
enqueue_migration_run_signal so a signal publish failure commits an
attempt_count increment and exponential not_before backoff instead of rolling
back unchanged claim state. Add and enforce a maximum-attempt limit, preserving
the existing exception behavior when the signal remains unavailable.
backend/tests/test_forward_migration_run.py (2)

392-395: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

결정적 tie-break 정렬 키를 단언하지 않습니다.

claim_one_migration_dispatchnot_before 다음에 migration_run_dispatch_uuid로 정렬합니다. 이 두 번째 키가 동일한 not_before 값에서 claim 순서를 결정합니다. 현재 단언은 첫 번째 키만 확인하므로, tie-break 제거가 회귀로 감지되지 않습니다.

♻️ 제안 변경
     assert "ORDER BY migration_run_dispatch.not_before" in compiled
+    assert (
+        "ORDER BY migration_run_dispatch.not_before, "
+        "migration_run_dispatch.migration_run_dispatch_uuid" in compiled
+    )
     assert "FOR UPDATE SKIP LOCKED" in compiled
🤖 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 `@backend/tests/test_forward_migration_run.py` around lines 392 - 395, Update
the SQL assertions for claim_one_migration_dispatch to also require ordering by
migration_run_dispatch_uuid after migration_run_dispatch.not_before, preserving
the deterministic tie-break key in the compiled query checks.

440-472: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

테스트 이름이 검증 범위와 일치하지 않습니다.

test_dispatch_claim_and_publish_require_timezone_aware_time은 Line 461-472에서 attempt_count가 0인 claim의 거부도 검증합니다. 이 검증은 timezone과 무관합니다. 별도 테스트로 분리하거나 이름을 확장하십시오.

🤖 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 `@backend/tests/test_forward_migration_run.py` around lines 440 - 472, Update
test_dispatch_claim_and_publish_require_timezone_aware_time so its scope matches
the timezone-aware validation only, and move the attempt_count=0 rejection
assertion into a separate test with a name describing invalid attempts;
alternatively expand the existing name to explicitly include attempt validation.
backend/app/api/migration_plans.py (2)

75-106: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

내부 예외 메시지 문자열을 공개 오류 코드 매핑 키로 사용합니다.

MigrationRunContractError의 메시지가 바뀌면 매핑이 조용히 기본값 run_action_rejected로 떨어집니다. 상태 코드 회귀가 테스트 없이 발생할 수 있습니다. MigrationRunContractError에 안정적인 code 속성을 추가하고 그 값으로 매핑하면 계약이 명시적으로 고정됩니다.

🤖 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 `@backend/app/api/migration_plans.py` around lines 75 - 106, Update
MigrationRunContractError to expose a stable code attribute, then change
_creation_contract_error to map status codes and public error codes using
error.code instead of str(error). Preserve the existing fallback for unknown
codes and assign each current failure case its corresponding stable code.

51-72: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

두 라우터가 상관 ID 헬퍼와 오류 envelope 생성기를 동일하게 복제했습니다. 공용 오류 envelope 모듈이 없어 같은 구현이 두 파일에 존재합니다. 한쪽만 수정하면 상관 ID 길이 검증이나 응답 구조가 갈라집니다.

  • backend/app/api/migration_plans.py#L51-L72: _request_id_creation_error를 공용 모듈(예: app/api/_errors.py)로 옮기고 여기서는 import하십시오.
  • backend/app/api/migration_runs.py#L37-L62: _request_id_action_error의 로컬 정의를 제거하고 같은 공용 모듈을 사용하십시오. 계약별 매핑 함수인 _cancellation_contract_error는 이 파일에 유지하십시오.
🤖 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 `@backend/app/api/migration_plans.py` around lines 51 - 72, Move the duplicated
_request_id helper and creation/action error envelope logic into a shared module
such as app/api/_errors.py, preserving the existing correlation ID validation
and response structure. In backend/app/api/migration_plans.py lines 51-72,
remove the local _request_id and _creation_error definitions and import the
shared implementations; in backend/app/api/migration_runs.py lines 37-62, remove
the local _request_id and _action_error definitions and use the same shared
helpers. Keep _cancellation_contract_error in migration_runs.py unchanged.
backend/tests/test_postgres_migration_run_integration.py (1)

205-221: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

실제 PostgreSQL에서 동시 claim 격리를 검증하지 않습니다.

이 테스트는 단일 세션에서만 claim을 수행합니다. FOR UPDATE SKIP LOCKED의 핵심 계약은 두 relay가 동일한 pending 행을 동시에 claim하지 않는 것입니다. 두 번째 세션을 열고 같은 시점에 claim_one_migration_dispatch를 호출해 None이 반환되는지 확인하는 단언을 추가하십시오. 이 검증은 실제 PostgreSQL에서만 가능하므로 이 파일이 적합한 위치입니다.

🤖 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 `@backend/tests/test_postgres_migration_run_integration.py` around lines 205 -
221, Extend the integration test around claim_one_migration_dispatch to use two
separate PostgreSQL sessions attempting to claim the same pending dispatch
concurrently. Keep the first claim successful, invoke the second claim at the
same time, and assert it returns None, demonstrating FOR UPDATE SKIP LOCKED
isolation; retain the existing publish assertions afterward.
backend/tests/test_migration_dispatch_relay.py (1)

105-130: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

publish 이후 acknowledgement 실패 경로의 커버리지가 없습니다.

mark_migration_dispatch_published는 claim이 stale이면 MigrationRunContractError를 발생시킵니다. 이 경우 큐 신호는 이미 발행된 상태이고 호출자는 롤백해야 합니다. 이 경로를 검증하는 테스트를 추가하십시오. 이는 relay의 at-least-once 계약에서 가장 위험한 구간입니다.

🤖 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 `@backend/tests/test_migration_dispatch_relay.py` around lines 105 - 130,
Extend the relay tests around publish_one_migration_dispatch to cover
acknowledgement failure after successful signal publication: make
enqueue_migration_run_signal return true, have mark_migration_dispatch_published
raise MigrationRunContractError for a stale claim, and assert that exception
propagates while the signal was published. Verify publish_one_migration_dispatch
does not commit or perform the rollback itself, leaving the caller responsible
for rollback.
backend/app/jobs/valkey_queue.py (1)

144-171: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

두 enqueue 함수의 중복을 공용 헬퍼로 줄이는 것을 검토하세요.

enqueue_migration_run_signalenqueue_job_signal(Line 119-141)과 큐 키, 로그 메시지, 시각 검증만 다릅니다. 나머지 클라이언트 생성, zadd, 예외 처리, 종료 흐름은 동일합니다. 공용 내부 함수로 추출하면 이후 재시도나 타임아웃 정책을 한 곳에서 바꿀 수 있습니다.

♻️ 제안 리팩터
+async def _enqueue_signal(
+    queue_key: str,
+    member: str,
+    score: float,
+    failure_message: str,
+) -> bool:
+    """Best-effort sorted-set write that always releases the client."""
+
+    client: Any | None = None
+    try:
+        client = await _client()
+        await client.zadd(queue_key, {member: score})
+        return True
+    except Exception:  # noqa: BLE001
+        _logger.warning(failure_message, exc_info=True)
+        return False
+    finally:
+        if client is not None:
+            await _close_client(client)
+
+
 async def enqueue_migration_run_signal(
     migration_run_uuid: uuid.UUID,
     run_after: dt.datetime | None = None,
 ) -> bool:
     """Publish only one migration-run UUID on its isolated Valkey key."""
 
     if not valkey_queue_enabled():
         return False
 
     due_at = run_after or dt.datetime.now(dt.timezone.utc)
     if due_at.tzinfo is None or due_at.utcoffset() is None:
         raise ValueError("migration run signal time must include a timezone")
-    client: Any | None = None
-    try:
-        client = await _client()
-        await client.zadd(
-            settings.valkey_migration_run_queue_key,
-            {str(migration_run_uuid): due_at.timestamp()},
-        )
-        return True
-    except Exception:  # noqa: BLE001
-        _logger.warning("Valkey migration-run enqueue signal failed", exc_info=True)
-        return False
-    finally:
-        if client is not None:
-            await _close_client(client)
+    return await _enqueue_signal(
+        settings.valkey_migration_run_queue_key,
+        str(migration_run_uuid),
+        due_at.timestamp(),
+        "Valkey migration-run enqueue signal failed",
+    )
🤖 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 `@backend/app/jobs/valkey_queue.py` around lines 144 - 171, Extract the shared
Valkey enqueue flow from enqueue_job_signal and enqueue_migration_run_signal
into a private helper that accepts the queue key, member, score, and
context-specific log message. Preserve each function’s existing enabled checks,
timezone validation, return values, exception logging context, zadd behavior,
and client cleanup while routing both functions through the helper.
backend/tests/test_security_headers.py (1)

162-192: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

실제 애플리케이션의 CORS 배선을 테스트하세요.

현재 backend/app/main.pyCORS_ALLOW_HEADERSCORS_EXPOSE_HEADERSCORSMiddleware에 전달합니다. 그러나 이 테스트는 별도의 FastAPI 앱에 해당 값을 직접 전달하므로, 실제 배선이 끊겨도 통과합니다. 프로덕션 app을 사용하거나 CORS 설정을 공유 팩토리로 추출해 배선 회귀를 검증하세요.

🤖 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 `@backend/tests/test_security_headers.py` around lines 162 - 192, Update
test_cors_preflight_allows_dry_run_idempotency_key to exercise the production
app and its actual CORSMiddleware wiring instead of constructing a separate
FastAPI instance with manually supplied CORS_ALLOW_HEADERS. If isolation is
required, reuse a shared application factory/configuration path that performs
the production wiring, while preserving the existing preflight assertions.
🤖 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/ci.yml:
- Around line 83-84: Update all three actions/checkout steps to include a with
configuration setting persist-credentials to false. Apply this to each Checkout
action, including the one identified by the visible actions/checkout reference,
without changing their existing versions or other settings.
- Around line 66-69: Remove plaintext PostgreSQL credentials, DSNs, and
APP_SECRET values from the workflow sections defining the PostgreSQL service and
test jobs; inject one-time generated values or GitHub Actions secrets at
runtime, and percent-encode DSN credentials. Update every actions/checkout step
in the CI workflow to set persist-credentials: false, resolving the CKV_SECRET_4
finding without retaining checkout tokens.

In `@backend/app/api/migration_plans.py`:
- Around line 327-333: MigrationRunCreation에는 cancellation_requested가 없어 재사용된
migration run의 실제 취소 상태가 응답되지 않습니다. migration run 생성 및 재사용을 처리하는 흐름에서 기존 또는 새
run의 cancellation_requested 값을 확보하도록 업데이트하고, MigrationRunActionOut 생성 시 하드코딩된
False 대신 해당 값을 전달하십시오.

In `@backend/app/schemas.py`:
- Around line 300-301: Update create_dry_run in migration_plans.py to populate
cancellation_requested from the cancellation flag stored in the
create_migration_run result instead of always returning False, including when an
idempotent request is reused after cancellation. Preserve the existing reused
and state_version response behavior.

In `@backend/tests/test_documentation_contract.py`:
- Line 199: Replace the ambiguous EN DASH characters in the affected docstring
and string literal with ASCII hyphens; where the test must continue validating
an EN DASH, represent it using the \u2013 escape. Update the documentation
contract test near the PostgreSQL 14–18 assertion while preserving its intended
validation.

In `@backend/tests/test_postgres_migration_run_integration.py`:
- Around line 32-36: Update the module-level pytest skip condition for the
PostgreSQL integration tests to require both POSTGRES_INTEGRATION_URL and
EXPECTED_POSTGRES_MAJOR. Use the existing _POSTGRES_URL and the expected-major
configuration lookup so tests skip cleanly when either required environment
variable is absent, avoiding the later direct access in the migration test.

In `@docs/contracts/forward-engineering-v1.md`:
- Around line 458-462: The opening error-envelope statement should be scoped to
read-only endpoints so it does not conflict with the nested run-action envelope.
Update the surrounding contract text to refer to mutating run-action endpoints
collectively, including current dry-run and cancellation routes, while
preserving the sanitized machine-classifiable shape.

In `@docs/DATA_MODEL.md`:
- Around line 299-305: Update docs/DATA_MODEL.md lines 299-305 by removing
implemented atomic outbox, FOR UPDATE SKIP LOCKED, publish CAS, and UUID-only
publishing invariants, leaving only the planned relay loop and consumer; update
lines 286-297 to describe the Planned FK table as the target state, remove
implemented migration_run_dispatch.migration_run_uuid and
migration_run_event.migration_run_uuid entries, and declare the implementation
table at lines 178, 180, and 183 authoritative for deletion behavior; update
docs/DOCUMENTATION_AUDIT.md lines 125-136 so its fourth unimplemented item
covers only recovery and no-replay reconciliation, eliminating duplication with
the relay/queue and implemented outbox, claim, CAS, and cancellation items.

---

Nitpick comments:
In `@backend/app/api/migration_plans.py`:
- Around line 75-106: Update MigrationRunContractError to expose a stable code
attribute, then change _creation_contract_error to map status codes and public
error codes using error.code instead of str(error). Preserve the existing
fallback for unknown codes and assign each current failure case its
corresponding stable code.
- Around line 51-72: Move the duplicated _request_id helper and creation/action
error envelope logic into a shared module such as app/api/_errors.py, preserving
the existing correlation ID validation and response structure. In
backend/app/api/migration_plans.py lines 51-72, remove the local _request_id and
_creation_error definitions and import the shared implementations; in
backend/app/api/migration_runs.py lines 37-62, remove the local _request_id and
_action_error definitions and use the same shared helpers. Keep
_cancellation_contract_error in migration_runs.py unchanged.

In `@backend/app/jobs/migration_dispatch_relay.py`:
- Around line 41-44: Remove the unnecessary now conditional around
mark_migration_dispatch_published and always pass now=now, matching the existing
claim_one_migration_dispatch call. Update the related assertion in
test_migration_dispatch_relay.py to reflect the explicit now argument.
- Around line 34-40: Update the migration dispatch failure path around
claim_one_migration_dispatch and enqueue_migration_run_signal so a signal
publish failure commits an attempt_count increment and exponential not_before
backoff instead of rolling back unchanged claim state. Add and enforce a
maximum-attempt limit, preserving the existing exception behavior when the
signal remains unavailable.

In `@backend/app/jobs/valkey_queue.py`:
- Around line 144-171: Extract the shared Valkey enqueue flow from
enqueue_job_signal and enqueue_migration_run_signal into a private helper that
accepts the queue key, member, score, and context-specific log message. Preserve
each function’s existing enabled checks, timezone validation, return values,
exception logging context, zadd behavior, and client cleanup while routing both
functions through the helper.

In `@backend/tests/test_forward_migration_run.py`:
- Around line 392-395: Update the SQL assertions for
claim_one_migration_dispatch to also require ordering by
migration_run_dispatch_uuid after migration_run_dispatch.not_before, preserving
the deterministic tie-break key in the compiled query checks.
- Around line 440-472: Update
test_dispatch_claim_and_publish_require_timezone_aware_time so its scope matches
the timezone-aware validation only, and move the attempt_count=0 rejection
assertion into a separate test with a name describing invalid attempts;
alternatively expand the existing name to explicitly include attempt validation.

In `@backend/tests/test_migration_dispatch_relay.py`:
- Around line 105-130: Extend the relay tests around
publish_one_migration_dispatch to cover acknowledgement failure after successful
signal publication: make enqueue_migration_run_signal return true, have
mark_migration_dispatch_published raise MigrationRunContractError for a stale
claim, and assert that exception propagates while the signal was published.
Verify publish_one_migration_dispatch does not commit or perform the rollback
itself, leaving the caller responsible for rollback.

In `@backend/tests/test_postgres_migration_run_integration.py`:
- Around line 205-221: Extend the integration test around
claim_one_migration_dispatch to use two separate PostgreSQL sessions attempting
to claim the same pending dispatch concurrently. Keep the first claim
successful, invoke the second claim at the same time, and assert it returns
None, demonstrating FOR UPDATE SKIP LOCKED isolation; retain the existing
publish assertions afterward.

In `@backend/tests/test_security_headers.py`:
- Around line 162-192: Update test_cors_preflight_allows_dry_run_idempotency_key
to exercise the production app and its actual CORSMiddleware wiring instead of
constructing a separate FastAPI instance with manually supplied
CORS_ALLOW_HEADERS. If isolation is required, reuse a shared application
factory/configuration path that performs the production wiring, while preserving
the existing preflight assertions.
🪄 Autofix

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: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: f841cfb0-6fed-4c0f-89bc-9cb60b4b05ce

📥 Commits

Reviewing files that changed from the base of the PR and between 5739fd3 and 519c2b3.

📒 Files selected for processing (35)
  • .env.example
  • .github/workflows/ci.yml
  • ARCHITECTURE.md
  • CHANGELOG.md
  • backend/alembic/versions/0010_migration_run.py
  • backend/app/api/migration_plans.py
  • backend/app/api/migration_runs.py
  • backend/app/forward/migration_run.py
  • backend/app/jobs/migration_dispatch_relay.py
  • backend/app/jobs/valkey_queue.py
  • backend/app/main.py
  • backend/app/models.py
  • backend/app/observability.py
  • backend/app/schemas.py
  • backend/app/settings.py
  • backend/tests/test_api_migration_plans.py
  • backend/tests/test_api_migration_runs.py
  • backend/tests/test_documentation_contract.py
  • backend/tests/test_forward_migration_run.py
  • backend/tests/test_migration_dispatch_relay.py
  • backend/tests/test_observability.py
  • backend/tests/test_postgres_migration_run_integration.py
  • backend/tests/test_schema_validation.py
  • backend/tests/test_security_headers.py
  • backend/tests/test_valkey_queue.py
  • docs/DATA_MODEL.md
  • docs/DOCUMENTATION_AUDIT.md
  • docs/PRD.md
  • docs/TEST_STRATEGY.md
  • docs/TRD.md
  • docs/adr/ADR-0004-durable-runs-and-recovery.md
  • docs/contracts/forward-engineering-v1.md
  • docs/runbooks/forward-engineering.md
  • docs/security/forward-engineering-threat-model.md
  • docs/superpowers/specs/2026-08-09-forward-engineering-design.md
🚧 Files skipped from review as they are similar to previous changes (7)
  • docs/PRD.md
  • backend/app/main.py
  • backend/app/forward/migration_run.py
  • docs/security/forward-engineering-threat-model.md
  • docs/runbooks/forward-engineering.md
  • backend/tests/test_api_migration_plans.py
  • docs/superpowers/specs/2026-08-09-forward-engineering-design.md

Comment thread .github/workflows/ci.yml Outdated
Comment thread .github/workflows/ci.yml
Comment thread backend/app/api/migration_plans.py
Comment thread backend/app/schemas.py
Comment thread backend/tests/test_documentation_contract.py Outdated
Comment thread backend/tests/test_postgres_migration_run_integration.py
Comment thread docs/contracts/forward-engineering-v1.md Outdated
Comment thread docs/DATA_MODEL.md Outdated

Copy link
Copy Markdown
Collaborator Author

@coderabbitai review

Please review exact head 3c019a800e9c3258379e7e83eb90c469779b2716. Prior reviews and statuses are predecessor-head evidence only.

@coderabbitai

coderabbitai Bot commented Aug 11, 2026

Copy link
Copy Markdown

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

head="3c019a800e9c3258379e7e83eb90c469779b2716"
prior="160f9eb3a4f2e7925e1503463c35f85f770ef946"
base="72afe6db712b145baaba084f64a1ff4fb36d9fd0"

echo '== Exact head and PR identity =='
git cat-file -e "${head}^{commit}"
git show -s --format='commit=%H%nparents=%P%nsubject=%s' "$head"
printf 'checked_out='; git rev-parse HEAD
git status --short
gh pr view 834 --json headRefOid,baseRefOid,url --jq '{headRefOid,baseRefOid,url}'

echo '== Ancestry and exact-head delta =='
git merge-base --is-ancestor "$prior" "$head"
echo "prior_is_ancestor=$?"
git log --oneline "${prior}..${head}"
git diff --name-status "${prior}..${head}"
git diff --check "${prior}..${head}"
git diff --find-renames --find-copies "${prior}..${head}"

echo '== Changed Python and workflow structure =='
git diff --name-only "${prior}..${head}" -- '*.py' '*.yml' '*.yaml' |
while IFS= read -r file; do
  [ -n "$file" ] || continue
  echo "-- $file"
  case "$file" in
    *.py) ast-grep outline "$file" --items all || true ;;
    *) nl -ba "$file" | sed -n '1,260p' ;;
  esac
done

echo '== Complete exact-head changed-file diffs =='
git diff -U60 --find-renames --find-copies "${prior}..${head}"

echo '== Exact-head affected symbol call sites =='
rg -n -C 5 --glob '*.py' \
  '\b(MigrationRunDispatch|MigrationDispatchClaim|claim_one_migration_dispatch|mark_migration_dispatch_published|publish_one_migration_dispatch|enqueue_migration_run_signal|pop_due_job_signal|migration_run_uuid)\b' \
  backend/app backend/tests || true

echo '== Exact-head workflow checks =='
gh pr checks 834 --json name,state,workflow,link,startedAt,completedAt \
  --jq '.[] | {name,state,workflow,startedAt,completedAt,link}' || true

echo '== Exact-head whitespace checks =='
git diff --check "${head}^" "$head"
git diff --check "${base}..${head}"

Length of output: 69369


🏁 Script executed:

#!/bin/bash
set -euo pipefail

head="3c019a800e9c3258379e7e83eb90c469779b2716"
prior="160f9eb3a4f2e7925e1503463c35f85f770ef946"

show() {
  local file="$1" start="$2" end="$3"
  echo "-- ${file}:${start}-${end}"
  awk -v start="$start" -v end="$end" \
    'NR >= start && NR <= end { printf "%6d  %s\n", NR, $0 }' "$file"
}

echo '== Complete relay and lifecycle implementations =='
show backend/app/jobs/migration_dispatch_relay.py 1 180
show backend/app/main.py 1 135
show backend/app/db.py 1 220
show backend/app/jobs/valkey_queue.py 1 310
show backend/app/settings.py 85 135

echo '== Focused lifecycle and relay tests =='
show backend/tests/test_migration_dispatch_lifecycle.py 1 180
show backend/tests/test_migration_dispatch_relay.py 1 260
show backend/tests/test_api_migration_runs.py 100 225
show backend/tests/test_postgres_migration_run_integration.py 1 250

echo '== Focused AST maps =='
ast-grep outline backend/app/jobs/migration_dispatch_relay.py --items all || true
ast-grep outline backend/app/main.py --items all || true
ast-grep outline backend/tests/test_migration_dispatch_lifecycle.py --items all || true
ast-grep outline backend/tests/test_migration_dispatch_relay.py --items all || true

echo '== Production relay callers and migration execution consumers =='
rg -n -C 5 --glob '*.py' \
  '\b(run_migration_dispatch_relay_forever|publish_one_migration_dispatch|enqueue_migration_run_signal|pop_due_migration|consume_migration|migration.*worker|worker.*migration)\b' \
  backend/app backend/tests || true

echo '== Current-head workflow, without executing repository code =='
awk '{printf "%6d  %s\n", NR, $0}' .github/workflows/ci.yml | sed -n '1,260p'

echo '== Exact-head checks and runs =='
gh pr checks 834 --json name,state,workflow,link,startedAt,completedAt \
  --jq '.[] | {name,state,workflow,startedAt,completedAt,link}' || true
gh run list --branch "$(gh pr view 834 --json headRefName --jq .headRefName)" --limit 20 \
  --json databaseId,headSha,status,conclusion,workflowName,createdAt,updatedAt \
  --jq '.[] | select(.headSha == "3c019a800e9c3258379e7e83eb90c469779b2716")' || true

echo '== Exact-head commit set and whitespace =='
git log --format='%H %s' "${prior}..${head}"
git diff --check "${head}^" "$head"
git diff --check "72afe6db712b145baaba084f64a1ff4fb36d9fd0..${head}"

Length of output: 108769


@seonghobae I reviewed exact head 3c019a800e9c3258379e7e83eb90c469779b2716.

I found one issue.

  1. P2 — Correct the startup-validation claim for the relay.
    docs/adr/ADR-0004-durable-runs-and-recovery.md, Line 137 states that startup fails closed when Valkey is unavailable. backend/app/main.py, Lines 49-52 only reject startup when valkey_queue_enabled() is false. backend/app/jobs/valkey_queue.py, Lines 51-56 shows that this check only validates the selected backend and the presence of a URL or Sentinel host string. It does not validate redis-py availability, URL validity, or connectivity. Those failures occur in the relay loop, are logged as migration_dispatch_relay_iteration_failed, and are retried.

    Update the documentation to state that startup rejects an unconfigured Valkey backend. Alternatively, add startup validation for the Valkey client and connection if fail-closed service availability is required.

The relay uses a fresh metadata transaction for each claim. It publishes only migration_run_uuid. It does not add a queue consumer, migration worker, plan loader, or SQL execution path. The lifecycle shutdown cancels and awaits its background tasks.

git diff --check passed for the exact-head change and the full PR range.

Exact-head CodeQL, Semgrep, Valkey acceptance, and PostgreSQL 18 acceptance succeeded. The backend, frontend, PostgreSQL 14–17, and several security checks were still in progress. I cannot confirm full CI acceptance for this head.

You are interacting with an AI system.

@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: 5

Caution

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

⚠️ Outside diff range comments (1)
docs/DOCUMENTATION_AUDIT.md (1)

125-127: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

scheduled relay lifecycle를 미구현 항목에서 분리하십시오.

이 항목은 Relay loop/queue delivery를 미구현으로 표시합니다. 그러나 Line 158과 Line 171은 opt-in scheduled relay lifecycle과 UUID-only publication이 구현되었다고 명시합니다.

미구현 범위를 queue consumer, worker execution, failover, retention으로 제한하십시오. 구현된 scheduled relay lifecycle는 별도로 표시하십시오.

🤖 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 `@docs/DOCUMENTATION_AUDIT.md` around lines 125 - 127, Update the documentation
entry describing unimplemented functionality so scheduled relay lifecycle is
listed separately as implemented, including opt-in scheduling and UUID-only
publication. Restrict the remaining unimplemented scope to queue consumer,
worker execution, failover, and retention, and keep the existing
implemented-status wording consistent with the related entries.
🤖 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 `@backend/tests/test_documentation_contract.py`:
- Around line 237-239: Update the workflow assertion in
test_documentation_contract.py to inspect each actions/checkout@ step block
individually and verify its with.persist-credentials setting is false. Replace
the aggregate string-count comparison so an unrelated persist-credentials entry
cannot satisfy the test when a checkout step is missing the setting.

In `@backend/tests/test_migration_dispatch_lifecycle.py`:
- Around line 15-23: _blocking_task에 반환 타입 주석을 추가해 엄격한 타입 검사에서 통과하도록 수정하세요. 내부
async 함수 run은 유지하고, _blocking_task가 반환하는 호출 가능 객체를 Callable[...,
Awaitable[None]] 등으로 명시하세요.

In `@backend/tests/test_migration_dispatch_relay.py`:
- Around line 32-45: The _session_factory helper must create and return a fresh
session/transaction mock pair for every factory invocation instead of reusing
one pair. Track each generated pair, then update the relay-loop tests around the
existing factory call-count assertions to verify object identity across
iterations and that every session and transaction context exits correctly.
- Line 180: Replace the credential-shaped value assigned to secret in the
migration dispatch test with an opaque marker string that contains no DSN
structure or password-like data, while preserving the test’s intended
log-redaction validation.

In `@docs/DATA_MODEL.md`:
- Around line 297-303: Resolve the status conflict in the “Additional Planned
invariants” section by moving the scheduled relay lifecycle invariant to the
document’s implemented/physical invariants table, or renaming the section to
explicitly cover both implemented and planned items. Keep queue consumption and
worker execution identified as Planned, and preserve the relay’s implemented
scope.

---

Outside diff comments:
In `@docs/DOCUMENTATION_AUDIT.md`:
- Around line 125-127: Update the documentation entry describing unimplemented
functionality so scheduled relay lifecycle is listed separately as implemented,
including opt-in scheduling and UUID-only publication. Restrict the remaining
unimplemented scope to queue consumer, worker execution, failover, and
retention, and keep the existing implemented-status wording consistent with the
related entries.
🪄 Autofix

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: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: e64ca083-7201-478c-a646-41b2841159ed

📥 Commits

Reviewing files that changed from the base of the PR and between 519c2b3 and 3c019a8.

📒 Files selected for processing (26)
  • .env.example
  • .github/workflows/ci.yml
  • ARCHITECTURE.md
  • CHANGELOG.md
  • backend/app/api/migration_plans.py
  • backend/app/forward/migration_run.py
  • backend/app/jobs/migration_dispatch_relay.py
  • backend/app/main.py
  • backend/app/settings.py
  • backend/tests/test_api_migration_runs.py
  • backend/tests/test_documentation_contract.py
  • backend/tests/test_forward_migration_run.py
  • backend/tests/test_migration_dispatch_lifecycle.py
  • backend/tests/test_migration_dispatch_relay.py
  • backend/tests/test_postgres_migration_run_integration.py
  • backend/tests/test_valkey_queue.py
  • backend/tests/test_valkey_queue_integration.py
  • docs/DATA_MODEL.md
  • docs/DOCUMENTATION_AUDIT.md
  • docs/STANDARDS.md
  • docs/TEST_STRATEGY.md
  • docs/TRD.md
  • docs/adr/ADR-0004-durable-runs-and-recovery.md
  • docs/contracts/forward-engineering-v1.md
  • docs/runbooks/forward-engineering.md
  • docs/security/forward-engineering-threat-model.md
🚧 Files skipped from review as they are similar to previous changes (12)
  • .env.example
  • backend/tests/test_valkey_queue.py
  • backend/app/forward/migration_run.py
  • ARCHITECTURE.md
  • backend/tests/test_postgres_migration_run_integration.py
  • backend/app/api/migration_plans.py
  • docs/TEST_STRATEGY.md
  • docs/adr/ADR-0004-durable-runs-and-recovery.md
  • backend/tests/test_forward_migration_run.py
  • docs/security/forward-engineering-threat-model.md
  • docs/runbooks/forward-engineering.md
  • backend/tests/test_api_migration_runs.py

Comment thread backend/tests/test_documentation_contract.py Outdated
Comment thread backend/tests/test_migration_dispatch_lifecycle.py Outdated
Comment thread backend/tests/test_migration_dispatch_relay.py
Comment thread backend/tests/test_migration_dispatch_relay.py Outdated
Comment thread docs/DATA_MODEL.md Outdated

Copy link
Copy Markdown
Collaborator Author

Exact-head remediation is published at c6e89ef6279d90b1f5fd1c3931114d682b28b259.

All five current-head findings were verified and addressed:

  • checkout credential persistence is now asserted per checkout step block;
  • lifecycle test helpers are fully typed;
  • relay-loop acceptance uses a fresh session/transaction pair per iteration and verifies every context exit;
  • the credential-shaped test fixture was replaced by an opaque redaction marker;
  • Data Model and Documentation Audit now separate the Implemented scheduled UUID-only relay from Planned consumer/worker/failover/retention scope, including the outside-diff audit finding.

Test-first evidence: the fresh-session and documentation maturity assertions failed on the predecessor, then passed after remediation. Final evidence: 26 focused tests passed; full backend 673 passed / 3 skipped; mypy passed for 78 source files; compileall and diff-check passed.

@coderabbitai review the unchanged exact head c6e89ef6279d90b1f5fd1c3931114d682b28b259. Predecessor review/check evidence is not treated as passing.

@coderabbitai

coderabbitai Bot commented Aug 11, 2026

Copy link
Copy Markdown

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

head="c6e89ef6279d90b1f5fd1c3931114d682b28b259"
prior="3c019a800e9c3258379e7e83eb90c469779b2716"
base="72afe6db712b145baaba084f64a1ff4fb36d9fd0"

echo '== Exact commit identity and PR head =='
git cat-file -e "${head}^{commit}"
git show -s --format='commit=%H%nparents=%P%nsubject=%s' "$head"
printf 'checked_out='; git rev-parse HEAD
git status --short
gh pr view 834 --json headRefOid,baseRefOid,url --jq '{headRefOid,baseRefOid,url}'

echo '== Ancestry and exact-head delta =='
git merge-base --is-ancestor "$prior" "$head"
echo "prior_is_ancestor=$?"
git log --oneline "${prior}..${head}"
git diff --name-status "${prior}..${head}"
git diff --check "${head}^" "$head"
git diff --check "${base}..${head}"
git diff --find-renames --find-copies "${prior}..${head}"

echo '== Changed-file structure =='
git diff --name-only "${prior}..${head}" |
while IFS= read -r file; do
  [ -n "$file" ] || continue
  echo "-- $file"
  case "$file" in
    *.py) ast-grep outline "$file" --items all || true ;;
    *) nl -ba "$file" | sed -n '1,300p' ;;
  esac
done

echo '== Remediation implementation and tests =='
for file in \
  .github/workflows/ci.yml \
  backend/app/main.py \
  backend/app/jobs/migration_dispatch_relay.py \
  backend/app/jobs/valkey_queue.py \
  backend/tests/test_migration_dispatch_lifecycle.py \
  backend/tests/test_migration_dispatch_relay.py \
  backend/tests/test_documentation_contract.py \
  backend/tests/test_job_worker_security.py \
  docs/DATA_MODEL.md \
  docs/DOCUMENTATION_AUDIT.md \
  docs/adr/ADR-0004-durable-runs-and-recovery.md
do
  if [ -f "$file" ]; then
    echo "-- $file"
    nl -ba "$file" | sed -n '1,360p'
  fi
done

echo '== Startup and relay call sites =='
rg -n -C 5 --glob '*.py' \
  '\b(run_migration_dispatch_relay_forever|publish_one_migration_dispatch|migration_dispatch_relay_enabled|valkey_queue_enabled|enqueue_migration_run_signal)\b' \
  backend/app backend/tests || true

echo '== Current exact-head workflow and check metadata =='
gh pr checks 834 --json name,state,workflow,link,startedAt,completedAt \
  --jq '.[] | {name,state,workflow,startedAt,completedAt,link}' || true
gh run list --branch "$(gh pr view 834 --json headRefName --jq .headRefName)" --limit 30 \
  --json databaseId,headSha,status,conclusion,workflowName,createdAt,updatedAt \
  --jq '.[] | select(.headSha == "c6e89ef6279d90b1f5fd1c3931114d682b28b259")' || true

Length of output: 15916


🏁 Script executed:

#!/bin/bash
set -euo pipefail

head="c6e89ef6279d90b1f5fd1c3931114d682b28b259"

show_range() {
  file="$1"
  start="$2"
  end="$3"
  echo "-- ${file}:${start}-${end}"
  sed -n "${start},${end}p" "$file" | awk -v start="$start" '{printf "%6d  %s\n", start + NR - 1, $0}'
}

echo '== Current checkout step blocks =='
awk '
  /^      - / { if (block != "") print block; block = $0 ORS; next }
  { if (block != "") block = block $0 ORS }
  END { if (block != "") print block }
' .github/workflows/ci.yml |
awk 'BEGIN { RS=""; ORS="\n---\n" } /uses: actions\/checkout@/ { print }'

echo '== Focused remediation tests =='
show_range backend/tests/test_documentation_contract.py 220 265
show_range backend/tests/test_migration_dispatch_lifecycle.py 1 100
show_range backend/tests/test_migration_dispatch_relay.py 1 245

echo '== Relay implementation and lifecycle wiring =='
show_range backend/app/jobs/migration_dispatch_relay.py 1 180
show_range backend/app/main.py 1 125
show_range backend/app/jobs/valkey_queue.py 1 100
show_range backend/app/settings.py 85 140

echo '== Current documentation claims =='
show_range docs/DATA_MODEL.md 280 325
show_range docs/DOCUMENTATION_AUDIT.md 108 155
show_range docs/adr/ADR-0004-durable-runs-and-recovery.md 125 150

echo '== All checkout references and relay consumers =='
rg -n -C 4 'uses: actions/checkout@|persist-credentials:' .github/workflows/ci.yml
rg -n -i -C 4 --glob '*.py' \
  '\b(run_migration_dispatch_relay_forever|publish_one_migration_dispatch|enqueue_migration_run_signal|pop_due_migration|consume_migration|migration.*worker|worker.*migration)\b' \
  backend/app backend/tests || true

echo '== Exact-head workflow runs and checks =='
gh run list --branch "$(gh pr view 834 --json headRefName --jq .headRefName)" --limit 50 \
  --json databaseId,headSha,status,conclusion,workflowName,createdAt,updatedAt \
  --jq '.[] | select(.headSha == "c6e89ef6279d90b1f5fd1c3931114d682b28b259")' || true
gh pr checks 834 --json name,state,workflow,link,startedAt,completedAt \
  --jq '.[] | {name,state,workflow,startedAt,completedAt,link}' || true

echo '== Exact-head integrity =='
git rev-parse HEAD
git status --short
git diff --check "${head}^" "$head"
git diff --check "72afe6db712b145baaba084f64a1ff4fb36d9fd0..${head}"

Length of output: 77102


@seonghobae I reviewed exact head c6e89ef6279d90b1f5fd1c3931114d682b28b259.

The five stated remediations are present:

  • Each actions/checkout step has its own persist-credentials: false setting.
  • The lifecycle helper has an explicit Callable[..., Awaitable[None]] return type.
  • The relay-loop test creates a fresh session and transaction for each iteration. It verifies each context exit.
  • The failure fixture now uses an opaque marker.
  • docs/DATA_MODEL.md and docs/DOCUMENTATION_AUDIT.md separate the implemented scheduled UUID-only relay from planned consumer, worker, failover, and retention scope.

I found one remaining issue.

  1. P2 — Correct the ADR startup-validation claim.
    docs/adr/ADR-0004-durable-runs-and-recovery.md, Line 140 states: “Startup fails closed when Valkey is unavailable.” However, backend/app/main.py, Lines 49-52 only reject startup when valkey_queue_enabled() returns false. backend/app/jobs/valkey_queue.py, Lines 51-56 show that this function checks only the selected backend and the presence of a Valkey URL or Sentinel-host configuration. It does not validate redis-py availability, connection-string validity, or Valkey connectivity. The relay loop handles those failures after startup and retries them.

    Update the ADR to say that startup rejects an unconfigured Valkey backend. Alternatively, add startup-time client and connectivity validation.

git diff --check passed for the exact-head change and the full PR range.

The current-head Semgrep, CodeQL, Valkey acceptance, and PostgreSQL 16–18 jobs succeeded. The backend, frontend, PostgreSQL 14–15, Trivy, and Strix jobs were still incomplete during inspection. I cannot confirm full current-head CI acceptance.

You are interacting with an AI system.

Copy link
Copy Markdown
Collaborator Author

Exact-head UUID-only signal lease slice is published at be1bd78dfa1b4392ef51935d521856e808b8b988.

The ready sorted-set member remains only migration_run_uuid. Atomic Lua primitives now:

  • reclaim at most 100 expired processing leases per claim attempt;
  • move one due UUID to an isolated processing set with a bounded 0–3600 second exact lease-token stored separately;
  • reject stale acknowledgement/release tokens;
  • acknowledge the current lease or return it to the ready set at an aware retry instant;
  • reject colliding generic/ready/processing/token keys before client I/O;
  • quarantine hostile non-UUID members and use fixed non-secret adapter failure codes.

This is consumer-safety infrastructure only. No consumer lifecycle, plan/credential loading, target access, worker, sandbox, preflight, or SQL execution was added.

Test-first evidence: seven focused tests failed before the primitives/settings existed, and the documentation maturity contract failed before canonical reconciliation. Final local evidence: 41 focused tests passed with one environment-gated real-Valkey case; app.jobs.valkey_queue is 100% statement/branch covered; full backend 685 passed / 3 skipped; mypy passed for 78 source files; compileall and diff-check passed. CI remains authoritative for the digest-pinned real-Valkey path.

@coderabbitai review the unchanged exact head be1bd78dfa1b4392ef51935d521856e808b8b988. Predecessor checks and reviews are not passing evidence.

@coderabbitai

coderabbitai Bot commented Aug 11, 2026

Copy link
Copy Markdown

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

head="be1bd78dfa1b4392ef51935d521856e808b8b988"
prior="c6e89ef6279d90b1f5fd1c3931114d682b28b259"
base="72afe6db712b145baaba084f64a1ff4fb36d9fd0"

echo '== Exact commit and PR identity =='
git cat-file -e "${head}^{commit}"
git show -s --format='commit=%H%nparents=%P%nsubject=%s' "$head"
printf 'checked_out='; git rev-parse HEAD
git status --short
gh pr view 834 --json headRefOid,baseRefOid,url --jq '{headRefOid,baseRefOid,url}'
git merge-base --is-ancestor "$prior" "$head"
echo "prior_is_ancestor=$?"

echo '== Exact-head delta and whitespace =='
git log --oneline "${prior}..${head}"
git diff --name-status "${prior}..${head}"
git diff --check "${head}^" "$head"
git diff --check "${base}..${head}"
git diff --find-renames --find-copies "${prior}..${head}"

echo '== Changed-file structure =='
git diff --name-only "${prior}..${head}" |
while IFS= read -r file; do
  [ -n "$file" ] || continue
  echo "-- $file"
  case "$file" in
    *.py) ast-grep outline "$file" --items all || true ;;
    *) nl -ba "$file" | sed -n '1,340p' ;;
  esac
done

echo '== Valkey lease implementation and settings =='
for file in \
  backend/app/jobs/valkey_queue.py \
  backend/app/settings.py \
  backend/tests/test_valkey_queue.py \
  backend/tests/test_valkey_queue_integration.py \
  backend/tests/test_documentation_contract.py \
  docs/DATA_MODEL.md \
  docs/DOCUMENTATION_AUDIT.md \
  docs/adr/ADR-0004-durable-runs-and-recovery.md \
  .github/workflows/ci.yml
do
  if [ -f "$file" ]; then
    echo "-- $file"
    nl -ba "$file" | sed -n '1,520p'
  fi
done

echo '== Lease API call sites, key construction, and migration consumers =='
rg -n -C 5 --glob '*.py' \
  '\b(claim_migration|acknowledge_migration|release_migration|migration.*lease|migration.*processing|migration.*token|enqueue_migration_run_signal|pop_due_migration|consume_migration|migration.*worker|worker.*migration)\b' \
  backend/app backend/tests || true

echo '== Exact-head workflow runs and checks =='
gh run list --branch "$(gh pr view 834 --json headRefName --jq .headRefName)" --limit 50 \
  --json databaseId,headSha,status,conclusion,workflowName,createdAt,updatedAt \
  --jq '.[] | select(.headSha == "be1bd78dfa1b4392ef51935d521856e808b8b988")' || true
gh pr checks 834 --json name,state,workflow,link,startedAt,completedAt \
  --jq '.[] | {name,state,workflow,startedAt,completedAt,link}' || true

Length of output: 53857


🏁 Script executed:

#!/bin/bash
set -euo pipefail

head="be1bd78dfa1b4392ef51935d521856e808b8b988"
prior="c6e89ef6279d90b1f5fd1c3931114d682b28b259"

show_range() {
  local file="$1" start="$2" end="$3"
  echo "-- ${file}:${start}-${end}"
  sed -n "${start},${end}p" "$file" |
    awk -v start="$start" '{printf "%6d  %s\n", start + NR - 1, $0}'
}

echo '== Complete changed-file names =='
git diff --name-only "${prior}..${head}"

echo '== Lease implementation =='
show_range backend/app/jobs/valkey_queue.py 1 430
show_range backend/app/settings.py 90 140

echo '== Focused unit and real-Valkey acceptance tests =='
show_range backend/tests/test_valkey_queue.py 200 560
show_range backend/tests/test_valkey_queue_integration.py 1 180

echo '== Exact changed test/documentation diffs =='
git diff -U25 "${prior}..${head}" -- \
  backend/tests/test_valkey_queue.py \
  backend/tests/test_valkey_queue_integration.py \
  backend/tests/test_documentation_contract.py \
  docs/DATA_MODEL.md \
  docs/DOCUMENTATION_AUDIT.md \
  docs/contracts/forward-engineering-v1.md \
  docs/TRD.md \
  docs/runbooks/forward-engineering.md \
  docs/adr/ADR-0004-durable-runs-and-recovery.md

echo '== Production lease API call sites and prohibited-scope search =='
rg -n -C 4 --glob '*.py' \
  '\b(claim_due_migration_run_signal|ack_migration_run_signal|release_migration_run_signal|MigrationRunSignalClaim)\b' \
  backend/app backend/tests || true
rg -n -i -C 3 --glob '*.py' \
  '\b(pop_due_migration|dequeue_migration|consume_migration|migration.*consumer|migration.*worker|worker.*migration|migration.*execute|execute.*migration)\b' \
  backend/app backend/tests || true

echo '== Current exact-head workflow evidence =='
gh run list --branch "$(gh pr view 834 --json headRefName --jq .headRefName)" --limit 50 \
  --json databaseId,headSha,status,conclusion,workflowName,createdAt,updatedAt \
  --jq '.[] | select(.headSha == "be1bd78dfa1b4392ef51935d521856e808b8b988")' || true
gh pr checks 834 --json name,state,workflow,link,startedAt,completedAt \
  --jq '.[] | {name,state,workflow,startedAt,completedAt,link}' || true

echo '== Exact source integrity =='
git status --short
git rev-parse HEAD
git diff --check "${head}^" "$head"
git diff --check "72afe6db712b145baaba084f64a1ff4fb36d9fd0..${head}"

Length of output: 127223


@seonghobae I reviewed exact head be1bd78dfa1b4392ef51935d521856e808b8b988.

I found two issues.

  1. P2 — Quarantine non-UTF-8 queue members.
    backend/app/jobs/valkey_queue.py, Line 294 decodes a claimed bytes member before UUID validation. A hostile member that is not valid UTF-8 raises UnicodeDecodeError. The broad exception handler returns None, but it does not remove the member from the processing sorted set or delete its lease token. The member remains until expiry and can repeat on later claims.

    Decode failures must use the same cleanup path as other invalid UUID members. Add a test that returns non-UTF-8 bytes from client.eval() and verifies processing and token cleanup.

  2. P2 — Correct the ADR startup-validation claim.
    docs/adr/ADR-0004-durable-runs-and-recovery.md, Line 146 states: “Startup fails closed when Valkey is unavailable.” The application startup guard validates only that the Valkey backend is configured. It does not validate the redis-py client, URL validity, or Valkey connectivity. The relay loop handles these failures after startup.

    State that startup rejects an unconfigured Valkey backend. Alternatively, add startup-time client and connectivity validation.

The ready signal remains UUID-only. The processing member is also the run UUID. The lease token is stored separately. The claim, acknowledgement, and retry-release Lua operations bind acknowledgement and release to the exact token. I found no migration consumer, worker, plan loading, target access, or SQL execution path.

git diff --check passed for the exact change and the full PR range. The exact-head CI, CodeQL, Semgrep, real-Valkey, and PostgreSQL jobs were queued during inspection. I cannot verify CI acceptance yet.


✏️ Learnings added
Learnt from: seonghobae
URL: https://github.com/ContextualWisdomLab/pg-erd-cloud/pull/834

Timestamp: 2026-08-11T05:05:31.387Z
Learning: In PR `#834`, `backend/app/jobs/valkey_queue.py` implements UUID-only migration signal lease primitives. The ready and processing sorted-set members are `migration_run_uuid`; an exact `lease_token` is stored separately and is required for acknowledgement or retry release. These primitives do not load plans or credentials, access a target, create a consumer lifecycle, or execute SQL.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

You are interacting with an AI system.

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