Raxx · internal docs

internal · gated

Velvet UI Cluster Sprint Plan — 2026-05-20 UTC

T-3 to v1 launch (2026-05-23 UTC) Operator: Kristerpher PM review date: 2026-05-20 UTC Parent epic: #907


1. Scope + Context

Operator has authorised dispatch of the full Velvet UI cluster (6 cards: #911, #912, #949, #952, #953, #954). This document records the PM review findings, card-by-card dispositions, dependency ordering, realistic v1 cutline, and blocking questions.

What is already shipped

V2 schema migrations (001-003) exist at velvet/db/migrations/rotation_jobs_v2 + rotation_job_consumers tables and indexes. These supersede #910's v1 schema but have not been confirmed shipped to production; the feature-developer must verify and run these before #949 or #911 work is meaningful.


2. Architecture Drift Findings

Finding 1 — #911 body is stale (v1 scope, v2 additions required)

911's issue body describes the v1 kickoff endpoint: POST /tokens/{name}/rotate returning {job_id, status: "pending"}. The 2026-05-03 revision comment on #911 correctly identifies the v2 additions required per v2-rotation-flows.md:

The issue body has never been updated to reflect this. The acceptance criteria in #911 test the v1 shape (no flow_type, no stage endpoint, no SSE). The v2 requirements are in comments only, not in the card body. This creates dispatch risk: a feature-developer reading only the body will build the wrong thing.

Disposition: NEEDS-GROOMER-PASS — body must be updated to v2 scope before dispatch. The groomer should rewrite AC against the v2 API contract in v2-rotation-flows.md Section 4.

Finding 2 — #912 is superseded by #974

912 (service-token auth middleware + rotation-authz matrix) describes an M1 stub using a single VELVET_API_KEY env var. Issue #974 (feat(velvet/auth): Phase 5 — Velvet B10 auth middleware against RBAC v2 permission names) is explicitly scoped as the production-grade replacement for #912's stub, aligned to ADR-0043 and the RBAC v2 schema. #974 is currently OPEN, blocked.

Shipping #912 and then #974 as two sequential PRs is the correct sequencing. However #912's risk section acknowledges the stub is not production-grade, and it is marked sprint:next with no blocked label despite #974's dependency on the RBAC v2 phase being in progress.

Disposition: READY-FOR-DEV — #912 ships the M1 stub. Its PR must include a TODO comment citing #974 so the replacement is visible to reviewers. The stub is acceptable for v1 launch as long as #974 is on the post-launch plan. Recommend adding not-blocking-launch label only if Kristerpher confirms the stub is acceptable for v1.

Finding 3 — #949 depends on v2 schema migration being live

949 requires rotation_jobs_v2 + rotation_job_consumers tables (Section 3 of v2-rotation-flows.md). The v2 migration files exist in the codebase (velvet/db/migrations/001-003) but #910 (which closed) shipped the v1 schema. It is unclear whether the v2 migrations have been applied to Heroku databases. Before #949 dispatches, the feature-developer must confirm:

Since the Velvet app is pre-launch with no customer data, this is a DROP + CREATE replacement, not a live migration. But the task must be scoped explicitly in the PR.

Disposition: READY-FOR-DEV — card is well-specified and groomed. Size L is warranted. Dependency on v2 schema migration must be confirmed live on staging before PR merges to main.

Finding 4 — velvet_v2_rotation feature flag not in feature_flags.yaml

v2-rotation-flows.md Section 8 specifies that POST /rotate is gated behind FLAG_VELVET_V2_ROTATION. This flag does not currently exist in backend_v2/api/feature_flags.yaml. Per the memory rule feedback_new_flag_needs_b1_migration_same_pr, any PR adding a flag to the YAML must include a B1 promotion migration in the same PR or the B1 gate fails. This affects #911 (the kickoff endpoint) and any card that reads the flag.

Disposition: Blocker on #911 — the feature-developer picking up #911 must add velvet_v2_rotation (and velvet_v2_rotation_ui for #952) to feature_flags.yaml AND include the corresponding B1 promotion migration in the same PR.

Finding 5 — UI cards (#952, #953, #954) will add new console static assets

Cards #952 and #953 will require new CSS/JS for the multi-panel stage wizard modal. Per memory feedback_asset_manifest_layer_a, any PR touching console/app/static/** must run bash scripts/ci/update-asset-manifest.sh and commit docs/asset-manifest.json in the same PR or merge blocks. The feature-developer on #952 and #953 must be explicitly instructed to follow ADR-0051 Layer A. This is not currently called out in either card.

Disposition: Flag for card-groomer on #952 and #953 — add a "Dev notes" section to each card body noting the asset manifest requirement.

Finding 6 — #954 TOTP gate requires Velvet TOTP seed access

954 includes a TOTP gate on Stage 3 revoke. The TOTP seed must be accessible to Velvet's process. The risks section identifies this: "TOTP seed stored in Velvet's own vault path." The current Velvet codebase has no TOTP validation code. This is a non-trivial addition to what looks like a UI/auth card. If TOTP validation is a net-new library integration (pyotp or similar), the feature-developer must also handle vault secret path provisioning. This could silently inflate the card's scope.

Disposition: READY-FOR-DEV — but the feature-developer should be told explicitly: if TOTP validation is not already implemented in the Velvet codebase, scope it as a sub-task in the PR. The card's current AC is testable as written; no rewrite needed.

Finding 7 — NV-series internal references (#949 → NV8, #952 → NV9, etc.)

949 references "NV8 (modal UI)" and "NV9 (subscriber table)" as downstream dependencies. These internal NV codes map to: NV5=#949, NV8=#952, NV9=#953, NV10=#954. These are consistent but not cross-linked by issue number in the card bodies, which creates navigation friction for feature-developers. The groomer should add Blocks: #952, #953, #954 explicitly to #949's Related section.


3. Dependency Order

#912  (auth middleware stub)     — no blockers; can dispatch now
  |
  +-- #911  (kickoff API + stage endpoints + SSE)
             Depends on: #912 (auth), v2 schema migration confirmed live on staging
             flag velvet_v2_rotation must be added to feature_flags.yaml in same PR
  |
  +-- #949  (three-stage flow state machine)
             Depends on: #911 (kickoff endpoint + stage action endpoint)
             Depends on: v2 schema migration (rotation_job_consumers) live
             Largest card (size L); ~3-4 days for a focused developer
  |
  +-- #953  (subscriber status table component)
             Depends on: #949 (SSE stream + rotation_job_consumers data shape)
             Size S; can be built against mocked SSE for parallel delivery
  |
  +-- #952  (three-stage modal shell)
             Depends on: #949 (flow runner live), #953 (status table component ready)
             flag velvet_v2_rotation_ui must be in feature_flags.yaml
  |
  +-- #954  (yaml-driven revocation auth gate)
             Depends on: #949 (proceed_revoke action), #952 (modal context — renders within)
             Can be built in parallel with #952 if TOTP lib is pre-confirmed available

Confirmed dependency order:

  1. 912 (can dispatch immediately)

  2. 911 (after groomer rewrites body; dispatch alongside #912 or immediately after)

  3. 949 (after #911 + #912 merge; largest card)

  4. 953 (after #949 merges; can start against mocked data if #949 takes long)

  5. 952 (after #949 + #953 ready)

  6. 954 (after #949; parallel with #952)


4. T-3 Realism Assessment

Available calendar: 2026-05-20 to 2026-05-23 UTC = 3 days.

Tier 1 — v1 blockers (must ship by 2026-05-23 UTC)

These cards are the minimum for Velvet rotation to be operator-usable at launch:

Card What it delivers Estimated effort Assessment
#912 Auth middleware stub 0.5 days Shippable by 2026-05-21
#911 Kickoff + stage + SSE endpoints 1.5 days (after groomer pass) Shippable by 2026-05-22 if groomer runs same day
#949 Three-stage flow state machine 3+ days (size L) DOES NOT FIT T-3

The honest cutline: #949 cannot ship by 2026-05-23 UTC. It is size L, depends on #911+#912 merging first, and involves a complex orchestration engine (three distinct state machines, SSE stream, parallel fan-out, per-consumer audit rows). A minimum realistic timeline for #949 is 3 working days from dispatch, which puts it at 2026-05-26 at the earliest.

Tier 2 — v1-adjacent (valuable but not blocking launch)

Card What it delivers Assessment
#953 Subscriber status table component Size S; shippable post-#949
#952 Three-stage modal shell Size L; depends on #949 + #953; post-launch
#954 Yaml-driven revocation auth gate Size M; depends on #949; post-launch

Ship by 2026-05-23 UTC: - #912 — auth middleware stub (M1 production-grade stub; post-launch #974 hardens it) - #911 — kickoff + stage + SSE API surface (after groomer body rewrite; gated behind velvet_v2_rotation flag = OFF at launch)

Ship post-launch (wave 1 post 2026-05-23): - #949 — flow state machine (target 2026-05-27) - #953 — subscriber table (target 2026-05-28) - #952 — three-stage modal (target 2026-05-30) - #954 — revocation auth gate (target 2026-05-30)

This ships the API contract and auth foundation at launch with the flag off. The console UI and flow engine follow in the week after launch as a defined wave. Rotation is an operator-only infrastructure feature — it does not affect customer-facing product behaviour and does not block the 2026-05-23 launch.


5. Blocking Questions for Kristerpher

Q1 — v2 schema migration status (blocks #911 + #949) The v2 migration files exist in the codebase but it is unclear whether 001_create_rotation_jobs_v2.sql has been applied to raxx-velvet-staging. Can you confirm via heroku run python -m velvet.db.migrate --list -a raxx-velvet-staging which migrations have run? If the v1 schema (#910) is still live, the feature-developer for #911 must include the v2 migration as part of that PR.

Q2 — Acceptable auth model at launch: single VELVET_API_KEY stub or wait for #974?

912 ships a single shared VELVET_API_KEY. #974 (RBAC v2 per-caller scoped tokens, currently blocked) is the production-grade replacement. The stub is documented and the risk is low for a pre-revenue service where Velvet is operator-only. Is the stub acceptable for v1 launch, or should the velvet_v2_rotation flag be held off until #974 ships? This determines whether #912 alone is sufficient or whether #974 must be on the v1 list.

Q3 — Full cluster desired post-launch even if not at v1? The operator authorised dispatch of the "full Velvet UI cluster." Given that #949 does not fit T-3, do you want all 6 cards dispatched immediately (with #949-#954 landing in the post-launch window), or should dispatch of the UI cards (#952, #953, #954) wait until #949 is confirmed merged to avoid wasted work in feature-developer worktrees?


6. Pre-Dispatch Actions Required

Before any feature-developer can be dispatched on these cards, the following must happen:

  1. #911 groomer pass — body must be rewritten to v2 scope (flag the flow_type param, stage endpoint, SSE stream, corrected response shapes, updated AC). The current body will produce wrong implementation if dispatched as-is.

  2. #952 and #953 groomer note — add "Dev notes: this PR touches console/app/static/**; run bash scripts/ci/update-asset-manifest.sh and commit docs/asset-manifest.json in the same PR per ADR-0051 Layer A" to each card body.

  3. Operator answer on Q1 — confirm v2 schema migration status before #911 or #949 dispatch.

  4. Operator answer on Q2 — confirms whether #912 stub is acceptable for v1 or whether #974 must be included.


7. Card-by-Card Disposition Summary

# Title Disposition v1 cutline Notes
#911 POST /rotate kickoff + stage + SSE NEEDS-GROOMER-PASS v1 (after groomer pass) Body is v1-scope; v2 additions in comments only; flag must be added to feature_flags.yaml
#912 Service-token auth middleware READY-FOR-DEV v1 Stub acceptable; cite #974 in PR TODO
#949 Three-stage flow state machine READY-FOR-DEV POST-LAUNCH Size L; does not fit T-3; target 2026-05-27
#952 Three-stage rotation modal READY-FOR-DEV POST-LAUNCH Depends on #949 + #953; asset manifest required in PR
#953 Per-subscriber distribute-status table READY-FOR-DEV POST-LAUNCH Depends on #949; asset manifest required in PR
#954 Yaml-driven revocation auth gate READY-FOR-DEV POST-LAUNCH Depends on #949; confirm TOTP lib available before dispatch

Sprint plan authored 2026-05-20 UTC. card-groomer has been tagged on #911, #952, #953.