Skip to content

ci: validate stacked pull request bases - #1562

Draft
seonghobae wants to merge 37 commits into
chore/stacked-pr-governance-gate-supportfrom
codex/stacked-pr-workflow-triggers
Draft

ci: validate stacked pull request bases#1562
seonghobae wants to merge 37 commits into
chore/stacked-pr-governance-gate-supportfrom
codex/stacked-pr-workflow-triggers

Conversation

@seonghobae

@seonghobae seonghobae commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

Current inherited governance repair

Exact head: 0648eace4186c9ba813102df4f26571e20cea3c0; complete parent fac3437c03d928e45632763530a4f130dfe505fd integrated by ordinary merge, with no conflicts or discarded consumer delta. This merge changes only three governance files (34 additions / 10 removals); gate and harness match the parent. Own committed-head full fake-GitHub harness PASS exit 0; own 40 source/governance tests PASS, zero skips; ShellCheck and diff check PASS. CI first attempted a nonexistent third test filename and exited 4 with zero collection; corrected actual two-file command passed. No dependency sync or full PostgreSQL lifecycle was rerun in this increment, so prior DB and security receipts below remain historical and do not certify this new head. Hosted checks, qualifying review and prerequisite-first protected integration remain pending; no approval/merge claim.

Historical predecessor receipts

Current merge-ref local security — 2026-09-06

Head b0d1bb6edfa4b505a6bd4c2c42c178237c00b502, fetched merge 92b509590e04548c120d68e18b71d1dcd928ac96 with verified current base/head parents. After tracked archive extraction completed successfully, Trivy vulnerability/misconfiguration scan (include-dev-deps, HIGH/CRITICAL, fixable only) and separate secret scan each exited0 with zero reported findings. No ignore rule or scanner policy changed; tracked trivy.yaml retained. Report SHA-256: security 49f2eff87e3dc15dc11cd30cab7f594034361a44f5e86618ec1f741f80fd94cb; secrets 6a56a4eb4f4f1f37e36505ce469478973de303fe54a0ad7ca24216c98933345c.

Initial scans started before extraction completion and are explicitly INVALID, not evidence. Only the *_complete.json reruns are accepted. Local scans are not hosted gate success, application/runtime security certification, independent approval or protected merge. Remaining publication concurrency finding is unaffected.

Exact-head full PostgreSQL revalidation — 2026-09-06

Unchanged head b0d1bb6edfa4b505a6bd4c2c42c178237c00b502: 1877 passed /2 explicit LIVE_BASE_URL-only skips /122.79s /runner exit0. Fresh and repeated real migrations completed; generated project naruon-test-mxfot2n1 has no remaining containers, networks or volumes. Actions hash requirements dry-run:113 packages, no changes; compatibility check passed. Worktree clean and head unchanged after the run.

JUnit SHA-256: 76867b1b433a2c486c6f71ce7a1d2f932c2562c4057127ad2e827deb090a640a.

Independent read-only Agent review of owner e058f3e four-file delta found no actionable regressions; inherited script/test blobs match owner. This is local source review, not GitHub approval. New-head hosted/security gates and unresolved release-concurrency finding remain separate requirements.

Current governance inheritance — 2026-09-06

Head b0d1bb6edfa4b505a6bd4c2c42c178237c00b502 normally merges complete #1531 e058f3ead35f9a19d3c3b20c6ab5fc04d2e2cbb2. Multiline CodeRabbit notices retain wait/fallback semantics without hiding external warnings; existing stacked test moves into CI-collected backend tree. Own full fake-GitHub harness PASS, focused documentation/governance 40 passed /0 skipped /0.19s, ShellCheck/diff check pass. No runtime dependency or release-policy changes. Prior full PostgreSQL/security receipts below belong only to their recorded heads, not this new head; fresh hosted checks/reviews remain required. Publication concurrency finding remains open.

Historical receipts

Current process-ownership repair

Head 1f538b188bf0c6a8193fbea005d0c537a01095ec, tree c5756d111a71b5eb7d4a1b038a5c29de646f7d03, ordinary child of b2e98a52588db72e501c0843b33816e2e5bc698b. Draft on unchanged #1531. Complete existing owner/prerequisite delta is retained.

A real SIGTERM between background launch and PID assignment left the original command group alive even after cleanup. The task-owned DEBUG barrier reproduced a five-second communication failure against the unchanged runner. Deferring cancellation only until PID registration closes that gap; cancellation exit143, owned-group reaping, report redaction and the existing cleanup bound remain. No runtime/model timeout increase or consumer copy.

Exact committed-head focused verification: 19 passed / 0 skipped / 18.90 s, full Ruff, ShellCheck and whitespace checks pass. JUnit SHA-256 19de589226957518e0b61e85fcf1f6fdbfa9625797a6d10e94a892ee12d8e66d. The following full migrated PostgreSQL run uses a clean synchronization of the same core and optional-agent hash files used by Actions (113 packages, compatibility check passes); it completed 1876 passed / 2 explicitly unconfigured LIVE_BASE_URL skips / 91.86 s, fresh/repeated migrations and complete scoped cleanup with exit0. JUnit SHA-256 6d58e7387c41577302376ebb158ed4ff561cfb8b864d22b9fad110eba9fcdd5e. The test project has no remaining containers, networks or volumes. This proves the current local lifecycle, not resolution of the historical daemon incident or hosted approval. The local uv lock differs in four transitive versions, so no uv-lock result is substituted for Actions-pin evidence. No dependency source/pin changed.

A bounded independent read-only review found no actionable issue in signal windows, cleanup recursion, ownership or test validity; it is not a qualifying GitHub approval. Root cause, RED/GREEN, alternatives and evidence limits.

Correction to historical teardown attribution: the two prior full runners ended137 during Docker removal. This is consistent with the20-second watchdog, but the saved logs do not establish signal attribution, precise elapsed timing or the cause of daemon latency. They remain failed full-lifecycle receipts; this independently reproduced signal gap is not their established cause. The later18-case clean diagnostic does not replace them. The current local lifecycle and merge-ref security receipts below do not waive hosted checks, qualifying independent review or prerequisite-first protected landing; any new head must be revalidated.

Merge ref 0977ebfec612d5fb6aa98caa9af22ea18e0485d9 matches current base/head/tree. Refreshed Trivy HIGH/CRITICAL fixable vulnerability/misconfiguration scan including dev dependencies: 0 findings, SHA-256 1deff476ca58270ace7085a7b31f575550b35590c0b8f0918d88c7d6fb239bc4. Separate all-severity secret scan: 0, SHA-256 935d3a1c1146a920424eb27f7810148c3db3e1c6fdf31b10607f4af802238bf3.

Fresh current-head check-runs show six queued checks and three skipped deployment/publication jobs. Neither pending evidence nor skipped jobs establishes qualifying review/protected approval. Remain Draft on the unmerged prerequisite; no protected dependency advisory is resolved by a local scan.

Historical receipts through b2e9, with the attribution limitation above

Current authority

Head b2e98a52588db72e501c0843b33816e2e5bc698b, tree b9796d6306ffc852091592dc0f3b32cb1c12fd6d, ordinary child of 30d8476b5fa1d4379684acaf2f334414597e97c4. Remains Draft on #1531 at 550798ccafebea4b1a9a65018e63b9661ff25a53; complete migration, dependency and isolation histories retained.

New regression repair

Consumer #1417's immutable 56025b17 run failed before SIGTERM at the five-second startup observation. Retained trace reached the controlled stage later; an isolated passing rerun was not accepted as a fix. A six-second delayed-start case reproduced RED. Only pre-signal setup observation becomes 30 seconds; post-signal completion remains five seconds, with exit 143, exactly one completed cleanup, redaction and owned-process reaping assertions intact. The production runner shell, cleanup bounds and model/application settings are unchanged.

All 18 runner/signal probes and full backend Ruff pass. A combined command initially omitted the required conftest and failed three application-settings imports; the documented isolated command was corrected without changing runtime defaults or reading operator files. A bounded read-only reviewer found no actionable issue; not a GitHub approval.

The exact committed-head suite completed 1875 passed / 2 explicit live-only skips in 837.94 s, but the runner exited 137 when scoped Docker teardown exceeded its unchanged 20-second cleanup bound. This is not a successful full lifecycle / GREEN receipt. Sanitized JUnit SHA-256: b45206540f4683773f8d8dd7aeee66a3cf1b69c198ee2c99043be0e68aca444d. After verifying task ownership and zero connected containers, the sole remaining generated test network was removed by exact ID. Both generated projects now have no containers/networks/volumes. This recovery does not convert the original failed run into a pass. Investigate daemon teardown timing and rerun the full lifecycle; no cleanup-bound change is justified by pytest success alone.

A subsequent bounded diagnostic, not a full-suite replacement, ran the unchanged owner via bash scripts/ci/run_backend_postgres.sh tests/test_ci_postgres_runner.py tests/test_ci_postgres_signals.py: fresh/repeated migrations, 18 passed / 0 skipped / 47.63 s, complete task cleanup and exit 0. JUnit SHA-256 e3d0ebafe18391ba49f9159505f0c1672e4c67d7c530ddcf126534962b0ead17. This shows teardown can complete without changing the bound; it does not prove the cause of the earlier slow operation or repair those failed complete runs. All resources of this diagnostic's generated project are absent.

Merge ref 6c04a43d3d491fb96e0f9762bbbc53fd2e9d8dec has the current base/head and identical tree. Refreshed local Trivy HIGH/CRITICAL fixable vulnerability/misconfiguration scan including dev dependencies: zero findings, SHA-256 b7e1c25d089dbe6c420735c956b4125bc0ac0b2602bcc083d9656e1d45bd2c48. Separate secret scan: zero findings, SHA-256 4df65a1cad7a7d090db9d07af08d917393a4d71f792857ae0572543483036363.

Failed receipt, root cause, deterministic regression and APA reference. #1417 inherits this complete child normally. Current-head hosted checks/review and protected prerequisite integration remain required; no Linux, release, deployment, delivery or p95 claim.

Historical receipts through 30d8, not current-head evidence

Current-head nested migration isolation repair

Head 30d8476b5fa1d4379684acaf2f334414597e97c4, tree 89a68659baeab385f195ec37c11ffe6b73b52d3f, ordinary child of 4d2e4abc2c369d5e85bced4027b6f81857721ea2. Existing base #1531 550798ccafebea4b1a9a65018e63b9661ff25a53 retained; Draft/unmerged. Four-file follow-up only: three migration subprocess launch sites explicitly retain /dev/null bootstrap isolation, three boundary regressions verify dispatch without reading operator files, and doctoring explains root cause. No runtime default or dependency changes, deletions or source copies.

All three dispatch tests failed with the absent selector before the fix and passed afterward. On the committed current head, bash scripts/ci/run_backend_postgres.sh completed fresh/repeated migration, 1874 passed, 2 LIVE_BASE_URL-only skips, zero failures/errors in 54.82s, and cleaned its test-only DB/network. JUnit SHA-256 ced9e9330ccc670588f59e52b4bdf8cf288ca6e8368cc414875b918c9b3d7f7b. Earlier full-candidate run: 1874/2 in 43.62s. Focused Ruff passes.

Exact GitHub merge ref 72206670444ee0e775de7c72631bdb0592aedcc4 has the stated base/head and identical tree. Refreshed Trivy HIGH/CRITICAL fixable vulnerability/misconfiguration scan including dev dependencies: zero findings, SHA-256 c5b36a42a166e67bcc3412273c51dc8b4d0da4f5cbb08127f02c29576aa6e99f. Separate all-severity secret scan: zero findings, SHA-256 5bb072257e36173e3b9472efa74bdfa4c400beacb661a0b09d10355a8cbfd3d2. Current-head backend/frontend/image/security checks are queued. No protected merge, Linux Actions pass, coverage percentage, provider/browser execution or deployment is claimed. Consumer #1417 inherits the complete follow-up normally.

Current doctoring and Python subprocess authority.

Historical prerequisite receipts below

Earlier-head CI installation and isolation repair

Head 4d2e4abc2c369d5e85bced4027b6f81857721ea2, tree 4a140dca9ffb2f5a182794a7e40375f0e7df5edd. Normal history preserves existing owner bc91b36dec70c14e0cde526e2330638f5e0ce352, full migration prerequisite #1503 19d5860bc27e860acba940390f5792721cd99e5e (including #1565), and complete dependency owner #1571 through 3f568412da61f12ba36c71765bf915acc8abb85d. Direct #1531 base remains 550798ccafebea4b1a9a65018e63b9661ff25a53. No predecessor delta, central required workflow, protection or model routing was removed/bypassed.

Baseline: old CI's five search PostgreSQL tests exited zero with all skipped; a real fresh database then failed in revision 0001 with missing emails. The existing migration prerequisite repairs installation. The shared runner provisions a hardened digest-pinned task-only DB, runs fresh/repeated Alembic, executes the complete suite and cleans only its generated project. Bootstrap selection and explicit child environment prevent implicit operator-file/provider/replica inheritance. Actual pytest reports reject collection skips and PostgreSQL skip/xfail false-green outcomes. Cancellation uses task-owned process groups, report redaction precedes bounded cleanup, and failure/cancel statuses are retained.

Exact committed-head execution: bash scripts/ci/run_backend_postgres.sh1871 passed, 2 skipped, zero failures/errors, 29.29s. Both skips are explicitly unconfigured live API cases requiring LIVE_BASE_URL; all ten PostgreSQL cases ran. Fresh/repeated migration and task DB/network removal completed. JUnit SHA-256: e2f2ac6593cd0a088b23624d4c4506a5a479346c7eb99becbffd8215d7001b40. Environment installed both hash-locked core and optional Noema requirements in backend/.venv. The same head passes frozen pnpm 11.5.3 installation, all 52 frontend files / 439 tests (4.28s), lint, typecheck and whitespace checks. Earlier focused Ruff/actionlint/shellcheck and independent two-test no-network signal receipts cover unchanged CI source, not new GitHub approval or DB proof.

Dependency RED → owner fix → local GREEN: preceding head ef858172152615d54b393ea3ca5748ab2c4e03db passed 1871/2 backend tests but its GitHub merge ref a7d2d409d146a134817df4c258ece4ab8e171508 failed the refreshed Trivy HIGH/CRITICAL fixable scan for js-yaml 4.3.0 / GHSA-5p4m-2wfm-xmqj. Complete #1571 was merged normally. Its original test passed Vitest but failed tsc with TS1503; the existing owner repaired the named capture to an equivalent numbered capture while retaining ES2017 and lock blob 018f0382c815ea7a35899e64ddb6c3645399fcb6. This owner child was then normally merged and all combined-head tests repeated.

GitHub merge ref 0ca44cfa8302e6b0228de24a4eed284f1d0c4a99 has the exact base/head parents above and identical tree 4a140dca9ffb2f5a182794a7e40375f0e7df5edd. Its tracked archive passes Trivy vulnerability/misconfiguration scanning with development dependencies included, HIGH/CRITICAL, fixable-only, exit 0 and zero findings. Report SHA-256 cc64d4f5ad685b52e98079a243915a636e35ea04fc51a38220ab079897354933. Separate all-severity secret scan exits 0 with zero findings; report SHA-256 c148c3f0eb704a9c50b7a74fa24fb19d8f467cb26b09d57853c98546ac533c8e. No rule suppression; these local scans do not close protected-branch alerts or establish hosted security approval.

Decision, RED/GREEN, rejected alternatives, evidence limits and APA references.

Open Draft, unmerged. Fresh PR-triggered first-page workflow lookup shows Application CI 34024085621 and image validation 34024085830 queued, Bandit 34024085668 pending; it is not an exhaustive required-check inventory. Re-fetch unchanged head/base and obtain terminal hosted checks, qualifying independent review and prerequisite-first protected integration. No live provider/browser/deployment, representative performance or full-coverage claim. Earlier receipts below are historical.


Problem

Stacked Naruon PRs target non-default parent branches, but repository-owned Application CI, Bandit, and image validation historically filtered pull requests by selected base names. Their concurrency and frontend package-manager bootstrap also made current-head evidence fragile.

Owner stack

Child delta

  • run Application CI, Bandit, and pull-request image validation on every PR base;
  • scope validation concurrency by workflow/repository/PR while preserving safe publication boundaries;
  • keep tag image publication non-cancellable;
  • install repository-pinned pnpm@11.5.3 before setup-node asks for cache metadata;
  • document the same stacked-base/publication contract in merge-gate policy;
  • preserve fix(actions): cancel superseded Bandit PR scans #1554's valid Bandit invariant: only a first-attempt PR run may cancel an older first-attempt run; push, manual dispatch, and reruns retain unique run identity.

Predecessor delta adoption

#1554 advanced after this successor was created. RED 948f1e36412b9918ecf0736a1142220adca1792a ports its focused concurrency contract; bc91b36... composes first-attempt-only cancellation with #1562's workflow/repository/base-or-PR identity prefix. #1554 remains provenance-only until this successor actually merges and a fresh post-merge equivalence audit proves complete succession. No predecessor check/review evidence transfers.

Current exact-head evidence

On unchanged bc91b36..., the three repository-owned workflows this PR repairs are now terminal GREEN on the non-default #1531 base:

  • Application CI 33927276046 — success;
  • Bandit Security Scan 33927276079 — success;
  • Build and Publish Docker Images 33927276320 — success.

This proves the branch-filter/concurrency repair can activate and complete those repository-owned lanes on a stacked base. It does not substitute for organization-required review/security/coverage/image contexts or independent approval, and #1531 is still the unresolved parent prerequisite.

Fresh downstream counterexamples remain useful acceptance targets. After non-force ownership restacks, #1300 0833fcbd... on #1565, #1301 ae299ce... on #1300, and #1302 8739454f... on #1301 each currently have zero repository-owned PR workflow runs. #1560/#1569 remain previously recorded stacked-base reproduction targets. Once this prerequisite is protected-integrated, each then-current unchanged parent/head pair must regenerate its own hosted evidence; no predecessor run may transfer.

The zero-run downstream observations are not a reason to duplicate #1562 into those product branches or manufacture no-op commits. Repair remains owned here.

Merge boundary

Keep Draft until #1531 lands first and one unchanged exact head has the complete then-live required check set terminal-success, zero valid unresolved findings, and qualifying current-head independent review evidence. After protected integration, rerun the downstream acceptance targets against their then-current heads. No force push, destructive rebase, dummy/no-op requeue commit, self-approval, dismissal, admin bypass, or gate weakening.

claude and others added 15 commits September 1, 2026 01:17
…space rows

Workspace/Document (workspace_entities/workspace_documents) have been
declared in db/models.py since June, but no Alembic migration ever created
them explicitly, and no production write path ever inserted a Workspace
row. A database that incrementally migrated forward before these models
existed never gets the tables (0001's Base.metadata.create_all only
reflects today's model metadata, not a historical snapshot), and even
where the tables exist, /api/data/documents' Document inserts always
violated the workspace_id foreign key since nothing ever created the
referenced Workspace row for a real signed session.

- Add 0018_workspace_registry.py: idempotent (has_table-guarded) creation
  of both tables, matching the current model shape and this repo's
  structured-migration convention.
- Add services/workspace_scope.get_or_create_workspace and wire it into
  both Document-creating endpoints in api/data.py, keyed by the signed
  session's real workspace claim (workspace-<organization_id>, confirmed
  against every other call site that derives it) rather than the model's
  own opaque uuid default.
- Fix two unrelated, independently-discovered bugs blocking the documented
  Alembic path (scripts/migrate_db.py) from ever completing on a genuinely
  fresh database: schema_backfill_sql() and 0011_email_read_state.py both
  still targeted the "emails" table, renamed to "email_records" by
  0011_email_model_reconciliation long ago.
- Add test_workspace_document_migration.py: runs the real
  scripts/migrate_db.py against a disposable Postgres database (never
  create_all) and proves /api/data/documents serves cleanly both from an
  empty database and from one that had already migrated past the point
  where the registry tables would otherwise be missing.

Verified locally against a real PostgreSQL 16 instance: the full Alembic
chain now runs 0001->head cleanly from empty, and the full backend test
suite passes (1836 passed; the 2 remaining failures are a pre-existing,
unrelated is_read NOT NULL smoke-test bug, confirmed present on
unmodified develop before this change).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016ErwZSYW3pm585NiM3Q7aN
CodeRabbit flagged that test_data_quality_surface_includes_workspace_document_assets
didn't actually exercise the new Document.organization_id predicate added in
37bcd6e: doc_owned had no organization_id set, and MockAsyncSession treats a
None organization_id as matching any query filter (intentionally, to keep
older organization_id-less fixtures usable), so the test passed regardless of
whether the real query filtered by organization at all.

Give doc_owned its real organization_id and add doc_other_org: same
workspace_id, different organization_id. Verified this reproduces the gap
(reverting the organization_id predicate in get_data_quality_surface turns
this red with object_count 2 instead of 1) and passes with the fix restored.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016ErwZSYW3pm585NiM3Q7aN
Devin Review correctly identified two real gaps in 0018_workspace_registry:

1. A database that truly never had workspace_entities/workspace_documents
   (one whose own 0001_initial_control_plane ran before these models
   existed, and has only applied incremental migrations since) crashes on
   0016_document_org_scope with NoSuchTableError, because 0016 calls
   inspector.get_columns() unconditionally and it sits before 0018 in the
   chain. My existing regression test only dropped the tables after 0017,
   which never exercised this because 0001 always recreates them via live
   create_all for a genuinely fresh test database -- masking the real bug.
   Reproduced directly (migrate to 0015, drop the tables, continue to head)
   and confirmed the crash; 0016 is now has_table-guarded like the rest of
   this repo's idempotent migrations, and the regression test's pre-registry
   boundary moved from 0017 to 0015 so it actually crosses 0016 with the
   tables absent.

2. 0018's downgrade unconditionally dropped both tables, including when its
   own upgrade was a no-op because they already existed -- so a rollback on
   any database would destroy workspace_documents.document_content (real
   uploaded content, not rebuildable derived state like most other tables
   this repo's migrations manage). Made downgrade a documented no-op,
   matching the same judgment call 0001_initial_control_plane already makes
   for the same reason.

Verified: the reproduction above now completes cleanly end-to-end through
/api/data/documents; full backend suite still 1837 passed (same 2
pre-existing unrelated is_read failures), ruff clean.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016ErwZSYW3pm585NiM3Q7aN
test_legacy_document_scope_postgres.py (e05f1b3) reproduced Devin Review's
Finding 4 against real PostgreSQL: 0016_document_org_scope left existing
workspace_documents.organization_id unbackfilled (NULL), and the strict
Document.organization_id == auth_context.organization_id predicate added in
37bcd6e made such a row invisible even to the organization that actually
owns its workspace.

Add _document_organization_filter: an exact organization_id match, OR a NULL
organization_id, but only when the requesting session's own
workspace_id/organization_id pairing is the canonical
workspace-<organization_id> (or workspace-<user_id>) derivation -- not an
internally inconsistent claim. This is what keeps the cross-tenant boundary
37bcd6e added intact: a session whose workspace_id doesn't match what its
own organization_id would derive gets the strict, no-NULL-fallback check,
so a forged or malformed claim pairing still can't read a same-workspace
document under a different organization.

Wired into both _get_workspace_document and get_data_quality_surface's
Document query.

Verified: test_legacy_document_scope_postgres.py now passes (was failing on
this branch's previous commit). Full backend suite: 1838 passed (same 2
pre-existing unrelated is_read failures noted earlier in this PR), ruff
clean.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016ErwZSYW3pm585NiM3Q7aN
PR #1502 (opened in parallel, ~5 minutes before this one) independently
diagnosed the same underlying gap from the same root cause -- running the
real backend suite against actual PostgreSQL for the first time -- and found
something this PR didn't: app-ci.yml's backend job has never had a Postgres
services: container, so every @pytest.mark.postgres test (including all the
new ones in this PR) has always silently skipped in real CI. It ships the
authoritative fix for that, plus its own version of the 0011_email_read_state
/bootstrap_db.py fix, with a pinned contract test.

Landing two different versions of the same files from two open PRs would
conflict. Adopt #1502's exact pattern for the overlapping files instead of
this PR's earlier approach:

- 0011_email_read_state.py: has_table("emails")-guarded no-op, keeping the
  original "emails" target, rather than retargeting to "email_records".
  (0011_email_model_reconciliation's own docstring clarifies no migration
  ever renamed "emails" to "email_records" for a real managed database --
  email_records was the actual table name since inception; "emails" was
  only ever a stale copy-pasted string. Both approaches are safe in
  practice, so there's no reason to diverge from the already-tested pattern.)
- bootstrap_db.py / 0001_initial_control_plane.py: schema_backfill_sql()'s
  callers now go through execute_schema_backfill(), which skips the legacy
  ix_emails_owner_date statement via identity-matching a LEGACY_EMAILS_INDEX
  sentinel rather than this PR's simpler unconditional deletion.
- test_alembic_migrations.py: contract test now asserts
  execute_schema_backfill, plus #1502's own
  test_email_read_state_legacy_table_guard_is_reversible pinning the
  reconciled 0011 file's shape.
- test_bootstrap_db.py / test_data_api.py: the 4 raw SQL
  `INSERT INTO email_records` smoke-seeding call sites now set is_read
  explicitly (Python-side ORM default only, no DB server default, so real
  Postgres rejects the omission) -- the exact bug flagged as a follow-up
  earlier in this PR's own investigation.

Re-verified end-to-end: fresh-database migration to head, and the true
historical-database reproduction (migrate to 0015, drop the workspace
registry tables, continue to head crossing both 0011_email_read_state and
0016_document_org_scope) both still complete cleanly. Full backend suite:
1841 passed, 0 failed, 3 skipped (up from 1838/2 failed -- the last 2
pre-existing failures are now fixed too), ruff clean.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016ErwZSYW3pm585NiM3Q7aN
Devin Review found two real gaps in the has_table("emails")-only pattern
adopted from PR #1502 in the previous commit:

1. A genuinely historical database -- one whose own 0001 ran before
   is_read was added to the Email model, so it has email_records without
   is_read -- silently never gets the column: has_table("emails") is False
   (per 0011_email_model_reconciliation's docstring, no managed database
   ever really had a table literally named "emails"), so upgrade() returned
   without touching email_records at all. Reproduced directly: migrated to
   0009, dropped email_records.is_read to simulate that historical state,
   continued to head with the has_table("emails")-only version -- it
   completed with no error, but is_read was permanently missing. Confirmed
   the same reproduction now correctly adds is_read to email_records.

2. Not idempotent: a legacy "emails" table that already has is_read (e.g.
   from a partial/earlier application) made upgrade() crash with a
   duplicate-column error, since it only checked has_table before calling
   op.add_column. Reproduced directly (manually created an "emails" table
   with is_read already present, migrated to head) and confirmed it no
   longer crashes.

Now checks both "email_records" (the table that actually matters) and
"emails" (defensive, in case a real one somehow exists), guarded by column
existence via the same _has_column helper this repo's other migrations
already use, so upgrade/downgrade are safely idempotent either way.

This diverges from PR #1502's exact pinned file shape (its
test_email_read_state_legacy_table_guard_is_reversible asserted the
has_table-only version byte-for-byte), so updated this PR's own contract
test to check for the corrected shape instead of matching that exact text.
Worth flagging on #1502 too, since the same gaps apply to its own version
of this file if it hasn't already been fixed there.

Full backend suite: 1841 passed, 0 failed, 3 skipped, ruff clean.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016ErwZSYW3pm585NiM3Q7aN
Devin Review found the same ownership-ambiguity problem in this migration's
downgrade that 0018_workspace_registry's downgrade already had (fixed
earlier in this PR): a fresh database's email_records.is_read comes from
0001's live Base.metadata.create_all, not from 0011_email_read_state, so
there is no way for downgrade() to tell "this revision added the column"
apart from "the baseline already had it" -- and is_read holds real
per-message read/unread state, not rebuildable derived data.

Reproduced directly against real PostgreSQL: migrated a fresh database to
head, then ran alembic downgrade to 0009 -- the previous op.drop_column
version silently deleted email_records.is_read and its data. Made downgrade
a documented no-op instead, matching the same judgment call already applied
to 0001_initial_control_plane and 0018_workspace_registry.

Also added backend/tests/test_email_read_state_migration_postgres.py:
permanent real-Postgres coverage for all three scenarios this migration
must handle (historical email_records missing is_read gets it added;
idempotent against a legacy "emails" table that already has it; downgrade
does not destroy a fresh database's read state), addressing Devin's
separate note that the existing contract test's string-matching assertions
can't detect a destructive downgrade or prove idempotence. Confirmed the
downgrade test fails red against the reverted (destructive) version before
restoring the fix.

Full backend suite: 1844 passed, 0 failed, 3 skipped, ruff clean.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016ErwZSYW3pm585NiM3Q7aN
Devin Review's most structurally important finding yet: editing
0011_email_read_state.py cannot repair a database whose alembic_version
history already records "0011_email_read_state" as applied -- Alembic never
re-runs an already-stamped revision's upgrade(), regardless of how the file
content changes afterward. Every fix to that file earlier in this PR only
helps a database that hasn't reached 0011 yet.

Reproduced directly: migrated a fresh database to head, dropped
email_records.is_read to simulate a database that's already past 0011 (via
any path -- an earlier broken version of that revision, a partial apply,
manual intervention) but never actually got the column, then re-ran
`alembic upgrade head` -- nothing happened, since the database was already
at head with no revision left to apply. is_read stayed permanently missing.

Added 0019_email_read_state_repair.py: a new forward migration appended
after the current head (0018_workspace_registry), matching the same
pattern already used for the workspace registry gap itself -- idempotently
(has_table/has_column guarded) ensures email_records.is_read exists,
regardless of what 0011 already did or didn't do for a given database.
Downgrade is a no-op for the same ownership-ambiguity reasons as
0011_email_read_state's and 0018_workspace_registry's.

Confirmed the same reproduction now correctly repairs the column. Added a
permanent regression test for exactly this scenario (stamp through 0018,
drop is_read, upgrade to head) and confirmed it fails red without 0019.

Full backend suite: 1845 passed, 0 failed, 3 skipped, ruff clean.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016ErwZSYW3pm585NiM3Q7aN
github-code-quality flagged the bare except/pass in the disposable test
database's cleanup as unexplained; it's deliberate (a transient
connectivity error tearing down the scratch database must not mask the
test's actual assertions), so document it inline.
Run application, Bandit, and image validation for every pull-request base while keeping push and release triggers unchanged. Scope concurrency by workflow, repository, and pull request; release publication remains non-cancellable.

Assisted-by: OpenAI Codex
Signed-off-by: Seongho Bae <me@seonghobae.me>
@coderabbitai

coderabbitai Bot commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

Next included review available in 53 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used the included review currently available.

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Learn how review limits work.

Review configuration:

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Advanced

Run ID: 5ccd12d0-eb43-4851-9d86-ea90709fa8d7

📥 Commits

Reviewing files that changed from the base of the PR and between bd0135d and cdef603.

⛔ Files ignored due to path filters (1)
  • backend/uv.lock is excluded by !**/*.lock
📒 Files selected for processing (40)
  • .github/workflows/app-ci.yml
  • .github/workflows/bandit.yml
  • .github/workflows/docker-publish.yml
  • ARCHITECTURE.md
  • CHANGELOG.md
  • CLAUDE.md
  • backend/alembic/versions/0001_initial_control_plane.py
  • backend/alembic/versions/0011_email_read_state.py
  • backend/alembic/versions/0016_document_org_scope.py
  • backend/alembic/versions/0018_workspace_registry.py
  • backend/alembic/versions/0019_email_read_state_repair.py
  • backend/api/data.py
  • backend/ci_postgres_gate.py
  • backend/core/env_paths.py
  • backend/pyproject.toml
  • backend/pytest.ini
  • backend/requirements-hashes.txt
  • backend/requirements.txt
  • backend/scripts/bootstrap_db.py
  • backend/scripts/start_backend.py
  • backend/services/workspace_scope.py
  • backend/tests/test_alembic_migrations.py
  • backend/tests/test_bootstrap_db.py
  • backend/tests/test_ci_postgres_runner.py
  • backend/tests/test_ci_postgres_signals.py
  • backend/tests/test_config.py
  • backend/tests/test_container_dependency_pin_contract.py
  • backend/tests/test_data_api.py
  • backend/tests/test_email_read_state_migration_postgres.py
  • backend/tests/test_env_paths.py
  • backend/tests/test_legacy_document_scope_postgres.py
  • backend/tests/test_release_governance.py
  • backend/tests/test_start_backend.py
  • backend/tests/test_workflow_concurrency.py
  • backend/tests/test_workspace_document_migration.py
  • docker-compose.test.yml
  • docs/development/merge-gate-policy.md
  • docs/doctoring/application_ci_postgres.md
  • docs/doctoring/starlette-httpx2-testclient-dependency.md
  • scripts/ci/run_backend_postgres.sh

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.

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 4, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-09-05T05:31:45.785650Z bc91b36 Draft marked ready
ℹ️ About Codex in GitHub

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

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

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

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

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: b4cb934031

ℹ️ About Codex in GitHub

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

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

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

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

Comment thread backend/tests/test_release_governance.py
Align the authoritative merge-gate policy with all-base pull-request validation and lock the contract with a focused governance regression.

Assisted-by: OpenAI Codex
Signed-off-by: Seongho Bae <me@seonghobae.me>
Install the repository-pinned pnpm release before setup-node queries its cache path, avoiding an unpinned pnpm/latest lookup from the repository root.

Assisted-by: OpenAI Codex
Signed-off-by: Seongho Bae <me@seonghobae.me>
Preserve PR 1531 governance fallback and stacked-base contracts while keeping this child focused on shared validation concurrency and the pinned pnpm bootstrap.

Assisted-by: OpenAI Codex

Signed-off-by: Seongho Bae <me@seonghobae.me>
@seonghobae
seonghobae changed the base branch from develop to chore/stacked-pr-governance-gate-support September 4, 2026 15:14
seonghobae and others added 2 commits September 6, 2026 19:35
Reproduce the launch-to-PID signal window with a real SIGTERM and preserve owned-group cleanup. Keep historical Docker cleanup failures separate from this causal repair.

Co-Authored-By: Codex <noreply@openai.com>
Signed-off-by: Seongho Bae <me@seonghobae.me>
Signed-off-by: Seongho Bae <me@seonghobae.me>

Co-Authored-By: Codex <noreply@openai.com>
Integrate parent fac3437 without deleting CI isolation or migration prerequisite delta. Parent repair has RED/GREEN evidence in PR1531 review3939597997; this child requires its own verification. No force rewrite or closure.
@seonghobae

Copy link
Copy Markdown
Contributor Author

Full PostgreSQL revalidation at 0648eac is FAILED/INCOMPLETE, not GREEN. The 113-package Actions hash dry-run reported no changes and compatibility passed; fresh/repeat migrations completed. After the execution handle disappeared, process inspection confirmed the runner/pytest were no longer live. Retained JUnit naruon-postgres.PKQQuCYu/pytest.xml has tests=649, skipped=2, errors=1, failures=0; the error is pytest terminal flush BrokenPipeError: [Errno 32] Broken pipe. Output-consumer disconnection is observed; its initiating cause and final runner exit code are not established. The log stops near 30 percent, so this does not validate the remaining suite. Both task containers and volumes were absent; only their verified empty task-labelled networks were removed by exact ID. No global prune, daemon restart, warning suppression, source change or merge. Preserve this failed receipt before any unchanged-head retry with durable output handling.

@seonghobae

Copy link
Copy Markdown
Contributor Author

Same-head full PostgreSQL retry completed at 0648eac. Command: bash scripts/ci/run_backend_postgres.sh, with stdout/stderr redirected directly to a durable local file; final runner exit recorded separately as 0. No source or test-scope changes.

Pytest: 1877 passed, 2 skipped in 613.42s. Both skips are tests/live/test_live_api_sequence.py:107 because LIVE_BASE_URL is required. Redacted JUnit: 1879 tests, 0 errors, 0 failures, 2 skipped; SHA-256 a9c20970c9ff1f4b37524fe350b1d28430b165f9dea49cf77a69e0bf92943b3b. Its suite time is 611.269s, distinct from pytest terminal elapsed time.

Cleanup completed and separate Docker label queries confirmed zero containers (including stopped), volumes and networks for naruon-test-u8nwcw5a. Exact local head is unchanged and worktree clean. Evidence directory: naruon-postgres.u8nWCw5A.

The earlier 649-test internal BrokenPipe failure remains failed historical evidence: #1562 (comment) . The initiating pipe closure is not attributed. This retry proves local full-suite/cleanup completion only; it does not replace current-head hosted checks, review, current merge-ref security scans or protected merge.

@seonghobae

Copy link
Copy Markdown
Contributor Author

Fresh local merge-ref security evidence, not hosted gate certification:

  • Head 0648eac; base fac3437; GitHub merge commit c1a2555. git parent readback matches base/head; REST was rechecked after scanning and is unchanged.
  • Complete tracked git archive (1870 entries), extracted fully before scanning. No operator dotenv, installed worktree dependencies or live service data included.
  • Trivy 0.74.0; download-db-only completed exit 0 before scanning. Vulnerability DB UpdatedAt 2026-09-06T07:00:11.537152697Z. Repository trivy.yaml retained.
  • trivy fs --scanners vuln,misconfig --severity HIGH,CRITICAL --ignore-unfixed --include-dev-deps --exit-code 1 --format json: exit 0; 0 reported vulnerabilities and 0 misconfigurations.
  • Separate trivy fs --scanners secret --severity UNKNOWN,LOW,MEDIUM,HIGH,CRITICAL --exit-code 1 --format json: exit 0; 0 reported secrets.
  • JSON SHA-256: security 664f8a65aabae467d8152c6636a6f4668a1d598e730f203273e4266be5b4646c; secrets dab6501cadb779a415615d41c63f6c0db522078ec39b6eb0d6cb49f0fb56ac4c. No WARN/ERROR/FATAL/Timeout/Denied log matches.
    This does not prove absence of all vulnerabilities, deploy/runtime safety, hosted reviews/checks or protected merge. Prior-head scans remain historical. No scanner or protection setting was weakened.

@seonghobae

seonghobae commented Sep 6, 2026

Copy link
Copy Markdown
Contributor Author

Ordinary non-force merge: predecessor 0648eac plus complete parent b6d6c28 produces current head 938d4b1. Exactly three inherited files changed (85 additions/13 deletions), no conflicts or discarded consumer delta. Gate and full harness blobs match the parent.

Own committed-head full fake-GitHub governance harness PASS/exit0, 40 focused source contract tests passed with zero skips (0.22s), ShellCheck/diff checks passed; pushed without force after remote predecessor verification. Full PostgreSQL runner completed: 1877 passed, 2 skipped in 133.85s; both skips are live API tests requiring LIVE_BASE_URL. Separately recorded runner exit 0; redacted JUnit 1879 tests/errors0/failures0/skipped2, SHA-256 e07981305b70dd56c53ad075db063a9a1869986e0158522bcd2afbc5addaee18. Cleanup completed and independent Docker label queries confirmed zero containers (including stopped), volumes and networks for naruon-test-b4ie3qws. Local head unchanged and clean. Evidence directory naruon-postgres.b4IE3qWs. Prior-head PostgreSQL and merge-ref security receipts are historical, not certification of this head.

Keep Draft/open until prerequisite and review-admission conditions are satisfied; then obtain fresh hosted checks/review. No protection change, self-resolution, release or protected merge claim.

Fresh local merge-ref security: 11a371d, parents match live base/head before and after scanning. Complete tracked git archives, no operator environment files. Trivy0.74.0 download-db-only exit0; DB updated2026-09-06T07:00:11.537152697Z. Repository configuration retained. vuln+misconfig HIGH/CRITICAL fixable with --include-dev-deps: exit0/reported0; separate all-severity secret scan: exit0/reported0. No WARN/ERROR/FATAL/Timeout/Denied log matches. JSON SHA-256 security 7eb1090df3eab166678c39c3ad4a37b31d06a69577746127a44b02529341187c, secrets 0b8918eaac76323d08c9206d77cfdebf67e82a414f04548b841c3759c57df20e. Evidence naruon-938d-5dea-security.0r7038. Scope-limited local results are not hosted gate, review, release or runtime certification.

Preserve all owner and consumer delta through a normal merge. Parent f2e2ac0 fixes review3944130242 with eleven regressions; CI-specific workflow changes remain intact. No branch history or PR is discarded.
@seonghobae

Copy link
Copy Markdown
Contributor Author

Current-head integration receipt: 0ec1cf9 is a normal merge of prior938d4b12 and owner f2e2ac0. All delta retained; gate/harness blobs exactly match the owner repair for review3944130242. Full governance harness exit0/PASS; 40 source contracts with -W error, ShellCheck and diff checks exit0. Full disposable PostgreSQL runner exit0: 1877 passed, 2 LIVE_BASE_URL-dependent skips in89.41s. Redacted JUnit SHA25619bab813b4c67cab529771a823c670121ca07c79dce7f096f1ee1da8049b24d1. Independent Docker container(-a), volume and network label queries confirm zero task resources for naruon-test-7dk8mt1j. Clean head unchanged after tests; ordinary push succeeded. These are local integration results, not current hosted security/review, protected merge, deployed runtime or Visual Inspection proof. Older merge-ref security evidence is historical until revalidated against this head. No Ready toggle or run cancellation performed.

@seonghobae

Copy link
Copy Markdown
Contributor Author

최신 보안 검사에서 수정 대상 확인: head 0ec1cf9, merge 9d7b804. Trivy0.74 기본 HIGH/CRITICAL fixable 검사와 별도 전체 심각도 secret 검사는 exit0이었으나, 기본 결과에 requirements-strix-ci-hashes.txt가 누락됐습니다. 동일 archive에 --file-patterns "pip:requirements-.*\.txt"를 추가하자 해당 파일의 aiohttp3.14.1에서 CVE-2026-69244 HIGH(fixed3.14.3)가 탐지되어 exit1로 종료됐습니다. 확장 결과 SHA256: e9f1d573f65a68ebd7b13d778c52d61167976d5e73207797a438e1adb2e2aacb. DB는 이미 이 CVE를 인식하므로 기존 zero 결과로 취약점을 반박할 수 없습니다. 실제 보안 통과가 아닙니다. 기존 #1244가 aiohttp3.14.3 hash-lock 수정 소유자이며 중앙 manifest 탐지 설정도 별도 보완해야 합니다. 최초 DB 갱신 명령은 옵션 위치 오류로 exit1, 도움말 확인 뒤 trivy image --download-db-only로 수정해 exit0. 검사 후 head/base/merge 동일함을 확인했습니다. 보호 병합, 배포, 최신 Visual Inspection은 미완료입니다.

Preserve governance and1244/1571 dependency delta through normal merge; CI own workflow changes remain intact. No force or PR closure.
@seonghobae

Copy link
Copy Markdown
Contributor Author

선행 보안 수정 상속: cdef60348a7da793a24828ec3d2a1bd6cc680f02는0ec1cf92와1531의bd0135de를 일반 병합했습니다. 유효 delta는 보존됐고 실제 파일 변경은aiohttp hash-lock 1개입니다. full governance harness exit0/PASS,40source-Werror/ShellCheck/diff exit0. 실제PostgreSQL1877pass/2LIVE_BASE_URLskip137.75s runnerexit0; 해당task containers(-a),volumes,networks0을 확인했습니다. JUnit SHA256095282ca7eed960bba0a459dd978fc71ca2f43f32f6ceb4ae07d6321e9a09ccc. trackedarchive expanded MEDIUM/HIGH/CRITICAL fixable Trivy(vuln,secret,misconfig) exit0,11language manifests/0findings, JSON0e50e2bef5119a1cd13f1f1e4f190080cce3a7e16a3f8350b7a9182d5642e581. 일반push완료. 현행hostedgate/merge-ref/보호병합/최신VI 증거와 구분하며 중앙탐지수정1969는 아직미병합입니다.

Copy link
Copy Markdown
Contributor Author

Current authority correction — 2026-09-11

The PR body’s opening Exact head: 0648eace... is stale. Live PR/ref authority is base #1531@bd0135de778a01a51deae536084447ce2481280c (chore/stacked-pr-governance-gate-support) → exact head cdef60348a7da793a24828ec3d2a1bd6cc680f02 (codex/stacked-pr-workflow-triggers). Exact cdef603... currently has repository-owned Application CI 34039683807, Bandit 34039683860, and Docker 34039683978 terminal-success. Current review inventory has no qualifying review on this head; older Codex comments stop at predecessor commits.

The live head is an ordinary merge child preserving the governance lane while inheriting the dependency-security prerequisite stack. Keep Draft: #1531/#1244 are still unintegrated and required owner/control-plane evidence remains separate. No predecessor review transfer or merge claim.

@coderabbitai review

Please review exact base bd0135de778a01a51deae536084447ce2481280c → head cdef60348a7da793a24828ec3d2a1bd6cc680f02; verify current effective delta and do not inherit predecessor conclusions without checking this exact head.

@coderabbitai

coderabbitai Bot commented Sep 10, 2026

Copy link
Copy Markdown
Contributor
🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

repo="ContextualWisdomLab/naruon"
base="bd0135de778a01a51deae536084447ce2481280c"
head="cdef60348a7da793a24828ec3d2a1bd6cc680f02"

echo "== Live PR metadata =="
gh pr view 1562 --repo "$repo" --json number,state,isDraft,baseRefName,baseRefOid,headRefName,headRefOid,mergeable,reviewDecision

echo
echo "== Exact commit metadata and ancestry =="
git show -s --format='commit=%H%nparents=%P%nsubject=%s' "$base"
git show -s --format='commit=%H%nparents=%P%nsubject=%s' "$head"
git merge-base --is-ancestor "$base" "$head"
echo "base_is_ancestor_of_head=$?"

echo
echo "== Effective delta statistics =="
git diff --stat "$base" "$head"
git diff --numstat "$base" "$head"
git diff --name-status "$base" "$head"

echo
echo "== Effective delta =="
git diff --find-renames --find-copies --no-ext-diff "$base" "$head"

echo
echo "== Check runs for exact head =="
gh api "repos/$repo/commits/$head/check-runs?per_page=100" \
  --jq '.check_runs[] | [.name, .status, .conclusion, .details_url] | `@tsv`'

echo
echo "== Reviews associated with PR `#1562` =="
gh pr view 1562 --repo "$repo" --json reviews \
  --jq '.reviews[] | [.author.login, .state, .commit.oid, .submittedAt] | `@tsv`'

Length of output: 148598


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== Relevant source maps =="
ast-grep outline backend/core/env_paths.py --view expanded
ast-grep outline backend/scripts/migrate_db.py --view expanded
ast-grep outline backend/tests/test_legacy_document_scope_postgres.py --view expanded
ast-grep outline backend/tests/test_workspace_document_migration.py --view expanded
ast-grep outline backend/tests/test_email_read_state_migration_postgres.py --view expanded
ast-grep outline .github/workflows/app-ci.yml --view expanded || true

echo
echo "== Subprocess calls that supply a replacement environment =="
rg -n -U --type py \
  'subprocess\.run\(\n(?:.|\n)*?\benv=\{' \
  backend/tests

echo
echo "== Explicit bootstrap selector use in affected paths =="
rg -n -C 4 'NARUON_ENV_FILE|operator_env_file_paths|load_dotenv|dotenv' \
  backend/core backend/scripts backend/tests/test_legacy_document_scope_postgres.py \
  backend/tests/test_workspace_document_migration.py \
  backend/tests/test_email_read_state_migration_postgres.py

echo
echo "== Migration entrypoint and environment settings sections =="
sed -n '1,240p' backend/scripts/migrate_db.py
sed -n '1,180p' backend/core/env_paths.py
sed -n '1,220p' backend/core/config.py
sed -n '1,115p' backend/tests/test_legacy_document_scope_postgres.py

echo
echo "== Workflow trigger and backend job =="
sed -n '1,155p' .github/workflows/app-ci.yml
sed -n '1,100p' .github/workflows/bandit.yml
sed -n '1,105p' .github/workflows/docker-publish.yml

Length of output: 38390


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== Alembic environment map and settings imports =="
ast-grep outline backend/alembic/env.py --view expanded
rg -n -C 5 'core\.config|from .*config import|settings|DATABASE_URL' \
  backend/alembic/env.py backend/alembic backend/db

echo
echo "== Numbered source context for the subprocess boundary =="
nl -ba backend/tests/test_legacy_document_scope_postgres.py | sed -n '30,58p'
nl -ba backend/tests/test_workspace_document_migration.py | sed -n '52,73p'
nl -ba backend/tests/test_email_read_state_migration_postgres.py | sed -n '27,72p'
nl -ba backend/core/env_paths.py | sed -n '34,43p'
nl -ba backend/core/config.py | sed -n '160,174p'

echo
echo "== Configuration imports reachable from Alembic migration setup =="
sed -n '1,220p' backend/alembic/env.py

Length of output: 15952


@seonghobae I verified the live review range: bd0135de778a01a51deae536084447ce2481280ccdef60348a7da793a24828ec3d2a1bd6cc680f02.

The base is an ancestor of the head. The effective delta contains 41 files. The reported repository-owned checks are terminal-success on this exact head. No qualifying review targets this head.

I found one issue in the exact effective delta.

  • P1 — Preserve the explicit dotenv exclusion in the legacy migration child. backend/tests/test_legacy_document_scope_postgres.py Line 41 supplies a replacement env mapping to subprocess.run() but omits NARUON_ENV_FILE=/dev/null. Python replaces, rather than extends, the parent environment. Alembic imports core.config.settings through backend/alembic/env.py, and Settings then falls back to ~/.env, ../.env, and .env when the selector is absent. The comparable migration helpers explicitly restore the selector. Add "NARUON_ENV_FILE": "/dev/null" to this child environment and extend the boundary regression to cover this helper. This keeps the PostgreSQL test isolated from operator dotenv files.

🐇🔎


✏️ Learnings added
Learnt from: seonghobae
URL: https://github.com/ContextualWisdomLab/naruon/pull/1562

Timestamp: 2026-09-10T17:48:17.702Z
Learning: For Naruon PR `#1562`, each review must verify the live base/head and evaluate the exact effective delta. Predecessor checks, reviews, and conclusions do not transfer to a later merged head.

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.

⚠️ Action not completed

Review rate limited.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

maintenance priority: medium Normal-priority or P2 work status: draft Draft pull request type: maintenance Maintenance, build, dependency, or operational upkeep

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants