Skip to content

fix: create workspace_entities/workspace_documents registry and provision Workspace rows - #1503

Draft
seonghobae wants to merge 15 commits into
codex/starlette-testclient-dependencyfrom
fix/workspace-document-registry-migration
Draft

fix: create workspace_entities/workspace_documents registry and provision Workspace rows#1503
seonghobae wants to merge 15 commits into
codex/starlette-testclient-dependencyfrom
fix/workspace-document-registry-migration

Conversation

@seonghobae

@seonghobae seonghobae commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

Root cause and owner boundary

Naruon's persisted Data workspace models declared workspace_entities and workspace_documents, but incremental Alembic history never created them for databases that had already applied the original bootstrap migration. Document creation also had no production path that provisioned the signed session's workspace row, so its foreign key could fail even where the tables existed.

This PR adds an idempotent structured Alembic migration, race-safe PostgreSQL workspace provisioning, and calls that owner service from both document-creating endpoints. It also repairs historical emails references so the real migration chain targets email_records.

The signed server-derived workspace_id remains the authority. No client-supplied workspace identity, cross-workspace lookup, raw SQL DDL, or environment-secret path is added.

Exact current verification — 2026-09-05

  • Head: 19d5860bc27e860acba940390f5792721cd99e5e; verified tree 793aadd78a7ad2d6033ca6e12a931dfa776b7374.
  • Direct base: fix(test): install Starlette TestClient dependency #1565 codex/starlette-testclient-dependency@52dfc863d1a5d6e4e80b6366f719dd09f2aa6172.
  • Normal merge parents: previous owner 9c1851336fa04bcdc77c1c6e531afdb882583af1 and that exact fix(test): install Starlette TestClient dependency #1565 head. Both ancestors and complete deltas are preserved; no force push.
  • Exact uv sync --locked in the task-owned backend environment, then disposable PostgreSQL 16.15 + pgvector fresh and repeat scripts/migrate_db.py: both exit 0, recorded revision 0019_email_read_state_repair.
  • uv run --frozen python -m pytest -q -W error -ra --tb=short tests/test_alembic_migrations.py tests/test_bootstrap_db.py tests/test_data_api.py tests/test_email_read_state_migration_postgres.py tests/test_legacy_document_scope_postgres.py tests/test_workspace_document_migration.py tests/test_container_dependency_pin_contract.py: 75 passed, 0 failed, 0 skipped, 10.39 seconds.
  • Ruff on all changed migration/service/API/test surfaces and diff checks: passed.
  • Test image: pgvector/pgvector@sha256:ccc6e83d6e35e931dc7c5def2022729d5a6c370318d099181995567ff1fb4d6b. Separate localhost port, random test-only credentials, read-only root, tmpfs storage, scoped cleanup verified. Existing databases were untouched.

Root-cause repair and changed stack decision

The previous head's 73-test local rerun used an undeclared httpx2 2.5.0 installation. Exact synchronization removed it; the Data API test then failed collection (exit 4) with missing httpx2 and Starlette's deprecation warning under -W error. That pass was supplemented-local evidence, not clean-lock proof. #1565 already owns the declared dependency and lock correction, so its unchanged delta is inherited here instead of copied or manually installed.

The historical #1502-before-#1503 proposal is superseded by #1565#1503. #1502 remains the CI-service lane on #1562; it can later inherit this forward migration repair without a reciprocal prerequisite. Neither PR is closed.

Consumer propagation through #1468#1427#1497 has not happened. #1497's current fresh install still fails on the old emails table name. Its eventual integration must preserve the bootstrap tests and join the provenance and workspace Alembic branches with a forward merge revision; do not rewrite existing revision IDs or stamp past a failure.

Evidence boundary

The former 63-fast/8-PostgreSQL and 73-test receipts remain historical observations, not current clean-lock gate evidence. This current receipt proves the local committed tree only; fresh current-head hosted PostgreSQL, security, and independent review evidence is still required. API tests using dependency overrides do not prove a deployed signed HTTP path.

Keep Draft while #1565 is open and hosted gates remain unverified. Historical CHANGES_REQUESTED and failed/cancelled central-workflow records are not transferred to, or treated as success on, this new head. No self-approval, dismissal, dummy commit, force push, admin bypass, or gate weakening.

…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
@coderabbitai

coderabbitai Bot commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

The pull request hardens Alembic migrations, adds workspace registry tables, provisions workspaces during document uploads, and applies organization-aware document filtering with legacy-row handling. It adds unit and PostgreSQL regression coverage for migrations, concurrency, uploads, and access isolation.

Changes

Schema and workspace document integrity

Layer / File(s) Summary
Migration backfills and repairs
backend/alembic/versions/0001_initial_control_plane.py, backend/alembic/versions/0011_email_read_state.py, backend/alembic/versions/0019_email_read_state_repair.py, backend/scripts/bootstrap_db.py, backend/tests/test_alembic_migrations.py, backend/tests/test_bootstrap_db.py, backend/tests/test_email_read_state_migration_postgres.py, backend/tests/test_data_api.py
Schema backfills now skip missing legacy tables. Email read-state changes check table and column existence. Revision 0019_email_read_state_repair restores missing email_records.is_read. Downgrades preserve read state.
Workspace registry and upload provisioning
backend/alembic/versions/0016_document_org_scope.py, backend/alembic/versions/0018_workspace_registry.py, backend/services/workspace_scope.py, backend/api/data.py, backend/tests/test_workspace_document_migration.py
The migration chain creates workspace tables and indexes idempotently. Upload endpoints call get_or_create_workspace, which handles concurrent inserts. PostgreSQL tests cover fresh, upgraded, and concurrent uploads.
Organization-scoped document access
backend/api/data.py, backend/tests/test_data_api.py, backend/tests/test_legacy_document_scope_postgres.py
Document lookups and quality-surface results apply organization filters. Canonical workspace claims may access legacy rows with null organizations. Tests reject documents belonging to another organization and validate legacy-row access.

Estimated code review effort: 4 (Complex) | ~45 minutes

Merge Risk: 🟡 Moderate · up to da2d7

This PR adds workspace registry tables and provisions workspaces before document creation, but it is not merge-ready until the downgrade path preserves existing organization assignments. A validly signed but mismatched workspace and organization claim pair could also create inconsistent ownership records, while migration tests currently have configuration-handling and status-validation gaps.

Sequence Diagram(s)

sequenceDiagram
  participant DocumentClient
  participant DataAPI
  participant WorkspaceScope
  participant WorkspaceDatabase
  DocumentClient->>DataAPI: Upload document
  DataAPI->>WorkspaceScope: get_or_create_workspace
  WorkspaceScope->>WorkspaceDatabase: Insert workspace with conflict handling
  WorkspaceDatabase-->>WorkspaceScope: Created or existing workspace
  WorkspaceScope-->>DataAPI: Workspace
  DataAPI->>WorkspaceDatabase: Insert document
  WorkspaceDatabase-->>DataAPI: Document record
  DataAPI-->>DocumentClient: Upload response
Loading
🚥 Pre-merge checks | ✅ 4
✅ 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 The title clearly and concisely describes the primary changes: adding the workspace registry tables and provisioning Workspace rows.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/workspace-document-registry-migration

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.

@github-actions

github-actions Bot commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

PR governance metadata gate is not ready for 9c1851336fa04bcdc77c1c6e531afdb882583af1:

  • Review decision is CHANGES_REQUESTED; address requested changes before merge.
  • Required check strix is CANCELLED on the current head.
  • Current-head CodeRabbit issue comment has blocking warning/failure evidence on 9c18513.

@seonghobae

Copy link
Copy Markdown
Contributor Author

Exact-head audit found and fixed a workspace-provisioning concurrency defect at 31b400c. The former SELECT-then-INSERT allowed simultaneous first uploads for one signed workspace to race on the workspace_entities.workspace_id primary key. Provisioning now uses PostgreSQL ON CONFLICT DO NOTHING with RETURNING and selects the winning row only when another transaction created it.

Verification: real PostgreSQL migration/API suite 3 passed (fresh install, pre-registry upgrade, and 16 concurrent first uploads); focused Data API 15 passed and 1 skipped; Ruff and git diff --check passed.

@coderabbitai review
@opencode-agent

@coderabbitai

coderabbitai Bot commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

@seonghobae I will review the exact-head changes, including the concurrent workspace-provisioning path.

⚠️ 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.

@seonghobae

Copy link
Copy Markdown
Contributor Author

Follow-up exact-head audit fix at 37bcd6e: workspace_id alone was used by document lookup and Data repository listing even though workspace resources must also retain organization scope. A valid signed session carrying the same workspace identifier under another organization could therefore read or mutate that organization’s document. Both query paths now require workspace_id plus organization_id; regression coverage uses the same workspace with a different explicit organization and proves 404. Combined focused validation: 18 passed, 1 skipped; real PostgreSQL fresh/upgrade/concurrency coverage included; Ruff and git diff --check passed.

@coderabbitai review
@opencode-agent

@seonghobae

Copy link
Copy Markdown
Contributor Author

PR governance metadata gate is not ready라고 띄우는 Workflow가 무슨 소용이 있지? 이슈가 있으면 이슈가 있다고 알리고, GitHub Checks가 실패했으면 실패를 고치기 위한 OpenCode Agent를 contextual-orchestrator 를 orchestrator/free 로 대동해서 띄우는 게 더 생산적이지 않을까? 그리고 OpenCode Agent는 Ready for Review로 넘겨주든 어쩌든 의사결정을 하고.

@coderabbitai

coderabbitai Bot commented Sep 1, 2026

Copy link
Copy Markdown
Contributor
🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

git status --short
git show --no-ext-diff --format=fuller --stat 37bcd6e38dd26c4aaf9af5837d8f920328bcca16
git diff --no-ext-diff 37bcd6e38dd26c4aaf9af5837d8f920328bcca16^ 37bcd6e38dd26c4aaf9af5837d8f920328bcca16 -- backend

printf '\n--- Python file outlines affected by the commit ---\n'
git diff --name-only 37bcd6e38dd26c4aaf9af5837d8f920328bcca16^ 37bcd6e38dd26c4aaf9af5837d8f920328bcca16 -- 'backend/**/*.py' |
while IFS= read -r file; do
  [ -n "$file" ] || continue
  printf '\n### %s\n' "$file"
  ast-grep outline "$file" --items all --view expanded || true
done

printf '\n--- Workspace/document query candidates ---\n'
rg -n -C 4 --glob 'backend/**/*.py' \
  '(Document\.workspace_id|workspace_id\s*==|workspace_id\s*=' backend

Length of output: 35493


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- Scoped helper and document endpoints ---'
sed -n '2470,2520p' backend/api/data.py
sed -n '3150,3395p' backend/api/data.py
sed -n '3900,3985p' backend/api/data.py

printf '%s\n' '--- Mock session query emulation ---'
sed -n '55,205p' backend/tests/test_data_api.py

printf '%s\n' '--- Changed document-scope regression test and neighboring listing test ---'
sed -n '2520,2740p' backend/tests/test_data_api.py

printf '%s\n' '--- Direct Document query statements in data API ---'
ast-grep run --lang python \
  --pattern 'select(Document)' \
  backend/api/data.py || true
ast-grep run --lang python \
  --pattern 'update(Document)' \
  backend/api/data.py || true
ast-grep run --lang python \
  --pattern 'delete(Document)' \
  backend/api/data.py || true

printf '%s\n' '--- Callers of the scoped document helper ---'
rg -n -C 3 '\b_get_workspace_document\s*\(' backend/api/data.py

printf '%s\n' '--- Document workspace and organization predicates ---'
rg -n -C 3 'Document\.(workspace_id|organization_id)\s*==' backend/api/data.py backend/tests/test_data_api.py

Length of output: 32842


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- Document model and registry migration ---'
rg -n -C 10 'class Document\b|organization_id.*(Column|mapped_column)' backend/db/models.py
sed -n '1,280p' backend/alembic/versions/0018_workspace_registry.py

printf '%s\n' '--- All backend Document query and write candidates ---'
rg -n -C 4 --glob 'backend/**/*.py' \
  '(select\(Document\)|update\(Document\)|delete\(Document\)|query\(Document\)|Document\))' \
  backend

printf '%s\n' '--- Organization-collision coverage in document and migration tests ---'
rg -n -i -C 5 \
  '(other_organization|org-rival|organization_id|quality-surface|workspace-org-acme)' \
  backend/tests/test_data_api.py backend/tests/test_workspace_document_migration.py

printf '%s\n' '--- Exact-head test-file changes since the parent commit ---'
git diff --no-ext-diff --check 37bcd6e38dd26c4aaf9af5837d8f920328bcca16^ 37bcd6e38dd26c4aaf9af5837d8f920328bcca16

Length of output: 81549


@seonghobae I found one test coverage issue.

P2 — Test the organization boundary in the repository listing

MockAsyncSession.execute treats a document with organization_id=None as a match for an explicit organization. PostgreSQL does not match that row for Document.organization_id == 'org-acme'. As a result, test_data_quality_surface_includes_workspace_document_assets passes with its doc_owned fixture even though the fixture lacks organization_id.

The test also does not add a document with the same workspace_id and a different explicit organization_id. Therefore, it does not verify the new predicate in get_data_quality_surface.

Set doc_owned.organization_id="org-acme". Add a same-workspace fixture with organization_id="org-rival". Assert that the quality-surface response excludes the rival document. Make the mock require exact organization matching when the query contains an organization predicate.

⚠️ 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.

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

Copy link
Copy Markdown
Contributor Author

Fixed the P2 test-coverage gap CodeRabbit flagged at bfe347e7: test_data_quality_surface_includes_workspace_document_assets now gives doc_owned its real organization_id and adds doc_other_org (same workspace_id, different organization_id), so the test actually exercises the Document.organization_id predicate added in 37bcd6e3 rather than passing regardless of it.

Verified: reverting that predicate in get_data_quality_surface turns this test red (object_count: 2 instead of 1); restoring it and running the full backend suite locally against real PostgreSQL 16 stays green (1837 passed, the same 2 pre-existing unrelated is_read failures noted in the PR description, ruff clean).


Generated by Claude Code

@seonghobae
seonghobae marked this pull request as ready for review September 1, 2026 04:16
devin-ai-integration[bot]

This comment was marked as resolved.

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

Copy link
Copy Markdown
Contributor Author

Thanks, this review caught a real gap. Addressed at 317c721e:

Finding 1 (🔴 historical upgrades never reach registry creation) — confirmed and fixed. Reproduced directly against real PostgreSQL: migrated a fresh database to 0015_merge_newsdom_email_heads, dropped workspace_entities/workspace_documents to simulate a database whose own 0001_initial_control_plane predates these models, then continued to head — 0016_document_org_scope crashed with NoSuchTableError exactly as you said, before 0018_workspace_registry ever ran. My original regression test never caught this because it only dropped the tables after 0017 (by which point 0016 had already run against tables that existed via create_all's live metadata) — that masked the real bug. 0016 is now has_table-guarded like the rest of this repo's idempotent migrations, and the test's pre-registry boundary moved from 0017 to 0015 so it actually crosses 0016 with the tables absent. Confirmed the same reproduction now completes cleanly end-to-end through /api/data/documents.

Finding 2 (📝 fresh migration uses current metadata) — already disclosed, both in the PR description and in 0018's own docstring. No action needed.

Finding 3 (🔴 rollback deletes pre-existing workspace data) — fixed. 0018's downgrade unconditionally dropped both tables even when its own upgrade was a no-op (tables already existed). Made it a documented no-op instead, matching the same judgment call 0001_initial_control_plane already makes for the same reason (document_content holds real uploaded content, not rebuildable derived state — production rollbacks should restore from backup).

Finding 4 (🟡 legacy organization documents disappear) — investigated, not changing. This can't currently manifest: every Document-creating code path always sets organization_id=auth_context.organization_id at insert time, and before this PR no Document row could ever be successfully created in any real deployment (the FK-violation bug this PR fixes meant every upload 500'd). So there's no legacy NULL-organization_id row for a real org session to lose access to today. Personal-scope docs (organization_id=NULL, created by a personal-scope session) stay correctly reachable, since Document.organization_id == auth_context.organization_id becomes IS NULL for a personal-scope session's own None. I'd rather not loosen the predicate defensively — doing so would reopen the exact cross-tenant read 37bcd6e3 closed (a NULL-organization_id row in a shared workspace becoming visible to any org sharing that workspace_id) for a scenario that isn't currently reachable. Happy to revisit if a real write path is ever added that can leave organization_id unset for an org-scoped document.

Full backend suite after this fix: 1837 passed (same 2 pre-existing unrelated is_read failures noted earlier in the thread), ruff clean.


Generated by Claude Code

seonghobae and others added 2 commits September 1, 2026 13:41
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

Copy link
Copy Markdown
Contributor Author

Picked up test_legacy_document_scope_postgres.py (e05f1b34) — good reproduction, and it changes my earlier call on Devin's Finding 4. I'd reasoned that a NULL-organization_id document couldn't currently arise from any real app write path, so I left it unfixed; this test is right that the data model allows it regardless (the column is nullable, and nothing guarantees only the app's own endpoints ever touch it), so it's worth hardening now rather than waiting for a write path that needs it.

Fixed at 5054a8e7. _document_organization_filter in api/data.py: exact organization_id match, OR a NULL organization_id — but the NULL fallback only applies when the requesting session's own workspace_id/organization_id pairing is the canonical workspace-<organization_id> (or workspace-<user_id>) derivation. That's what keeps both assertions in the new test correct: the matching-org session (self-consistent pairing) reaches the legacy row; the other-org session using the same workspace_id but a different, non-derivable organization_id (an internally inconsistent claim pairing, same class of scenario 37bcd6e3 defends against) still gets refused with the strict, no-fallback check — so a same-workspace document that already has an explicit conflicting organization_id stays protected, and a forged/malformed claim pairing can't use the NULL fallback to read across tenants either.

Wired into both _get_workspace_document and get_data_quality_surface's Document query.

test_legacy_document_scope_postgres.py now passes (confirmed it was failing against the prior commit first). Full backend suite: 1838 passed (same 2 pre-existing unrelated is_read failures noted earlier in the thread), ruff clean.


Generated by Claude Code

devin-ai-integration[bot]

This comment was marked as resolved.

Copy link
Copy Markdown
Contributor Author

Migration overlap / historical-upgrade finding on current head 5054a8e73b70ef542bcee65f5a232f320b152771:

0011_email_read_state is a parallel historical branch (down_revision = 0009_project_graph_projection) later merged by 0014_merge_email_read_state. Retargeting that revision from legacy emails to current email_records is unsafe for an actually old database whose branch has emails but not yet email_records: _has_column(inspector, "email_records", "is_read") can inspect a table that does not exist, and even when it exists it changes the historical revision's ownership semantics.

PR #1502 already carries the narrower compatibility contract: preserve op.add_column("emails", ...) / op.drop_column("emails", ...), but guard both with sa.inspect(op.get_bind()).has_table("emails") so a fresh current-schema install no-ops while a genuine legacy database still receives the historical mutation. Its backend/tests/test_alembic_migrations.py::test_email_read_state_legacy_table_guard_is_reversible pins that behavior.

Please preserve that legacy-table guard rather than the email_records retarget when reconciling the overlapping files. Required RED acceptance before changing production: exercise 0011_email_read_state.upgrade() against a schema where legacy emails exists and email_records does not; it must add is_read to emails without inspecting/mutating a nonexistent current table. Fresh-current-schema migration must still complete as a no-op. This is independent of the workspace registry fix and should not broaden this PR's migration authority.

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

Copy link
Copy Markdown
Contributor Author

Reconciled at 6fe1de1d — thanks for the pointer to #1502.

Adopted its exact pattern for the overlapping files instead of my earlier approach:

  • 0011_email_read_state.py: has_table("emails")-guarded no-op, keeping the original "emails" target, rather than retargeting to "email_records".
  • bootstrap_db.py/0001_initial_control_plane.py: execute_schema_backfill() with the LEGACY_EMAILS_INDEX identity-matched sentinel, rather than my simpler unconditional deletion.
  • test_alembic_migrations.py: pulled in test_email_read_state_legacy_table_guard_is_reversible to pin the reconciled shape.
  • Also absorbed fix(ci): add Postgres service container to backend job, close fresh-install migration gap #1502's fix for the 4 raw-SQL is_read NOT NULL smoke-seeding failures in test_bootstrap_db.py/test_data_api.py — this is the exact bug I'd flagged as a follow-up earlier in this thread, so no reason to leave it half-fixed here now that these files were already being touched.

For what it's worth, checking 0011_email_model_reconciliation's own docstring: no migration ever actually renamed emails to email_records for a real managed database — email_records was the real table name since inception, and "emails" was only ever a stale copy-pasted string in these two spots. So both approaches are safe in practice; no reason to diverge from the already-tested pattern in #1502.

Re-verified end-to-end against real PostgreSQL: 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 gone too), ruff clean.

Also worth flagging explicitly since it matters for this PR too: #1502's real finding is that app-ci.yml's backend job has never had a Postgres services: container, so every @pytest.mark.postgres test — including all the new ones added here (test_workspace_document_migration.py, test_legacy_document_scope_postgres.py) — has always silently skipped in actual CI, not just run green. Everything in this PR has been validated locally against real PostgreSQL 16, but until #1502 lands, none of it runs for real in CI either.


Generated by Claude Code

devin-ai-integration[bot]

This comment was marked as resolved.

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

Copy link
Copy Markdown
Contributor Author

Two real gaps in the pattern adopted from #1502 — fixed at 053c066c:

"Existing email databases miss read state" (🔴) — confirmed and fixed. Reproduced directly: migrated a fresh database to 0009, dropped email_records.is_read to simulate a database whose own 0001 ran before is_read was added to the Email model, then continued to head with the has_table("emails")-only version — it completed with no error, but is_read was silently, permanently missing (since no managed database ever really had a table literally named "emails", so that guard was always false and upgrade() always returned without touching email_records at all). Confirmed the same reproduction now correctly adds is_read.

"Legacy read-state migration is not idempotent" (🟡) — confirmed and fixed. Reproduced: manually created an "emails" table with is_read already present, migrated to head — the old version crashed with a duplicate-column error (only checked has_table, not column existence, before op.add_column). Confirmed it no longer crashes.

Fix now checks both "email_records" (the table that actually matters) and "emails" (defensive), guarded by column existence via the same _has_column helper this repo's other migrations already use — safely idempotent either way.

This diverges from #1502's exact pinned file shape for 0011_email_read_state.py (its test_email_read_state_legacy_table_guard_is_reversible asserts the has_table-only version byte-for-byte), so I updated this PR's own contract test to check for the corrected shape instead. Worth flagging on #1502 directly too, since the same two gaps apply to its own version of this file if it hasn't been independently caught there — happy to leave that to you/that PR rather than commenting there myself, since you're driving both.

"Raw index DDL bypasses governance" (🔍) — investigated, not changing. LEGACY_EMAILS_INDEX lives in bootstrap_db.py, not an Alembic migration file — CLAUDE.md explicitly documents this script as "the local/dev-only schema compatibility path; Alembic history is authoritative," distinct from the "Alembic migrations use structured operations" convention that governs alembic/versions/*.py. Every other statement in schema_backfill_sql() (~80 of them, predating this PR) already uses raw text() SQL in exactly this style, so singling out this one line would be inconsistent with the rest of the file rather than a real governance gap.

Re-verified end-to-end against real PostgreSQL for all three scenarios above. Full backend suite: 1841 passed, 0 failed, 3 skipped, ruff clean.


Generated by Claude Code

devin-ai-integration[bot]

This comment was marked as resolved.

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

Copy link
Copy Markdown
Contributor Author

"Fresh database downgrades lose read state" (🔴) — confirmed and fixed at d3020ca7. Same ownership-ambiguity class as 0018_workspace_registry's downgrade, fixed earlier: a fresh database's email_records.is_read comes from 0001's live create_all, not from 0011_email_read_state, so downgrade() can't tell which one is responsible for the column. Reproduced directly: migrated a fresh database to head, ran alembic downgrade to 0009 — the previous version silently dropped is_read and its data. Made downgrade() a documented no-op, matching the same judgment call already applied to 0001_initial_control_plane and 0018_workspace_registry.

"Migration behavior remains untested" (🔍) — addressed. Added backend/tests/test_email_read_state_migration_postgres.py: permanent real-Postgres coverage for all three scenarios this migration has to handle (historical email_records missing is_read gets it added; idempotent against a legacy "emails" table that already has it; downgrade doesn't destroy a fresh database's read state). Confirmed the new downgrade test fails red against the reverted destructive version before restoring the fix.

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


Generated by Claude Code

Copy link
Copy Markdown
Contributor Author

Acknowledged — I'll treat fix/workspace-document-registry-migration as read-only from here and won't push further commits to it. Deferring the #1502/#1503 convergence (restack/retarget, dedup of migration/bootstrap behavior once #1502 narrows to its CI-enablement delta) to the Naruon hourly writer as instructed. Still watching for CI/review status, but no more writes from this session.


Generated by Claude Code


Generated by Claude Code

seonghobae pushed a commit that referenced this pull request Sep 2, 2026
…on fix with #1503

Owner's request on this PR (2026-09-02): this branch had accumulated
unrelated content over a long, CI-congestion-heavy session. Narrows it
back to its stated purpose -- the dependency-root CI-enablement slice
(Postgres service container + the minimum fix needed to make the newly
real-executing tests pass) -- and converges the overlapping migration fix
with #1503's independently-derived, more complete implementation so the
two branches don't carry conflicting historical-migration semantics.

Moved out entirely (zero diff vs develop now), extracted to
#1531:
- .github/workflows/{bandit,dependency-review,docker-publish}.yml and
  app-ci.yml's `pull_request: branches:` removal (stacked-PR base support)
- tests/test_stacked_pr_workflow_contract.py
- backend/tests/test_release_governance.py's stacked-PR assertions
- scripts/ci/pr_governance_gate.sh + scripts/ci/test_pr_governance_gate.sh
  (CodeRabbit approval-pending + OpenCode fallback governance-gate fixes)

Converged with #1503 (adopted its exact implementation, verified
byte-identical via diff against its branch):
- backend/alembic/versions/0011_email_read_state.py: #1503 independently
  found and fixed the same relation "emails" does not exist crash, more
  completely -- guards on column existence (not just table existence) and
  checks both "email_records" and legacy "emails", vs. this branch's
  simpler has_table-only guard. Adopting it here means #1503's later
  rebase onto this PR (once merged) is a clean no-op on this file instead
  of a conflict between two divergent fixes for the same bug.
- backend/scripts/bootstrap_db.py: same convergence for
  execute_schema_backfill's legacy-index handling.
- backend/tests/test_alembic_migrations.py: swapped this branch's
  test_email_read_state_legacy_table_guard_is_reversible (asserted the
  old implementation's exact shape) for #1503's
  test_email_read_state_guards_both_legacy_and_current_table_names,
  which asserts the now-shared implementation instead.

backend/alembic/versions/0001_initial_control_plane.py,
backend/tests/test_bootstrap_db.py, and backend/tests/test_data_api.py
needed no changes -- diffed identically against #1503's branch already
(both PRs independently reached the exact same is_read raw-SQL fix and
0001 refactor while investigating the same real-Postgres failures).

Net result: 10 files changed vs. develop (down from 18), matching the
"dependency-root CI-enablement slice" scope the owner asked for.

Verification (real-Postgres re-run blocked by this sandbox having no
usable Docker daemon -- ExpressJS-style hashes/lint/isolated-assertion
checks substitute; the point of this very PR is to make GitHub's own CI
runners do the authoritative check):
- python3 -m py_compile on every touched Python file: clean.
- python3 -m ruff check on every touched backend file: all checks passed.
- backend/tests/test_alembic_migrations.py's three targeted test
  functions executed directly (bypassing this sandbox's unrelated
  conftest.py/cryptography import breakage): all 3 pass.
- git diff against develop for every "moved out" file: zero lines,
  confirming a clean revert.
- git diff against #1503's branch for the converged migration/bootstrap
  files: zero lines, confirming byte-identical adoption.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019YgmaEKnPhrsbSyQBierFH

Copy link
Copy Markdown
Contributor Author

FYI: per the owner's overlap-audit request on #1502, that PR has been narrowed down to just the Postgres CI-enablement slice, and its overlapping backend/alembic/versions/0011_email_read_state.py / backend/scripts/bootstrap_db.py fix has been replaced with an exact byte-identical copy of this PR's implementation (verified via git diff against this branch — zero lines). backend/alembic/versions/0001_initial_control_plane.py, backend/tests/test_bootstrap_db.py, and backend/tests/test_data_api.py needed no changes on #1502 since they already matched this branch exactly.

Once #1502 merges to develop, this PR's 0001/0011/bootstrap_db.py/test_alembic_migrations.py/test_bootstrap_db.py/test_data_api.py deltas should collapse to a no-op on rebase, leaving only the unique workspace-registry/document-migration work (0016, 0018, 0019, services/workspace_scope.py, api/data.py, and the three new real-Postgres test files) as this PR's actual diff. Full detail on #1502's narrowing: #1502 (comment)

Claude-Session: https://claude.ai/code/session_019YgmaEKnPhrsbSyQBierFH


Generated by Claude Code

@seonghobae seonghobae added priority: high High-priority or P1 work status: needs-review Open pull request requiring current-head review or checks type: bug Defect or incorrect behavior labels Sep 2, 2026 — with ChatGPT Codex Connector

Copy link
Copy Markdown
Contributor Author

The central noema-review gate fix promised in the earlier comment has landed: ContextualWisdomLab/.github#1671 ("fix(noema): report actual rejected review location", commit bb14b01) is merged to .github's main. It corrects the misleading array-position message and makes the actual rejected (path, line, side) diagnosable, while keeping the changed-line validation strict.

Re-triggered the previously-failed noema-review job (rerun_failed_jobs on run 33489389355) so it re-evaluates against the fixed gate. No commits pushed to this branch — still treating it as read-only per the prior instruction, deferring restack/convergence with #1502 to the Naruon hourly writer.


Generated by Claude Code


Generated by Claude Code

Copy link
Copy Markdown
Contributor Author

noema-review failed again on unchanged current head 9c185133 (run 33489389355, re-triggered after .github#1671's fix landed) — this is a different failure from the one #1671 fixed, and it's not this PR's diff.

Root cause from the job log: the "prepare verdict" step mints a GitHub App installation token at 15:16:03Z, then spends ~17 min bringing up the contextual-orchestrator review sidecar and ~44 min probing/calling free-tier models (several hit TimeoutError/404 before a working route was found — see the runtime preflight summary in the log: probed_count: 12, rejected_count: 7). By the time two_phase.py's prepare phase needed gh again at 16:17:46Z, the installation token (GitHub's fixed ~1-hour TTL) had expired: gh: Bad credentials (HTTP 401). The workflow's two-phase design already mints a separate fresh token for the publish step, but that doesn't help here — the prepare step itself ran long enough to outlive its own token before publish is ever reached.

This is a central-repo (ContextualWisdomLab/.github) workflow defect (noema-review.yml / .github/actions/noema-review/two_phase.py), not something in naruon's own code. Checked whether an existing fix covers it: the noema-token-lifetime-quality-ci.yml work that landed today addresses a different problem (that CI quality gate's own missing concurrency: block), not this token-TTL-during-a-long-prepare-phase issue — so no existing fix to port yet. I'm scoping a fix against the central repo now. Re-ran the failed job once (rerun_failed_jobs on run 33489389355); given the deterministic cause, expect it may recur if the same job again takes over an hour, but re-running once is what this protocol calls for before standing down.

Still treating this branch as read-only per the prior instruction — no commits pushed here.


Generated by Claude Code


Generated by Claude Code

Copy link
Copy Markdown
Contributor Author

Update on the noema-review token-expiry failure from my last comment: I delegated a fix and it found a more complete answer than I expected.

While preparing that fix I discovered ContextualWisdomLab/.github@a28fc2f ("fix(noema): remove caller repair deadline and duplicate model call") had already landed on .github's main — independently — and it removes the exact code path that caused this failure. It wasn't written to fix this bug specifically; it fixed a different, related problem (a duplicate repository-side model call after a malformed first response, which could turn one slow gateway call into two and blow past required-check budgets). But as a side effect, call_llm() is now a single gateway request with zero gh/GitHub-API calls inside it, and prepare_verdict()'s only GitHub API calls now all happen up front — before the potentially-slow model call, not after — so the installation token can no longer expire mid-review in a way that crashes an internal gh call the way it did on run 33489389355.

My delegated fix (which caught-and-handled that stale-token failure rather than eliminating its cause) is now redundant and was closed: .github#1745 (closed, not merged — see that PR for the full reasoning). No commits pushed to this branch, still read-only.

The re-triggered noema-review job (run 33489389355) is still queued as of this comment (shared runner backlog, unrelated to either fix) — expect it to run against the now-fixed central script once a runner picks it up, and no further action should be needed here for this specific failure mode.


Generated by Claude Code


Generated by Claude Code

@seonghobae
seonghobae marked this pull request as draft September 4, 2026 21:05
seonghobae added a commit that referenced this pull request Sep 4, 2026
Trace PR #1503's signed workspace provisioning and real PostgreSQL migration evidence into the product-technical baseline.\n\nAssisted-by: OpenAI Codex <codex@openai.com>
Non-force merge of PR #1565 into migration owner #1503. Exact synchronization exposed the undeclared TestClient dependency previously masked by an extra local package. Preserve both complete deltas and use #1565 as the direct prerequisite, superseding the historical #1502-before-#1503 proposal without closing either lane.

Verified merged tree 793aadd with fresh and repeat Alembic upgrade to 0019_email_read_state_repair, 75 strict PostgreSQL/dependency tests, Ruff, and diff checks. Consumer propagation and hosted gates remain unverified.

Co-Authored-By: Codex <noreply@openai.com>
Signed-off-by: Seongho Bae <me@seonghobae.me>
@seonghobae
seonghobae changed the base branch from develop to codex/starlette-testclient-dependency September 5, 2026 06:57
seonghobae added a commit that referenced this pull request Sep 5, 2026
Preserve historical receipts while recording the real fresh-install failure, supplemented environment defect, and verified non-force #1565 to #1503 owner integration. Retain remaining consumer propagation, Alembic head merge, and conflicting import acceptance work as Proposed. Narrow the earlier redirect-safety claim to observed configuration evidence.

Verification: source-linked logs and artifact hashes, 224 provenance tests, clean-lock owner fresh/repeat migrations and 75 strict tests, diff checks, and independent read-only evidence review. No protected merge or release claim.

Co-Authored-By: Codex <noreply@openai.com>
Signed-off-by: Seongho Bae <me@seonghobae.me>
seonghobae added a commit that referenced this pull request Sep 5, 2026
Merge #1503 normally as the prerequisite to #1468, preserving the unique bootstrap regression assertion and changelog. The owner already carries the is_read fixture delta. Verified merged tree 00e170c with fresh Alembic upgrade and 75 clean-lock strict PostgreSQL tests, Ruff, and diff checks. No delta is discarded or protected merge claimed.

Co-Authored-By: Codex <noreply@openai.com>
Signed-off-by: Seongho Bae <me@seonghobae.me>
seonghobae added a commit that referenced this pull request Sep 5, 2026
Preserve the unique no-unconditional-legacy-index assertion and both owner histories.

Correct the historical-only canonical-index release claim; conditional legacy handling remains inherited from #1503.

Exact dependency sync, fresh and repeat migration 0020, and 131 strict PostgreSQL tests pass.

Search and migration performance remain Draft acceptance gates in #1572; no protected completion is claimed.

Co-authored-by: Codex <noreply@openai.com>
Signed-off-by: Seongho Bae <me@seonghobae.me>
seonghobae added a commit that referenced this pull request Sep 6, 2026
Normally integrate migration owner #1503 at 19d5860 without discarding #1317 history. Preserve both migration branches with a no-DDL merge revision and retain owner-scoped mail and graph identities.

Record actual bounded-pool, cancellation, lost-connection and signed API evidence in doctoring; keep ADR Proposed and hosted CI prerequisite #1562 explicit. Candidate verification: 242 focused tests passed against fresh and repeated PostgreSQL migrations; no protected merge or release claimed.
seonghobae added a commit that referenced this pull request Sep 6, 2026
Normally merge full migration prerequisite #1503 at 19d5860, including #1565; preserve #1562/#1531/#1554 history and current #1531 base. Record decision and original missing-emails/skip failures in doctoring before commit. No delta discarded, no copied central workflow, no gate bypass.

Add hardened task-only DB lifecycle, isolated bootstrap/child settings, actual pytest collection/xfail guard, cancellation-safe scoped cleanup and redacted artifacts. Full candidate: 1871 passed and 2 explicitly unconfigured live API skips. Independent signal tests passed. Revalidate this exact committed head before protected integration.
@seonghobae seonghobae removed the status: needs-review Open pull request requiring current-head review or checks label Sep 6, 2026
@seonghobae seonghobae added the status: draft Draft pull request label Sep 6, 2026 — with ChatGPT Codex Connector
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working priority: high High-priority or P1 work status: draft Draft pull request type: bug Defect or incorrect behavior

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants