Skip to content

feat: Add ability to limit visibility of roadmaps and changelogs by segment#269

Open
arcdigital wants to merge 2 commits into
QuackbackIO:mainfrom
arcdigital:feat/additional-visibility-options
Open

feat: Add ability to limit visibility of roadmaps and changelogs by segment#269
arcdigital wants to merge 2 commits into
QuackbackIO:mainfrom
arcdigital:feat/additional-visibility-options

Conversation

@arcdigital

@arcdigital arcdigital commented Jun 19, 2026

Copy link
Copy Markdown

Add audience visibility controls to roadmaps and changelogs

Summary

This MR brings roadmaps and changelogs onto the same tier-based access model
already used by boards, so an admin can scope a roadmap or changelog entry to
anonymous (public), authenticated users, specific segments, or
team-only.

Access model

Both reuse the shared AccessTier (anonymous | authenticated | segments | team)
so the policy layer stays uniform. Unlike BoardAccess (view/vote/comment/submit),
roadmaps and changelogs have only a view action, so each gets a minimal shape:

interface RoadmapAccess   { view: AccessTier; segments: { view: string[] } }
interface ChangelogAccess { view: AccessTier; segments: { view: string[] } }

Key behaviors:

  • Fail closed on segments. A segments tier with an empty allowlist hides
    the item and is rejected on save — it never silently falls open to public.
  • 404, never 403. A roadmap the actor may not view returns not-found, so a
    restricted tier can't be used to probe which items exist.
  • Policy parity. Every canViewX() single-row check is paired with a
    xViewFilter() SQL predicate for list queries, and a parity test asserts the
    two agree row-by-row — so a list and a direct fetch can never disagree about
    visibility.
  • Changelog visibility is orthogonal to publish lifecycle. publishedAt
    still decides whether/when an entry is live; access decides who may see
    a live entry. The audience gate is AND-ed into the existing public-visibility
    conditions shared by all public read paths.

API compatibility

The REST API keeps the legacy isPublic boolean for backward compatibility:

  • On read, isPublic is derived (access.view === 'anonymous') and the
    full access object is also returned.
  • On write, access takes precedence; when only isPublic is supplied it
    is mapped onto an access object (true → anonymous, false → team).

Migrations

  • 0114_roadmap_accessbreaking column swap. Adds the access jsonb,
    backfills from is_public (true → anonymous, false → team), drops the
    roadmaps_is_public_idx index, then drops the is_public column. The
    backfill is mandatory: without it, previously-private roadmaps would inherit
    the anonymous default and become public.
  • 0115_changelog_accesspurely additive. Adds the access jsonb with
    an anonymous default, so every existing entry stays public. No backfill
    needed.

What changed

Data / types

  • packages/dbRoadmapAccess / ChangelogAccess types + defaults, schema
    columns, migrations 0114 / 0115, seed + schema/type tests.

Policy

  • New policy/roadmaps.ts and policy/changelog.ts (single-row check + list
    filter), exported from policy/index.ts.
  • New parity tests (*-view-filter-parity.test.ts) plus dedicated policy tests.

Server / API

  • Roadmap & changelog services, queries, and public readers now resolve the
    caller's actor and apply the view filter.
  • REST routes (/api/v1/roadmaps, /api/v1/changelog) and internal server
    functions accept access (with isPublic back-compat) and return both.

Admin UI

  • New RoadmapAccessControl and ChangelogAccessControl components (tier
    picker + segment allowlist), wired into the roadmap sidebar and the changelog
    create dialog / metadata sidebar / modal. Save is disabled while a segments
    selection has an empty allowlist.

Testing notes

  • New unit tests cover the policy checks and list-filter parity for both
    surfaces; existing roadmap/portal-gate tests were updated from isPublic to
    access.
  • Run db:migrate against a copy first0114 drops roadmaps.is_public.
    Verify any external readers of that column are updated.
  • Suggested manual check: create a segment, set a roadmap and a changelog entry
    to that segment, and confirm a non-member (anonymous and authenticated) gets a
    404 / omission while a member sees it.

Implements #262 and #271

Copilot AI review requested due to automatic review settings June 19, 2026 21:09
@CLAassistant

CLAassistant commented Jun 19, 2026

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces segment-based audience visibility controls for roadmaps and changelog entries, aligning them with the existing tiered access model used elsewhere (e.g., boards). It adds access JSONB fields (with defaults/migrations), policy checks + SQL filters (with parity tests), and wires the new access controls through REST endpoints and the admin UI.

Changes:

  • Add RoadmapAccess / ChangelogAccess types + DB columns/migrations, replacing legacy roadmaps.is_public.
  • Implement policy-layer authorization (canView*) and list-query SQL predicates (*ViewFilter) with parity tests.
  • Update REST routes, server functions, and admin UI to read/write access (keeping roadmap isPublic for back-compat on REST).

Reviewed changes

Copilot reviewed 50 out of 50 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
packages/db/src/types.ts Adds RoadmapAccess / ChangelogAccess types and defaults.
packages/db/src/seed.ts Seeds roadmaps using access instead of isPublic.
packages/db/src/schema/changelog.ts Adds changelog_entries.access JSONB column default in schema.
packages/db/src/schema/boards.ts Replaces roadmaps.isPublic with roadmaps.access and drops index.
packages/db/src/tests/types.test.ts Updates Roadmap type expectation from isPublicaccess.
packages/db/src/tests/schema.test.ts Updates schema tests for new access column + regression guard.
packages/db/drizzle/meta/_journal.json Registers migrations 0114/0115 in Drizzle journal.
packages/db/drizzle/0115_changelog_access.sql Adds access column to changelog_entries (additive).
packages/db/drizzle/0114_roadmap_access.sql Adds/backfills roadmaps.access, drops is_public + index.
apps/web/src/routes/api/v1/roadmaps/index.ts REST create/list: accept access + derive legacy isPublic.
apps/web/src/routes/api/v1/roadmaps/$roadmapId.ts REST get/update: return access + derive legacy isPublic.
apps/web/src/routes/api/v1/changelog/index.ts REST create/list: accept/return access.
apps/web/src/routes/api/v1/changelog/$entryId.ts REST update: accept access; response includes access.
apps/web/src/lib/shared/schemas/roadmaps.ts Adds shared Zod validation for RoadmapAccess.
apps/web/src/lib/shared/schemas/changelog.ts Adds shared Zod validation for ChangelogAccess.
apps/web/src/lib/shared/db-types.ts Re-exports new access types/defaults for client-safe imports.
apps/web/src/lib/server/policy/roadmaps.ts Adds roadmap visibility policy + SQL filter.
apps/web/src/lib/server/policy/index.ts Exports new roadmap/changelog policies.
apps/web/src/lib/server/policy/changelog.ts Adds changelog visibility policy + SQL filter.
apps/web/src/lib/server/policy/tests/roadmaps.test.ts Adds matrix tests for canViewRoadmap.
apps/web/src/lib/server/policy/tests/roadmap-view-filter-parity.test.ts Adds execution-level parity test for roadmap filter.
apps/web/src/lib/server/policy/tests/changelog.test.ts Adds matrix tests for canViewChangelog.
apps/web/src/lib/server/policy/tests/changelog-view-filter-parity.test.ts Adds execution-level parity test for changelog filter.
apps/web/src/lib/server/functions/roadmaps.ts ServerFns updated to read/write access.
apps/web/src/lib/server/functions/public-posts.ts Public roadmap listing now resolves actor + applies access filter.
apps/web/src/lib/server/functions/portal.ts Portal public roadmaps now resolve actor + return access.
apps/web/src/lib/server/functions/embeds.ts Embed changelog meta lookup now includes actor for access filtering.
apps/web/src/lib/server/functions/changelog.ts Public changelog read/list now resolve actor + apply access.
apps/web/src/lib/server/functions/tests/portal-gate-public-posts.test.ts Updates roadmap fixtures to use access.
apps/web/src/lib/server/functions/tests/portal-gate-extended.test.ts Updates roadmap fixtures to use access.
apps/web/src/lib/server/domains/roadmaps/roadmap.types.ts Updates service input types to accept access.
apps/web/src/lib/server/domains/roadmaps/roadmap.service.ts Persists access, adds isPublic back-compat helpers, updates public list to actor-aware filter.
apps/web/src/lib/server/domains/roadmaps/roadmap.query.ts Public roadmap posts now 404 on denied access (uses canViewRoadmap).
apps/web/src/lib/server/domains/roadmaps/tests/roadmap-public-moderation.test.ts Updates mock roadmap shape to use access.
apps/web/src/lib/server/domains/roadmaps/tests/roadmap-public-audience.test.ts Updates mock roadmap shape to use access.
apps/web/src/lib/server/domains/changelog/changelog.types.ts Adds access to changelog domain types.
apps/web/src/lib/server/domains/changelog/changelog.service.ts Persists access with default, supports updates, returns access.
apps/web/src/lib/server/domains/changelog/changelog.query.ts Includes access in list results.
apps/web/src/lib/server/domains/changelog/changelog.public.ts ANDs audience gate into public changelog visibility conditions.
apps/web/src/lib/server/domains/api/schemas/roadmaps.ts OpenAPI schema now includes access and legacy isPublic semantics.
apps/web/src/lib/server/domains/api/schemas/changelog.ts OpenAPI schema now includes access.
apps/web/src/lib/client/mutations/roadmaps.ts Client mutations updated to send access.
apps/web/src/lib/client/hooks/use-roadmaps-query.ts Roadmap view type updated to expose access.
apps/web/src/components/admin/roadmaps/roadmap-access-control.tsx New admin UI control for selecting roadmap visibility tier + segments.
apps/web/src/components/admin/roadmap-sidebar.tsx Replaces public switch with access control + validation.
apps/web/src/components/admin/changelog/create-changelog-dialog.tsx Adds access state + passes to create mutation; disables submit on invalid segments.
apps/web/src/components/admin/changelog/changelog-modal.tsx Adds access state + passes to update mutation; disables submit on invalid segments.
apps/web/src/components/admin/changelog/changelog-metadata-sidebar.tsx Plumbs access props into metadata sidebar.
apps/web/src/components/admin/changelog/changelog-metadata-sidebar-content.tsx Renders access control in changelog metadata sidebar UI.
apps/web/src/components/admin/changelog/changelog-access-control.tsx New admin UI control for changelog audience tier + segments.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread apps/web/src/components/admin/roadmaps/roadmap-access-control.tsx
Comment thread packages/db/src/schema/changelog.ts Outdated
Comment thread apps/web/src/lib/server/domains/api/schemas/roadmaps.ts
Comment thread apps/web/src/lib/server/domains/api/schemas/changelog.ts
@arcdigital arcdigital force-pushed the feat/additional-visibility-options branch from 69a1634 to 8fb0083 Compare June 19, 2026 21:30
@arcdigital arcdigital force-pushed the feat/additional-visibility-options branch from 8fb0083 to adec079 Compare June 19, 2026 21:31
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.

3 participants