Skip to content

feat: separate read/write access (drop read_write) + edit UI for write-grantees (#1127)#1128

Merged
chronoai-shining merged 7 commits into
developfrom
feature/1127-read-write-split
Jun 16, 2026
Merged

feat: separate read/write access (drop read_write) + edit UI for write-grantees (#1127)#1128
chronoai-shining merged 7 commits into
developfrom
feature/1127-read-write-split

Conversation

@chronoai-shining

Copy link
Copy Markdown
Collaborator

Summary

Separates read and write access — the combined read_write level is gone — and fixes the bug where a user with write access saw no edit UI.

Closes #1127

What changed

Model: read_writewrite (API + both SDKs + web + docs). Each grant is read or write; a write grant implies read, but there's no combined label and a principal holds at most one level. Gate behaviour is unchanged from #1123 — only the value renamed.

Migration (non-disruptive, rollback-safe): a boot step rewrites any stored read_write grant → write on skills + skillsets (idempotent; read grants + legacy lists + privacy untouched). coerceStoredGrants also maps a stored read_writewrite at read time, so an un-migrated doc (or one written by an older pod mid-deploy) never loses access.

The bug fix — write-grantees can now edit: a shared useSkillAccess(skill) hook computes the frontend tiers (canWrite = owner / platform admin / a write grant matching the user directly or via a granted org). The Edit button, inline file editor, and Save are re-pointed at canWrite; admin actions (permissions, transfer, delete, visibility, refresh, version mgmt) stay owner/admin. EditSkillPage gates its package update on canWrite and its visibility toggle on canManage, with a read-only notice for non-writers.

Verification

  • API 1923 tests, web 566, TS SDK 38, Python SDK 45 — all pass. typecheck (api/web/sdk) clean; lint 0 errors.
  • Migration test pins read_write→write + non-disruption + idempotency; new web tests cover write-grantee-sees-edit and reader-sees-read-only.

🤖 Generated with Claude Code

Separate read and write access: the combined `read_write` level is gone.
`SkillPermissionLevel` is now `"read" | "write"` — a `write` grant implies
read (you can't edit what you can't see) but there's no combined label and
a principal holds at most one level.

Behaviour is unchanged from #1123: `canReadSkill` (any grant ⇒ read),
`canWriteSkill` (a `write` grant), `canManageSkill` (owner/admin) are the
same gates with the value renamed. Updated the Zod enum, `normalizeGrants`
(write wins), `levelAllowsWrite`, the `permissions_changed` analytics count
(`readWriteGrants` → `writeGrants`), and the skillset delegation comments.

`coerceStoredGrants` defensively maps a stored legacy `read_write` → `write`
at read time, so a doc the boot migration hasn't rewritten yet (or one
written by an older pod mid-rolling-deploy) still resolves to write —
never dropped, never silently downgraded.

Part of #1127
Rewrite any stored grant carrying the legacy `read_write` level to `write`
on skills + skillsets, at boot, right after the typed-grants backfill.
Idempotent (only docs with a `read_write` grant match) and non-destructive
(read grants + all other fields untouched; `write` confers exactly what
`read_write` did). Failure is non-fatal — `coerceStoredGrants` maps the
legacy value at read time, so no access is lost.

Integration tests pin the rename (read_write→write, read grants intact,
legacy lists + privacy preserved), idempotency, and the skillset twin.

Part of #1127
`SkillPermissionLevel` becomes `read | write` in both SDKs, matching the
API. Value-only rename — method names and shapes are unchanged. Tests
updated; TS vitest + Python pytest/ruff/mypy green.

Part of #1127
`SkillPermissionLevel` → `read | write` in the web model; the permissions
editor's Write tab emits `write` grants; the visibility-card count prop
`readWriteCount` → `writeCount`. Value-only rename matching the API/SDK.

Part of #1127
Fix the bug where a user with write access saw no edit affordances: the UI
gated everything on `isOwner` and never learned about the write tier.

Add a shared `useSkillAccess(skill)` hook → { isOwner, isAdmin, canWrite,
canManage }, mirroring the backend gates (`canWrite` = owner OR platform
admin OR a `write` grant matching the user directly or via a granted org,
resolved against `useMyOrgs`). Re-point the content-edit affordances — the
Edit button, the inline file editor, and Save — at `canWrite`, while
admin actions (permissions, transfer, delete, visibility, refresh, version
mgmt) stay on owner/admin. `SkillHeroStrip`'s edit button now keys off the
handler's presence (dropped its `isOwner` prop). EditSkillPage gates its
package update on `canWrite` and its visibility toggle on `canManage`, with
a read-only notice for non-writers.

Tests: write-grantee sees Update Package but not the visibility toggle; a
read-only viewer sees the notice and no controls.

Part of #1127
CONVENTIONS §5.4 tier renamed READ_WRITE → WRITE, grant level value and
prose updated to `write` (write implies read); ERRORS accepted levels now
read/write; ARCHITECTURE analytics field readWriteGrants → writeGrants.

Part of #1127
@chronoai-shining chronoai-shining merged commit bc41406 into develop Jun 16, 2026
17 checks passed
@chronoai-shining chronoai-shining deleted the feature/1127-read-write-split branch June 16, 2026 06:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature] Separate read/write access (drop read_write level) + show edit UI to write-grantees

1 participant