Skip to Content

← All archived runs

Run: settings-hub

run.md

Run: settings-hub

  • branch: claude/charming-wozniak-qoo4ri
  • pr: #494

01_define/output/spec.md

Spec: Consolidated admin settings hub with a deterministic settings registry

  • slug: settings-hub
  • personas: admin, CSM, SDM
  • touches: apps/web/app/(app)/admin/settings, apps/web/app/(app)/{services,products}, apps/web/app/(app)/admin/{industries,territories,commercial,sla,escalation,integrations,status,demo-data,setup-checklist}, apps/web/lib/nav.ts, packages/services/src/db/services/tenant-setting, packages/services/src/db/seed/tenant-defaults.ts, packages/services/src/db/models/tenant-setting.ts
  • complexity: complex

Problem

Platform configuration is scattered across the admin surface of apps/web. Tenant settings live at /settings, while locations (/admin/territories), services (/services), products (/products), industries (/admin/industries), and the rest of the admin config (commercial, SLA, escalation, integrations, statuses, setup checklist, demo data) are spread across three different sidebar sections (Catalogue, Configure, and the footer) using inconsistent URL patterns. An admin has no single place to configure the tenant, and the navigation is cluttered with one entry per area.

Worse, the settings keys themselves are non-deterministic. tenant_settings is a free-form key/category/value collection (packages/services/src/db/models/tenant-setting.ts): keys are typed ad-hoc through a modal with no validation beyond a character regex. Domain code reads hardcoded keys — matching.minFitScore, commercial.vendorRevenuePercent, commercial.pricingRulesSet, escalation.defaultEnabled — and silently falls back to a default if the row is missing, so settings that should exist by default simply don't appear until someone creates them. The one seed that does exist (SETTING_DEFAULTS in tenant-defaults.ts: default_currency, timezone, sla_business_days_only) is orphaned — no code ever reads those keys. There is no canonical registry, so an admin can't see what settings should exist, what they default to, or what they do.

This advances Refine the Bridge (dashboard usability and the small details of each interaction): a single, predictable configuration surface removes a recurring source of operator confusion and mis-configuration during vendor onboarding.

Proposed change

Two coordinated changes, both in apps/web and packages/services:

1. A canonical settings registry (the determinism fix). Introduce a single typed registry in packages/services that declares every known tenant setting — key, category, value type, default, and a human description. This registry becomes the source of truth:

  • The existing domain methods (getMinFitScore, getCommercialConfig, getEscalationConfig, etc.) resolve their defaults from the registry instead of hardcoding them inline.
  • Tenant seeding (seedTenantDefaults) seeds every registry entry create-if-missing, replacing the short orphaned SETTING_DEFAULTS list, and backfills all existing tenants so every tenant has every default-bearing setting present.
  • The settings UI renders all registry-known settings (showing the effective value, the default, and the description) merged with any tenant-stored values — so an admin always sees the full, deterministic set, not just whatever ad-hoc rows happen to exist. Unknown/legacy keys still in the DB remain visible and editable, but flagged as not in the registry.

2. A single tabbed settings hub (the consolidation). Build one consolidated admin screen with a tab per configuration area, reusing the existing manager/list components as tab panels:

  • Tabs: Settings (registry-driven), Locations, Services, Products, Industries, Commercial, SLA, Escalation, Integrations, Statuses (read-only viewer), Setup checklist, Demo data.
  • The active tab is reflected in the URL so a tab is linkable and survives refresh.
  • Existing standalone routes (/services, /products, /admin/industries, /admin/territories, /admin/commercial, /admin/sla, /admin/escalation, /admin/integrations, /admin/status, /setup-checklist, /admin/demo-data) redirect into their corresponding hub tab. Per-item create/detail/edit routes (e.g. /services/[id]/edit) are preserved so deep links and forms keep working.
  • The admin sidebar collapses the scattered Catalogue/Configure/footer entries into a single configuration entry pointing at the hub. CSM and SDM keep access to the taxonomy areas they use today (Industries, Products, Services, Skills, Statuses) via the same hub, scoped to the tabs their role is permitted to see — no CSM/SDM capability is removed.

Acceptance criteria

  • A single consolidated settings hub screen exists in the admin platform with one tab per area: Settings, Locations, Services, Products, Industries, Commercial, SLA, Escalation, Integrations, Statuses, Setup checklist, Demo data.
  • Each tab renders the existing functionality for that area (create/edit/delete or read-only where applicable) — no configuration capability is lost in the move.
  • The selected tab is encoded in the URL and is preserved on refresh and via direct link.
  • A typed settings registry exists in packages/services declaring every known setting's key, category, type, default value, and description, and is exported for use by the app and seeds.
  • The Settings tab lists every registry-known setting with its effective value, default, and description — settings that have never been edited still appear with their default value.
  • Existing-but-unregistered keys still present in tenant_settings remain visible and editable, and are visually distinguished as not part of the registry.
  • Domain settings methods (matching min fit score, commercial, escalation) source their default values from the registry rather than inline literals; existing reads return the same effective values as before for tenants that already configured them.
  • seedTenantDefaults seeds the full registry create-if-missing and backfills existing tenants, so every tenant has every default-bearing setting present after the seed runs; already-set values are never overwritten.
  • The scattered sidebar entries for these areas are replaced by a single configuration entry that opens the hub, for the admin role.
  • Old standalone routes for the consolidated areas redirect into the matching hub tab; per-item create/detail/edit routes continue to work.
  • CSM and SDM retain access to the taxonomy areas they have today, surfaced through the hub and limited to the tabs their role may see.
  • All copy is sentence case; dashboards typography rules do not apply (this is apps/web, which imports from @sustentus/ui).

Out of scope

  • Redesigning or changing the data models for products, services, industries, or locations — the hub reuses the existing models, services, and forms; only tenant_settings gains a registry layer.
  • Adding new configuration areas or new individual settings beyond cataloguing the keys that already exist in code today.
  • Changing the matching, commercial, escalation, or SLA behaviour — only where their default values are sourced from.
  • The /admin/status workflow itself (statuses/transitions remain version-controlled JSON changed via PR); the hub only embeds the existing read-only viewer.
  • Per-setting fine-grained RBAC beyond the existing role-based tab visibility (admin full; CSM/SDM limited to their current taxonomy areas).
  • Any change to apps/dashboards — this work is entirely in apps/web and packages/services.

Open questions

  • none

02_build/output/notes.md

Build notes: settings-hub

  • commits: feat: settings-hub — registry + seed; feat: settings-hub — consolidated tabbed hub, redirects, nav

What changed

Settings registry (the determinism fix) — packages/services

  • db/services/tenant-setting/registry.ts (new): the canonical SETTINGS_REGISTRY declaring every known setting's key, category, type, default, and description, plus getSettingDefinition and seedableSettings helpers. This is the single source of truth for what settings exist and what they default to.
  • db/services/tenant-setting/index.ts: domain reads (getMinFitScore, getEscalationConfig, getCommercialConfig) now resolve their defaults and $setOnInsert descriptions from the registry instead of inline literals. Effective values for already-configured tenants are unchanged.
  • db/seed/tenant-defaults.ts: SETTING_DEFAULTS is now derived from seedableSettings() (every registry entry whose default is non-null). The seed already loops over all tenants create-if-missing, so existing tenants are backfilled and never overwritten.
  • db/services/index.ts: exports the registry + types via @sustentus/services/server.

Consolidated tabbed hub — apps/web/app/(app)/admin/settings

  • layout.tsx (new): role-gated shell (admin, csm, sdm) with heading and a role-filtered tab nav.
  • _components/tabs-config.ts (new): the tab catalogue + role visibility + defaultTabForRole.
  • _components/settings-tabs.tsx (new): client tab nav (routed links, active state from usePathname) — the active tab is encoded in the URL path.
  • page.tsx: hub index now redirects to the first tab the role may see.
  • Tab pages (new): general, locations, services, products, industries, commercial, sla, escalation, integrations, statuses, setup, demo-data — each reuses the existing feature component(s) for that area.
  • _components/settings-registry-manager.tsx (new) + general/page.tsx: the Settings tab merges the registry with stored rows. Every registry setting always appears (with its default when never edited); stored keys not in the registry are shown and flagged "Unregistered". Edit creates-or-updates; registered rows offer "reset to default", unregistered rows offer delete.

Redirects, nav, policies

  • Old standalone routes redirect into the matching hub tab: /services, /products, /admin/industries, /admin/territories→locations, /admin/commercial, /admin/sla, /admin/escalation, /integrations, /admin/status→statuses, /setup-checklist→setup, /admin/demo-data. Per-item create/[id]/edit routes are untouched and still work.
  • lib/nav.ts: admin's scattered Catalogue/Configure/footer config entries collapse to one "Settings" entry → /admin/settings; CSM/SDM's taxonomy links collapse to a single "Configuration" entry → /admin/settings/services (the hub shows only the tabs their role may see).
  • lib/route-policies.ts: added shared-tab policies (/admin/settings/{services, products,industries,statuses} → admin/csm/sdm) before the admin-only /admin/settings rule (first match wins).
  • settings/page.tsx: the personal settings page drops the admin tenant-settings section (now in the hub) and links admins to the hub; org profile, notification preferences, and the matching section (admin/sdm) stay.

Acceptance criteria status

  • Single tabbed hub with one tab per area — admin/settings/layout.tsx + 12 tab pages.
  • Each tab renders existing functionality — tab pages reuse the original components.
  • Selected tab encoded in URL, preserved on refresh/direct link — routed sub-paths.
  • Typed settings registry in packages/services, exported — registry.ts.
  • Settings tab lists every registry setting with effective value, default, description — settings-registry-manager.tsx; defaults shown even when unedited.
  • Unregistered stored keys remain visible/editable and visually distinguished — "Unregistered" badge.
  • Domain methods source defaults from the registry — tenant-setting/index.ts.
  • Seed seeds the full registry + backfills existing tenants without overwriting — tenant-defaults.ts.
  • Scattered admin sidebar entries replaced by a single entry — nav.ts.
  • Old routes redirect into the matching tab; per-item routes still work.
  • CSM/SDM retain taxonomy access via the hub, limited to their tabs — tabs-config roles + route policies.
  • All copy sentence case.

Verify result

  • mechanical checks (format · lint · typecheck · build) run in CI + the Vercel preview. pnpm typecheck was run locally and passed (9/9 tasks) given the size of the change.

Notes for review

  • matching.minFitScore now appears in the hub Settings tab (registry list) and the matching section still lives on /settings (kept so SDM, who has no hub general tab, retains it). Minor intentional overlap for admins.
  • commercial.vendorRevenuePercent has a null default (absence is meaningful), so it is listed but not seeded — matching the existing "presence means configured" behaviour.
  • /admin/integrations (a separate tenant-integration config surface, not in the sidebar) is left as-is; the hub Integrations tab and the /integrations redirect use the catalogue list the admin sidebar actually pointed at.
  • settings/_components/admin-tenant-settings-section.tsx and the older tenant-settings-manager.tsx are now unused (superseded by the registry manager); left in place to keep the diff focused.

03_release/output/changelog.md

All your organisation settings in one place

Everything you configure for your organisation now lives on a single settings screen with a tab for each area, instead of being scattered across the platform:

  • Admins get one place for tenant settings, locations, services, products, industries, commercial terms, SLAs, escalation, integrations, statuses, the setup checklist, and demo data.
  • The settings tab now always shows every available setting together with its default value and a short description, so a setting that should exist by default is there from the start — no more hunting for one that was never created.
  • The areas you used before still open from the same links and now land on the matching tab; CSM and SDM keep access to the catalogues they manage.

03_release/output/investor-update.md

Admins configure the whole platform from one settings screen

Who it's for: Platform admins, plus CSM and SDM managers What shipped: A single tabbed settings hub, backed by a registry that guarantees every default setting always exists. Why it matters: A predictable, one-stop configuration surface — part of Refine the Bridge — cuts setup confusion during vendor onboarding.

New tenants now start with the full default settings set, not a blank slate.

Dig deeper: <merged-PR URL> · <changelog entry URL>

03_release/output/release.md

Release: settings-hub

  • pr: #494 · merged: pending (Ready to merge ticked; merging on green CI)
  • CI: green before the review commit (typecheck · format · lint · audit · preview migrate all success; production migrate skipped on PR)
  • technical docs: updated apps/docs/app/technical/development/database/page.mdx (seed now sources defaults from the registry)
  • business docs: updated apps/docs/app/business/roles/page.mdx (admin settings hub + statuses tab location)
  • release notes: both — changelog entry apps/help/app/changelog/2026-06-18-settings-hub/page.mdx + investor draft in this PR
  • deploy: pending (post-merge production check for web + help)
  • sent: pending (after green deploy)

Review summary

  • /code-review run on the branch diff (correctness + removed-behavior + conventions finders).
  • Removed-behavior audit: PASS — every redirect resolves to a tab rendering the same feature, per-item create/[id]/edit routes intact, no role lost access (CSM/SDM keep taxonomy tabs via the hub).
  • Conventions: PASS — arrow functions, type over interface, named imports all satisfied; "sentence case" flags were false positives (single-word labels and "New setting"/"Edit setting" are already sentence case).
  • Finding addressed on branch: the reset/delete confirm in settings-registry-manager.tsx swallowed action errors — added a resetError state so a failed reset/delete surfaces instead of silently closing the dialog.
  • Falsy-zero / closure / role-gating candidates were refuted: 0/false defaults are handled (nullish, not falsy, checks), the AlertDialog reads current resetRow state, and non-admins can't reach admin-only tabs (tabs are role-filtered and middleware blocks /admin/settings/* admin paths).

Acceptance check (vs spec)

  • Single tabbed hub with one tab per area — /admin/settings layout + 12 tab routes.
  • Each tab renders existing functionality — tab pages reuse the original components.
  • Selected tab encoded in URL, preserved on refresh/direct link — routed sub-paths.
  • Typed settings registry in packages/services, exported — tenant-setting/registry.ts.
  • Settings tab lists every registry setting with effective value, default, description — defaults shown even when unedited.
  • Unregistered stored keys visible/editable and visually distinguished — "Unregistered" badge.
  • Domain methods source defaults from the registry — tenant-setting/index.ts.
  • Seed seeds the full registry + backfills existing tenants without overwriting — tenant-defaults.ts.
  • Scattered admin sidebar entries replaced by a single entry — nav.ts.
  • Old routes redirect into the matching tab; per-item routes still work.
  • CSM/SDM retain taxonomy access via the hub, limited to their tabs.
  • All copy sentence case.