Document workspace design pass: themes, verdict undo, sandboxed reports, type scale - #7
Merged
Merged
Conversation
Co-Authored-By: GPT-6.1 Sol <noreply@openai.com>
Co-Authored-By: GPT-6.1 Sol <noreply@openai.com>
Co-Authored-By: GPT-6.1 Sol <noreply@openai.com>
Co-Authored-By: GPT-6.1 Sol <noreply@openai.com>
Co-Authored-By: GPT-6.1 Sol <noreply@openai.com>
Co-Authored-By: GPT-6.1 Sol <noreply@openai.com>
Co-Authored-By: GPT-6.1 Sol <noreply@openai.com>
…on budget Co-Authored-By: GPT-6.1 Sol <noreply@openai.com>
Co-Authored-By: GPT-6.1 Sol <noreply@openai.com>
Co-Authored-By: GPT-6.1 Sol <noreply@openai.com>
Co-Authored-By: GPT-6.1 Sol <noreply@openai.com>
Co-Authored-By: GPT-6.1 Sol <noreply@openai.com>
Co-Authored-By: GPT-6.1 Sol <noreply@openai.com>
Co-Authored-By: GPT-6.1 Sol <noreply@openai.com>
… asset bases Co-Authored-By: GPT-6.1 Sol <noreply@openai.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…, declared titles Ribbon thumbnails keep their caption as the only name (captioned faces). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Preserve column bands, animate card transfers with FLIP, and return Desk focus to the current constitution. Fade neighbour margins, desaturate the receded Desk, and keep the phone veil opaque. Co-Authored-By: GPT-6.1 Sol <noreply@openai.com>
…Desk-card sidebar Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: GPT-6.1 Sol <noreply@openai.com>
Co-Authored-By: GPT-6.1 Sol <noreply@openai.com>
Co-Authored-By: GPT-6.1 Sol <noreply@openai.com>
Co-Authored-By: GPT-6.1 Sol <noreply@openai.com>
Co-Authored-By: GPT-6.1 Sol <noreply@openai.com>
…arial messages Co-Authored-By: GPT-6.1 Sol <noreply@openai.com>
Reserve dot for conversation everywhere; audio forward uses Shift-dot. Pending verdicts survive navigation but not tab closure. Co-Authored-By: GPT-6.1 Sol <noreply@openai.com>
Track prior overview visits and per-fiber outcome/review changes, prioritize needed attention, and give older work compact lookup layouts. Co-Authored-By: GPT-6.1 Sol <noreply@openai.com>
Exercise first visits, reloads, inline lenses, declared-title Find, folded latest files, per-fiber acknowledgement and phone visibility in system Chrome. Co-Authored-By: GPT-6.1 Sol <noreply@openai.com>
The dot follows the served working phase; no last-step or exact foreground-turn signal is served by the daemon. Co-Authored-By: GPT-6.1 Sol <noreply@openai.com>
Co-Authored-By: GPT-6.1 Sol <noreply@openai.com>
Co-Authored-By: GPT-6.1 Sol <noreply@openai.com>
Co-Authored-By: GPT-6.1 Sol <noreply@openai.com>
Co-Authored-By: GPT-6.1 Sol <noreply@openai.com>
Co-Authored-By: GPT-6.1 Sol <noreply@openai.com>
Ran new schema/size and rate regressions against unfixed code: 2 failed (501 candidates accepted, 20 callbacks rather than 4). After fix: bridge/viewer/host tests 51 passed; targeted reference/navigation Chrome e2e 5 passed plus rate/size probe passed; typecheck passed. All message types have closed bounded schemas. References allow 500 strings of 256 characters and four batches per sliding second per frame. The injected scanner coalesces ordinary rescans to keep the latest layout reachable without bypassing the parent rate guard. Co-Authored-By: GPT-6.1 Sol <noreply@openai.com>
Apply the sandbox CSP and nosniff to /file and /file-assets independently of extension, MIME type, owner, and status. Wrap Phoenix's endpoint error boundary so malformed query/JSON errors, early gate refusals, HEAD, ranges, and cache responses retain the policy. Remove the extension denylist; add no exemptions. The hub applies its own policy rather than trusting remote headers. Ran the regressions against unchanged production code before the fix (git diff --exit-code confirmed the controller and endpoint matched HEAD): cd daemon && unbuffer mix test test/shuttle_web/controllers/file_controller_test.exs test/shuttle_web/controllers/file_relay_test.exs test/shuttle/origin_router_test.exs test/shuttle_web/relay_helpers_test.exs --seed 0 Observed 107 tests, 53 failures: XML/RSS/Atom/unknown/PDF/media and relayed/error responses lacked CSP; HTML/SVG lacked nosniff. Real httpc relay, malformed JSON/query errors, CORS failures, and booting responses also failed the header assertions. The same command passes after the fix: 107 tests, 0 failures. Additional file-invalid-range/readiness/CORS/peer/large-body-parser suites: 56 tests, 0 failures (1 excluded). mix format --check-formatted and git diff --check pass. A throwaway Bandit endpoint with Chrome 154.0.8037.93 renders the native PDF viewer and plays MP3/MP4 with both headers; screenshot inspected. Safari 26.5.2 WebDriver could not start because Allow remote automation is disabled, so Safari compatibility is unverified. Full daemon/UI suites not run; no UI changes, CLI installation, release build, live daemon restart, or push. Co-Authored-By: GPT-6.1 Sol <noreply@openai.com>
Co-Authored-By: GPT-6.1 Sol <noreply@openai.com>
Co-Authored-By: GPT-6.1 Sol <noreply@openai.com>
Co-Authored-By: GPT-6.1 Sol <noreply@openai.com>
Co-Authored-By: GPT-6.1 Sol <noreply@openai.com>
Co-Authored-By: GPT-6.1 Sol <noreply@openai.com>
Co-Authored-By: GPT-6.1 Sol <noreply@openai.com>
Co-Authored-By: GPT-6.1 Sol <noreply@openai.com>
Co-Authored-By: GPT-6.1 Sol <noreply@openai.com>
Full Chrome run exposed a rate-fixture race: a normal decoration rescan consumed one of the four slots, so the hostile sender saw three replies. Remove reference nodes and let scanner traffic settle before flooding. Production limits are unchanged. The targeted Chrome regression passes twice after fixture isolation. Co-Authored-By: GPT-6.1 Sol <noreply@openai.com>
Adversarial Chrome regression failed before fix: escaped fonts.googleapis.com hostname requested fonts.googleapis.com.evil.test and loaded unscoped CSS. Decode CSS escapes, validate the resulting HTTPS Google Fonts URL including port/credentials, and serialize that canonical URL while retaining import modifiers. Browser regression plus font/URL/scoping tests: 4 passed after fix; typecheck and diff check passed. Updated existing Google import assertion for canonical quoting. Co-Authored-By: GPT-6.1 Sol <noreply@openai.com>
Workspace integration regression run (America/Los_Angeles): red 6 failed, 28 passed (34 total); green 34 passed, 0 failed. Indexed identities that disappear cannot authorize a path write. Off-index linked fibers require a fresh owner-routed UID check. Co-Authored-By: GPT-6.1 Sol <noreply@openai.com>
KanbanModal verdict regression run (America/Los_Angeles): red 3 failed, 3 passed (6 total); green 6 passed, 0 failed. No-op verdicts cannot become delayed writes after reopening. Gesture basis, normal queuing, and commit-time worker confirmation remain covered. Co-Authored-By: GPT-6.1 Sol <noreply@openai.com>
Use the named title size for serif navigation glyphs so phone chevrons remain legible; text labels keep the UI size. Co-Authored-By: GPT-6.1 Sol <noreply@openai.com>
Co-Authored-By: GPT-6.1 Sol <noreply@openai.com>
… nosniff on every file response, one verdict path, theme font/url confinement, bounded bridge messages Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… eleven fixes Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> # Conflicts: # ui/src/board/workspace/ChannelThemes.test.ts # ui/src/board/workspace/ChannelThemes.ts
… drop, excess batches The frame and parent reference limiters read Date.now, so a backward wall-clock step (NTP, a report stubbing Date, Playwright's page.clock) deferred rescans by hours and pinned parent budget entries in the future. Both sides now use performance.now; the frame clamps its wait to [0, interval] and the parent expires future-dated entries. The parent keeps the newest batch over budget and delivers it when the window reopens, since the frame never resends. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LwX9WpuD7VnV6mfmN9eb67
The security plug matched raw path segments while the router decodes them, so /api/v1/fil%65 reached FileController without the CSP sandbox or nosniff. Match the percent-decoded first segment and default-deny: every /api response (and any undecodable path) carries both headers; the board page does not. This also covers /transcript/raw's relayed bytes. The new encoded-route test failed before this change (97 tests, 1 failure). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…o expiry Verdicts were queued for the 6 s undo window before the hasWorkerToStop confirm, so the modal fired six seconds after the click, drag, menu or plate, and every verdict on an app card with a durable thread produced one. Workspace.queueVerdict now asks at queue time (confirmWorkerStop, shared with the Dock's unqueued path and the board's no-workspace path) and carries the answer to the delayed write; an expired verdict whose card gained an unconfirmed worker is refused with a toast, like the awaiting-review recheck. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LwX9WpuD7VnV6mfmN9eb67
Under Node 22 (CI's runtime) undici's fetch/Response takes more microtask hops than Node 25, so a 25-tick flush returned before the reader mounted and 8 integration tests read an absent frame. Pre-existing since the design lanes merged at 43bee76; reproduced with node@22 and fixed by draining 200 ticks. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The parent now defers an over-budget batch until its window reopens, so the report scanner's last batch can occupy a slot past the old 1.1 s settle and the probe saw 3 responses instead of 4 (1 run in 3). Settle 2.1 s; 6/6. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
cailmdaley
marked this pull request as ready for review
October 5, 2026 09:39
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This is the visual and interaction design pass on the document workspace from #6, together with the security hardening that the pre-merge review required. The UI and daemon ship together: the daemon now adds the sandbox CSP and
nosniffheaders and the/file-assetsroute that sandboxed reports load through.Opus 5.5 on behalf of Cail.
What changes for a person using the board
c).t/x, andzto undo. A new verdict on a fiber replaces its pending one. A pending verdict binds to the fiber's uid and owner, and is re-checked against the current card when it commits.theme.cssbeside it. Themes are CSSOM-scoped to their channel and stop at the act zone. A Plain item in the page menu drops a theme. Seedocs/concepts/themes.md.[and]seek back and forward 5 s, andpplays or pauses.tokens.css.The screenshots come from the fixture harness (
npm run harness:board). Live-data shots for every lane, themes included, are in the felt constitutionai-futures/felt/constitutions/document-workspace(shots-6/).Security
The pre-merge review's two must-fixes, each with a regression test that fails before its fix:
keymap.ts, so a new intent is excluded by default. Verdicts, undo, compose, conversation, audio transport and expand are refused. The bridge also forwards only trusted key events.Content-Security-Policy: sandbox …andX-Content-Type-Options: nosniffare applied to every response whose percent-decoded path starts with/api(and to any path that doesn't decode), whatever the extension. That covers.xml,.svg, unknown types, error bodies, 206/304 responses, and responses relayed from an owner host: the hub sets the headers and doesn't trust the owner's. Matching decoded segments matters because the router decodes them too:/api/v1/fil%65?path=…reaches the file controller. The board page itself is unaffected.Fix-soons, also landed:
xfollowed by Temper wrote twice.@font-facefamilies are renamed per channel, and themeurl()may reference only the fiber's folder,data:or Google Fonts.theme.cssper fiber. Only the open reader loads custom CSS.referencesmessages are capped and rate-limited per frame.Known limitation. A report at an opaque origin can't
fetch()sibling data files. A sample of real reports turned up none that do. The proposed remedy is a bounded parent-bridge fetch, limited to the fiber's own folder and owner, rather thanAccess-Control-Allow-Origin: null.Verification
Gates on this head, observed:
npm test: 1,497 passed under each pinned time zone, on Node 25 locally and Node 22 (CI's runtime).npm run typecheck: clean.npm run e2e: 81/81; the reference-limit probe passes 6 of 6 runs in isolation.mix test test/shuttle_web: 558/558, including 97 file-controller tests.make build SKIP_CLI=1: passes.An independent Opus review (high effort) of the merged hardening and finish diffs confirmed that must-fix #1 is closed, with no other parent-side route around the allowlist. It also confirmed the verdict path, the theme confinement and the merge resolution. Its findings, and what happened to each:
nosniff. This was a regression from the base, where the controller sandboxed by thepathparameter. Fixed (007f0792) by the decoded, default-deny rule above, with an encoded-route test that failed before the fix.referencesthrottle used the wall clock. A backward jump inDate.now(), from NTP or a report stubbingDate, stalled rescans for hours. This was why the "ambiguous basenames" e2e test was flaky. Fixed (aaff0937): both sides now useperformance.now(), the wait is clamped, and the parent defers the newest over-budget batch rather than dropping it.d6ed27bd): it now asks at queue time, and the commit is refused with a toast if a worker started during the undo window.b7930db3).[and]need Option on AZERTY and QWERTZ layouts. Accepted for now;pstill plays and pauses.CI's UI job also caught that 8 workspace integration tests failed under Node 22. The cause was too short a microtask flush in the test harness, present since the earlier design lanes, and it's fixed (
5d33379a).Follow-ups
last_steporturn_active.🤖 Generated with Claude Code
https://claude.ai/code/session_01LwX9WpuD7VnV6mfmN9eb67