Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
e88f41b
feat(review): HTML annotation substrate + doc-frame runtime (attn-61t)
angusbezzina Aug 5, 2026
102e4f8
test(review): real-browser tests for the HTML annotation runtime (att…
angusbezzina Aug 5, 2026
b4e094b
chore(beads): close attn-05x/80u/yob/17g/ges; record design deviation
angusbezzina Aug 5, 2026
db85b03
feat(review): comment on shared HTML documents end to end (attn-lon, …
angusbezzina Aug 6, 2026
bbf8650
test(review): exercise the shipped bridge, not a stand-in (attn-08r)
angusbezzina Aug 6, 2026
1df07e2
feat(review): wire HTML annotation into the native shell too (attn-08r)
angusbezzina Aug 6, 2026
cd107cd
test(review): dual-instance E2E for HTML annotation (attn-7ev)
angusbezzina Aug 6, 2026
5c94656
style: rustfmt manager.rs after the Phase 6 command additions
angusbezzina Aug 6, 2026
0385917
chore(beads): close attn-61t; file security-review, verification, and…
angusbezzina Aug 6, 2026
4fee2c5
fix(review): harden HTML annotation after adversarial review (attn-mz25)
angusbezzina Aug 7, 2026
4f86651
test(fixtures): an HTML document worth smoke-testing annotation against
angusbezzina Aug 10, 2026
069ca18
fix(ui): stop dialog content overflowing its frame horizontally (attn…
angusbezzina Aug 10, 2026
7e34f50
fix(dev): dev-collab readiness probe assumed a markdown fixture
angusbezzina Aug 11, 2026
5bb122b
Add HTML commenting across owner surfaces
angusbezzina Aug 12, 2026
d8b0072
docs: record hosted web Impeccable audit
angusbezzina Aug 12, 2026
2aa663d
Remediate hosted web audit findings
angusbezzina Aug 12, 2026
2605826
Complete hosted audit integration gate
angusbezzina Aug 12, 2026
cca1204
fix(ui): native icon resolver looped Svelte effects and blanked every…
angusbezzina Aug 13, 2026
d329a7d
Make HTML element commenting actually work end to end (attn-yqun)
angusbezzina Aug 14, 2026
4b27b1f
fix(review): route HTML comments to the focused room; tear down revok…
angusbezzina Aug 14, 2026
d61d6eb
chore(dev): add task dev:site / dev:app, sync html-annotation task de…
angusbezzina Aug 14, 2026
900330f
fix(build): collapse nested if in runtime injection to satisfy clippy
angusbezzina Aug 14, 2026
cec2d06
Darken the modal scrim so a dialog reads as the only thing to attend to
angusbezzina Aug 14, 2026
865bc09
fix(test): resident notification burst raced a 15ms debounce on CI
angusbezzina Aug 14, 2026
a3f6f25
fix(relay): anti-enum tests blew their budget on harness overhead, no…
angusbezzina Aug 10, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .beads/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,5 @@
# - linear.api-key
# - github.org
# - github.repo

sync.remote: "git+ssh://git@github.com/lightsofapollo/attn.git"
42 changes: 42 additions & 0 deletions .beads/interactions.jsonl

Large diffs are not rendered by default.

1,047 changes: 568 additions & 479 deletions .beads/issues.jsonl

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ web/playwright-report/
web/test-results/
web/src/lib/icons/vscode-generated/
web/src/lib/vscode-icon-map.generated.ts
web/src/lib/vscode-icon-packs/

# wrangler dev state
web/.wrangler/
134 changes: 134 additions & 0 deletions .impeccable/critique/2026-08-12T18-21-40Z__web-hosted.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
---
target: hosted web product in web/
total_score: 27
max_score: 40
na_heuristics: ""
p0_count: 0
p1_count: 6
timestamp: 2026-08-12T18-21-40Z
slug: web-hosted
---
Method: dual-agent (A: /root/impeccable_design_review · B: /root/impeccable_detector_evidence), with an independent technical audit by /root/impeccable_technical_audit

# Critique — hosted web product

Scope: `web/` only — homepage `/`, Desk `/app`, owner workspace `/app/w/:workspaceId/:filePath`, invited review `/review/:roomId` and `/s/:shareId`, and unknown/malformed routes. The separate `site/` package is deferred; it must later inherit the same brand and state grammar.

## Design Health Score

| # | Heuristic | Score | Key issue |
|---|---|---:|---|
| 1 | Visibility of System Status | 3 | Save, storage, and share states are strong; bad routes and review failures conceal what happened. |
| 2 | Match System / Real World | 3 | Desk/workspace/review fit; invited-review failures expose fragment/relay internals. |
| 3 | User Control and Freedom | 3 | Core navigation and mobile sheets work; review failures offer no recovery action. |
| 4 | Consistency and Standards | 3 | Core themes and surfaces cohere; lifecycle and not-found states lose the product grammar. |
| 5 | Error Prevention | 3 | Local persistence and destructive flows are thoughtful; routing creates false-success states. |
| 6 | Recognition Rather Than Recall | 3 | Core actions are labeled; missing-key recovery depends on finding the original link elsewhere. |
| 7 | Flexibility and Efficiency | 3 | Strong daily-user workspace; review history differs between desktop and mobile. |
| 8 | Aesthetic and Minimalist Design | 3 | Authored and restrained; mobile Desk rows collapse and the homepage trust proof runs long. |
| 9 | Error Recovery | 1 | Invalid review is a dead end and unknown paths masquerade as successful pages. |
| 10 | Help and Documentation | 2 | Security evidence is unusually concrete; product failure states have no contextual help. |
| **Total** | | **27/40** | **Acceptable, close to Good** |

## Design Specificity Verdict

The steady-state product is highly authored for attn: warm PAPER/cool INK, serif reading versus sans chrome, one pencil accent, a lit document plane, and local/encrypted state language form a coherent world that cannot be transferred unchanged to generic SaaS. Specificity collapses at the seams: review loading/errors become blank utilities, malformed routes silently become valid surfaces, and mobile Desk rows no longer preserve the desktop information hierarchy.

The deterministic CLI scan reported one `broken-image` at `web/src/hosted/landing/Hero.svelte:79`; it is a confirmed false positive caused by `<img>` in an HTML comment. The actual responsive screenshot has real fallback/AVIF sources, intrinsic dimensions, alt text, and no broken image in either inspected viewport.

Browser overlay injection succeeded. Stable findings were: homepage 4 (`oversized-h1`, two `all-caps-body`, pinned-paper `cream-palette`); populated Desk 2 (`kicker-above-heading`, pinned-paper palette); owner workspace 2 (`flat-type-hierarchy`, pinned-paper palette); invalid review 2 (`flat-type-hierarchy`, pinned-paper palette). The palette signal describes the intentional design system and is not a remediation item. Unknown routes repeat the homepage findings because they incorrectly render that page.

## Overall Impression

The core surfaces already feel like one precise editorial tool. The opportunity is to extend that authority through small screens, lifecycle failures, durable review history, and routing—not to redesign the product.

## What’s Working

1. The “Lit Reading Room” identity holds across homepage, Desk, and workspace in PAPER and INK.
2. Local-first trust is operational: on-device storage, autosave, deliberate sharing, backup state, and ciphertext-only relay language appear where decisions are made.
3. The workspace puts reading first. Desktop chrome recedes; mobile uses a thumb dock and contextual Review sheet without displacing the document.

## Priority Issues

### [P1] Unknown and malformed paths are false successes

Unknown root paths render the homepage with HTTP 200; malformed `/app/*` paths render Desk; malformed review paths render invite-error UI with HTTP 200. Add strict route recognition and an intentional branded 404 with real HTTP 404 behavior. Preserve the distinction between a malformed route, a valid workspace absent on this device, and a valid review capability that is expired/revoked/denied.

Evidence: `web/src/lib/hosted/routes.ts:16-24,66-89`, `web/worker.ts`, `web/vite.browser.config.ts`, and `web/src/hosted/app/AppShell.svelte`.

Suggested command: `/impeccable harden`.

### [P1] Populated Desk rows break at phone width

At 390×844, sharing state overlaps the title and metadata collapses into narrow word-per-line columns. Replace the inherited desktop grid with an explicit mobile card hierarchy: title/admin, wrapping metadata, review counts, then share/backup state. Keep the entire card as the open target and place administration behind a labeled overflow action.

Evidence: `web/src/hosted/app/DeskHome.svelte:374-474`, `web/src/hosted/app/app-shell.css:297-383,2592-2620`.

Suggested command: `/impeccable adapt`.

### [P1] Reviewer lifecycle states are unbranded dead ends

Invalid `/review` and fragmentless `/s` states expose internal error messages, have no semantic heading, and offer zero actions. Build one branded lifecycle shell for loading, invalid, denied, expired, deleted, revoked, offline, and bootstrap failure. Each state needs plain-language diagnosis, an honest privacy reassurance where relevant, and state-appropriate retry/paste/new-link/Home/Desk actions. Raw diagnostics stay in logs.

Evidence: `web/src/BrowserReviewApp.svelte:1340-1400`, `web/src/browser-review.ts:133-171`.

Suggested commands: `/impeccable harden`, `/impeccable clarify`.

### [P1] Review history disappears when “live” is false

Mobile demo behavior exposes persisted cards while desktop gates its rail on `reviewRoomActive`. Product decision: history remains available; “live” is connection state only. Establish one durable review projection and one count/label model across desktop and mobile. Live connectivity adds presence and authoring; it does not decide whether history exists.

Evidence: `web/src/hosted/app/EditorShell.svelte` desktop rail versus mobile sheet; real `WorkspaceDetail.reviewCards` currently initializes empty while mock data carries cards.

Suggested commands: `/impeccable clarify`, `/impeccable harden`.

### [P1] Desktop workspace eagerly bundles the full icon catalog

Opening the desktop workspace loads a 3,332,459-byte raw / 783,523-byte gzip chunk containing thousands of SVG modules. Split the hosted icon resolver so it loads only the selected pack and icons actually rendered. Existing exact tracker item: `attn-7xl.7.8`; do not duplicate it.

Evidence: `web/src/lib/FileTree.svelte`, `web/src/lib/vscode-icon-map.generated.ts`, `web/src/lib/icons/vscode-generated`.

Suggested command: `/impeccable optimize`.

### [P1] Homepage install command overflow is not keyboard operable

Axe reports `scrollable-region-focusable` at 390×844. Make long commands focusable and arrow-scrollable, or wrap without corrupting the copyable command. Verify 320px and 200% text.

Evidence: `web/src/hosted/landing/CopyCode.svelte`, `web/src/hosted/landing/landing.css:617-622`.

Suggested command: `/impeccable adapt`.

### [P2] Homepage trust proof overwhelms the persuasion path

Keep a concise security guarantee visible. Place the full relay ledger behind an explicit “Read the threat model” disclosure while retaining direct source links. Reduce the first viewport to one primary and one returning-user action; move Import/Join to the quieter start block.

Evidence: homepage is 5,412px at 1440×1000 and 7,109px at 390×844; first viewport exposes roughly nine choices.

Suggested command: `/impeccable distill`.

### [P2] Compact product controls and one destructive dialog miss the accessibility contract

Measured controls include 15–31px-high targets across Desk, owner mobile Share, reviewer status/toggle, and review file tabs. Preserve compact visuals while providing at least 44×44 coarse-pointer hit areas. The owner file-delete `role=alertdialog` lacks the contract Svelte requires; use the shared dialog primitive or honest inline-confirmation semantics.

Evidence: `web/src/hosted/app/EditorShell.svelte`, `web/src/BrowserReviewApp.svelte`, `web/src/lib/ReviewerStatusChip.svelte`, `web/src/lib/ReviewFileNav.svelte`; `svelte-check` reports one accessibility warning at the file-delete confirmation.

Suggested commands: `/impeccable adapt`, `/impeccable harden`.

## Persona Red Flags

- James, daily owner: the desktop workspace can lose the review obligation the Desk and mobile sheet advertise; malformed bookmarked paths silently become Desk.
- Jordan, invited reviewer: internal invite/fragment language, no brand, no action, and no safe way to paste/reopen a complete link make a failure the remembered end of the journey.
- Casey, distracted mobile user: the owner workspace is strong, but the highest-priority Desk row looks corrupted and the homepage takes more than seven mobile viewports of vertical content.

## Minor Observations

- INK is a genuine second material system; preserve it.
- The pinned paper palette is intentional and detector findings against it are not issues.
- No horizontal page overflow or rendered broken images were found in the inspected desktop/mobile states.
- Connected invited-review states were not visually verified because no authenticated capability/relay fixture was available; implementation acceptance must include a real multi-role loop.

## Questions Resolved

- The full relay evidence belongs behind an explicit disclosure; a concise security guarantee stays visible.
- Review history stays available after live connectivity ends; “live” is connection state only.
- `site/` remains out of scope. Its later epic must inherit typography roles, PAPER/INK parity, one-pencil accent use, quiet voice, local-first terminology, CTA vocabulary, and shared loading/error/not-found grammar—preferably through shared tokens or parity tests.
4 changes: 4 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ task dev
task dev ATTN_PATH=tests/fixtures/basic.md
```

For the marketing site (`site/`), use `task dev:site` — same HMR workflow against `site/src`.

For the hosted browser app (desk, `/open` import page, review rooms), use `task dev:app` and visit `/open` — it proxies to the staging relay by default, so no local relay is needed.

Optional overrides:

* `DEV_HOST` (default `127.0.0.1`)
Expand Down
37 changes: 37 additions & 0 deletions Taskfile.yml
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,32 @@ tasks:
ATTN_DEV_SERVER_URL="${ATTN_DEV_SERVER_URL}" ATTN_RELAY_URL="${ATTN_RELAY_URL}" ATTN_BROWSER_REVIEW_URL="${ATTN_BROWSER_REVIEW_URL}" cargo run --bin attn -- --no-fork "${ATTN_PATH}"
'

dev:site:
desc: Run the marketing site (site/) with Vite HMR — edit site/src files and the browser updates live
cmds:
- |
bash -lc '
set -euo pipefail
if [ ! -d "site/node_modules" ]; then
echo "==> Installing site dependencies..."
(cd site && npm ci)
fi
cd site && npm run dev
'

dev:app:
desc: Run the hosted browser app (web/ browser build) — the desk + /open import page; proxies to the staging relay
cmds:
- |
bash -lc '
set -euo pipefail
if [ ! -d "web/node_modules" ]; then
echo "==> Installing web dependencies..."
(cd web && npm ci)
fi
cd web && npm run dev:browser
'

dev:collab:
desc: One-command local collab harness — boots Miniflare relay + owner + reviewer attn daemons (attn-nnj.11.7). Ctrl+C cleans up.
cmds:
Expand Down Expand Up @@ -130,6 +156,17 @@ tasks:
cmds:
- scripts/test-html-share-e2e.sh

test:html-annotation:
desc: "Headless E2E for HTML doc annotation — owner and reviewer both receive an annotatable HTML frame, while owner path mode retains local sub-resource resolution. Honors ATTN_SKIP_HTML_ANNOTATION_E2E=1."
cmds:
- scripts/test-html-annotation-e2e.sh

test:html-annotation:runtime:
desc: "Real-browser tests for the injected annotation runtime + shell bridge (attn-ges/attn-08r). Needs Chromium via `npx playwright install chromium`."
dir: web
cmds:
- npm run test:e2e:html-annotation

test:webrtc:
desc: "Run the WebRTC end-to-end test (Rust transport + bash daemon shape). Honors ATTN_SKIP_WEBRTC_E2E=1 on flaky CI."
cmds:
Expand Down
8 changes: 7 additions & 1 deletion planning/collab/amendments.md
Original file line number Diff line number Diff line change
Expand Up @@ -346,10 +346,16 @@ The product is reframed as **agentic collaboration** — primary use case is an

**18. Live collaboration authority is published-snapshot-epoch-bound and browser-checkpointed.** Every `submit`, `broadcast`, and `resync` collab message carries the current published `snapshotId` as its authority epoch; missing epochs are accepted only by an explicitly legacy authority. Reviewers accept authoritative broadcasts only from a device whose authenticated room-directory kind is `owner`. A browser owner persists each accepted batch before authority commit/broadcast in a workspace-key-sealed checkpoint whose base snapshot equals the epoch. Checkpoints are keyed by room/file/epoch, version-CASed, and fenced by the single-writer workspace lease, so reload/takeover catches up without duplicate application and a delayed old tab cannot roll state back. Collab signals remain ephemeral; durable comments, suggestions, and decisions continue through the mailbox while live authority is absent. Pins: `data-model.md` Transport Ownership and Snapshot Graph; browser-owned sharing Step 4.

### HTML annotation (decisions 19–20)

**19. The HTML annotation runtime runs inside the existing opaque-origin sandbox, not a distinct localhost origin.** This supersedes the "Design B" decision recorded on epic `attn-61t` (2026-06-18), which called for serving the shared document from a real `localhost` origin so the frame would have a checkable origin. Design B is unimplementable for the hosted browser reviewer, which has no Rust process: it renders decrypted bytes via `srcdoc`, so there is no server, port, or origin to serve from. Adopting it would have supported only the native daemon and left every browser-side reviewer unable to comment. The opaque-origin sandbox is retained (`allow-scripts` without `allow-same-origin`), the runtime is injected into the HTML source as a pure content transform so native and hosted run identical code, and the shell↔frame channel is established by a `hello` → `MessageChannel` handshake bound on `event.source` (origin checking is useless on an opaque frame, where `event.origin === "null"`, and unnecessary once traffic moves to a private `MessagePort`). Retaining the opaque origin also denies the untrusted document the storage and same-origin capability a real origin would have granted it. Pins: `html-annotation.md` §1.

**20. The HTML document frame is untrusted input; page scripts keep running.** The frame may *propose* anchors and *report* geometry; it may never create, mutate, or resolve review state. Comment bodies and the submit action originate in shell-owned UI. This is required because the document is agent-authored or peer-supplied and its own scripts share a JS context with the injected runtime — a hostile page can tamper with the runtime and forge protocol messages, so the trust boundary must sit at the shell rather than inside the frame. Given that boundary, the document's own scripts are deliberately left enabled (a nonce CSP admitting only the runtime was rejected): self-contained AI-generated HTML routinely needs charting/animation libraries, and a review surface that renders every chart blank is not a review surface. The residual risk is bounded to a misleading anchor *preview*, which the user sees in the composer before committing. HTML anchors are correspondingly **unverified-by-authority** — Rust cannot check them without an HTML parser, and a bad anchor yields a misplaced highlight, not corrupted review state. attn-z64t applies the same decision separately to the native owner, hosted author, and hosted reviewer: all are share-first, all retain `allow-scripts` without `allow-same-origin`, and the hosted author gets no additional confirmation beyond their explicit share action. Pins: `html-annotation.md` §3, §4, §7.

### Inconsistencies Fixed

- `data-model.md` line 202: `"attn file"` → `"attn file v2"` to match the v2-suffix convention used everywhere else in the key derivation tree (`crypto-spec.md` uses the v2 form).

---

**Total: 18 decisions, all previously-open questions closed.** Open implementation work is now bounded by the work itself, not by undecided design.
**Total: 20 decisions, all previously-open questions closed.** Open implementation work is now bounded by the work itself, not by undecided design.
Loading
Loading