ADORSYS-GIS / ADORSYS-GIS/lightbridge-authz

[Story]: Phase 4 — self-service refill requests and admin review queue

Aperta
#191 5 commenti 0 reazioni 0 assegnatari Vedi su GitHub
governance user-story
Lingua principale
Rust
Stelle
0
Fork
1
Merge medio
6h 42m
PR unite (30g)
246

Descrizione

### Story Statement

As a **user who has run out of AI budget**,
I want to **request more budget from the self-service UI and get it immediately when policy allows**,
so that **I am not blocked from working, and nobody has to hand-edit configuration on my behalf.**

### Real Intent

This is the story the whole epic exists for. Everything before it — ledger, policy engine, lifecycle — is plumbing; this is where a user notices anything changed.

It also removes the single worst operational pattern in the current system: a maintainer editing rate-limit values by hand to unblock someone. That is unlogged, unauditable, and it touches the configuration surface that ADR-0084 exists because of.

### Background and Context

Today a user who exhausts their monthly budget is stopped until the period rolls, and the only remedy is a support request that ends in a values edit.

⚠️ **The self-service UI already exists: `lightbridge-ss` / converse-frontends. It is an Expo app, not Vite.** No new frontend is needed, and proposing one would be a mistake worth catching in review.

### Source of truth (links)

- Epic: #188
- Depends on: #189 (ledger), #190 (policy engine)
- Roadmap: ADORSYS-GIS/ai-helm#872 decision 6 ("all clients get refills via the self-service (converse-ui)") and §4 Wave 3
- Plan detail: `plans/lightbridge-dynamic-budget.md` §4 Phase 4, and §0.1 for the two consequences users will meet

### ⚠️ Two behaviours that will look like bugs and are not

Both need to be *designed for in the UI*, not just documented:

1. **A refill resets the window's counter.** Moving up a rung grants the **full new tier**, not the difference. A user who has spent $40 of $50 and moves to a $100 rung has $100, not $110 — and their $40 of history no longer counts against them.
2. **The tier arrives as a Keycloak claim, so it takes effect at the next token refresh** — not instantly. The UI must say so, or every refill generates a "it didn't work" report.

### Acceptance Criteria

#### Functional
- [ ] Given a user with budget remaining under the unaided allowance, when they request the next rung, then it is granted immediately and recorded in the ledger.
- [ ] Given a user who has used their unaided allowance for the period, when they request again, then the request goes to a **review queue** rather than being granted or silently refused.
- [ ] Given a queued request, when an admin approves it, then the grant is issued and the user is informed.
- [ ] Given a queued request, when an admin rejects it, then the user is informed **with a reason**.
- [ ] Given a granted refill, when the user views their budget, then the UI states clearly that it applies **after the next token refresh**.

#### Negative / Edge Cases
- [ ] Given an **internal or API-key client**, when a refill is attempted, then it is refused. Refills are **OIDC users only** — internal clients have a different access model.
- [ ] Given a user who is already on the top rung, when they request more, then they get a clear message rather than a failed grant.
- [ ] Given a user submitting the same request twice (double-click, retry), when both arrive, then exactly one grant results.
- [ ] Given the policy engine is unavailable, when a request is made, then it is **refused and queued**, not granted.

#### Non-Functional
- [ ] Every path through this story writes a ledger entry — including refusals and rejections. An unrecorded decision is the thing this epic exists to eliminate.
- [ ] The reset-not-add semantic and the token-refresh delay are both surfaced **in the UI**, not only in docs.
- [ ] `cargo clippy --all-targets --all-features -- -D warnings` clean.

### Out of Scope

- **Automatic augmentation** — Phase 5, its own story.
- The enforcement re-key (Phase 6a) and writing the Keycloak attribute (Phase 6b). Until those land, this story records intent without changing what the gateway enforces — which is fine, and worth stating so nobody reports it as broken.
- Refills for internal clients.
- A new frontend.

### Dependencies and Blockers

- Depends on #189, #190.
- Needs UI work in `lightbridge-ss` (converse-frontends) — coordinate; that is a different repo.
- ⚠️ **The refill has no gateway effect until Phase 6a/6b.** Sequence the user-facing launch accordingly, or the first user to try it will correctly report that nothing happened.

### Assumptions

1. **Discrete rungs are acceptable UX.** "Request the next tier up" rather than "request $5". This was the decision that took the epic out of the red-risk tier.
2. Two unaided rungs per period to start; policy, so it changes without a deploy.
3. Admins will action the review queue promptly enough that it is not a bottleneck. If not, that is a policy problem, not an engineering one.
4. Users tolerate the token-refresh delay **if it is stated up front**.

### Implementation Notes

Guidance, not gospel:

- Say the awkward things in the UI. "You have $100 for the rest of this period" is honest; showing a running total that silently reset is not.
- Make the review queue's rejection reason mandatory. A rejection without a reason turns into a support conversation.
- Idempotency keys from the client; the ledger already enforces exactly-once.
- Refused-because-unavailable and refused-because-policy must be distinguishable to an admin, even if the user sees one message.

### Test Expectations

- Unit: allowance accounting, rung transitions.
- Integration: request → grant → ledger entry; request → queue → approve → grant; request → queue → reject → informed.
- Negative: internal client refused; top rung; double-submit; policy engine down.
- Manual: the UI copy for the reset semantic and the refresh delay actually appears, and is understandable to someone who has not read this ticket.

### Verification evidence

*To be completed on delivery.*

Required:
- `just all-checks` output.
- Integration output for the grant path and both review-queue paths.
- Screenshots of the UI copy explaining the reset semantic and the token-refresh delay.
- The negative tests, named.

### Human accountable owner

@stephane-segning

### AI Usage Declaration

Drafting this story, Refining acceptance criteria

### Human verification completed

- [x] I checked the story against the source of truth
- [x] I confirmed the acceptance criteria
- [x] I documented remaining risks
- [x] I am the accountable owner and accept responsibility for this story.

Guida per i contributori

Apri la guida per i contributori

Valutazione

Questa issue non è ancora stata valutata.

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.