Skip to content

Add operator lockdown UI#70

Merged
isuttell merged 5 commits into
mainfrom
t3code/a71ba3b3
May 25, 2026
Merged

Add operator lockdown UI#70
isuttell merged 5 commits into
mainfrom
t3code/a71ba3b3

Conversation

@isuttell

@isuttell isuttell commented May 25, 2026

Copy link
Copy Markdown
Contributor

Summary

Builds the Phase 3 operator lockdown screen so allowed operators can manage active workspace and artifact lockdowns from the web app instead of relying only on the API.

Changes

  • Adds /admin loader wiring for active lockdowns through the existing operator API.
  • Adds operator UI components for setting and lifting workspace/artifact lockdowns.
  • Adds server mutations with contract validation, idempotency keys, and WorkOS bearer forwarding.
  • Updates route/mutation tests and Phase 3 status ledgers.
  • Adds a shared test fixture for lockdown rows and validates missing lift input.

Risk: HIGH

  • Areas touched: apps/web admin route/components/server mutations, packages/contracts, ops status docs.
  • Security: operator-only lockdown controls; calls rely on the existing WorkOS/operator API authorization path and do not add new credentials or secrets.
  • Performance: one admin list request on /admin; no DB/query changes.
  • Breaking: no.

Test plan

  • bun run ci:check
  • pre-commit hooks: gitleaks, biome, docs formatting, typecheck
  • pre-push hooks: scoped tests
  • CodeRabbit pre-flight: initial findings fixed; second-pass finding fixed; third pass rate-limited by organization usage credits before producing findings

Summary by CodeRabbit

  • New Features
    • Admin page: operators can list, set, and lift lockdowns for artifacts or workspaces (scope, target, reason, who/when shown).
  • Tests
    • Added tests covering form validation, set/lift flows, success/error and pending states for UI and server mutations.
  • Chores
    • Added server mutations and tightened request validation for lockdown APIs.
  • Documentation
    • Ops docs and changelog updated to reflect the implemented operator lockdown UI.

Review Change Stack

@isuttell isuttell temporarily deployed to pr-preview-70 May 25, 2026 21:45 — with GitHub Actions Inactive
@coderabbitai

coderabbitai Bot commented May 25, 2026

Copy link
Copy Markdown

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: c7a88a38-cc2d-4af6-af7d-2ae5febf7ef2

📥 Commits

Reviewing files that changed from the base of the PR and between f7f3bda and 8d5c474.

📒 Files selected for processing (9)
  • apps/web/src/components/admin/LockdownForm.tsx
  • apps/web/src/components/admin/LockdownList.tsx
  • apps/web/src/routes/_authed.admin.tsx
  • apps/web/src/server/web-mutations.ts
  • apps/web/test/LockdownForm.test.tsx
  • apps/web/test/LockdownList.test.tsx
  • apps/web/test/web-mutations.test.ts
  • docs/ops/web-app-todo.md
  • packages/contracts/src/lockdown.ts

Walkthrough

This PR implements a web operator /admin UI for lockdown management. It adds LockdownForm and LockdownList React components, server mutations setLockdownFn and liftLockdownFn validated by updated lockdown contracts, a route loader that fetches lockdowns and returns them as loader data, comprehensive tests and fixtures for mutations and components, and documentation updates across project status, changelog, coverage, backlog, and web-app TODO files.

Sequence Diagram

sequenceDiagram
  participant Operator
  participant AdminPage
  participant LockdownForm
  participant setLockdownFn
  participant LockdownAPI
  participant LockdownList
  participant liftLockdownFn

  Operator->>AdminPage: open /admin
  AdminPage->>LockdownList: loader GET /v1/web/admin/lockdowns
  LockdownList->>LockdownAPI: GET /v1/web/admin/lockdowns
  Operator->>LockdownForm: submit scope,target_id,reason_code
  LockdownForm->>setLockdownFn: validated input
  setLockdownFn->>LockdownAPI: POST /v1/web/admin/lockdowns
  setLockdownFn-->>LockdownForm: result
  Operator->>LockdownList: click Lift
  LockdownList->>liftLockdownFn: scope,target_id
  liftLockdownFn->>LockdownAPI: DELETE /v1/web/admin/lockdowns/{scope}/{target_id}
  liftLockdownFn-->>LockdownList: result
Loading

Possibly related PRs

  • zaks-io/agent-paste#46: Related mutation-pattern changes in web-mutations that this PR extends with setLockdownFn/liftLockdownFn.
  • zaks-io/agent-paste#45: Backend set/lift lockdown endpoints and operator auth that the frontend mutations call.
  • zaks-io/agent-paste#48: Implements the GET /v1/web/admin/lockdowns cursor-paginated endpoint used by this PR’s loader.

Poem

🐰 I hopped into code to add a form,
Operators click to keep systems calm,
Set a lock, lift a lock, list them too,
Tests green, docs changed — a carrot for you! 🥕

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Add operator lockdown UI' directly and accurately describes the main change: implementing the operator lockdown user interface component for the admin page, which is the primary objective of this PR.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch t3code/a71ba3b3

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint skipped: no ESLint configuration detected in root package.json. To enable, add eslint to devDependencies.


Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 5

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@apps/web/src/components/admin/LockdownForm.tsx`:
- Around line 42-57: The try/finally around the setLockdownFn call doesn't catch
rejected promises, so wrap the mutation call to explicitly handle rejections
from setLockdownFn (transport/runtime failures) by adding a catch block that
logs/pushes a user-facing error via push(errorToast(...)) and returns early;
keep the existing result.error handling and the finally that calls
setPending(false). Reference setLockdownFn, push, errorToast, setPending, and
onSuccess when adding the rejection handler so rejected promises produce the
same actionable toast behavior as result.error.

In `@apps/web/src/components/admin/LockdownList.tsx`:
- Around line 27-41: The try/finally around liftLockdownFn doesn't catch
rejections, so thrown errors escape; wrap the await liftLockdownFn call in a
try/catch/finally (or add a catch) to handle thrown errors from liftLockdownFn,
call push(errorToast("Couldn't lift lockdown", err)) inside the catch (or a
generic message if err has no message), and return after pushing so onLift isn't
called; keep the existing finally block that updates setPendingIds(key) to
ensure pending cleanup always runs.

In `@apps/web/src/routes/_authed.admin.tsx`:
- Around line 36-44: handleSuccess currently does two refreshes: it calls
loadLockdownsFn(), sets local state with setLockdowns(updated), then calls
router.invalidate() which re-runs the loader and fetches again; fix by choosing
a single refresh path — prefer router.invalidate() as the single source of
truth. Update handleSuccess to remove the direct fetch and setLockdowns call
(remove loadLockdownsFn() and setLockdowns(updated)) and simply await
router.invalidate(); rely on Route.useLoaderData()/initialLockdowns to supply
the updated data after invalidation (or if you must keep local caching, remove
router.invalidate() instead and only update via setLockdowns). Ensure references
to handleSuccess, loadLockdownsFn, setLockdowns, and router.invalidate are
adjusted accordingly.

In `@docs/ops/web-app-todo.md`:
- Around line 58-59: Update the contradictory sentence that claims the "operator
lockdown list remains EmptyState" to reflect that the `/admin` operator lockdown
listing, set, and lift functionality has been implemented (per the Shipped/Done
bullets); replace that sentence with a clear single-source-of-truth line stating
that the `/admin` view now lists lockdowns and allows operators to set or lift
workspace/artifact lockdowns (remove references to "EmptyState" for the operator
lockdown list).

In `@packages/contracts/src/lockdown.ts`:
- Around line 15-19: The validators allow whitespace-only target_id values;
update the schema for LiftLockdownRequest (and align SetLockdownRequest and
LockdownDetail) to trim input before validating length so strings like "   " are
rejected — replace the current z.string().min(1) usage for the target_id fields
with a trimmed-and-validated variant (e.g., use z.string().trim().min(1) or an
equivalent transform+min) for the named types LiftLockdownRequest,
SetLockdownRequest, and LockdownDetail so all three enforce non-whitespace IDs
consistently.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 60f5a952-6670-4a6e-8ce8-1aa2a9894bc7

📥 Commits

Reviewing files that changed from the base of the PR and between dab6704 and ee1838c.

📒 Files selected for processing (14)
  • apps/web/src/components/admin/LockdownForm.tsx
  • apps/web/src/components/admin/LockdownList.tsx
  • apps/web/src/routes/_authed.admin.tsx
  • apps/web/src/server/web-mutations.ts
  • apps/web/test/fixtures.ts
  • apps/web/test/routes.test.tsx
  • apps/web/test/web-mutations.test.ts
  • docs/ops/project-status.md
  • docs/ops/status/changelog.md
  • docs/ops/status/coverage.md
  • docs/ops/status/implementation.md
  • docs/ops/status/phase-backlog.md
  • docs/ops/web-app-todo.md
  • packages/contracts/src/lockdown.ts

Comment thread apps/web/src/components/admin/LockdownForm.tsx
Comment thread apps/web/src/components/admin/LockdownList.tsx
Comment thread apps/web/src/routes/_authed.admin.tsx
Comment thread docs/ops/web-app-todo.md
Comment thread packages/contracts/src/lockdown.ts
@isuttell isuttell temporarily deployed to pr-preview-70 May 25, 2026 21:52 — with GitHub Actions Inactive
@github-actions

Copy link
Copy Markdown

@isuttell isuttell temporarily deployed to pr-preview-70 May 25, 2026 21:58 — with GitHub Actions Inactive
@github-actions

Copy link
Copy Markdown

@isuttell isuttell merged commit 72304c5 into main May 25, 2026
4 checks passed
@isuttell isuttell deleted the t3code/a71ba3b3 branch May 25, 2026 22:02
@github-actions

Copy link
Copy Markdown

agent-paste PR preview resources were cleaned up. The pr-preview-${context.issue.number} environment is left in place; remove it from the GitHub UI if desired.

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