Estimate: 45m
Type: decision (the keystone ADR the schema + access cards [6.12.3+] build against; no app behavior ships, but the shapes it fixes are load-bearing). Write motir-core/docs/decisions/public-projects.md, EXTENDING (not forking) 6.4’s access-level ADR. It MUST fix:
public extends ProjectAccessLevel (open / limited / private / public). Add public to the existing 6.4 enum; the openness ladder is public > open > limited > private. State that this is a one-value extension of the 6.4 model + the SAME projectAccessService policy — NOT a parallel access system.public = ANYONE reads CROSS-ORG, no sign-in (the single exception). Decide precisely: 6.4’s canBrowse returns true for ANYONE — INCLUDING an unauthenticated/anonymous request — when the project is public, BYPASSING the 6.10 org/workspace membership gate FOR READ ON PUBLIC PROJECTS ONLY. The public read is server-rendered + crawlable (SEO/GEO). Every other level stays org/workspace-bounded; the 404-not-403 cross-tenant posture is preserved for non-public projects (a cross-org user hitting a non-public project is still not-found, never forbidden). Fix WHERE this exception lives so it is a single, auditable branch in the access policy (not scattered).canEdit relaxation. A public viewer is not a member, so 6.4 canEdit is FALSE for every normal write (create/move/assign/status/field-edit). The three permitted writes are NEW narrow capabilities checked explicitly: canSubmitToTriage, canUpvotePublicRequest, canCommentPublicRequest — each true for any authenticated account on a public project, each independent of canEdit. State that no other write path may ever key off "is on a public project".work_item), attributed to the cross-org account. Fix the duplicate-detection contract (a deterministic title/text match over existing PUBLIC requests, surfaced BEFORE create so the user upvotes the existing one — Canny’s behaviour) and the vote model (one vote per account per item, server-enforced; the count is a sort key the 6.11 triage queue reads). Decide the vote storage (a PublicRequestVote join, unique on (workItemId, userId)).projectAccessService.canEdit, and the EXACT hidden-field set (assignees, estimates, internal comments) stripped by a public PROJECTION at the read layer.scripts/plan-seed/data/story-6.4.ts — the shipped ProjectAccessLevel (open/limited/private) + the projectAccessService canBrowse/canEdit policy this extends.scripts/plan-seed/data/story-6.11.ts — the triage intake (6.11.4) + queue (6.11.3) the public submit reuses + the vote-count sort feeds.scripts/plan-seed/data/story-6.10.ts — the org gate the public cross-org READ exception bypasses (for public projects only).motir-core/lib/services/workItemsService.ts — the write authority submit/upvote/comment route through.