Estimate: 60m · Depends on: 6.12.2
Implement the public access level + the access-check extension per 6.12.2. This is the load-bearing correctness work the whole story rides — it must EXTEND 6.4’s shipped projectAccessService, not fork a parallel policy.
public to the existing 6.4 ProjectAccessLevel enum (open | limited | private | public) with a migration; no default change (existing projects stay their current level — adding an enum value locks nothing out). Add the PublicRequestVote join ({ id, workItemId, userId, createdAt }, unique on (workItemId, userId)) modelled as Prisma @relation on BOTH sides (to work_item + User) per the CLAUDE.md FK-as-@relation rule — NO raw-SQL-only FK (6.12.6 uses it, but the model lands with the access foundation so the schema is coherent in one migration).publicOverviewMd (the public Overview/README field): add a nullable String? column publicOverviewMd to project (Markdown authored by the admin in 6.12.8, rendered on the public Overview tab in 6.12.4). It lands in THIS migration so the schema is coherent in one shot; it is a public-safe field included in the public projection (6.12.4) only when the project is public.canBrowse so that when a project is public, it returns true for ANYONE — INCLUDING an unauthenticated request (no session) — bypassing the 6.10 org/workspace membership gate FOR READ ON PUBLIC PROJECTS ONLY (so the public view route does NOT getSession()-gate the read; it renders server-side for crawlers + logged-out visitors). Every other level keeps its 6.4 semantics; the 404-not-403 posture is UNTOUCHED for non-public projects (a cross-org user on a non-public project still gets not-found). canEdit is UNCHANGED — a public viewer (non-member) gets false for every normal write. Add the three explicit grants canSubmitToTriage / canUpvotePublicRequest / canCommentPublicRequest (true for any authed account on a public project), independent of canEdit, so later cards check THEM, never a relaxed edit gate.Stay 4-layer: the enum + vote model in prisma/schema.prisma + the repository, the access policy in the service layer (extend the existing projectAccessService), no raw Prisma in routes.
public to ProjectAccessLevel, the PublicRequestVote join (every FK an @relation on both sides), and the nullable project.publicOverviewMd Markdown column; prisma migrate dev reports no drift; no existing project’s level changes.canBrowse returns true for ANYONE — including an unauthenticated request — on a public project (cross-org), in a single auditable branch; non-public projects keep 6.4 semantics + the 404-not-403 cross-tenant posture unchanged.canEdit is unchanged (a public non-member viewer → false for every normal write); the three explicit grants (canSubmitToTriage / canUpvotePublicRequest / canCommentPublicRequest) exist and are true only for an authenticated account on a public project, independent of canEdit.scripts/plan-seed/data/story-6.4.ts (6.4.2 schema + 6.4.3 projectAccessService) — the enum + canBrowse/canEdit policy this EXTENDS (mirror its shape; do not fork).motir-core/lib/repositories/ + lib/services/ — the project access service + repositories the extension threads into.motir-core/CLAUDE.md § 4-layer + § migration FK-as-@relation rule.