docs(planning): add FF-EPIC-17 selection lists epic (FFRNT-186) - #528
Open
izzywdev wants to merge 5 commits into
Open
docs(planning): add FF-EPIC-17 selection lists epic (FFRNT-186)#528izzywdev wants to merge 5 commits into
izzywdev wants to merge 5 commits into
Conversation
Version-controlled source of truth for the SelectionList microservice, mirroring the FFRNT-186 epic created in FuzeFront SCRUM. Records the four owner-confirmed decisions and, most importantly, why authorization is per-list rather than per-item: items are the high-cardinality entity (hundreds of values x 11 locales), and a partially-visible selection list is a broken one — the list is the unit of meaning, a closed vocabulary, so a viewer-dependent option set makes another user's stored UUIDs un-interpretable. Action granularity (read / add_value / update_value / remove_value / translate / update / delete / manage_access) answers the governance question without per-item ACLs, and items keep created_by so per-item ownership stays a cheap ABAC addition later. Also captures the schema (archive-not-delete, immutable code, side translation tables), the read-model mirror that must never become an authorization source, atomic advisory-locked quota enforcement, and the 16-story / 51-sub-task breakdown authored against the FuzePlan ticket-creator skills and validated against the ticket-enforcer rubric. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018PKRvNfpskTKPUfDc8X1G1
Contributor
Automated code review (gate-code-review)Credit balance is too low Report-only — this check never blocks merge. |
The Governance Sync bot pushed b53bd83 ("reconcile managed files to FuzeSDLC v1") on top of this branch with [skip ci], so no checks ran against the current head SHA — every green result belongs to a175723, the commit below it. This empty commit gives CI a head SHA it will actually evaluate, so required checks can report and the PR can leave mergeable_state=blocked. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018PKRvNfpskTKPUfDc8X1G1
Resolves mergeable_state=behind (master gained #524 and #517) and gives CI a head SHA with real file changes — the path-filtered workflows never ran against the empty commit cafae89. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018PKRvNfpskTKPUfDc8X1G1
Contributor
Automated code review (gate-code-review)Credit balance is too low Report-only — this check never blocks merge. |
scope Extends the epic to four shipped artifacts: the deployed service, two npm packages, and a Python client. Owner decisions recorded — @FuzeOne scope on GitHub Packages, Python client to PyPI, service stays in the monorepo. Records two publishing defects found while planning, both of which this epic now has to work around rather than inherit: - packages-publish.yml has never published anything. It is guarded on repository_owner == 'fuzefront' while the repo is owned by izzywdev, so the job has been a no-op for its entire life. Four other publishers are guarded on izzywdev and do run, so publishing is split across two inconsistent guards — and nothing in CI distinguishes a dormant job from a passing one. - No Python publish workflow exists, yet packages/identity-py already instructs consumers to pip install fuzefront-identity. That command resolves against nothing. Hence FFRNT-266 requires a dormant publish job to fail loudly, and FFRNT-276 verifies each artifact by installing it from a clean checkout against the real registry — a green publish job is not evidence that anything was published. Adds S17 (FFRNT-264, Python client), S18 (FFRNT-265, Swagger UI + consumer guide) and S19 (FFRNT-266, publishing) with 10 sub-tasks; 19 stories / 61 sub-tasks / 342 h. The critical path is unchanged — the new stories run parallel to it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018PKRvNfpskTKPUfDc8X1G1
Contributor
Automated code review (gate-code-review)Credit balance is too low Report-only — this check never blocks merge. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
📋 Description
Adds
docs/planning/epics/EPIC-17-selection-lists.md— the version-controlled source of truth for the SelectionList microservice, mirroring the FFRNT-186 epic in FuzeFront SCRUM (19 stories, 61 sub-tasks, 342 h).Docs only — one file. No service, contract, or UI code in this PR.
The problem it records. FuzeFront has no way for an organization to define its own reference data. Every dropdown in every consuming product is hard-coded or invented ad hoc, so two apps in the same org disagree about what "Region" means, and renaming a label needs a code change and a deploy. There is also no runtime i18n path for user-authored content —
packages/i18nandpackages/i18n-translatetranslate static UI strings at build time into git, which is structurally the wrong mechanism for content an org authors at runtime.🔄 Type of Change
🧪 Testing
parent = FFRNT-186returns exactly 19 stories;labels = selection-lists AND issuetype = Sub-taskreturns exactly 61. Story points equal the sum of their sub-tasks; every sub-task is{2, 4, 8}.Unit / integration / E2E are not applicable — this PR adds a markdown document.
🔧 Implementation Details
Changes Made
docs/planning/epics/EPIC-17-selection-lists.md, following the frontmatter and section structure ofEPIC-09…EPIC-16.No frontend, backend, or SDK changes.
The decision worth reviewing
Per-list ReBAC with action granularity, and explicitly not per-item ACLs. Two reasons, recorded so they survive into implementation:
What the governance question actually needs is action granularity:
read/add_value/update_value/remove_value/translate/update/delete/manage_access, held per-user per-list.translateis deliberately separate — a translator renders an existing vocabulary and must not change what it contains. Items keepcreated_byfrom day one, so "you may only edit values you created" later becomes an ABAC condition on an existing column rather than a new authorization subsystem.This follows
Organization.roles['org-admin']inbackend/src/permit/schema.ts, which already derives viagranted_to.users_with_role[].linked_by_relation.Other decisions recorded
selection_list_accessis a read-model mirror, never an authorization source. It exists only so the list index paginates in SQL instead of making N PDP calls. Every mutation still calls the PDP, fail-closed. A test asserts a stale mirror row cannot authorize — a mirror that quietly becomes the authority is a classic security regression.QuotaResolverinterface (per-org DB override → Permit plan-tier attribute → platform config ceiling), so the planned move to the key-value configuration-storage microservice is a new implementation plus a wiring change. Enforcement is advisory-locked because count-then-insert races.@fuzeone/*on GitHub Packages (scope must equal the owning org), Python client to PyPI, service stays in this monorepo.Two publishing defects this epic had to work around
Found while planning, both now tracked on FFRNT-266 — worth a reviewer's attention because they affect the whole family, not just this epic:
packages-publish.ymlhas never published anything. It is guardedif: github.repository_owner == 'fuzefront'while this repo is owned byizzywdev, so the job has been a no-op for its entire life — its own header comment states the constraint. Four other publishers (auth-ui,chat,design-system,security) guard onizzywdevand do run. Publishing is split across two inconsistent guards, and nothing in CI distinguishes a dormant job from a passing one.packages/identity-py/README.mdalready instructs consumers topip install fuzefront-identity. That command resolves against nothing.Hence FFRNT-266 requires a dormant publish job to fail loudly, and FFRNT-276 verifies each artifact by installing it from a clean checkout against the real registry — a green publish job is not evidence that anything was published.
Code Quality
Documentation
🚨 Breaking Changes
None. Documentation only.
📋 Checklist
Pre-submission
Code Quality
Security Checklist
🔗 Related Issues and PRs
📝 Additional Notes
Deployment Notes
None — no code ships in this PR, and the diff touches nothing under
services/**orfrontend/**, sorelease.yml's path filters will not fire an image build. The eventual service is gated byfuzefront.selection-lists.service, default OFF, with its Helm block defaulting toenabled: false.Deploy window: the epic is labelled
deploy-window.masteris deploy-on-push withrequired_signatures.Future Work
The 19 stories under FFRNT-186. S1 (frozen contract) and S2 (approved design frames) are the two sequential gates; everything else fans out behind them.
gate-frames-firstblocks any feature-UI PR without approved frames.Questions for Reviewers
SIZING.mdconvention where 1 point = 1 hour and sub-tasks are strictly{2, 4, 8}— this differs from the story-point scale used on the EPIC-09…16 stories.@fuzeoneonly publishes if the repo actually moves to that org. GitHub Packages matches scope to owner, so underizzywdevtoday@fuzeone/*fails exactly the way@fuzefront/*does. FFRNT-266 names this as its central assumption.📊 Performance Impact
🔄 Backwards Compatibility