Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Add: tab-scoped recovery pause, in one unified popup off-switch card (spec 0010 FR-7e–h, ADR-0018/0019)#237

Merged
twschiller merged 2 commits into
mainfrom
tab-scoped-recovery-pause
Jun 9, 2026
Merged

Add: tab-scoped recovery pause, in one unified popup off-switch card (spec 0010 FR-7e–h, ADR-0018/0019)#237
twschiller merged 2 commits into
mainfrom
tab-scoped-recovery-pause

Conversation

@twschiller

@twschiller twschiller commented Jun 9, 2026

Copy link
Copy Markdown
Contributor

What

Two tab-scoped, non-persistent recovery controls in the popup for the "this page looks broken" case — deliberately distinct from the permanent Disable on this site denylist (ADR-0018):

  • Reveal everything on this page — one-click panic button. Reveals everything the shield hid and stops re-hiding for the current page load. Page-scoped: cleared on the next top-frame navigation, so a reload restores protection.
  • Time-boxed snoozeUntil closed / 15 min / 1 hour. Tab-scoped: survives navigation within the tab (so a multi-page checkout stays unblocked) until expiry or tab close. The popup shows a live countdown and a Resume now button.

Why

When a rule eats real content today, the only escapes are hunting for the per-element reveal, or writing a permanent denylist entry a daily driver never cleans up — silently unprotected on that site forever. There was no panic button and no "just let me through this once."

How

  • One mechanism, reused teardown. Both controls write a per-tab TabPause into a webext-storage StorageItemMap on the session area. It becomes a third input to effective-enforcement.ts (global && !denylist && !tabPaused); flipping it false routes through the engine's existing revealAll + teardown path — no new reveal code.
  • chrome.storage.session survives MV3 service-worker restarts and auto-clears on browser restart — exactly the ephemerality we want, no cleanup debt.
  • No chrome.alarms, no new permissions. Liveness is lazy (expiresAt > now). A timed snooze that expires while the page is open leaves the page revealed and re-engages protection on the next navigation — a forgotten timer never yanks content back mid-task. Manual Resume now re-enforces immediately.
  • Toolbar gains a "paused" off-reason, reusing the greyed icon + off badge (FR-2a).
  • Content bridge: content scripts can't read the session area or know their own tabId, so the background resolves liveness for them via get-tab-pause + tab-pause-changed messages. Popup and background (privileged) read/write the map directly, mirroring the denylist flow.

Popup hierarchy

Landing recovery as a third standalone card under Disable on this site left the popup with four ungrouped "turn protection off" controls whose visual weight inverted their scope — the narrowest, most ephemeral action (Reveal) shouted loudest with an orange fill.

Consolidated the site-disable + recovery cards into one Protection card with an internal scope ladder, each row carrying an eyebrow that states scope + persistence once:

  • This tab · temporary — Reveal (now an orange outline, not a fill) + the tab snooze.
  • This site · saved — the denylist toggle.

New ProtectionSection wraps both rows; the divider is a CSS adjacent-sibling rule, so it only appears when both render (recovery still drops out on denylisted / non-content pages, leaving the site row alone). Also shortened the indefinite-pause label to Until closed so it stops wrapping to three lines at 280px.

Docs

  • Spec 0010 — new FR-7e–FR-7h, updated FR-2a / FR-4, user story, current-impl, related.
  • ADR-0019 — the session-scoped, alarm-free, lazy-expiry decision (incl. rejected alternatives).
  • docs/install.md — user-facing "Recovering a page the shield broke" section.

Verification

  • bun run preflight (build → biome+eslint → tsc → knip → coverage): green; 2027 tests pass, including new tab-pause unit + fast-check property tests and extended toolbar-protection tests.
  • bun run build: bundles cleanly; background-worker purity canary passes.
  • Popup hierarchy refactor verified against the real popup CSS in all three states (default, paused, denylisted) at the 280px popup width.
  • Markdown pre-commit run: mdformat + markdownlint pass.

Review note

A denylisted-and-paused tab reports the site toolbar reason (the popup only offers recovery when not denylisted, so they don't overlap in practice) — documented as a consequence in ADR-0019.

🤖 Generated with Claude Code

…e (spec 0010 FR-7e–h, ADR-0019)

Fast, reversible recovery for the "this page looks broken" case, distinct
from the permanent per-site denylist:

- "Reveal everything on this page" — one-click panic button; reveals all
  hidden content and stops re-hiding for the current page load. Page-scoped,
  cleared on the next top-frame navigation.
- Time-boxed snooze — "Pause for this tab" / "15 min" / "1 hour". Tab-scoped,
  survives navigation within the tab (so a checkout stays unblocked) until
  expiry or tab close. Live countdown + "Resume now".

Both are stored per tab in chrome.storage.session via webext-storage's
StorageItemMap, so they survive service-worker restarts and auto-clear on
browser restart — no denylist-style cleanup debt. The pause is a third input
to effective-enforcement, reusing the engine's existing reveal-all + teardown
path. No chrome.alarms and no new permissions: liveness is lazy, so a timed
snooze that expires while the page is open leaves it revealed and re-engages
protection on the next navigation. The toolbar gains a "paused" off-reason
(greyed icon + off badge). Content scripts learn pause state by message since
they can't read the session area or know their own tabId.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
@vercel

vercel Bot commented Jun 9, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
agent-browser-shield-demo-site Ready Ready Preview, Comment Jun 9, 2026 10:57pm

Request Review

…010, ADR-0018/0019)

The four "turn protection off" controls (global Enforcement, Disable on
this site, Reveal everything, Pause this tab) had no scope hierarchy and
inverted visual weight — the most ephemeral, narrowest action (Reveal)
shouted loudest with an orange fill.

Collapse the site-disable + recovery cards into one "Protection" card
with an internal scope ladder, each row labeled by an eyebrow that states
scope + persistence once: "This tab · temporary" (reveal + tab snooze)
then "This site · saved" (denylist). Demote Reveal to an orange outline
so it stays findable without dominating. New ProtectionSection wraps both
rows; the divider is a CSS adjacent-sibling rule so it only shows when
both rows render (recovery still drops out on denylisted/non-content
pages, leaving the site row alone).

Also: shorten the indefinite-pause label to "Until closed" so it stops
wrapping to three lines at 280px, and drop the now-stale "use Disable on
this site above" cross-reference (that control is now an adjacent row).

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
@twschiller twschiller changed the title Add: tab-scoped recovery pause — reveal-everything + time-boxed snooze (spec 0010 FR-7e–h, ADR-0019) Add: tab-scoped recovery pause, in one unified popup off-switch card (spec 0010 FR-7e–h, ADR-0018/0019) Jun 9, 2026
@twschiller twschiller added the enhancement New feature or request label Jun 9, 2026
@twschiller twschiller merged commit b4ffcb7 into main Jun 9, 2026
7 checks passed
@twschiller twschiller deleted the tab-scoped-recovery-pause branch June 9, 2026 23:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant