Org support gap survey: evaluate and file remaining org-feature tickets vs Forgejo/GitHub parity #349

Closed
opened 2026-09-11 19:02:41 +00:00 by crueber · 3 comments
Owner

What's requested

A survey of the remaining organization-feature gaps versus Forgejo/GitHub parity, with one ticket each filed for what's worth building — so org support reaches the "fully usable orgs" bar rather than the current create/manage core.

This ticket is the survey + filing pass: investigate each candidate gap against the current tree, file tickets for the real ones, and report here which were filed (with numbers) and which were judged already-covered or not worth a ticket.

Candidate gaps to evaluate (initial evidence from a tree scan — verify each)

  1. Org rename / display-name identity separation. The org id is immutable (orgs/<org>/ keys; no rename service exists). GitHub allows renaming with redirect. Evaluate: rename-with-redirect vs display-name-only (DisplayName already exists on Org). Likely worth a small ticket or an explicit "display name only" ruling.
  2. Org avatar / logo. No avatar field or storage anywhere (verified: no avatar/logo in identity surfaces or the settings UI). GitHub/Forgejo both have org avatars. Evaluate against the repo-attachment/LFS storage precedent for where bytes could live.
  3. Org deletion repo handling. DeleteOrg (internal/identity/orgs.go:617-635) refuses with 409 while the org owns repos ("transfer or delete them first") — but no repo transfer mechanism exists (no transfer service, no UI). The 409 message references a capability that doesn't exist. Evaluate: repo transfer to another user/org (Forgejo has it), or delete-org-with-repos as an admin operation, or reword the 409. The dangling reference is the bug either way.
  4. Team ↔ repo access bindings from the UI. team:<org>/<slug> subjects are fully supported in access.json role bindings (internal/identity/access.go:41-49) and web/src/pages/Access.jsx edits role bindings — but verify whether the Access UI makes binding a team discoverable (free-text subject vs a team dropdown fed from the org's teams). If it's free-text, that's a UX ticket.
  5. Org-level default permissions / new-repo defaults. GitHub orgs have a default repo permission for members. Evaluate whether member-created repos under an org should get automatic bindings, or whether the #346 admission rule + explicit bindings suffice for v1 (likely: document as not-needed-yet).
  6. Org audit log / activity. GitHub orgs have an audit log; walhub has an events/notify machinery that could surface org-level activity (member changes, team changes, repo creates). Evaluate cost vs value; probably a fast-follow.
  7. Two-factor / member security posture. Out of scope for v1 — note as explicitly deferred, don't file.
  8. Org profile completeness. #348 covers create + profile page + management; check whether location/website/avatar fields belong with the org profile (GitHub has them) — bundle into #348 as a small amendment or a tiny follow-up.
  9. Member roles granularity. v1 is owner/member only (OrgOwner/OrgMember, identity.go:90-94). Forgejo has more (admin, write, read tiers). Evaluate: probably fine for v1; file only if team-scoped roles don't cover the need.
  10. Invitation lifecycle UI. Invites have TTLs and accept links (invites.go:36,155,181); verify the settings Invitations tab lists pending invites with revoke/expiry display, and whether invite acceptance is a usable page. File gaps found.
  11. Org-level webhook/event fan-out. Webhooks are repo-scoped (internal/notify); org-level webhooks (events across all org repos) don't exist. Evaluate — Forgejo/GitHub both have org webhooks. Likely a real ticket, medium size.
  12. Cross-org naming/namespace rules. Org names share the owner namespace with user names (an org and a user can't collide — verify CreateOrg guards against colliding with an existing user principal, and that a user can't be created with an org's name). If unguarded, that's a correctness ticket.

Process

  • For each candidate: verify against the current tree (not this list — the tree moves fast), decide file / defer / already-covered, and file with the standard shape (evidence → design → acceptance criteria) plus the standing label+milestone.
  • Prefer few, well-scoped tickets over one mega-ticket; bundle trivial pairs (e.g. avatar + profile fields) when they share an implementation surface.
  • Report back here with the filed list and the explicitly-deferred list with one-line reasons.

Acceptance criteria

  • Every candidate above has a verified verdict (filed as #N / already covered by #M / deferred with reason).
  • Tickets filed carry the standard label+milestone and reference this survey ticket.
  • The dangling-capability finding (DeleteOrg's 409 referencing non-existent transfer) is resolved one way or another — it's a live contradiction in the current code.
  • Namespace collision rules (org vs user names) verified with evidence either way.
## What's requested A survey of the remaining organization-feature gaps versus Forgejo/GitHub parity, with one ticket each filed for what's worth building — so org support reaches the "fully usable orgs" bar rather than the current create/manage core. This ticket is the **survey + filing pass**: investigate each candidate gap against the current tree, file tickets for the real ones, and report here which were filed (with numbers) and which were judged already-covered or not worth a ticket. ## Candidate gaps to evaluate (initial evidence from a tree scan — verify each) 1. **Org rename / display-name identity separation.** The org id is immutable (`orgs/<org>/` keys; no rename service exists). GitHub allows renaming with redirect. Evaluate: rename-with-redirect vs display-name-only (DisplayName already exists on `Org`). Likely worth a small ticket or an explicit "display name only" ruling. 2. **Org avatar / logo.** No avatar field or storage anywhere (verified: no `avatar`/`logo` in identity surfaces or the settings UI). GitHub/Forgejo both have org avatars. Evaluate against the repo-attachment/LFS storage precedent for where bytes could live. 3. **Org deletion repo handling.** `DeleteOrg` (`internal/identity/orgs.go:617-635`) refuses with 409 while the org owns repos ("transfer or delete them first") — but **no repo transfer mechanism exists** (no transfer service, no UI). The 409 message references a capability that doesn't exist. Evaluate: repo transfer to another user/org (Forgejo has it), or delete-org-with-repos as an admin operation, or reword the 409. The dangling reference is the bug either way. 4. **Team ↔ repo access bindings from the UI.** `team:<org>/<slug>` subjects are fully supported in `access.json` role bindings (`internal/identity/access.go:41-49`) and `web/src/pages/Access.jsx` edits role bindings — but verify whether the Access UI makes binding a *team* discoverable (free-text subject vs a team dropdown fed from the org's teams). If it's free-text, that's a UX ticket. 5. **Org-level default permissions / new-repo defaults.** GitHub orgs have a default repo permission for members. Evaluate whether member-created repos under an org should get automatic bindings, or whether the #346 admission rule + explicit bindings suffice for v1 (likely: document as not-needed-yet). 6. **Org audit log / activity.** GitHub orgs have an audit log; walhub has an events/notify machinery that could surface org-level activity (member changes, team changes, repo creates). Evaluate cost vs value; probably a fast-follow. 7. **Two-factor / member security posture.** Out of scope for v1 — note as explicitly deferred, don't file. 8. **Org profile completeness.** #348 covers create + profile page + management; check whether location/website/avatar fields belong with the org profile (GitHub has them) — bundle into #348 as a small amendment or a tiny follow-up. 9. **Member roles granularity.** v1 is owner/member only (`OrgOwner`/`OrgMember`, `identity.go:90-94`). Forgejo has more (admin, write, read tiers). Evaluate: probably fine for v1; file only if team-scoped roles don't cover the need. 10. **Invitation lifecycle UI.** Invites have TTLs and accept links (`invites.go:36,155,181`); verify the settings Invitations tab lists pending invites with revoke/expiry display, and whether invite *acceptance* is a usable page. File gaps found. 11. **Org-level webhook/event fan-out.** Webhooks are repo-scoped (`internal/notify`); org-level webhooks (events across all org repos) don't exist. Evaluate — Forgejo/GitHub both have org webhooks. Likely a real ticket, medium size. 12. **Cross-org naming/namespace rules.** Org names share the owner namespace with user names (an org and a user can't collide — verify `CreateOrg` guards against colliding with an existing user principal, and that a user can't be created with an org's name). If unguarded, that's a correctness ticket. ## Process - For each candidate: verify against the current tree (not this list — the tree moves fast), decide file / defer / already-covered, and file with the standard shape (evidence → design → acceptance criteria) plus the standing label+milestone. - Prefer few, well-scoped tickets over one mega-ticket; bundle trivial pairs (e.g. avatar + profile fields) when they share an implementation surface. - Report back here with the filed list and the explicitly-deferred list with one-line reasons. ## Acceptance criteria - [ ] Every candidate above has a verified verdict (filed as #N / already covered by #M / deferred with reason). - [ ] Tickets filed carry the standard label+milestone and reference this survey ticket. - [ ] The dangling-capability finding (DeleteOrg's 409 referencing non-existent transfer) is resolved one way or another — it's a live contradiction in the current code. - [ ] Namespace collision rules (org vs user names) verified with evidence either way.
crueber added this to the v1 milestone 2026-09-11 19:02:41 +00:00
Author
Owner

Org gap survey verdicts vs current tree (origin/main @83bf231, post-#348/#346/#347). All tickets carry labels issues+oidc, milestone v1, and reference this survey.

Filed (7)

  • #358 — Repo transfer + DeleteOrg handling + delete-org UI (candidate 3 + extra). DeleteOrg 409 (orgs.go:635) references a transfer capability that does not exist (no TransferRepo service/route/UI anywhere); orgs that own repos are undeletable. Also: DELETE org API + SDK exist but Org.jsx has no delete affordance. This resolves the acceptance criterion on the dangling 409.
  • #359 — Org profile parity + avatar (candidates 2+8 bundled). Org has display_name/description only (orgs.go:26-33) vs owner profiles with location/timezone/bio_markdown (api/profile.go:51); zero avatar storage anywhere.
  • #360 — Org rename decision ticket (candidate 1). No rename service; ids immutable (orgs// + repos//*). Recommends display-name-only ruling for v1.
  • #361 — Access tab team dropdown (candidate 4). team: subjects fully supported server-side (access.go:41-57) but Access.jsx:154-172 is free-text only; SDK teams.list already exists to feed a picker.
  • #362 — Invitation lifecycle UI (candidate 10). Backend complete (TTL + expires_at served, accept/decline endpoints) but: InvitesTab renders no expiry, and no invitee inbox/accept page exists (no route; accept_url is an API path).
  • #363 — Org-level webhooks (candidate 11). Hooks are repo-scoped only (/{o}/{r}/api/webhooks); notify has only @org/team mention expansion.
  • #364 — Org audit/activity log (candidate 6). collab-events are per-repo (notify/activity.go); no org-level aggregation.

Already covered — no ticket

  • Candidate 12 (namespace collisions): structurally impossible. Orgs match ^[a-z0-9-]{1,39}$ (identity.go:110-115, no @ possible); principals must mail.ParseAddress (identity.go:121-130, @ required); storage prefixes disjoint (orgs/ vs users/). No guard missing, no ticket.

Deferred with reasons — no ticket

  • Candidate 5 (org default permissions): #346 member-may-create + EnsureRepoAccess creator-admin binding + Resolve org-owner auto-admin (access.go:253) covers v1; GitHub-style default-permission tiers are not-needed-yet.
  • Candidate 7 (2FA/member security): explicitly out of scope per survey brief.
  • Candidate 9 (member-role granularity): owner/member + 5-rung team repo roles cover v1 via teams; file only if a concrete need appears.

Extras considered, not filed

  • Teams/members reads allow anonymous on public hosts (http.go routeMembers/routeTeams gate on anonymousRead only): matches Forgejo public-org behavior; not a gap.
  • No other org gaps found beyond the above (member invite flow exists end-to-end server-side; Team.jsx page exists; OrgNew create flow exists).
Org gap survey verdicts vs current tree (origin/main @83bf231, post-#348/#346/#347). All tickets carry labels issues+oidc, milestone v1, and reference this survey. ## Filed (7) - #358 — Repo transfer + DeleteOrg handling + delete-org UI (candidate 3 + extra). DeleteOrg 409 (orgs.go:635) references a transfer capability that does not exist (no TransferRepo service/route/UI anywhere); orgs that own repos are undeletable. Also: DELETE org API + SDK exist but Org.jsx has no delete affordance. This resolves the acceptance criterion on the dangling 409. - #359 — Org profile parity + avatar (candidates 2+8 bundled). Org has display_name/description only (orgs.go:26-33) vs owner profiles with location/timezone/bio_markdown (api/profile.go:51); zero avatar storage anywhere. - #360 — Org rename decision ticket (candidate 1). No rename service; ids immutable (orgs/<org>/ + repos/<org>/*). Recommends display-name-only ruling for v1. - #361 — Access tab team dropdown (candidate 4). team: subjects fully supported server-side (access.go:41-57) but Access.jsx:154-172 is free-text only; SDK teams.list already exists to feed a picker. - #362 — Invitation lifecycle UI (candidate 10). Backend complete (TTL + expires_at served, accept/decline endpoints) but: InvitesTab renders no expiry, and no invitee inbox/accept page exists (no route; accept_url is an API path). - #363 — Org-level webhooks (candidate 11). Hooks are repo-scoped only (/{o}/{r}/api/webhooks); notify has only @org/team mention expansion. - #364 — Org audit/activity log (candidate 6). collab-events are per-repo (notify/activity.go); no org-level aggregation. ## Already covered — no ticket - Candidate 12 (namespace collisions): structurally impossible. Orgs match ^[a-z0-9-]{1,39}$ (identity.go:110-115, no @ possible); principals must mail.ParseAddress (identity.go:121-130, @ required); storage prefixes disjoint (orgs/ vs users/). No guard missing, no ticket. ## Deferred with reasons — no ticket - Candidate 5 (org default permissions): #346 member-may-create + EnsureRepoAccess creator-admin binding + Resolve org-owner auto-admin (access.go:253) covers v1; GitHub-style default-permission tiers are not-needed-yet. - Candidate 7 (2FA/member security): explicitly out of scope per survey brief. - Candidate 9 (member-role granularity): owner/member + 5-rung team repo roles cover v1 via teams; file only if a concrete need appears. ## Extras considered, not filed - Teams/members reads allow anonymous on public hosts (http.go routeMembers/routeTeams gate on anonymousRead only): matches Forgejo public-org behavior; not a gap. - No other org gaps found beyond the above (member invite flow exists end-to-end server-side; Team.jsx page exists; OrgNew create flow exists).
Author
Owner

Survey complete: children filed #358 (transfer + DeleteOrg + delete UI), #359 (profile parity + avatar), #360 (rename decision), #361 (team dropdown), #362 (invite lifecycle UI), #363 (org webhooks), #364 (org audit log). This ticket closes once all seven children are closed.

Survey complete: children filed #358 (transfer + DeleteOrg + delete UI), #359 (profile parity + avatar), #360 (rename decision), #361 (team dropdown), #362 (invite lifecycle UI), #363 (org webhooks), #364 (org audit log). This ticket closes once all seven children are closed.
Author
Owner

All seven children closed: #358 (transfer + delete), #359 (profile + avatar), #360 (rename ruling), #361 (team picker), #362 (invite UI), #363 (org webhooks), #364 (audit log). Closing the survey parent.

All seven children closed: #358 (transfer + delete), #359 (profile + avatar), #360 (rename ruling), #361 (team picker), #362 (invite UI), #363 (org webhooks), #364 (audit log). Closing the survey parent.
Sign in to join this conversation.
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
crueber/walhub#349
No description provided.