Skip to content

Add read-only Confluence document identity - #37

Merged
jaydreyer merged 1 commit into
mainfrom
codex/milestone-7-confluence-identity
Aug 4, 2026
Merged

jaydreyer merged 1 commit into
mainfrom
codex/milestone-7-confluence-identity

Conversation

@jaydreyer

@jaydreyer jaydreyer commented Aug 4, 2026 •

Copy link
Copy Markdown
Owner

Summary

Implements Milestone 7 PR B only: selected ChangeOps enterprise documents can now carry a
validated, persisted, read-only Confluence Cloud page identity. The bounded production
configuration supports only the Manager Travel Approval Guide.

No live credential or page ID exists in the repository. The implementation was first verified with
captured provider fixtures and mocked CI tests, then live-validated against the environment-only
Acme Manager Travel Approval Guide configuration. No placeholder or real page ID is present in
production code.

Persistence design

  • Adds confluence_document_sources, a Confluence-specific one-to-one child keyed by the existing
    enterprise_documents.id.
  • Preserves the stable ChangeOps document identity used by typed dependencies, assessment
    fingerprints, evidence, paths, and actions.
  • Stores only validated imported fields: provider, external page ID/title, canonical URL, space
    ID/key, external version/status, source-updated time, imported/refreshed times, SHA-256 source
    fingerprint, and the latest safe refresh outcome.
  • A first failed refresh creates no unvalidated identity. A later failed refresh updates only
    outcome fields and never overwrites last-known-good metadata.
  • Re-importing an identical fingerprint returns already_current; successful changed metadata
    updates only the Confluence child row.
  • Migration 0013_confluence_document_sources upgrades, downgrades, and upgrades cleanly.

Confluence adapter boundary

  • Uses only Confluence Cloud REST API v2:
    • GET /wiki/api/v2/pages/{id}?include-version=true
    • GET /wiki/api/v2/spaces/{spaceId}
  • Uses environment-only email/API-token credentials, HTTPS-only site origin validation, a bounded
    timeout, and the account's existing page/space view permissions.
  • Equivalent granular OAuth scopes are read:page:confluence and read:space:confluence.
  • Validates configured/returned page identity, owning space identity, supported status, timestamps,
    field bounds, and response shape.
  • Differentiates not configured, authentication failure, permission denied, page not found,
    provider unavailable, validation failure, ambiguous response, success, and already current.
  • Performs no search, crawl, body retrieval, edit, publish, delete, webhook, poll, or background
    synchronization.

API changes

  • Adds:
    • POST /api/v1/catalog-objects/enterprise_document/{document_id}/confluence-refresh
  • Extends existing catalog object detail with:
    • external_source_state
    • last-known-good external_source metadata when imported
  • The request accepts only a stable internal document ID. The external page ID is resolved from the
    server environment; clients cannot submit arbitrary pages.

UI changes

  • Extends the existing catalog document detail without redesigning the explorer.
  • Shows provider, external page ID, space, version, external status, last refreshed time, and
    Open in Confluence.
  • Shows an honest not-imported state when the live environment is absent.
  • Shows a visible last-refresh-failed warning while retaining last-known-good metadata.
  • Keeps source timestamps, imported title, fingerprint, and refresh result behind progressive
    disclosure.
  • Refresh remains an explicit admin/API action; this PR does not invent a catalog-write
    authorization UI.

Configuration

Documented in .env.example, compose.yaml, and README.md:

  • CONFLUENCE_BASE_URL
  • CONFLUENCE_EMAIL
  • CONFLUENCE_API_TOKEN
  • CONFLUENCE_TIMEOUT_SECONDS
  • CONFLUENCE_MANAGER_TRAVEL_APPROVAL_GUIDE_PAGE_ID

The README includes exact manual Acme page creation, least-permission account setup, refresh, and
verification steps. Credentials and the real page ID remain uncommitted.

Tests run

  • ruff check src tests migrations
  • ruff format --check src tests migrations
  • complete PostgreSQL-backed backend suite: 258 passed
  • frontend npm run typecheck
  • frontend npm run lint
  • frontend Vitest suite: 48 passed
  • frontend npm run build
  • Alembic empty-database upgrade head -> downgrade -1 -> upgrade head
  • Alembic current --check-heads
  • extraction offline evaluation: passed
  • policy-analysis workflow offline evaluation: passed
  • interpretation offline evaluation: passed
  • approval-workflow offline evaluation: passed
  • guarded local demo reset: passed
  • full Compose build/start/health checks: passed
  • responsive Playwright walkthrough at 1440px, 1024px, and 390px: 0 console errors

Fixture-backed coverage includes adapter parsing, all required failure states, persistence,
idempotency, metadata-only updates, external-identity retarget rejection, API contracts,
last-known-good preservation, demo reset, frontend imported/unavailable/stale states, unchanged
typed relationships, and unchanged assessment fingerprint/counts.

Screenshots

Live-imported reviewer state (environment-only configuration; no credential or page ID is committed):

Live-imported Manager Travel Approval Guide

Fixture-imported reviewer state (the external identifiers shown are test fixtures, not production
configuration):

Fixture-imported Manager Travel Approval Guide

Honest unconfigured state:

Unconfigured Manager Travel Approval Guide

Responsive mobile state:

Mobile Manager Travel Approval Guide

Known limitations

  • The local credential used for live validation reports page update and delete operations. The
    adapter implements only GET requests; the broader credential is an explicitly accepted
    current-environment limitation, so the demo does not claim end-to-end least privilege.
  • Production configuration is intentionally limited to the Manager Travel Approval Guide.
  • Refresh is API-only and synchronous.
  • Direct email/API-token authentication matches the existing narrow Jira pattern; no OAuth
    authorization flow is introduced.
  • A successfully imported document cannot be silently retargeted to another page.
  • No content body/excerpt, owner, search, crawling, RAG, embedding, bulk sync, webhook, polling,
    editing, publishing, or delete operation is included.

Boundary confirmations

  • Relationships are unchanged: all existing typed policy dependency rows remain curated,
    deterministic ChangeOps data.
  • Assessment behavior is unchanged: the assessment loader never queries the Confluence table;
    fixture tests prove the same input fingerprint, worker classifications, impact identities,
    18-impact summary, and 13 proposed actions before and after refresh.
  • Confluence is read-only: the adapter sends only the two bounded GET requests.
  • ChangeOps remains authoritative for internal document identity, relationships, impact
    analysis, workflow, persistence, and audit lineage.
  • PR C remains deferred: no relationship-origin provenance fields, categories, approval state,
    editing, or AI proposals are included.

Follow-up work

  • Optionally replace or restrict the local Atlassian credential if a later deployment requires
    end-to-end least privilege.
  • Review PR C relationship-origin provenance separately only if the validated catalog/Confluence
    experience still needs it.

@jaydreyer
jaydreyer marked this pull request as ready for review August 4, 2026 17:07
@jaydreyer
jaydreyer merged commit a38b9e0 into main Aug 4, 2026
2 checks passed
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