admin-escalation-configrun.md00_intake/stub.mdThe EscalationRulesCard (rules active, default-escalation enabled) is dummy. There is no stored
escalation configuration, so the dashboard cannot show whether escalations are set up. Per the
storage decision, this simple config lives in the existing TenantSetting key/value collection.
escalation category in TenantSetting.false).touches: packages/services/src/db/models/tenant-setting.ts (keys, read/write),
apps/web/components/admin/dashboard/escalation-rules-card.tsx.01_define/output/spec.mdOn the admin "Platform setup" dashboard, the EscalationRulesCard (Active/Missing status,
"Default escalation enabled" yes/no) is driven entirely by dummy data in lib/mock/admin.ts —
there is no stored escalation configuration, so the card can never reflect whether a tenant has
actually set escalation up. This is feature 5 of the admin-dashboard-data epic, which advances
Build the Bridge / Q2 2026 Objective 3 — validate technical infrastructure & payout flow by
making each dashboard section operational from real, per-tenant data rather than a mock. Per the
epic's storage decision, this simple config lives in the existing TenantSetting key/value
collection rather than a dedicated model.
escalation category in
TenantSetting, with a single flag this round: escalation.defaultEnabled (boolean). The
category is kept as a namespace so future per-stage escalation keys (e.g.
escalation.<stage>.enabled) can slot in without a schema change.getEscalationConfig(tenantId) and
setEscalationConfig(tenantId, { defaultEnabled }) to TenantSettingService, following the
existing getMinFitScore / setMinFitScore precedent (upsert under the escalation category;
getEscalationConfig returns { defaultEnabled: false } when nothing is stored)./admin/escalation page. A server component reads the current
config; a client form (backed by a server action, following the /admin/settings actions
pattern) lets the admin toggle "default escalation enabled" and save. Sentence-case copy
throughout.d.escalationRules. defaultEscalationEnabled comes straight from the stored flag; the card's
active status is derived — active (status "Active") when default escalation is enabled,
otherwise "Missing". A never-configured tenant therefore renders as Missing / off, not a mock
value. The card links to /admin/escalation, and the matching "No escalation rule" entry in the
config-gaps panel points its action at /admin/escalation (currently /admin/automations, which
does not exist)./admin/escalation, and the value
persists per tenant in TenantSetting under the escalation category
(escalation.defaultEnabled).getEscalationConfig / setEscalationConfig exist on TenantSettingService; get returns
{ defaultEnabled: false } when no setting is stored.false), not the old mock value./admin/escalation, and the "No escalation rule" config-gap
action targets /admin/escalation.admin-sla-config.)defaultEnabled this round, not
stored independently.admin-readiness-gaps
feature). This feature only fixes the single "No escalation rule" gap's link target.apps/dashboards — that app stays the dummy source of truth.admin-data-foundation (feature 1) has converted /admin/dashboard to a server-fetched
per-tenant page; the escalation slice plugs into that server fetch. Build should adopt the
foundation's data-loading seam rather than re-introduce client-side fetching. If the foundation
has not merged when Build starts, land it first — Build does not gather requirements, it
implements this settled spec on top of the foundation.02_build/output/notes.mdpackages/services/src/db/services/tenant-setting/index.ts: added EscalationConfig type
and getEscalationConfig / setEscalationConfig helpers on TenantSettingService, following
the existing getMinFitScore / setMinFitScore precedent. Config is stored under key
escalation.defaultEnabled in the escalation category (a namespace, so future per-stage keys
slot in without a schema change). get returns { defaultEnabled: false } when nothing is stored.packages/services/src/db/services/index.ts: re-export the EscalationConfig type from the
services server barrel.apps/web/lib/admin-dashboard-data.ts: added an escalation slice to the dashboard data seam —
fetches the real config and exposes { defaultEnabled, active }, where active is derived
(active === defaultEnabled, no separately stored flag).apps/web/app/(app)/admin/dashboard/page.tsx: EscalationRulesCard now reads the real
escalation slice instead of d.escalationRules.apps/web/components/admin/dashboard/escalation-rules-card.tsx: added a footer link to
/admin/escalation (mirrors the SLA card's footer-link pattern).apps/web/app/(app)/admin/escalation/: new config page — server component reads the current
config; a client form (Switch + server action saveEscalationConfig, following the
matching-settings / /admin/settings actions pattern) lets an admin toggle and save. Saving
revalidates both /admin/escalation and /admin/dashboard.apps/web/lib/mock/admin.ts: pointed the "No escalation rule" config-gap action at
/admin/escalation (was /admin/automations, which does not exist).apps/web/lib/route-policies.ts: registered /admin/escalation as admin-only. The
proxy.ts Clerk middleware denies unmatched routes by default (redirects to the role
home), so without a policy entry every admin hitting /admin/escalation was bounced to
/admin/dashboard — the page was unreachable. Added alongside the other deep admin routes./admin/escalation, persisted per tenant in
TenantSetting under the escalation category (escalation.defaultEnabled) — page + form +
setEscalationConfig upsert.getEscalationConfig / setEscalationConfig exist on TenantSettingService; get returns
{ defaultEnabled: false } when no setting is stored.EscalationRulesCard reflects the real saved state — both the "Default escalation enabled"
badge and the Active/Missing status come from the stored config via the dashboard data seam, not
the mock.active = defaultEnabled in the data seam → ConfigCard renders Active/Missing).getEscalationConfig treats no stored value
as false./admin/escalation, and the "No escalation rule" config-gap action targets
/admin/escalation.gh pr checks. No check is expected to fail.Typography from @sustentus/ui has a closed prop type (no htmlFor), so the form
associates its label via the Switch's aria-label, matching the existing min-fit-score form.d.escalationRules is left in lib/mock/admin.ts (now unused) to keep the diff minimal, in line
with the file's other kitchen-sink mock entries; it can be pruned in a later cleanup.apps/dashboards.03_ship/output/changelog.mdPersona: admin
Admins can now set up escalation from the platform setup dashboard:
This is part of making your platform setup dashboard reflect your organisation's real configuration.
03_ship/output/investor-update.mdWe've made escalation handling configurable per organisation — the first operational control in the admin "platform setup" area to move from placeholder content to real, saved data. It's a small but foundational step in turning the admin dashboard into a working control panel rather than a preview.
This advances the Build the Bridge initiative and our Q2 2026 objective to validate technical infrastructure & payout flow — making the admin control surface something operators can genuinely configure.
03_ship/output/pr.mdapps/docs/technical page enumerates admin routes or tenant-setting keys; demo-environment mentions "Tenant Settings" only generically)initiatives/refine-the-bridge describe the future per-stage escalation engine, which this config flag deliberately does not build)apps/help)/code-review (high effort) found no correctness bugs. Verified the service helpers, route
policy, server action, dashboard-data slice, and card wiring against their call sites.Toaster is mounted in app/layout.tsx; toast() is the
established pattern); <Link><button> nesting in the card (identical to the existing
SlaConfigCard footer-link — house convention, not introduced here).loadAdminDashboardData fetches escalation config and the
setup checklist sequentially; they could run via Promise.all. Matches the foundation's existing
style; left as-is./admin/escalation had no ROUTE_POLICIES entry, so the deny-by-default
proxy.ts middleware redirected every admin to their home. Added it as admin-only (commit
654b8f9); verified the page now loads on the preview./admin/escalation; persisted per tenant in
TenantSetting under the escalation category (escalation.defaultEnabled).getEscalationConfig / setEscalationConfig exist on TenantSettingService; get returns
{ defaultEnabled: false } when unset./admin/escalation; the "No escalation rule" config-gap action targets it too./admin/escalation is reachable (route policy registered).04_release/output/release.mddashboards production deploy dpl_HhXenyQ3XXxaDAB8YhpnamPyAHgg (READY, target=production) for merge commit 1692cb1 on main; https://dashboards-git-main-sustentus.vercel.appf7f05510-d3ae-4a0d-8789-8113083037d5, 51c9617c-2065-4563-adcf-79697d6e9ccd) — each a real
HTTP 200 + id, not an inferred success.A first --send (recorded above before this fix) reported "2 sent" but was a false success: this
environment's egress to api.resend.com was blocked by the network allowlist (proxy Host not in allowlist, HTTP 403), so the request never reached Resend. The script's success check was hardened
to require a real 2xx and a Resend id (commit on the run branch), and the actual delivery above
was made once egress to Resend was available. The investor update went out verbatim from
03_ship/output/investor-update.md — no last-minute edits.