Skip to content

Complete the photo story offline, at reconnect, and under the second viewer's refusal - #33

Merged
LucaCappelletti94 merged 2 commits into
mainfrom
feat/r69-wasm-smoke-content
Sep 18, 2026
Merged

LucaCappelletti94 merged 2 commits into
mainfrom
feat/r69-wasm-smoke-content

Conversation

@LucaCappelletti94

@LucaCappelletti94 LucaCappelletti94 commented Sep 18, 2026

Copy link
Copy Markdown
Owner

This is R69 step F, the phase's last proof, and it ships one deliberate semantics change. A tab can now complete its whole protocol while the worker holds no connection, a staged photo lands in the browser store with its manifest and outbox entry and durable pending mutation, and nothing reaches the server until the connection opens, at which point R67's replay and watermark send each pending mutation exactly once and the upload and commit follow. That makes chapter 18's recovery table Frame row true in a new way, so the amendment ships in the same pull, frames are served from local state while the connection idles in recovery and stay held only while the attach or subscription replay owns the connection, with native parity and the watermark named in the rationale. The connect gate is a new boot option whose default keeps every existing worker boot byte-identical.

The offline suite proves the strong claim, staging with the socket never opened, the row visible on the mirror with content_state NULL, the server queried and still empty, then the gate opens, the outbox replays, chunks upload, and the flip to available arrives through replication with the bytes fetching back equal. The visibility suite proves the refusal travels the same mint path the owner's success takes, the second identity's resolve answers only TicketRefused, ordered owner Remote with fetch, viewer refused, owner Remote again on the same file id, so a local replica miss or upload timing cannot fake the pass, and the owner's access survives.

Verification, both new suites and photo_flow green through the live browser stack, the recovery regression trio green with the old pin rewritten to the new contract as a frame is served into pending during idle recovery, 57 lib plus all integration suites for connetto-web, fmt clean in all three workspaces and stable and nightly clippy clean for the two wasm workspaces. The plan row and both prose statuses now read D and E as the only open steps.

Summary by Sourcery

Complete the browser photo flow's offline staging, reconnect upload, and access-control proofs while preserving existing worker boot behavior by default.

New Features:

  • Support completing photo mutations while the browser worker is offline and upload them after reconnecting.
  • Add a configurable browser-worker connect gate for deterministic offline boot scenarios.
  • Prove that unauthorized viewers receive a ticket refusal while the owner retains photo access.

Enhancements:

  • Serve locally answerable frames during idle recovery while retaining ordering during attach and subscription replay.
  • Amend the file-handling recovery contract to document offline frame handling and durable exactly-once replay semantics.

Documentation:

  • Update the R69 implementation status and file-handling architecture documentation to record the completed offline and visibility proofs.

Tests:

  • Add live browser-stack coverage for offline photo staging, reconnect upload, replicated availability, byte-accurate fetching, and second-viewer refusal.

@sourcery-ai sourcery-ai 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.

Sorry @LucaCappelletti94, you've used your own review budget of 250,000 diff characters for the last 7 days.

You can request another review in 4 days and 1 hour by commenting @sourcery-ai review. Upgrade to get a review now.

@coderabbitai

coderabbitai Bot commented Sep 18, 2026

Copy link
Copy Markdown

Important

  • 🔍 Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

⚙️ Run configuration

Configuration used: Repository: LucaCappelletti94/coderabbit/.coderabbit.yaml

Review profile: ASSERTIVE

Plan: Advanced

Run ID: 4320cdb7-8630-4e42-8666-4189c81cdedb


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.

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 18, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-09-18T06:30:14.248687Z 3eefe9e PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@sourcery-ai

sourcery-ai Bot commented Sep 18, 2026

Copy link
Copy Markdown

Reviewer's Guide

Completes R69-F by making the browser worker a locally capable offline peer: photo mutations persist durably and replay exactly once after a gated reconnect, frames remain locally served during idle recovery, and end-to-end tests prove both reconnect upload behavior and owner-only visibility with native-parity recovery documentation.

Sequence diagram for offline photo staging and reconnect upload

sequenceDiagram
    participant Tab
    participant Worker
    participant Replica
    participant Pending as _connetto_pending
    participant Server
    participant FileServer

    Tab->>Worker: Frame
    Worker->>Replica: Commit photo mutation
    Worker->>Pending: Persist pending mutation
    Worker-->>Tab: Local frame result
    Note over Worker,Server: connect_gate keeps upstream closed
    Tab->>Worker: Open connect gate
    Worker->>Server: Replay pending mutation
    Server-->>Worker: R67 watermark acknowledgement
    Worker->>FileServer: Upload chunks
    Worker->>Server: Commit photo metadata
    Server-->>Worker: Replicated available state
    Worker-->>Tab: Fetch uploaded bytes
Loading

Sequence diagram for owner access and second-viewer refusal

sequenceDiagram
    participant Owner
    participant Server
    participant FileServer
    participant Viewer

    Owner->>Server: Resolve photo ticket
    Server->>FileServer: Mint owner ticket
    FileServer-->>Server: TicketRef
    Server-->>Owner: Remote with fetch
    Viewer->>Server: Resolve photo ticket
    Server->>FileServer: Mint viewer ticket
    FileServer-->>Server: TicketRefused
    Server-->>Viewer: TicketRefused
    Owner->>Server: Resolve photo again
    Server->>FileServer: Mint owner ticket
    FileServer-->>Server: TicketRef
    Server-->>Owner: Remote with fetch
Loading

Flow diagram for locally served frames during recovery

flowchart LR
    Frame[Frame arrives] --> Recovery{Idle recovery?}
    Recovery -->|Yes| Local[Serve from replica or hub state]
    Local --> Pending[Persist mutation to _connetto_pending]
    Pending --> Attach[Attach or subscription replay owns connection]
    Attach --> Replay[Replay exactly once under watermark]
    Recovery -->|Attach/replay active| Held[Hold frame until ordering is safe]
Loading

File-Level Changes

Change Details Files
Allow the browser relay to complete protocol and stage mutations while disconnected, then replay pending work on reconnect.
  • Serve Frame events from local state during idle recovery while retaining attach/replay ordering barriers.
  • Persist offline photo metadata, manifest, content, outbox entry, and pending mutation for later upload and commit.
  • Add an optional BroadcastChannel connect gate for deterministic offline boot; preserve the existing default boot path when unset.
  • Update recovery documentation and regression expectations for the amended Frame semantics.
crates/connetto-web/src/relay.rs
crates/connetto-web/src/workers/boot/mod.rs
crates/connetto-web/src/workers/boot/services.rs
docs/architecture/18-file-handling.md
Add end-to-end browser-stack coverage for offline photo recovery and owner-only visibility.
  • Exercise staging with no socket, verify local pending state and an empty server, then open the gate and verify replay, upload, replication, and byte-equivalent fetch.
  • Exercise owner success, second-identity TicketRefused, and owner success again to prove refusal uses the normal mint path without revoking owner access.
  • Generalize test login/session helpers to mint named identities and add the gated photo worker entry point.
examples/wasm-smoke/tests/photo_offline.rs
examples/wasm-smoke/tests/photo_visibility.rs
examples/wasm-smoke/src/lib.rs
examples/wasm-smoke/tests/common/mod.rs
examples/wasm-smoke/Cargo.toml
examples/wasm-smoke/Cargo.lock
Record R69-F as complete and amend the implementation plan to reflect the new recovery contract.
  • Mark offline/reconnect and two-viewer proofs as delivered while leaving D and E open.
  • Document the Frame-row amendment and link the new proof suites.
plans/master-implementation-plan.md

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@sonarqubecloud

Copy link
Copy Markdown

Quality Gate Failed Quality Gate failed

Failed conditions
17.2% Duplication on New Code (required ≤ 3%)

See analysis details on SonarQube Cloud

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 3eefe9efbb

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

| HubEvent::ForgetRetired(_, _)
| HubEvent::RefusedContent(_)
| HubEvent::RetryRefused(_, _)
| HubEvent::Frame(_, _)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Fan out offline mutations to subscribed sibling tabs

When one tab mutates a synced table during recovery, this newly served Frame is applied to the worker replica by handle_synced_mutation, but that path does not enqueue a LivePatch for the other tabs and ordinarily relies on the server echo to do so. Consequently, while the upstream remains offline, an already-subscribed sibling stays stale, whereas a tab subscribing afterward receives the new row from serve_snapshot; the tabs only converge after reconnect. The offline frame path should keep existing subscribers consistent with snapshots of the same replica.

Useful? React with 👍 / 👎.

@codecov

codecov Bot commented Sep 18, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 83.27%. Comparing base (3684c4b) to head (3eefe9e).

Additional details and impacted files
@@            Coverage Diff             @@
##             main      #33      +/-   ##
==========================================
+ Coverage   83.24%   83.27%   +0.03%     
==========================================
  Files         114      114              
  Lines       25693    25693              
  Branches    25693    25693              
==========================================
+ Hits        21388    21396       +8     
+ Misses       3079     3073       -6     
+ Partials     1226     1224       -2     
Flag Coverage Δ
client 64.21% <ø> (+0.04%) ⬆️
rest 56.36% <ø> (+0.53%) ⬆️
server 48.79% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@LucaCappelletti94

Copy link
Copy Markdown
Owner Author

Replying to the Codex P2 inline finding (comment 4044353005):

Fixed. A word on scope first: sibling fan-out has always been echo-only on the connected path. R69-F made the window observable by serving Frames during idle recovery; it did not introduce the gap.

The fix is in handle_synced_mutation. When the commit succeeds and the worker is not connected, the function collects every sibling tab subscription whose tables overlap the mutation's tables and calls serve_snapshot for each one. That issues SnapshotBegin/SnapshotPatch/SnapshotEnd from the current worker replica. The snapshot rest is cursor-free: SnapshotPatch passes None to apply_patch, so the tab's resume cursor is never touched. relay_cursor returns an empty Cursor while offline, so SnapshotEnd also carries nothing and leaves each tab's last_cursor unchanged. A sibling that subscribes after the offline commit gets the new row from its own initial snapshot and is unaffected.

On reconnect the server echo arrives as a LivePatch. The sibling applies it with the server_wins conflict handler (ConflictAction::Replace on a data conflict), landing cleanly and leaving the row count at one. The photo_offline suite now carries a second subscribed tab that asserts the row is present before the gate opens and that the count stays at one and content_state reaches available after reconnect.

@LucaCappelletti94
LucaCappelletti94 merged commit f2b69ee into main Sep 18, 2026
56 of 57 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