Skip to content

fix(workspace): make resource authority explicit - #6287

Merged
lefarcen merged 56 commits into
nexu-io:feat/workspace-teamfrom
lefarcen:fix/team-resource-explicit-workspace-scope
Jul 31, 2026
Merged

fix(workspace): make resource authority explicit#6287
lefarcen merged 56 commits into
nexu-io:feat/workspace-teamfrom
lefarcen:fix/team-resource-explicit-workspace-scope

Conversation

@lefarcen

@lefarcen lefarcen commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

Why

Real two-account validation exposed several cross-Workspace races in the Team
workspace feature. Resource reads and mutations mixed persisted project
bindings with daemon-global current/default Workspace state, so switching tabs,
restarting a shared project, or opening it from another account could select the
wrong authority, writer, or wallet.

The visible failures included funded Team projects being checked against a
Personal balance, the project creator reopening as a read-only viewer,
deep-linked conversations disappearing during hydration, presence writes
feeding a heartbeat storm, and Workspace owners being able to mutate or unshare
another member's project. Repeated file-list/preview navigation also discarded
warm preview state and repeatedly fetched the same raw HTML.

What users will see

  • Workspace-owned resources, runs, billing, collaboration, and catalog reads
    stay bound to the exact Workspace/member identity that initiated them.
  • The member who shared a Team project remains its single writer after restart.
    Other active Team members, including Workspace owners/admins, can view and
    comment but cannot edit files, start runs, rename/delete, or move that project
    to Personal.
  • A read-only shared project opens with chat collapsed to leave more room for
    the artifact. The panel is not locked: the viewer can expand it, and
    collaboration refreshes do not collapse it again during that visit.
  • Deep-linked conversations survive initial hydration, read-only empty chat
    settles instead of loading forever, and presence refreshes stay on the hot
    path without an event/write heartbeat loop.
  • File-list/preview navigation keeps the active preview mounted and reuses its
    exact HTML source instead of flashing a loading state or refetching it.
  • Personal, unshared, signed-out local-agent, and BYOK paths retain their
    existing behavior.

Surface area

  • UI — Workspace/project authority, read-only viewer controls, route
    hydration, preview caching, and presence behavior in apps/web
  • Keyboard shortcut — new or changed
  • CLI / env var — Workspace-aware od command paths and explicit
    Vela/AMR runtime scope propagation
  • API / contract — Workspace-scoped resource, collaboration, project,
    run, and SSE behavior
  • Extension point — new entry under skills/, design-systems/,
    design-templates/, or craft/, or change to the skills protocol
  • i18n keys — added new translation keys
  • New top-level dependency — adding any new entry to the root
    package.json
  • Default behavior change — Team resource authority now fails closed to
    the persisted project/Workspace identity; read-only shared projects initially
    collapse chat
  • None — internal refactor, docs, tests, or translation update only

Screenshots

Headed-Chrome captures were taken for both roles:

  • project creator: writable composer plus New Draft / New Document / Upload
    controls
  • another active Team member who is the Workspace owner: explicit
    view-and-comment-only state, default-collapsed but manually expandable chat,
    no file creation/upload controls, and disabled run/chat mutation path

The captures contain real test-account display names, so they are retained as
private acceptance evidence rather than published on this public PR.

Bug fix verification

Representative red specifications:

  • apps/daemon/tests/collab/project-request-authority.test.ts
  • apps/daemon/tests/routes/workspace-projects.test.ts
  • apps/daemon/tests/run-create-workspace-gate.test.ts
  • apps/web/tests/use-collab.test.tsx
  • apps/web/tests/use-project-collab.test.tsx
  • apps/web/tests/components/ProjectView.tabs-navigation.test.tsx
  • apps/web/tests/components/ProjectView.sharedTitleRefresh.test.tsx
  • apps/web/tests/components/FileViewer.test.tsx

The new specs were confirmed red against the PR's pre-fix base and green after
the fixes. This PR targets feat/workspace-team, so that branch is the
applicable pre-fix baseline.

Real two-account checks also reproduced the previous authorization split: a
non-project-owner Workspace owner could write a file and unshare the project
before authority was unified.

Validation

Exact final integration head: c0becbf5ebaf6e5798fa9f1b877943535a7152ea

  • pnpm guard
  • pnpm typecheck
  • pnpm --filter @open-design/daemon typecheck
  • pnpm --filter @open-design/daemon build
  • pnpm --filter @open-design/daemon test
    • 590 test files passed, 3 skipped
    • 7,516 tests passed, 6 skipped, 4 todo
  • pnpm --filter @open-design/web typecheck
  • pnpm --filter @open-design/web build
  • pnpm --filter @open-design/web test
    • 575 test files passed
    • 5,824 tests passed, 11 skipped
  • Focused shared-project/default-collapse and first-share recovery coverage passed:
    98 ProjectView tests, including lifecycle generation fences, A to B to A
    authority races, concurrent materialization signals, and 404 to 403/500
    error truth.
  • A real headed-Chrome two-account race opened the collaborator deep link while
    the creator share request was still in flight. The page recovered without a
    manual reload, kept the exact project title/file/conversation, and enforced
    view-and-comment-only controls with no sticky conversations 404.
  • Headed system-Chrome validation with isolated account/profile state:
    • member project ready in 5.357 seconds; creator project ready in 6.777 seconds
    • 10 Design Files ↔ preview rounds added zero raw HTML requests after warm-up
    • read-only chat defaulted collapsed, expanded on demand, stayed expanded
      across multiple heartbeat intervals, and could be collapsed/reopened again
    • creator chat remained expanded and stable
    • no browser console errors, page errors, HTTP 4xx/5xx responses, or unscoped
      downstream Workspace requests
  • Real isolated-account API/CLI verification:
    • non-project-owner file writes, run/chat creation, and move-to-Personal
      returned 403
    • the project owner's write returned 200 and the probe file was removed
    • the Team Workspace balance decreased for the real AMR run while the control
      Workspace balance did not
    • both isolated Vela CLI profiles resolved the same remote catalog owner; the
      collaborator reported canEdit: false
  • Real active-run Workspace-switch verification:
    • a Team A AMR run remained running after switching the client to Team B
    • Team B could not read or adopt the Team A run
    • the original run completed successfully and only Team A was billed
  • Signed-out local-agent verification:
    • isolated empty AMR/Vela state reported loggedIn:false
    • a real Codex run completed and wrote the expected proof file
    • Claude, Codex, OpenCode, and BYOK entry points remained visible without an
      AMR balance dialog

Adjacent issues

The signed-out run exposed the existing empty-final-output quota
misclassification. It is already addressed separately by #6261 and is not
duplicated here.

@lefarcen
lefarcen requested a review from mrcfps July 30, 2026 15:48
@lefarcen lefarcen added size/XXL PR changes 1500+ lines risk/high High risk: apps/desktop, daemon, auth, migration, workflows, package deps type/bugfix Bug fix labels Jul 30, 2026
@lefarcen
lefarcen marked this pull request as ready for review July 30, 2026 16:19

@mrcfps mrcfps left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@lefarcen Thank you for the substantial workspace-authority hardening—the persisted scope, fresh verification, and single-writer direction are thoughtful. I found three reproducible main-path gaps: the BYOK tool loop bypasses the same write boundary, several newly guarded UI/CLI consumers cannot carry the required authority, and the creator-only access change reopens the existing extracted-project move-to-Personal regression. pnpm guard, pnpm typecheck, and 177 focused daemon/web assertions passed, but the existing move regression spec now deterministically returns 403. I’m requesting changes for these focused issues; the overall direction looks strong once the remaining paths share the same authority contract.

🔁 Powered by Looper · runner=reviewer · agent=codex · An autonomous AI dev team for your GitHub repos.

Comment thread apps/daemon/src/routes/chat.ts Outdated
Comment thread apps/daemon/src/routes/project/index.ts
Comment thread apps/daemon/src/cli.ts
@lefarcen
lefarcen requested a review from mrcfps July 31, 2026 04:40
@lefarcen

lefarcen commented Jul 31, 2026

Copy link
Copy Markdown
Contributor Author

@mrcfps Final head is c0becbf5ebaf6e5798fa9f1b877943535a7152ea. The only blocking thread is fixed/resolved with its 96/96 CLI matrix; no unresolved threads remain. Local final gates are green (root typecheck/guard, daemon 7,516 tests, Web 5,824 tests + build), and the two-account headed-Chrome share/deep-link race recovers without reload. Could you please approve the pending fork workflow run and re-review this head? CI run: https://github.com/nexu-io/open-design/actions/runs/30623719420

@mrcfps mrcfps left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@lefarcen Thank you for the substantial follow-up work here. I verified that the previous BYOK media authorization, orphan-recovery, export/media polling, and pinned run-scope issues are addressed on this head. One clear CLI-parity blocker remains: several project commands still cannot carry the explicit Workspace/member identity to routes this PR now correctly fail-closes, so the Team-project creator cannot use those primary CLI paths. Please finish the same centralized transport migration for the remaining commands and cover them in the existing creator/non-creator/unbound matrix.

🔁 Powered by Looper · runner=reviewer · agent=codex · An autonomous AI dev team for your GitHub repos.

Comment thread apps/daemon/src/cli.ts
@lefarcen
lefarcen requested a review from mrcfps July 31, 2026 10:31

@mrcfps mrcfps left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Re-review complete on the final head. The explicit Workspace transport blocker is addressed across conversation/chat, plugin, and Figma commands with the 96/96 creator/non-creator/unbound matrix green. The first-share conversation recovery follow-up has red/green coverage for lifecycle generations, A→B→A authority reuse, concurrent completion signals, and non-404 error truth. Root typecheck, guard, daemon full tests, Web 575-file full tests/build, and real two-account headed-Chrome validation are green. No unresolved review threads remain.

@lefarcen
lefarcen merged commit 68612c8 into nexu-io:feat/workspace-team Jul 31, 2026
6 of 8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

risk/high High risk: apps/desktop, daemon, auth, migration, workflows, package deps size/XXL PR changes 1500+ lines type/bugfix Bug fix

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants