Skip to content

feat(core): MVP feature flags that hide non-MVP areas - #44

Merged
jhosepmyr merged 10 commits into
developfrom
feature/mvp-feature-flags
Oct 7, 2026
Merged

jhosepmyr merged 10 commits into
developfrom
feature/mvp-feature-flags

Conversation

@jhosepmyr

Copy link
Copy Markdown
Contributor

Description

Adds typed build-time feature flags so the deployed product shows only the MVP. Non-MVP areas stay in the code and the backend but disappear from the UI and their URLs stop matching. Turning a flag back on in both environment files restores the area with no other change.

  • features map in environment.ts and environment.prod.ts (FeatureKey union, FeatureFlags.isEnabled()); a missing key is a compile error.
  • featureGuard() canMatch: a disabled route behaves like an unknown URL (falls through to /projects, no blank page or "no access" toast).
  • Sidebar, command palette, user menu, cards and buttons drop what the flags hide.

All seven flags are off: billing (pages, checkout returns, Upgrade CTA), usage, integrations (Jira pages, OAuth callback, job banner, import/push actions), members (org and project members, invitation landing, palette action, ownership transfer), customRoles, notifications, tokens. With them off the app sends no requests to those endpoints. See docs/FEATURE-FLAGS.md.

Feature module / area: core / layout / shared / workspace / discovery / billing

Related issue / US: MVP scope ("prune the tree"): Jira export, billing, custom roles and members move to a later increment


Type of Change

  • feat — new feature or UI component
  • fix — bug fix
  • refactor — code change without behavior change
  • test — tests only
  • docs — documentation only
  • build / ci — build, dependencies, or CI/CD
  • chore — maintenance

Checklist

  • The PR targets develop (not main)
  • Branch name follows feature/*, bugfix/*, or hotfix/*
  • Commits follow Conventional Commits
  • bun run lint passes locally (ESLint + angular-eslint)
  • bun run test passes locally (Vitest) — 339 passed
  • bun run build passes locally (no type errors, no budget exceeded)
  • New components use ChangeDetectionStrategy.OnPush and Angular signals
  • No localStorage/sessionStorage access for JWT tokens (use the auth store)
  • No bypassSecurityTrust* calls without explicit review
  • No secrets, credentials, or .env content committed
  • CHANGELOG.md updated under [Unreleased]

How to Test

  1. Sign in as owner, admin and member: no billing, usage, integrations, members, roles, notifications or tokens entries anywhere (sidebar, ⌘K, user menu, project overview, stories toolbar, story detail).
  2. Open a hidden URL directly, e.g. /settings/billing or /projects/<id>/settings/roles → lands on /projects.
  3. Set every flag to true in both environment files → everything comes back.

Notes (optional)

  • The backend still enforces the FREE plan limits (25 projects, 50 glossary terms per project, 10 documents, 3 members). With billing off, a user who hits a limit sees the message but has no upgrade path.
  • With members off there is no screen to invite or remove members, and invitation links land on /projects.

Introduce a single source of truth for hiding non-MVP areas: a `features`
map in both environment files (all off), typed by the `FeatureKey` union
and checked with `satisfies FeatureFlagMap`, read through the
`FeatureFlags.isEnabled()` service. Tests override flags with
`provideFeatureFlags()`.

`featureGuard(key, next?)` is a functional canMatch guard: while the flag
is off the route does not match, so the URL falls through to the wildcard
fallback like an unknown path (no blank page, no lazy chunk). An existing
canMatch guard is chained through `next` because Angular runs every guard
of a canMatch array eagerly.
Gate every non-MVP route with featureGuard so a disabled feature's URLs
fall through to the `**` -> /projects fallback like unknown paths:
billing (settings page + checkout success/cancel), usage, integrations
(org + project pages and the Jira OAuth callback), members (org + project
pages and the invitation landing), customRoles (roles list/new/edit) and
the notifications/tokens account placeholders. Routes that already had a
permission canMatch chain it through featureGuard so a hidden page never
fetches permissions or toasts a denial.

The invitation landing follows `members`: with it off nobody can invite
from the UI and an owner has no screen to see or remove whoever joins, so
a stale link must not add people to an org.

The project settings landing skips members/roles while their feature is
off. A spec drives the real route table to pin both directions.
Move the nav segment lists and their visibility rule into shell-nav.ts so
they can be unit-tested, and add an optional `feature` to NavSeg that
visibleNavSegs checks alongside role/permission. Flagged entries: org
members/billing/integrations/usage, project roles/members/integrations
and the account notifications/tokens placeholders.

A "Settings" root entry with no reachable sub-page is now dropped, and
the org one opens the first reachable sub-page instead of the owner-only
General, so an admin never lands on a "no access" toast. The Jira job
banner only mounts while integrations are on.
…menu

The palette's "Members" quick action and backend MEMBER hits (both open
/settings/members) only appear while `members` is on; the user menu's
"Upgrade" CTA only while `billing` is on.
Wrap the backlog's Import from Jira / Push all / Push selected buttons and
the story detail's Push to Jira in the `integrations` flag, skip the
backlog's Jira-target lookup, and only let INTEGRATION_SYNC reveal the
row-selection column while bulk push exists (bulk delete still needs
STORY_DELETE).
The project overview drops its Members card (and no longer counts
members/roles toward the Settings card) while `members` / `customRoles`
are off. Org General hides the Transfer ownership card, and skips the
member fetch that only feeds it, while `members` is off: without members
there is nobody to pick and its empty state asks to invite someone.
Usage and billing are separate flags; keep the usage page free of a dead
link to /settings/billing when only `usage` is turned on.
Add docs/FEATURE-FLAGS.md (each flag, what it hides, how to turn it on or
add one, the invitation/ownership decisions and the backend FREE-plan
limits) and link it from the README environments section.
@jhosepmyr
jhosepmyr removed the request for review from salimramirez October 7, 2026 13:12
@jhosepmyr
jhosepmyr merged commit 2b5267a into develop Oct 7, 2026
5 checks passed
@jhosepmyr jhosepmyr mentioned this pull request Oct 7, 2026
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.

1 participant