Skip to content

feat(tenant): scoped archive export/import slice 1 - #1465

Open
seonghobae wants to merge 8 commits into
developfrom
feat/tenant-archive-roundtrip-slice1
Open

feat(tenant): scoped archive export/import slice 1#1465
seonghobae wants to merge 8 commits into
developfrom
feat/tenant-archive-roundtrip-slice1

Conversation

@seonghobae

@seonghobae seonghobae commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

Supports #1428

PR 제목 (Title)

feat(tenant): scoped archive export/import slice 1

목적 (Purpose) / Description

First verifiable slice of the tenant export/reimport P0 buyer gap tracked in #1428 and documented in docs/product-technical-gap-baseline.md ("Tenant export/reimport"). This slice makes the round trip real: an organization+user-scoped export of the email/thread/task domain as a deterministic, versioned archive bundle, and a dedupe-safe import that preserves opaque public ids and source provenance. Issue #1428 tracks the full multi-slice gap, so this PR references it with Supports #1428 rather than an auto-close keyword.

주요 변경 사항 (Key Changes)

  • Org+user-scoped export of the email/thread/task domain into a deterministic, versioned archive bundle (schema_version stamped in the manifest).
  • Dedupe-safe import that preserves opaque public ids and email/thread/task source provenance; re-importing the same bundle skips already-present records.
  • Credentials-bearing tables are excluded from the archive by design, so secrets cannot leak through export/import.
  • Manifest carries schema_version; validation fails closed (archive_schema_unsupported) on unsupported versions.
  • Deterministic error_codes mapped from exception types only: archive_schema_unsupported, archive_scope_mismatch, archive_bundle_malformed.
  • New tenant-archive API router registered in backend/main.py behind the default signed-session dependency (PRIVATE_API_DEPENDENCIES).
  • Contract documented in docs/architecture/tenant-archive-contract.md.

Type of change

  • New feature (non-breaking change which adds functionality)
  • This change requires a documentation update

Out of scope (later slices)

Attachment bytes, content/project graph, embedding regeneration, zip packaging, size caps, audit events.

변경 범위 / 영향도 (Scope / Impacted Areas)

  • Backend only: one new service module, one new private API router (+ registration), three focused test modules, one architecture doc. No frontend changes, no schema migrations, no dependency changes.
  • Runtime model: FastAPI (ASGI, async) — endpoints are async handlers over the async SQLAlchemy session; no sync WSGI worker constraint applies.
  • Data safety: export is read-only over scoped rows; import runs in bounded transactions and skips duplicates instead of rewriting provenance.
  • Pooling: no pooler-specific routing was added in this slice; export reads and import writes use the same primary DSN (transactional import stays on primary).
  • No secrets or credential values are introduced by this change; credentials-bearing tables never enter the bundle.

변경 사항 표 (Change Table)

Component Change Notes
backend/services/tenant_archive_service.py New: scoped export/import domain service Intent: make tenant export/reimport verifiable end-to-end. Why: deterministic bundle + dedupe-safe import preserves opaque public ids and provenance; credentials-bearing tables excluded by design.
backend/api/tenant_archive.py New: tenant-archive router Intent: expose export/import over the private API surface. Why: registered behind the default signed-session dependency so archive operations inherit owner/org scoping.
backend/main.py Register the tenant-archive router Intent: wire the new private router into the app. Why: keeps /api/* routers uniformly protected by the default signed-session dependency.
backend/tests/test_tenant_archive_service.py New: service-level tests Intent: pin scope filtering, versioned-manifest fail-closed behavior, and error_code mapping at the service boundary.
backend/tests/test_tenant_archive_api.py New: API-level tests Intent: pin route auth/scoping and deterministic error codes at the HTTP boundary.
backend/tests/test_tenant_archive_postgres.py New: real PostgreSQL smoke test Intent: prove the round trip on the live schema. Why: second import must be all-skipped (dedupe-safe idempotency) against real Postgres, not mocks.
docs/architecture/tenant-archive-contract.md New: archive contract doc Intent: freeze the bundle layout, schema_version, scoping, and exclusion rules reviewers can rely on for later slices.

Sequence Diagram(s)

Export/import round trip:

sequenceDiagram
    participant C as Client (signed session)
    participant A as backend/api/tenant_archive router
    participant S as tenant_archive_service
    participant D as PostgreSQL (scoped rows)

    C->>A: POST export (org+user scope)
    A->>A: default signed-session dependency check
    A->>S: export(...)
    S->>D: SELECT email/thread/task rows filtered by owner_user_id + organization_id
    Note over S: credentials-bearing tables excluded<br/>manifest.schema_version stamped
    S-->>C: deterministic archive bundle

    C->>A: POST import (bundle)
    A->>A: default signed-session dependency check
    A->>S: import(bundle)
    S->>S: validate manifest.schema_version -> else archive_schema_unsupported
    S->>S: scope mismatch -> archive_scope_mismatch<br/>malformed payload -> archive_bundle_malformed
    S->>D: INSERT missing rows keyed by opaque public ids + source provenance
    Note over S,D: duplicates skipped, ids/provenance preserved
    S-->>C: import result (created/skipped counts)
Loading

검증 (Verification)

Commands and results (pasted exactly):

  • PYTHONWARNINGS=error python -m pytest tests/test_tenant_archive_service.py tests/test_tenant_archive_api.py -q → 25 passed
  • PYTHONWARNINGS=error python -m pytest tests/test_tenant_archive_postgres.py -q → 1 passed (real PostgreSQL round trip incl. second-import all-skipped)
  • Regression: test_main.py + test_repo_hygiene.py + test_release_governance.py → 62 passed; ruff clean on touched files.

Checklist:

  • My code follows the style guidelines of this project
  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation
  • My changes generate no new warnings
  • I have added tests that prove my fix is effective or that my feature works
  • New and existing unit tests pass locally with my changes
  • Any dependent changes have been merged and published in downstream modules

Open in Devin Review

@coderabbitai

coderabbitai Bot commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

Next included review available in 59 minutes.

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: Pro Plus

Run ID: dda102a7-5a53-4c80-ba18-5cb6a215640d

📥 Commits

Reviewing files that changed from the base of the PR and between f3beb1c and 5b533d4.

📒 Files selected for processing (9)
  • CHANGELOG.md
  • backend/api/tenant_archive.py
  • backend/main.py
  • backend/services/tenant_archive_service.py
  • backend/tests/test_tenant_archive_api.py
  • backend/tests/test_tenant_archive_postgres.py
  • backend/tests/test_tenant_archive_review_regressions.py
  • backend/tests/test_tenant_archive_service.py
  • docs/architecture/tenant-archive-contract.md

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.

devin-ai-integration[bot]

This comment was marked as resolved.

@github-actions

github-actions Bot commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

PR governance metadata gate update for 5b533d4e60f4cf98fbf372d72259c48f2fad135a: no current blocking failures remain.

PR governance metadata gate is waiting on current-head requirements; see the latest check for pending reasons.

@devin-ai-integration devin-ai-integration 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.

Devin Review found 4 new potential issues.

Open in Devin Review

Comment thread backend/services/tenant_archive_service.py
Comment thread backend/services/tenant_archive_service.py
Comment thread backend/services/tenant_archive_service.py
Comment thread backend/services/tenant_archive_service.py
@seonghobae

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@seonghobae
seonghobae enabled auto-merge (squash) August 25, 2026 14:07
@coderabbitai

coderabbitai Bot commented Aug 25, 2026

Copy link
Copy Markdown
Contributor
⚠️ 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

Current-head local verification for 70596e269cbf05eb68dc419ad1dcd2375d67164a:\n\n- Focused archive service, API, and review-regression tests: 31 passed.\n- Ruff and git diff --check: passed.\n- Full backend: 1863 passed, 2 skipped, 2 failed; both failures are pre-existing PostgreSQL smoke seeds omitting required email_records.is_read, isolated in #1468 and unrelated to this archive diff.\n\nThe archive-specific current-head evidence is green; hosted Checks and independent approval remain authoritative.

@devin-ai-integration devin-ai-integration 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.

Devin Review found 3 new potential issues.

Open in Devin Review

Comment thread backend/services/tenant_archive_service.py
Comment thread backend/services/tenant_archive_service.py
Comment thread backend/services/tenant_archive_service.py
@seonghobae

Copy link
Copy Markdown
Contributor Author

Current-head review disposition for 70596e2: the five Devin threads were rechecked. The O(n) archive lookup is an explicitly bounded slice-1 limitation; fingerprint matching is the intentional strong-fingerprint idempotency contract; export fields are sanitized at ingestion; task relinking normalizes self-generated IDs; and email rows are flushed before dependent tasks. These are informational design notes, not unresolved source findings. The focused security regression suite is green; hosted Checks and independent review remain the merge gates.

@seonghobae
seonghobae enabled auto-merge (squash) August 25, 2026 14:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant