Groups: admin-managed user groups for Share and Collaborate #1028

Open
opened 2026-10-04 04:04:41 +00:00 by kayg · 4 comments
Owner

Owner decision (2026-10-04): "yes we need groups!"

Instance-wide user Groups, used by Share / Collaborate everywhere (Canvas #991 asked for it; Notes, Folders, Calendars, Canvases).

Shape (orchestrator defaults; small, follows §54)

  • Admin → People → Groups: create, rename, delete a Group; add/remove members (Users of this instance). Admin-only management. Copy link per Group (§33).
  • Share dialogs accept a Group anywhere they accept a User; the grant is to the Group (membership is resolved at access time: adding a member gives access, removing one revokes it live within the existing recheck window).
  • Members see items shared to their Groups in Shared, labelled with the Group name.
  • No nested groups, no group owners, no external members in v1.
  • API, CLI, MCP, WebMCP parity through the action registry; cross-user isolation matrix rows (non-member cannot read; removed member loses access mid-session; deleting a Group revokes its grants).
  • Plain names in the UI ("Groups", "Members").

Verification

e2e: admin creates "Family" with two Users, owner collaborates a Canvas and a Note with "Family", both members see and edit; removing one member revokes live. Screenshots 390/820/1440 light/dark of Admin → Groups and a share dialog with a Group.

## Owner decision (2026-10-04): "yes we need groups!" Instance-wide user Groups, used by Share / Collaborate everywhere (Canvas #991 asked for it; Notes, Folders, Calendars, Canvases). ## Shape (orchestrator defaults; small, follows §54) - **Admin → People → Groups**: create, rename, delete a Group; add/remove members (Users of this instance). Admin-only management. Copy link per Group (§33). - **Share dialogs** accept a Group anywhere they accept a User; the grant is to the Group (membership is resolved at access time: adding a member gives access, removing one revokes it live within the existing recheck window). - Members see items shared to their Groups in Shared, labelled with the Group name. - No nested groups, no group owners, no external members in v1. - API, CLI, MCP, WebMCP parity through the action registry; cross-user isolation matrix rows (non-member cannot read; removed member loses access mid-session; deleting a Group revokes its grants). - Plain names in the UI ("Groups", "Members"). ## Verification e2e: admin creates "Family" with two Users, owner collaborates a Canvas and a Note with "Family", both members see and edit; removing one member revokes live. Screenshots 390/820/1440 light/dark of Admin → Groups and a share dialog with a Group.
Author
Owner

Started #1028 on job/groups-1028, base f2f8491ff5. Reading the shared grant model and Admin People components before implementation. Group membership will be resolved at access time as requested.

Started #1028 on job/groups-1028, base f2f8491ff5c76ab28f140c964542c97362e6b119. Reading the shared grant model and Admin People components before implementation. Group membership will be resolved at access time as requested.
Author
Owner

Implemented the shared grant read relation: stored files_grants keeps one Group grant; files_shares resolves current active membership for existing Files, Notes, Calendar, Search, Photos and collaboration callers. No Canvas or public-link handler changes. The share dialog change is limited to its internal recipient controls.

Findings: the focused Group regression passed (non-member denial, membership add/remove, delete/Undo and overlapping direct User access). The first Files suite exposed fixtures that applied Files migrations without core migrations; fixed the fixtures. The existing upgrade assertion is changed from 20 to 21 only because #1028 adds migration 0021; no authorization/status expectation changed.

Decisions: use /settings/admin/groups/<UUID> for item links; bound plain Group names to 100 characters with trimmed surrounding whitespace and no control characters; use the existing recent-passkey admin guard for mutations. Deleting deactivates Group Security state, which removes it from all access reads; Undo restores its ID and grants. Active names are unique and may be reused after deletion. Retained inactive Security state is invisible to discovery and admin lists.

Fetched origin and merged origin/dev once: Already up to date. Confirmed the next free migration numbers on origin/dev: core 0013 and Files 0021. Full adversarial matrices, full web tests, staging/Mac interop and perf runs stay with the merge round under the verification policy. Running per-crate gates and preparing focused real-server/UI evidence now.

Implemented the shared grant read relation: stored `files_grants` keeps one Group grant; `files_shares` resolves current active membership for existing Files, Notes, Calendar, Search, Photos and collaboration callers. No Canvas or public-link handler changes. The share dialog change is limited to its internal recipient controls. Findings: the focused Group regression passed (non-member denial, membership add/remove, delete/Undo and overlapping direct User access). The first Files suite exposed fixtures that applied Files migrations without core migrations; fixed the fixtures. The existing upgrade assertion is changed from 20 to 21 only because #1028 adds migration 0021; no authorization/status expectation changed. Decisions: use `/settings/admin/groups/<UUID>` for item links; bound plain Group names to 100 characters with trimmed surrounding whitespace and no control characters; use the existing recent-passkey admin guard for mutations. Deleting deactivates Group Security state, which removes it from all access reads; Undo restores its ID and grants. Active names are unique and may be reused after deletion. Retained inactive Security state is invisible to discovery and admin lists. Fetched origin and merged origin/dev once: Already up to date. Confirmed the next free migration numbers on origin/dev: core 0013 and Files 0021. Full adversarial matrices, full web tests, staging/Mac interop and perf runs stay with the merge round under the verification policy. Running per-crate gates and preparing focused real-server/UI evidence now.
Author
Owner

The focused production-server test passed Group creation, pointer and keyboard membership, stable Copy link, Group Share, member/nonmember reads, two live Note editors, member removal, Group deletion and Undo. It captured 18 macOS-platform screenshots: Groups, Share and Shared at 390/820/1440 in light and dark.

The phone check found that the existing compact collection hid the Kind column and therefore the Group name. Fixed it through the existing detail slot; grid tiles also show that slot. The focused grid regression passes. The owner grant badge now reads stored grants (one badge per Group, including an empty Group); Search accepts the live share view.

Both authorization inventories now classify the new routes: 46 reviewed admin operations, 350 API operations and 990 generated entry points. These were offline inventory checks; full live matrices remain for the merge round. Final touch/Undo and deleted-Group membership regression checks are in progress.

The focused production-server test passed Group creation, pointer and keyboard membership, stable Copy link, Group Share, member/nonmember reads, two live Note editors, member removal, Group deletion and Undo. It captured 18 macOS-platform screenshots: Groups, Share and Shared at 390/820/1440 in light and dark. The phone check found that the existing compact collection hid the Kind column and therefore the Group name. Fixed it through the existing detail slot; grid tiles also show that slot. The focused grid regression passes. The owner grant badge now reads stored grants (one badge per Group, including an empty Group); Search accepts the live share view. Both authorization inventories now classify the new routes: 46 reviewed admin operations, 350 API operations and 990 generated entry points. These were offline inventory checks; full live matrices remain for the merge round. Final touch/Undo and deleted-Group membership regression checks are in progress.
Author
Owner

READY FOR MERGE: yes

Built: Instance Groups in Admin → People, with create, rename, delete, explicit User membership, stable Copy link and Undo. Group recipients use the shared grant model. Access resolves current active membership for Files, Notes, Calendar, Search, Photos and collaboration rechecks. Incoming Shared items show Group names at phone, tablet and desktop widths. Generated actions provide API, CLI, MCP and WebMCP parity.

Branch: job/groups-1028
Base: f2f8491ff5
Head: 2e3b519800
Atomic commits: 15. No push, deploy or merge into dev. The required origin/dev fetch and merge ran once and returned "Already up to date." Core migration 0013 and Files migration 0021 were free on origin/dev. No dependencies added.

Evidence: production web build and this branch's real server. Real passkeys and four fixture Users. Two real Yjs clients edit the Note and receive each other's edits. Cross-User rows: nonmember download returns 404; removing a member closes that member's active socket within 2 seconds and denies later reads while the other member remains connected; deleting the Group closes the other socket and denies reads. Undo restores the retained Group grant but does not re-add the removed member. A real Canvas file accepts the Group editor grant; its member read succeeds and nonmember read returns 404. Rename updates incoming labels without changing grant identity. Pointer, touch, keyboard and membership Undo pass. The deleted-Group membership regression returns 404.

UX gaps closed: compact Shared rows hid Group names; they now use the existing collection detail slot. Grid tiles also show that context. Owner badges now count one stored Group grant, including an empty Group, instead of one badge per member. Search detects the live share view. Background admin refresh preserves unfinished rename text. Group mutations and sharing actions refresh the shared stores; state changes have Undo. Loading failures expose a calm Retry action. Current names are shown without exposing rosters to share owners.

Known gaps / UX gaps left: Canvas scene editing needs job/canvas-collab-991 on the combined branch. This job tests its shared grant path and does not change that branch's files. The public-link view-only policy from job/pubedit-981 also needs the combined-branch check; this job does not change public-link controls or handlers. No other known Group feature gap. Visual quality review belongs to the orchestrator.

Decisions: Group item links use /settings/admin/groups/. Names are trimmed, bounded to 100 characters, exclude control characters and are case-sensitive unique among active Groups. Mutations use the existing recent-passkey admin guard. Delete deactivates Security state immediately; inactive names, memberships and grants stay hidden so guarded Undo can restore the original ID and grants. A name can be reused after deletion; restore returns conflict if that name is now taken. No inactive-state purge policy was added. Stored grants are the writer model; the existing files_shares name is a read view over current Users so other plugins use the same authority model. The common FileCollection addition extends its existing optional detail slot.

Performance: added bench/groups-grants.mjs for average (one Group, five Users) and worst-case burst (100 Groups, 10,000 Users). The profile reports p50/p95, CPU and RSS using production migrations. No Group baseline exists yet. No measurements ran: the current verification policy limits measurements to perf issues on the perf VM.

Gate commands: cargo fmt --check; cargo clippy -p --all-targets -- -D warnings and cargo test -p for calternal-db, calternal-plugin-files, calternal-search and calternal-server. Web: bun run check; bunx vitest run src/routes/settings/sections.test.ts src/lib/files/FileCollection.svelte.test.ts --maxWorkers=2; then the focused collection regression after its new test. Production build and bun apps/web/e2e/groups-1028.mjs passed. Rust used OPENSSL_NO_VENDOR=1, CARGO_BUILD_JOBS=4, CARGO_PROFILE_DEV_DEBUG=line-tables-only and CARGO_INCREMENTAL=0; CARGO_TARGET_DIR stayed preset. Doc comments were re-read before this report.

Gate output verbatim (log headings identify each gate; fmt has no stdout):

fmt.log
(no output; exit 0)

db-clippy.log
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 1.77s

db-test.log
    Finished `test` profile [unoptimized + debuginfo] target(s) in 3.54s
test result: ok. 21 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 5.79s
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.07s
test result: ok. 16 passed; 0 failed; 1 ignored; 0 measured; 0 filtered out; finished in 0.66s
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.04s
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

files-clippy.log
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 17.81s

files-test.log
    Finished `test` profile [unoptimized + debuginfo] target(s) in 41.22s
test result: ok. 159 passed; 0 failed; 1 ignored; 0 measured; 0 filtered out; finished in 117.58s
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

files-followup-clippy.log
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 13.15s

files-followup-test.log
    Finished `test` profile [unoptimized + debuginfo] target(s) in 26.35s
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 159 filtered out; finished in 0.53s

search-clippy.log
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 46.50s

search-test.log
    Finished `test` profile [unoptimized + debuginfo] target(s) in 1m 11s
test result: ok. 41 passed; 0 failed; 1 ignored; 0 measured; 0 filtered out; finished in 7.12s
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.59s
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.04s
test result: ok. 24 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 199.05s
test result: ok. 5 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.02s
test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.01s
test result: ok. 1 passed; 0 failed; 2 ignored; 0 measured; 0 filtered out; finished in 2.27s
test result: ok. 4 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

server-followup-clippy.log
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 44.82s

server-followup-test.log
    Finished `test` profile [unoptimized + debuginfo] target(s) in 1m 58s
test result: ok. 163 passed; 0 failed; 5 ignored; 0 measured; 0 filtered out; finished in 22.88s

web-check.log
svelte-check found 0 errors and 0 warnings

web-test.log
 Test Files  2 passed (2)
      Tests  23 passed (23)

web-collection-test.log
 Test Files  1 passed (1)
      Tests  8 passed (8)

actions.log
Action registry: 348 operations, 330 generated tools

parity.log
Parity matrix: 348 API actions, 126 shortcuts, 2 static commands, 140 menu actions, 51 settings groups, 0 actions with adapter gaps

admin-coverage.log
Admin coverage: 46 reviewed operations; contract and Rust guards agree

xuser-classify.log
Cross-User classification gate: 350 operations classified
Generated entry point classification: 990 tools classified

e2e.log
PASS admin Group creation, membership and guards
PASS groups-1028: admin pointer/touch/keyboard management, stable Copy link, Group Share, Shared labels, two live Note editors, removal/deletion revoke, Undo, Canvas grant path; 18 macOS screenshots

Full stdout: gate logs.

Screenshots: all are from the production app, with macOS platform emulation. Phone/tablet contexts also use touch.

Theme and width Groups Share Shared
Light, 390 px Groups Share Shared
Light, 820 px Groups Share Shared
Light, 1440 px Groups Share Shared
Dark, 390 px Groups Share Shared
Dark, 820 px Groups Share Shared
Dark, 1440 px Groups Share Shared

Files:

  • apps/web/e2e/groups-1028.mjs
  • apps/web/src/lib/files/FileCollection.svelte.test.ts
  • apps/web/src/lib/files/FilesBrowser.svelte
  • apps/web/src/lib/files/ShareDialog.svelte
  • apps/web/src/lib/files/api.ts
  • apps/web/src/lib/files/sharing.svelte.ts
  • apps/web/src/routes/settings/admin/AdminSection.svelte
  • apps/web/src/routes/settings/admin/GroupsGroup.svelte
  • apps/web/src/routes/settings/sections.test.ts
  • apps/web/src/routes/settings/sections.ts
  • bench/groups-grants.mjs
  • contracts/actions.json
  • contracts/openapi.json
  • crates/calternal-db/src/migrations.rs
  • crates/calternal-db/src/migrations/0013_groups.sql
  • crates/calternal-db/tests/groups.rs
  • crates/calternal-search/src/indexer.rs
  • crates/calternal-server/src/wire.rs
  • crates/calternal-server/src/wire/groups.rs
  • crates/plugins/files/migrations/0021_group_grants.sql
  • crates/plugins/files/src/agent_undo.rs
  • crates/plugins/files/src/index.rs
  • crates/plugins/files/src/lib.rs
  • crates/plugins/files/src/listing.rs
  • crates/plugins/files/src/shares.rs
  • crates/plugins/files/src/thumbnails.rs
  • docs/parity-matrix.md
  • packages/api-client/src/generated.ts
  • packages/ui/src/components/files/FileCollection.svelte
  • tests/adversarial/authz_matrix.py
  • tests/adversarial/xuser_matrix.py

For the merge round: bun run test -- --maxWorkers=2 in apps/web for the combined web suite; bun run test:e2e plus bun apps/web/e2e/groups-1028.mjs for integrated UI and Group behavior; bash tests/adversarial/run.sh for the full live matrices. After #991 and #981 join, verify both members can edit Canvas scenes through the Group, removal closes that access, and public links remain view-only. Run the release/staging/o2 and real Mac interop gates there under the owner policy. No full suite or perf/Mac session ran in this job.

Cleanup: cargo clean completed (Removed 19004 files, 11.4GiB total). Removed apps/web/build and apps/web/.svelte-kit/output. Screenshots and gate logs remain under artifacts/groups-1028. Working tree is clean.

READY FOR MERGE: yes Built: Instance Groups in Admin → People, with create, rename, delete, explicit User membership, stable Copy link and Undo. Group recipients use the shared grant model. Access resolves current active membership for Files, Notes, Calendar, Search, Photos and collaboration rechecks. Incoming Shared items show Group names at phone, tablet and desktop widths. Generated actions provide API, CLI, MCP and WebMCP parity. Branch: job/groups-1028 Base: f2f8491ff5c76ab28f140c964542c97362e6b119 Head: 2e3b5198001bf4da0e76416acefac68a536f7325 Atomic commits: 15. No push, deploy or merge into dev. The required origin/dev fetch and merge ran once and returned "Already up to date." Core migration 0013 and Files migration 0021 were free on origin/dev. No dependencies added. Evidence: production web build and this branch's real server. Real passkeys and four fixture Users. Two real Yjs clients edit the Note and receive each other's edits. Cross-User rows: nonmember download returns 404; removing a member closes that member's active socket within 2 seconds and denies later reads while the other member remains connected; deleting the Group closes the other socket and denies reads. Undo restores the retained Group grant but does not re-add the removed member. A real Canvas file accepts the Group editor grant; its member read succeeds and nonmember read returns 404. Rename updates incoming labels without changing grant identity. Pointer, touch, keyboard and membership Undo pass. The deleted-Group membership regression returns 404. UX gaps closed: compact Shared rows hid Group names; they now use the existing collection detail slot. Grid tiles also show that context. Owner badges now count one stored Group grant, including an empty Group, instead of one badge per member. Search detects the live share view. Background admin refresh preserves unfinished rename text. Group mutations and sharing actions refresh the shared stores; state changes have Undo. Loading failures expose a calm Retry action. Current names are shown without exposing rosters to share owners. Known gaps / UX gaps left: Canvas scene editing needs job/canvas-collab-991 on the combined branch. This job tests its shared grant path and does not change that branch's files. The public-link view-only policy from job/pubedit-981 also needs the combined-branch check; this job does not change public-link controls or handlers. No other known Group feature gap. Visual quality review belongs to the orchestrator. Decisions: Group item links use /settings/admin/groups/<UUID>. Names are trimmed, bounded to 100 characters, exclude control characters and are case-sensitive unique among active Groups. Mutations use the existing recent-passkey admin guard. Delete deactivates Security state immediately; inactive names, memberships and grants stay hidden so guarded Undo can restore the original ID and grants. A name can be reused after deletion; restore returns conflict if that name is now taken. No inactive-state purge policy was added. Stored grants are the writer model; the existing files_shares name is a read view over current Users so other plugins use the same authority model. The common FileCollection addition extends its existing optional detail slot. Performance: added bench/groups-grants.mjs for average (one Group, five Users) and worst-case burst (100 Groups, 10,000 Users). The profile reports p50/p95, CPU and RSS using production migrations. No Group baseline exists yet. No measurements ran: the current verification policy limits measurements to perf issues on the perf VM. Gate commands: cargo fmt --check; cargo clippy -p <crate> --all-targets -- -D warnings and cargo test -p <crate> for calternal-db, calternal-plugin-files, calternal-search and calternal-server. Web: bun run check; bunx vitest run src/routes/settings/sections.test.ts src/lib/files/FileCollection.svelte.test.ts --maxWorkers=2; then the focused collection regression after its new test. Production build and bun apps/web/e2e/groups-1028.mjs passed. Rust used OPENSSL_NO_VENDOR=1, CARGO_BUILD_JOBS=4, CARGO_PROFILE_DEV_DEBUG=line-tables-only and CARGO_INCREMENTAL=0; CARGO_TARGET_DIR stayed preset. Doc comments were re-read before this report. Gate output verbatim (log headings identify each gate; fmt has no stdout): ```text fmt.log (no output; exit 0) db-clippy.log Finished `dev` profile [unoptimized + debuginfo] target(s) in 1.77s db-test.log Finished `test` profile [unoptimized + debuginfo] target(s) in 3.54s test result: ok. 21 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 5.79s test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.07s test result: ok. 16 passed; 0 failed; 1 ignored; 0 measured; 0 filtered out; finished in 0.66s test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.04s test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s files-clippy.log Finished `dev` profile [unoptimized + debuginfo] target(s) in 17.81s files-test.log Finished `test` profile [unoptimized + debuginfo] target(s) in 41.22s test result: ok. 159 passed; 0 failed; 1 ignored; 0 measured; 0 filtered out; finished in 117.58s test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s files-followup-clippy.log Finished `dev` profile [unoptimized + debuginfo] target(s) in 13.15s files-followup-test.log Finished `test` profile [unoptimized + debuginfo] target(s) in 26.35s test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 159 filtered out; finished in 0.53s search-clippy.log Finished `dev` profile [unoptimized + debuginfo] target(s) in 46.50s search-test.log Finished `test` profile [unoptimized + debuginfo] target(s) in 1m 11s test result: ok. 41 passed; 0 failed; 1 ignored; 0 measured; 0 filtered out; finished in 7.12s test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.59s test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.04s test result: ok. 24 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 199.05s test result: ok. 5 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.02s test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.01s test result: ok. 1 passed; 0 failed; 2 ignored; 0 measured; 0 filtered out; finished in 2.27s test result: ok. 4 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s server-followup-clippy.log Finished `dev` profile [unoptimized + debuginfo] target(s) in 44.82s server-followup-test.log Finished `test` profile [unoptimized + debuginfo] target(s) in 1m 58s test result: ok. 163 passed; 0 failed; 5 ignored; 0 measured; 0 filtered out; finished in 22.88s web-check.log svelte-check found 0 errors and 0 warnings web-test.log Test Files 2 passed (2) Tests 23 passed (23) web-collection-test.log Test Files 1 passed (1) Tests 8 passed (8) actions.log Action registry: 348 operations, 330 generated tools parity.log Parity matrix: 348 API actions, 126 shortcuts, 2 static commands, 140 menu actions, 51 settings groups, 0 actions with adapter gaps admin-coverage.log Admin coverage: 46 reviewed operations; contract and Rust guards agree xuser-classify.log Cross-User classification gate: 350 operations classified Generated entry point classification: 990 tools classified e2e.log PASS admin Group creation, membership and guards PASS groups-1028: admin pointer/touch/keyboard management, stable Copy link, Group Share, Shared labels, two live Note editors, removal/deletion revoke, Undo, Canvas grant path; 18 macOS screenshots ``` Full stdout: [gate logs](https://git.kayg.org/attachments/e8cbd0e8-6ac8-4693-ac53-606a51b24541). Screenshots: all are from the production app, with macOS platform emulation. Phone/tablet contexts also use touch. | Theme and width | Groups | Share | Shared | | --- | --- | --- | --- | | Light, 390 px | [Groups](https://git.kayg.org/attachments/829c284e-1c06-49e3-ab9f-03ceadd6fac3) | [Share](https://git.kayg.org/attachments/369081c6-1902-4761-9e04-3551dacc533f) | [Shared](https://git.kayg.org/attachments/1c8bed78-dfe8-454e-89f3-f026c447fd8f) | | Light, 820 px | [Groups](https://git.kayg.org/attachments/b07d7998-da4c-456f-94c2-63f282f7af07) | [Share](https://git.kayg.org/attachments/729a82fe-ff17-4eb0-b640-dd6ffa4474d3) | [Shared](https://git.kayg.org/attachments/94982f2d-d763-488f-a50f-f33dd574555e) | | Light, 1440 px | [Groups](https://git.kayg.org/attachments/1cbef7d5-5945-4acb-99e7-2e1eba221157) | [Share](https://git.kayg.org/attachments/892ecd57-faa7-4dff-abd1-9dd5f95791f6) | [Shared](https://git.kayg.org/attachments/de8f3755-fcc9-4007-9e03-b7b4aeb7b64e) | | Dark, 390 px | [Groups](https://git.kayg.org/attachments/b02f4cce-71bd-4dde-8451-ac5b5387710b) | [Share](https://git.kayg.org/attachments/1b2c922a-7c2f-4f08-9b9b-5c23a1dfb6cb) | [Shared](https://git.kayg.org/attachments/e5eb18ac-7f94-4a2f-8ea1-de22ba5d63a6) | | Dark, 820 px | [Groups](https://git.kayg.org/attachments/56abd405-e9e9-4f19-a3b4-26f5626f49af) | [Share](https://git.kayg.org/attachments/0fe30749-614f-4194-acd9-b8fbb29b93dc) | [Shared](https://git.kayg.org/attachments/ee233ef9-2767-40dc-ac05-0731233ea5a4) | | Dark, 1440 px | [Groups](https://git.kayg.org/attachments/50430b48-7b37-4723-978f-e072aaba22b7) | [Share](https://git.kayg.org/attachments/c436ab54-f5b1-491c-93e0-605747bf2f52) | [Shared](https://git.kayg.org/attachments/499c16ba-022c-45ab-b5ee-c7f79c60cbd4) | Files: - `apps/web/e2e/groups-1028.mjs` - `apps/web/src/lib/files/FileCollection.svelte.test.ts` - `apps/web/src/lib/files/FilesBrowser.svelte` - `apps/web/src/lib/files/ShareDialog.svelte` - `apps/web/src/lib/files/api.ts` - `apps/web/src/lib/files/sharing.svelte.ts` - `apps/web/src/routes/settings/admin/AdminSection.svelte` - `apps/web/src/routes/settings/admin/GroupsGroup.svelte` - `apps/web/src/routes/settings/sections.test.ts` - `apps/web/src/routes/settings/sections.ts` - `bench/groups-grants.mjs` - `contracts/actions.json` - `contracts/openapi.json` - `crates/calternal-db/src/migrations.rs` - `crates/calternal-db/src/migrations/0013_groups.sql` - `crates/calternal-db/tests/groups.rs` - `crates/calternal-search/src/indexer.rs` - `crates/calternal-server/src/wire.rs` - `crates/calternal-server/src/wire/groups.rs` - `crates/plugins/files/migrations/0021_group_grants.sql` - `crates/plugins/files/src/agent_undo.rs` - `crates/plugins/files/src/index.rs` - `crates/plugins/files/src/lib.rs` - `crates/plugins/files/src/listing.rs` - `crates/plugins/files/src/shares.rs` - `crates/plugins/files/src/thumbnails.rs` - `docs/parity-matrix.md` - `packages/api-client/src/generated.ts` - `packages/ui/src/components/files/FileCollection.svelte` - `tests/adversarial/authz_matrix.py` - `tests/adversarial/xuser_matrix.py` For the merge round: `bun run test -- --maxWorkers=2` in apps/web for the combined web suite; `bun run test:e2e` plus `bun apps/web/e2e/groups-1028.mjs` for integrated UI and Group behavior; `bash tests/adversarial/run.sh` for the full live matrices. After #991 and #981 join, verify both members can edit Canvas scenes through the Group, removal closes that access, and public links remain view-only. Run the release/staging/o2 and real Mac interop gates there under the owner policy. No full suite or perf/Mac session ran in this job. Cleanup: cargo clean completed (`Removed 19004 files, 11.4GiB total`). Removed apps/web/build and apps/web/.svelte-kit/output. Screenshots and gate logs remain under artifacts/groups-1028. Working tree is clean.
Sign in to join this conversation.
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
kayg/calternal#1028
No description provided.