Skip to content

feat(admin): add a Configuration page to view and edit central-manager settings - #1988

Draft
Devin-Holland wants to merge 4 commits into
stagefrom
admin-configuration-page
Draft

Devin-Holland wants to merge 4 commits into
stagefrom
admin-configuration-page

Conversation

@Devin-Holland

@Devin-Holland Devin-Holland commented Oct 20, 2026 •

Copy link
Copy Markdown
Member

Deploy order: HarperFast/central-manager#982 should merge and deploy first. Until it does, fabric_admin does not hold config:write, so this page stays hidden from them; the core super user sees it, without lock badges because current rows carry no superUserOnly flag.

⊙ Problem

Staff cannot see or change central-manager's Configuration table from Studio; it takes the core super user and a direct API call.

🤔 Is a JSON editor safe enough for staff?

Yes, because central-manager decides: HarperFast/central-manager#982 lets fabric_admin edit an allowlist of operational keys and refuses the rest. This page only hides the edits that would fail.

💡 Solution

⚖️ Alternatives

  • Typed form per key — every new key would need a Studio change; the table's values are already JSON.
  • A server-side precondition on PATCH — closes the overwrite race fully, but Harper has no compare-and-set. The re-read before saving leaves only the gap between two requests.

🤔 Should read-only staff get the page too?

Not in this PR. Every staff role holds config:read, but the page exists to edit, so it is gated on config:write. Gating on config:read instead is a one-line change if read-only staff should see values.

🔧 Changes

✅ Verification

  • pnpm test 5875 passing on this head. Page tests cover:

    • the lock and Edit rules for fabric_admin and the super user;
    • invalid JSON, the type warning, numbers JSON cannot round-trip, and deep nesting;
    • the saved value, a refused save reported through the app's real error handler, and a stale save stopped before PATCH;
    • the editor locked mid-save, then closing and refreshing, and a failure still reported after the page unmounts.

    Reverting any of this round's fixes fails a test. Shell visibility and route tests include Configuration.

  • Rendered the real page against stub data in the browser (light and dark): lock badges, the editor, and its warnings look as described.

  • Not run against a deployed central-manager. The endpoint side was smoke-tested on released Harper 5.3.1 in HarperFast/central-manager#982.

🤖 Generated by Claude Code (Claude Opus 5.5); posted via @Devin-Holland.

Related PRs: #1975 overlaps (AdminShell nav items, admin routes and the visibility test; whichever merges second keeps both pages), #1780 overlaps (both add types to api.patch.d.ts)
Complexity: medium

Deferred findings

  • e3cf9431bf9907fc nit maintainability — src/features/admin/configuration/index.tsx — local imports omit file extensions — NOT FILED
  • 9662b6ba91556850 nit data-integrity — src/features/admin/configuration/mutations/useUpdateConfiguration.ts — whole-value PATCH from stale snapshot loses concurrent edits — NOT FILED
  • c061a4cfe6c1e8e6 nit performance — src/features/admin/configuration/index.tsx — per-keystroke stringify/lowercase/closed-dialog parse over all rows — NOT FILED
  • 64d604298c694ca2 nit maintainability — src/features/admin/configuration/queries/getConfiguration.ts — double cast instead of patched endpoint types — NOT FILED
  • 7244681086f2b812 nit maintainability — src/features/admin/configuration/index.tsx — exports with no consumer outside their own file — NOT FILED

Review-Coverage: authored=claude; ran=gemini,codex; adjudicated=domain; blocked=cursor-composer(auth); declined=cursor-grok,cursor-kimi,cursor-muse,muse; rounds=3; full=1 @ 3832ab9

Review-Attention: read ~5m (decisions: global-error-reporting, client-side-precondition, gate-page-on-config-write, raw-value-display, client-side-superuser-gate, strict-integer-rejection, do-less-alternative) @ 3832ab9

Devin-Holland and others added 4 commits October 20, 2026 10:41
…r settings

A staff-only /admin/configuration page listing every Configuration key with
its description and current value. Accounts holding config:write
(fabric_admin, and the core super user) can edit a value as JSON: the editor
rejects JSON that doesn't parse, warns when an edit changes the value's type,
and shows a refused save inline. Keys central-manager marks superUserOnly
(security controls such as the SSO session cap and host SSH keys) show a lock
and can only be edited by the core super user.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…SON can't round-trip

The editor no longer closes while a save is in flight, so a dismissed dialog
can't drop the result; the list refresh and success toast now live on the
mutation itself. Values containing a non-finite number (1e400) or an integer
past 2^53 are refused instead of being saved as null or rounded, and a
refused-save message clears once the value is edited.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… overwrite a newer value

A save now re-reads the key and stops if it changed since the editor opened,
since PATCH replaces the whole value. Failures go through the global toast, so
a refused or unanswered save is still reported after navigating away. The
editor locks while saving, deep nesting no longer throws during render, and a
row with no value edits as null.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…lue as null

Drops the stack-dependent depth case to 1,000 (100,000 still catches both
regressions), shows a row with no value as null in the table as the editor
does, and trims comments that restated the code.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a new Configuration section to the admin panel, allowing authorized staff and super users to view and edit central-manager settings with JSON validation and concurrency checks. It adds the /admin/configuration route, updates navigation visibility, and introduces new config:read and config:write permissions. The review feedback suggests improving the layout of the edit dialog by wrapping the title and description in a DialogHeader component, and adjusting the size and spacing of the LockIcon inside the "Super user" badge for better visual alignment.

@@ -0,0 +1,211 @@
import { Badge } from '@/components/ui/badge';
import { Button } from '@/components/ui/button';
import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogTitle } from '@/components/ui/dialog';

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Import DialogHeader from @/components/ui/dialog to wrap the dialog title and description, ensuring proper layout and padding according to shadcn/ui standards.

Suggested change
import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogTitle } from '@/components/ui/dialog';
import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from '@/components/ui/dialog';

Comment on lines +83 to +85
<DialogContent className="max-w-2xl">
<DialogTitle className="font-mono">{entry?.name}</DialogTitle>
<DialogDescription>{entry?.description || 'No description.'}</DialogDescription>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Wrap DialogTitle and DialogDescription inside a DialogHeader component to maintain correct layout, alignment, and spacing as defined by shadcn/ui guidelines.

Suggested change
<DialogContent className="max-w-2xl">
<DialogTitle className="font-mono">{entry?.name}</DialogTitle>
<DialogDescription>{entry?.description || 'No description.'}</DialogDescription>
<DialogContent className="max-w-2xl">
<DialogHeader>
<DialogTitle className="font-mono">{entry?.name}</DialogTitle>
<DialogDescription>{entry?.description || 'No description.'}</DialogDescription>
</DialogHeader>

Comment on lines +168 to +170
<Badge variant="secondary" className="text-[10px]">
<LockIcon />Super user
</Badge>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The LockIcon inside the Badge is disproportionately large for a text-[10px] badge. Add size classes to the icon and a gap to the badge for proper visual alignment.

Suggested change
<Badge variant="secondary" className="text-[10px]">
<LockIcon />Super user
</Badge>
<Badge variant="secondary" className="gap-1 text-[10px]">
<LockIcon className="h-3 w-3" />Super user
</Badge>

@github-actions

Copy link
Copy Markdown

Coverage Report

Status Category Percentage Covered / Total
🔵 Lines 80.05% 14992 / 18728
🔵 Statements 79.99% 15915 / 19895
🔵 Functions 75.49% 4012 / 5314
🔵 Branches 74.75% 11661 / 15600
File Coverage
File Stmts Branches Functions Lines Uncovered Lines
Changed Files
src/features/admin/routes.ts 72.72% 100% 60% 72.72% 33-34, 40-41, 47-48
src/features/admin/components/AdminShell.tsx 29.41% 46.66% 60% 33.33% 43-65
src/features/admin/configuration/index.tsx 91.8% 85.33% 88.88% 94.23% 74, 77, 122-123, 144
src/features/admin/configuration/mutations/useUpdateConfiguration.ts 100% 66.66% 100% 100%
src/features/admin/configuration/queries/getConfiguration.ts 100% 100% 100% 100%
Generated in workflow #2401 for commit 3832ab9 by the Vitest Coverage Report Action

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.

2 participants