A's org foundation returns org_not_configured (ok:false) plus thrown not_an_org_member /
device_key_unavailable, none of which had user-facing copy — they would render as the raw
code. Add friendly copy (org_not_configured ties into the add-org flow) and a focused test;
fixes the ERROR_COPY-coverage test that the foundation merge left red.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01W3b8DA2mXmLWkd4zDbHXHg
Wires 4 SW messages to the org-vault.ts read functions (Task 5b):
- org_switch: opens org vault via openOrg(), caches OrgHandleState, sets
session context; switching to 'personal' resets context without openOrg.
Offline-aware: if openOrg throws a network error and a prior cached state
exists, reuses it read-only (offline:true) via exported isNetworkError.
- org_list_items: returns cached, grant-filtered OrgManifestEntry[] (not
personal ManifestEntry[]) via listOrgItems(requireCurrentOrgState()).
- org_get_item: decrypts a single org item via getOrgItem.
- org_list_collections: returns granted Collection[] via listOrgCollections.
Three-place rule satisfied for all 4 messages:
1. messages.ts PopupMessage union (4 variants)
2. messages.ts POPUP_ONLY_TYPES (4 entries)
3. popup-only.ts switch (4 case arms)
Cache lifecycle / use-after-free guard:
- clearOrgCache() added to org-handlers.ts; exports.
- Called in popup-only.ts 'lock' handler (before session.clearCurrent).
- Called in index.ts inactivity-timer onExpired (before clearCurrent).
Plan doc fixes:
- Line ~410: org_list_items return type corrected to OrgManifestEntry[].
- Line ~47: org_unwrap_key signature updated (no device_private_key_base64
param — reads DEVICE_STATE internally).
9 new tests in org-handlers.test.ts (TDD: all watched fail before passing).
Full SW suite: 167 tests (17 files) green. build:all type-checks clean.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014s527M917W47LDrfQ4t47g
Introduces OrgConfig/OrgConfigSummary types, chrome.storage.local backing
via org-config.ts, and the first org SW message (org_list_configs) wired in
all three required places: PopupMessage union, POPUP_ONLY_TYPES set, and a
case arm in popup-only.ts. Handler projects to OrgConfigSummary — apiToken,
hostUrl, repoPath, memberId never leave the SW. 3 new TDD tests (RED→GREEN).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014s527M917W47LDrfQ4t47g
chrome.runtime.sendMessage serializes with a JSON-like algorithm that drops
ArrayBuffer/TypedArray payloads — they arrive as empty objects, so a carrier
JPEG reached the service worker as 0 bytes and failed with the opaque WASM
error "no SOF marker found in JPEG". This broke extension vault creation.
- shared/message-binary.ts: encodeBinary/decodeBinary deep-walk a message tree
and base64-envelope every binary buffer so it survives the channel (chunked
base64 to avoid stack overflow on multi-MB buffers); plain number[] untouched.
- Wire encode/decode at all four message boundaries: popup, service-worker
onMessage listener (decode request, encode response), setup swSend, vault
postToServiceWorker.
- handleCreateVault: magic-byte guard classifies empty / non-JPEG carriers into
actionable errors (carrier_image_empty / carrier_image_not_jpeg) instead of
the opaque WASM error; error-copy.ts adds user-facing text; setup errors now
route through lookupErrorCopy.
- router SETUP_ALLOWED: add create_vault / attach_vault / generate_recovery_qr —
the Phase-3 setup wizard sends these from the setup page and they were being
rejected as unauthorized_sender.
- Tests: message-binary round-trip suite (7) + carrier-guard (2) + setup
allowlist (3). 435/435 vitest, build:all clean.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pe8qw5KePDqAEBsAxnVQuJ
Renders the status indicator into #vault-status-slot on sidebar mount and on
a manual ↻ button. No timer polling — get_vault_status returns cached state
and sync is user-initiated. Closes the relicario status CLI/extension parity
gap.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Renders sidebar-footer indicator with ahead/behind/pending state. Pure
DOM; reuses shared/glyphs (four new status glyphs) and shared/relative-time.
Status fetch happens in the wiring layer (Task 6.3).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The session-lost intercept lived in vault.ts's local sendMessage; both surfaces
now consume it through the shared sendMessage() wrapper. On a vault_locked
response to any non-bypassed request, the wrapper calls host.navigate('locked').
The vault host's navigate gains a 'locked' branch (it shows its lock screen off
state.unlocked); the popup's navigate already handles 'locked'. vault.ts routes
ctx.sendMessage through the shared wrapper and registers a plain transport as
host.sendMessage, so internal RPCs keep the intercept without recursion.
grep -c vault_locked vault.ts == 0. New state-vault-locked.test.ts (TDD, 6 cases).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Adds the request shapes + response interfaces. POPUP_ONLY_TYPES set grows
by three. SW handlers in service-worker/vault.ts land in the next tasks.
The new union members would make popup-only.ts's exhaustive handle() switch
non-total (TS2366), so a default case is added returning an explicit
"unhandled popup message" error. create_vault/attach_vault get real cases
in Tasks 3.2-3.3; get_vault_status in Dev-C's Phase 6.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Replaces the previously any-typed StateHost contract with a typed interface.
Adds double-registration guard and __resetHostForTests for vitest.
sendMessage wrapper is currently a pass-through; Phase 4 will fill its body
with the vault_locked intercept lifted from vault.ts.
Widens PopupState/View on shared/popup-state.ts to cover vault-tab-only
views (history, backup, import) and vault-tab-only fields (unlocked,
drawerOpen, typePanelOpen) so VaultState satisfies StateHost.getState()
without a cast. The popup surface ignores the new optional fields.
Drops the `any` annotations on vault.ts's registerHost callbacks now that
the typed StateHost contract infers them from PopupState.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Foundation for Plan C Phase 1: shared/state.ts (next task) needs to import
PopupState without creating a popup->shared circular dep. popup.ts now
re-exports from the new location so existing callers don't break in this
task. Task 1.4 will sweep them onto the canonical import path.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
GLYPH_TYPE_IDENTITY changed from ⌬ to ◍ so it's visually distinct from
GLYPH_DEVICES (also ⌬). Adds a CSS rule asserting [hidden] over the
.form-actions display:flex so the fullscreen sticky save bar can hide
the inner action row by attribute.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- Add settings-security.ts with renderSecuritySection / teardownSecuritySection
- Three states: amber warning (no QR), green status (QR set up), modal overlay (show/print SVG)
- Device list with inline revoke; passphrase collected via prompt()
- QR payload never written to chrome.storage; only recovery_qr_generated_at timestamp stored
- Add generate_recovery_qr / unwrap_recovery_qr message types to messages.ts + POPUP_ONLY_TYPES
- Add SW handlers in popup-only.ts delegating to wasm_generate_recovery_qr / wasm_unwrap_recovery_qr
- Declare wasm_generate_recovery_qr and wasm_unwrap_recovery_qr in wasm.d.ts
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replaces the popup's regex-chain humanizeError with a total lookup over
every error code returned by extension/src/service-worker/router/. A
generated test discovers codes via grep so the registry can't drift.
The popup keeps its small set of regex translators for Rust/serde error
phrasing that doesn't go through the router's error vocabulary.
Subsumes B2 — fullscreen consumer lands in the next commit.
- Show revoked devices in collapsible section with strikethrough styling
- Fetch revoked.json via new list_revoked message + router case
- Registration flow uses register_device WASM API (private keys internal)
- Display revoked_by and timestamp for each revoked entry
- Update setup wizard to use new register_device API
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Code-review feedback on Task 1: the _HTML suffix makes the 'this is raw
HTML, do not escape' contract obvious at every call site. Cheap to do
now (zero consumers); would be 8 diffs once Tasks 4-6 wire the constant
into the type forms.
Plan updated in lockstep so Task 4 references the new name.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>