V1 Web PWA · Journey

Journey: Citation drift resolution

A source-reconciled journey record for V1 Web PWA — shipped behavior, state boundaries, failure modes, and the automation evidence available today.

automated · 1 spec verdict: partial· 2026-05-29
9sections13 minread

On this page
Journey at a glance
ActorsSophia ingestion daemonHouse operatorEditorial operatorDomain reviewerTrust-and-Safety operator
Automation1 E2E spec(s) · verdict: partial· 2026-05-29

Journey flow#

Generated from the authored steps below — click a node to jump to that section.

flowchart TD s1["1. Source refresh and per-claim diff"] s2["2. INC opens against affected artifact(s)"] s3["3. Operator opens /operator/admin and picks t…"] s4["4. Deep-link into Isis provenance inspector"] s5["5. Inspect the hash chain"] s6["6. Inspect the selected bundle detail"] s7["7. Cross-check sources against the drift list"] s8["8. Decide: recheck / re-render / retract"] s9["9. Audit-trail handoff"] s1 --> s2 --> s3 --> s4 --> s5 --> s6 --> s7 --> s8 --> s9 click s1 href "#1-source-refresh-and-per-claim-diff" click s2 href "#2-inc-opens-against-affected-artifacts" click s3 href "#3-operator-opens-operatoradmin-and-picks-the-inc" click s4 href "#4-deep-link-into-isis-provenance-inspector" click s5 href "#5-inspect-the-hash-chain" click s6 href "#6-inspect-the-selected-bundle-detail" click s7 href "#7-cross-check-sources-against-the-drift-list" click s8 href "#8-decide-recheck--re-render--retract" click s9 href "#9-audit-trail-handoff" s9 --> v(["verdict: partial"]) click v href "../results/citation-drift-resolution.html" style v stroke:#d6a531,color:#d6a531

A specific, hard-policy slice of incident triage. The shipped operator path starts from a citation-drift INC, opens the Isis provenance inspector, verifies the bundle hash chain, classifies affected sources, records a recheck, re-render, or retract decision, and closes the incident. The upstream Sophia refresh and the downstream generation/retraction cascade are still unchecked system legs. This separation matters: recording a correct operator decision is not evidence that a new artifact or customer notice was produced.

Personas#

  • Sophia ingestion daemon — non-human actor; produces the source.revision.created event that triggers the cascade
  • House operator — picks up the INC in /operator/admin
  • Editorial operator — owns the artifact (TR-, VR-, NS-, etc.); pulled in after the operator scopes the impact
  • Domain reviewer (theology / fact-check / ephemeris depending on artifact class) — second-pair-of-eyes for the re-grounding decision
  • Trust-and-Safety operator — escalation if the drift is severity-class retraction (per V1/features.md § Source Lifecycle)

Pre-conditions#

  • An Isis ProvenanceBundle exists for the artifact (every generated artifact has one — see operator/operator-admin-isis-provenance.md § Provenance bundle contract)
  • The artifact's sourceLineage.citationIds (Nisaba contract) or equivalent per-domain cite list is non-empty
  • Sophia per-claim diff has run on the source revision and produced a material-claim-change signal (paraphrase edits do not fire this path per V1/features.md § Change detection)
  • The artifact is in a customer-facing surface (drafts don't fire this; only published / live artifacts trigger the cascade)

Steps#

1. Source refresh and per-claim diff#

Background, non-interactive. The Sophia ingestion pipeline detects that a source has changed via changeFingerprint and runs claim-level diff (not text-level — per V1/features.md, paraphrase edits should not invalidate).

  • Sophia per-stage event log records: parser version, chunker strategy, enrichment classifier version, embedding model version — all in the typed event stream per V1/features.md § Per-stage audit
  • Claim diff produces a list of materially-changed claims
  • For each materially-changed claim: the downstream invalidation cascade fires (Veritas re-grounding, Metis lesson-validity check, Nisaba passage notice, Tara explainer re-check, Living Scenes re-render banner)

2. INC opens against affected artifact(s)#

  • For each affected artifact, an INC is created with title "Citation drift detected · " matching the fixture pattern
  • Severity:
    • S1 if the source moved to retracted state (retraction event)
    • S2 if the claim is high-stakes (medical, financial, legal, Metis assessment, Veritas published claim, Tara contemplative script)
    • S3 otherwise (advisory; informational drift)
  • Subtitle carries the drift summary (the fixture: "Confidence dropped from strong to moderate after 3 new sources")
  • Detail card carries driftSources[] — the fixture surfaces a three-row list [label, what-changed, source-quality-band]: [['Sci. Adv., 2025', 'May 04 · contradicts §02:14', 'medium-high journal'], ...]
  • Audit event: incident.created with source_revision_id, affected_bundle_id, materially_changed_claim_count

3. Operator opens /operator/admin and picks the INC#

  • Operator lands at /operator/admin (see incident-triage.md steps 2-3)
  • Inbox row visible; severity pill paints per sev
  • Operator clicks the row; detail card shows artifact code, drift summary, three drift-source rows
  • Detail card includes the "Open provenance" deep link used in step 4
  • Operator clicks "Open provenance" — navigates to /operator/admin/isis/provenance?inc=INC-NNNN; bundle selection happens inside the inspector because the current static INC fixture does not pin a bundle ID
  • Page is RSC; loadIsisProvenanceData({ tenantId, selectedBundleId }) runs server-side
  • Page renders two stacked inspectors:
    • IsisProvenanceInspector — bundle index + verification verdict + per- bundle 16-field detail
    • GeneratedArtifactProvenanceInspector — artifact-side provenance (surfaceId="operator-isis-provenance", surfaceRole="editorial")
  • Verify: operator/operator-admin-isis-provenance.md

5. Inspect the hash chain#

  • Verification card region (<article aria-live="polite" data-verification-ok="true|false">) reads:
    • Status"CLEAN — hash chain intact" (success) OR "BROKEN at index N" (failure)
    • Tenant scope — tenantId or (no tenant — platform ledger)
    • Chain length — integer; should match expected count
    • Last checked<time> element with ISO timestamp
  • If verdict is BROKEN at index N: this is an integrity event, not a drift event. Stop the drift workflow. Open an S1 INC against the ledger itself (separate from the drift INC). Escalate to platform security. Do not proceed with re-render or retract until ledger is reconciled. Current E2E verifies terminal action controls disable on the broken-ledger fixture
  • If verdict is CLEAN: proceed to step 6

6. Inspect the selected bundle detail#

The bundle detail <dl> enumerates 16 fields plus computed "Emitted at" — each <dd> has data-testid="isis-provenance-field-<key>":

  • Bundle ID matches the selected bundle detail
  • Generation ID identifies the specific generation run
  • Invoking user — author who triggered the generation
  • Tenant — tenant scope
  • Consent ID — consent grant in effect at generation time
  • Prompt hash — hash of the prompt used (privacy-preserving)
  • Model ID + Model version — what produced the artifact
  • Modality — text / image / audio / video / 3d-mesh / embedding
  • Watermark hash — present for image/video/audio; verify against the live artifact (a watermark mismatch means the artifact has been tampered with — escalate). Current E2E verifies field presence; live-artifact watermark comparison remains a downstream gap
  • Endpoint + Region — provider routing
  • Bundle fingerprint, Chain fingerprint, Prior fingerprint, Sequence — ledger position; first bundle in chain shows for prior fingerprint
  • Emitted at — ISO from emittedAtUnixSeconds * 1000

7. Cross-check sources against the drift list#

Returning to (or pivoting from) the admin inbox detail card — the operator compares the bundle's source set against the new driftSources:

  • For each drift source, determine class:
    • Refines existing claim — minor edit; surface a material-claim-update advisory but do not re-render
    • Contradicts existing claim — high-stakes; route to re-grounding
    • Retracts source — source moved to retracted state; cascade is a retraction (per V1/features.md § Retraction event)
  • Operator records the per-source decision in the drift detail (note thread on the INC)
  • Audit events per source: drift.source_classified with class + rationale

8. Decide: recheck / re-render / retract#

Three terminal paths. Each is audit-stamped against both the INC and the bundle.

A. Recheck-only (no claim materially changed)

  • Click "Mark drift as immaterial" in the INC detail card
  • Modal: rationale (≥ 50 chars) explaining why the drift does not require re-render
  • Confirm
  • Audit event: drift.recheck_immaterial
  • Artifact is unchanged on customer surfaces; no new provenance bundle is emitted; existing bundle gets an annotation entry referencing the reviewed source revision
  • INC transitions to resolved

B. Re-render (claim materially changed; artifact regenerable)

  • Click "Re-render with updated source"
  • Modal: target workflow class (defaults to the bundle's modelId + version), surface scope (which customer routes to re-render), rationale
  • Click "Confirm re-render"
  • Isis enqueues a generation job at the same workflow class; on completion a new ProvenanceBundle is appended to the ledger with a new sequence, bundleFingerprint, and updated priorFingerprint pointing to the prior bundle
  • Audit event: drift.re_render records the operator decision and selected bundle ID; the actual new-bundle append remains unchecked below
  • When new bundle is published, customer surface serves the re-rendered artifact; old artifact remains in the ledger (append-only)
  • INC transitions to resolved only after the new bundle clears Sophia publication gates (same gates as in editorial-review-approval.md step 6)

C. Retract (source was retracted; artifact unsalvageable)

  • Click "Retract artifact"
  • Modal: rationale (≥ 100 chars), notify-author toggle, customer-message toggle, takedown-cascade scope (V1/features.md § Takedown Cascade Across Kept and Shared Artifacts)
  • Click "Confirm retract"
  • Customer-facing surface serves a retracted-source notice in place of the artifact (per Veritas retraction model — see customer/05-veritas/veritas-retraction.md)
  • Kept-artifact takedown cascade fires (see V1/features.md § Takedown Cascade)
  • Audit event: drift.retract records the operator decision and selected bundle ID; linked notifications and takedown cascade execution remain unchecked above
  • INC transitions to resolved with resolution_class = 'actioned' in the current local-dev BFF state

9. Audit-trail handoff#

  • Operator returns to /operator/admin/isis/provenance?bundle=<id>
  • Selected bundle detail shows the updated state — for re-render, the new bundle's row appears in the recent-bundles <ol> with a newer ISO time
  • Verification card status remains CLEAN — hash chain intact
  • Audit-log queries for eventTypePrefix=drift. and eventTypePrefix=incident. show the full chain: incident.createddrift.source_classified (×N) → drift.<recheck_immaterial|re_render|retract>incident.resolved

Post-conditions#

  • INC state: resolved
  • Provenance ledger remains hash-chain intact (data-verification-ok="true"); the current re-render decision does not append a new bundle yet
  • The audited decision and source classifications are readable from the BFF and audit log, and the shared incident reaches resolved
  • Customer-surface replacement/retraction, cache invalidation, and author notification remain open downstream effects rather than claimed postconditions

Failure modes#

  • Hash chain BROKEN at index N — entire workflow halts. Verification card paints data-verification-ok="false". The drift INC cannot be resolved until a separate S1 ledger-integrity INC is closed by platform security. The bundle detail still renders but every action is disabled
  • Watermark hash mismatch — the published artifact's watermark doesn't match the bundle's watermarkHash field. Indicates the artifact has been replaced outside of Isis. Auto-escalates to T&S as deepfake / tamper class
  • Source moved to retired mid-resolution — operator selected re-render but the source moved to retired between classification and execution; re-render is aborted with an "underlying source retired" modal; operator must restart with retract path or pick a substitute source (V1/features.md § Replacement)
  • Re-render fails (provider outage or eval-gate failure) — new bundle not appended; INC stays open; an operational sub-INC fires against the provider. Original artifact stays live until re-render succeeds or the operator pivots to retract
  • Malformed ?bundle= query paramloadIsisProvenanceData returns empty selectedBundle; the detail article renders prompt copy. The operator-admin.md flags whether this 404s or shows empty as an open question (verify on walk)
  • Empty bundles for a tenantdata-testid="isis-provenance-empty-state" paragraph renders: "No provenance bundles persisted for this tenant yet."
  • Operator attempts retract on an artifact that has been kept/shared externally — retract path proceeds but the takedown-cascade modal enumerates the kept/shared surfaces (per V1/features.md § Takedown Cascade Across Kept and Shared Artifacts); operator must acknowledge each surface before confirm
  • Bad actor: operator marks material drift as recheck_immaterial without rationale — rationale validation blocks; cannot bypass
  • Concurrent drift INCs on the same artifact (multiple sources move together) — debouncer merges to one INC with multiple drift-source rows; operator handles them as a set in step 7

E2E coverage#

Spec: apps/oshun/web/e2e/citation-drift-resolution.spec.ts

Steps covered: operator opens /operator/admin and sees the S1 INC (step 3); follows the detail-card "Open provenance" handoff to /operator/admin/isis/provenance?inc=INC-2041, verifies the hash-chain verification article (data-verification-ok, status text, chain-length, <time> checkedAt), platform tenant-scope fallback, bundle index row metadata and row <time> values, tenant-preserving bundle links, and 16-field detail (step 5-6); asserts the empty bundle-detail state when no ?bundle= param is present; asserts the unknown-bundle fallback, the isis-provenance-empty-state when the tenant has no bundles, an explicit broken-ledger verdict (data-verification-ok="false", BROKEN at index 1) with terminal actions disabled, and the anonymous redirect-before-render guard; verifies the GeneratedArtifactProvenanceInspector renders with surfaceRole="editorial", all five artifact kinds, and a valid JSON bundle textarea (step 4 / step 9). The 2026-06-30 pass adds the terminal action surface under the selected bundle: the browser blocks a too-short rationale, records drift.recheck_immaterial, drift.re_render, and drift.retract through the real BFF POST /v1/admin/citation-drift/:incidentId/resolutions, reads the per-operator resolution list back from the BFF, and verifies matching drift.* rows in /v1/admin/audit-log/events. The 2026-07-01 pass adds the per-source classification surface: the browser classifies the three drift sources from the operator inbox (Sci. Adv., 2025, Nature Cities, May, Lancet Planetary, Apr) through the real BFF POST /v1/admin/citation-drift/:incidentId/source-classifications, blocks a too-short classification rationale, reads source classifications back, and verifies drift.source_classified audit-log rows for the same operator. The 2026-06-30 incident-lifecycle follow-up resets the real operator incident store before each browser run, records a chosen retract decision after source classification, reads INC-2041 back from /v1/admin/operator-incidents/INC-2041 as resolved, proves the incident history still starts with the seeded created entry and ends with the operator resolved entry, and verifies the linked incident.resolved audit row carries the drift resolution id/event payload.

Coverage depth: partial — see coverage.md. The pages are real (openAuthenticatedShellPath) and Playwright starts Next with the harness-only OSHUN_ISIS_PROVENANCE_LEDGER_FIXTURE=clean path, guarded by OSHUN_ENABLE_TEST_HARNESSES=true. That fixture is built through the same canonical Isis in-memory bundle/ledger helpers the durable store replays, so the hash-chain verdict and 16-field bundle assertions now run every local E2E pass instead of sitting behind test.skip(). The browser spec also drives explicit unavailable, unknown-bundle, empty-tenant, broken-ledger, and anonymous-redirect branches. The terminal write path now records audited BFF receipts for all three choices, and the source-classification write path now records audited BFF receipts for each drift source. A chosen terminal decision now also transitions the shared operator incident state to resolved and emits incident.resolved with a payload linking the drift resolution. The journey remains partial because the actual Isis generation append for re-render, customer-surface retraction cascade, and explicit incident.created admin-audit emission are not yet driven.

Uncovered: steps 1-2 (background Sophia cascade — no UI); step 8 downstream effects after the recorded decision (new bundle append/publication gates for re-render, customer notice and kept-artifact takedown cascade for retract, and customer-surface cache purge); and the explicit incident.created admin-audit row in the step 9 chain. The drift.source_classified, drift.* terminal audit events, incident.resolved audit event, and shared incident created→resolved history are now read back over real BFF routes.

Per-view files touched by this journey#

Cross-references#

  • Sibling journeys:
  • Feature spec:
    • V1/features.md § Sophia (Source Lifecycle, Downstream Invalidation), § Isis (Provenance Bundles), § Veritas Retraction
  • Component sources:
    • apps/oshun/web/src/components/admin/IsisProvenanceInspector.tsx
    • apps/oshun/web/src/components/admin/CitationDriftSourceClassifier.tsx
    • apps/oshun/web/src/components/admin/CitationDriftResolutionActions.tsx
    • apps/oshun/web/src/components/provenance/GeneratedArtifactProvenanceInspector.tsx
    • apps/oshun/web/src/lib/server/isis-provenance-loader.ts
    • apps/oshun/bff/src/routes/admin-citation-drift-resolution.ts
    • apps/oshun/bff/src/admin/citation-drift-resolution-store.ts
  • Tests:
    • apps/oshun/web/src/components/admin/__tests__/IsisProvenanceInspector.test.tsx
    • apps/oshun/web/src/components/admin/__tests__/CitationDriftSourceClassifier.test.tsx
    • apps/oshun/web/src/components/admin/__tests__/CitationDriftResolutionActions.test.tsx
    • apps/oshun/bff/src/admin/citation-drift-source-classification-store.test.ts
    • apps/oshun/bff/src/admin/citation-drift-resolution-store.test.ts
    • apps/oshun/bff/src/__tests__/admin-citation-drift-resolution-route.test.ts
  • Provenance bundle contract: 17-field IsisProvenanceBundleView interface, enumerated in operator/operator-admin-isis-provenance.md § Provenance bundle contract

Open questions#

  • Confirm whether operators need a separate "re-run hash-chain verification" affordance; the current page records the terminal drift.recheck_immaterial decision but still reads ledger verification from server data only
  • Document the BFF endpoint that triggers the actual generation job after a recorded drift.re_render decision; the new resolution endpoint records the audited operator decision but does not append a new Isis bundle
  • Snapshot the per-class severity thresholds: which materially-changed claim counts produce S1 vs S2 vs S3
  • Document the downstream invalidation event names emitted to Veritas, Metis, Nisaba, Tara, Living Scenes (V1/features.md lists them but the typed event names aren't pinned here)
  • Replace the generic takedown-cascade scope picker with a surface-aware enumerator for all kept/shared surfaces, or document the route that owns that enumeration
  • Determine which tenant-scoped vs platform-scoped drift events appear in the (no tenant — platform ledger) view