Skip to content

fix(desktop): enforce observer archive policy reconciliation on internal builds#1923

Merged
wpfleger96 merged 1 commit into
mainfrom
duncan/observer-archive-policy-reconcile
Jul 16, 2026
Merged

fix(desktop): enforce observer archive policy reconciliation on internal builds#1923
wpfleger96 merged 1 commit into
mainfrom
duncan/observer-archive-policy-reconcile

Conversation

@wpfleger96

@wpfleger96 wpfleger96 commented Jul 15, 2026

Copy link
Copy Markdown
Member

Summary

  • On internal builds, observer archive reconciliation now unconditionally ensures kind 24200 exists in the owner_p DB subscription on every startup, regardless of stale localStorage markers ("0" or "1"). A prior opt-out can no longer suppress the observer archive — the DB row is authoritative for internal policy.
  • OSS builds are a pure no-op — the reconciler returns immediately when the policy flag is off. The Settings toggle is the only mutation path for OSS users.
  • Observer reconciliation is identity-scoped: changing pubkey resets readiness so the old ArchiveSyncManager tears down before the new identity's reconciliation completes. The gate is exposed as a pure isReconciledFor helper, directly unit-testable without React mount infra.
  • Observer reconciliation runs eagerly (not behind useDeferredStartup) and must complete before ArchiveSyncManager opens relay listeners, closing the startup race where kind 24200 frames were permanently lost.
  • Settings toggle uses tri-state policy (undefined = unresolved, true = locked, false = OSS): disabled while unresolved, fail-closed on flag error, OFF only when policy is positively known as OSS. checked reflects actual DB state, never synthesized from policy.
  • Compiled-flag Rust test reads an independently supplied runtime expected value; desktop-tauri-test-compiled-flags Just recipe proves both clean (false) and internal (true) compiled states, wired into CI after desktop-tauri-test.
  • Mock E2E bridge now supports observer_archive_default_enabled, agent_metric_archive_default_enabled, merge_save_subscription_kinds, and remove_save_subscription_kind. A Playwright spec covers Settings toggle states (unresolved/internal/OSS) and asserts archive sync reaches the subscription path post-reconciliation, exercising the real AppShell gate wiring; registered in the smoke project.

Related: buzz-releases#62 (PR pipeline env var parity + validation guard)

Changed files

File Change
desktop/src/features/local-archive/useObserverArchiveSeed.ts Reconciler: policy-on merges unconditionally, policy-off is a no-op. hasExplicitChoice removed from deps. Hook stores reconciledPubkey; identity-scoped readiness delegates to exported pure isReconciledFor.
desktop/src/features/local-archive/useObserverArchiveSeed.test.mjs OSS tests assert zero merge + zero writes for all marker states; ordering test drives real ArchiveSyncManager + real reconciler with deferred promise; identity transition and rejection tests assert against the real exported isReconciledFor.
desktop/src/features/local-archive/ui/LocalArchiveSettingsCard.tsx Tri-state policy (undefined/true/false). Toggle disabled while unresolved or policy-locked. Fail-closed on flag error. checked reflects actual DB state. Guard inside handleObserverToggle.
desktop/src/features/local-archive/archiveSyncManager.ts Removed ready = true default on useArchiveSync — single caller must pass explicitly.
desktop/src/app/AppShell.tsx Observer reconciliation runs eagerly; useArchiveSync gated on success. Metric seed stays deferred.
desktop/src/testing/e2eBridge.ts Added bridge cases for observer_archive_default_enabled, agent_metric_archive_default_enabled, merge_save_subscription_kinds, remove_save_subscription_kind with configurable mock policy, plus an IPC call counter for list_save_subscriptions used by the E2E reconciliation-gate assertion.
desktop/tests/helpers/bridge.ts Added observerArchiveDefaultEnabled mock option.
desktop/tests/e2e/observer-archive-policy.spec.ts New spec: Settings toggle disabled/enabled/checked states across internal, OSS, and unresolved policy; reconciliation-gate test asserting list_save_subscriptions is reached only after successful reconciliation. Registered in playwright.config.ts's smoke project.
desktop/src-tauri/src/commands/observer_archive.rs Updated comments to describe reconciliation/policy (not first-run seeding). Rust test is #[ignore]d and reads BUZZ_TEST_EXPECTED_OBSERVER_ARCHIVE_DEFAULT at runtime, panicking if unset, so it can't silently no-op.
desktop/src/shared/api/tauriArchive.ts Updated observerArchiveDefaultEnabled docstring to describe policy enforcement (not auto-seeding).
Justfile Added desktop-tauri-test-compiled-flags recipe, running the focused Rust test under both clean and internal-flag compilations.
.github/workflows/ci.yml Wired desktop-tauri-test-compiled-flags into the desktop Tauri CI job after desktop-tauri-test.

@wpfleger96 wpfleger96 requested a review from a team as a code owner July 15, 2026 22:31
@wpfleger96 wpfleger96 marked this pull request as draft July 15, 2026 22:33
@wpfleger96 wpfleger96 force-pushed the duncan/observer-archive-policy-reconcile branch 2 times, most recently from 4290f11 to 3b14c18 Compare July 16, 2026 18:19
…nal builds

Internal builds unconditionally reconcile the kind-24200 owner_p DB
subscription every startup, ignoring stale localStorage markers. OSS
builds are a pure no-op — the Settings toggle is the only mutation path.

Reconciliation is identity-scoped and must succeed before
ArchiveSyncManager opens relay listeners, closing the startup race
where ephemeral observer frames were permanently lost.

Settings toggle uses tri-state policy (unresolved / locked / OSS):
disabled while unresolved, fail-closed on flag error, OFF only when
policy is positively known as OSS. Checked state reflects actual DB
subscription, never synthesized from policy.

Compiled-flag Rust test reads an independently supplied runtime expected
value; dual-compile Just recipe is committed and wired into CI, proving
both clean (false) and internal (true) states on every run.

Identity-scoped readiness is exposed as a pure isReconciledFor helper so
the gate can be unit-tested directly instead of shadowed by a test-local
variable.

Settings toggle states (unresolved/internal/OSS) and the post-
reconciliation subscription path are covered by a Playwright E2E spec,
registered in the smoke project so it actually runs.

Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
@wpfleger96 wpfleger96 force-pushed the duncan/observer-archive-policy-reconcile branch from 3b14c18 to 5fc3160 Compare July 16, 2026 18:42
@wpfleger96 wpfleger96 marked this pull request as ready for review July 16, 2026 19:25

@wesbillman wesbillman left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

The reconciliation ordering, identity-scoped gate, internal/OSS policy split, Settings tri-state behavior, and test coverage look correct. I found no code blocker. I am withholding approval because the head is currently 7 commits behind main; please rebase and rerun CI, then I can give the final approval against the current merge base.

@wpfleger96 wpfleger96 merged commit 8969745 into main Jul 16, 2026
48 of 49 checks passed
@wpfleger96 wpfleger96 deleted the duncan/observer-archive-policy-reconcile branch July 16, 2026 19:47
atishpatel added a commit that referenced this pull request Jul 16, 2026
…adata

* origin/main:
  feat: add invite QR and mobile direct join (#1957)
  feat: open add community from deep links (#1970)
  fix(desktop): enforce observer archive policy reconciliation on internal builds (#1923)
  feat(desktop): streamline nostr identity pairing (#1974)
  feat(desktop): ensure starter channels during onboarding (#1937)
  test(desktop): make omission-preservation test discriminate on instructions (#1988)
  fix(desktop): omission must not wipe team persona_ids/instructions (#1985)

# Conflicts:
#	Cargo.lock
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.

2 participants