Skip to Content

← All archived runs

Run: settings-registry-alignment

run.md

Run: settings-registry-alignment

  • branch: claude/settings-registry-alignment-u90nsn
  • pr: #617

00_intake/stub.md

Stub: Align the settings section to the settings registry

  • feature-slug: settings-registry-alignment
  • epic: onboarding-blueprint-alignment
  • personas: Admin, Vendor
  • initiative: Build the Bridge / objective: Q2-2026 O1 — Establish Product-Market Fit with Vendor Partners
  • depends-on: remove-categories-section
  • sequence: 4 of 6

Problem

The settings section invents arbitrary key/value datapoints that don't correspond to real platform settings. The platform has a fixed set of 12 known settings defined in SETTINGS_REGISTRY (keys, categories, types, defaults) — the blueprint should only ever propose those, not made-up settings.

Proposed change

Constrain the settings blueprint to the canonical settings registry: draft only the registry's known keys, each with its correct category, type and default, seeded from tenant-defaults, with the AI adjusting values from the source where meaningful and the operator confirming. Contract, prompt, review-panel editable fields, and commit all reflect the registry (commit already targets tenant_settings).

Acceptance criteria (rough)

  • The settings section proposes only keys present in SETTINGS_REGISTRY (no invented settings).
  • Each settings item carries the registry-correct key, category, value (typed to the entry's type) and description.
  • Settings seed from platform defaults, are AI-adjustable within type, then operator-confirmable.
  • Committing writes valid tenant_settings rows (unique per key) that pass the key pattern and type.
  • The review panel edits each settings value with the right control for its type.

Out of scope (this feature)

  • Adding to or removing entries from the settings registry itself.
  • The admin settings CRUD tabs.

Notes for Define

  • Registry is the source of truth: packages/services/src/db/services/tenant-setting/registry.ts (12 keys across general / sla / matching / escalation / commercial / coverage / marketSignals). Seed wiring in db/seed/tenant-defaults.ts. The model is db/models/tenant-setting.ts (key / category / value / description).
  • touches: packages/services/src/ai/onboarding/{contract.ts,prompt.ts,analyse-source.ts}, packages/services/src/db/services/onboarding-blueprint/commit.ts (the settings branch), apps/web/components/onboarding/types.ts (settings SECTION_META); reference tenant-setting/registry.ts.

01_define/output/spec.md

Spec: Align the settings section to the settings registry

  • slug: settings-registry-alignment
  • personas: Admin, Vendor
  • touches: packages/services/src/ai/onboarding/{contract.ts,prompt.ts,analyse-source.ts}, packages/services/src/db/services/onboarding-blueprint/commit.ts, apps/web/components/onboarding/{types.ts,blueprint-item.tsx}
  • complexity: standard

Problem

The AI onboarding concierge drafts a per-tenant blueprint across six sections, but its settings section invents arbitrary key/value datapoints with no home in the platform. The platform already has a canonical registry — SETTINGS_REGISTRY in tenant-setting/registry.ts (12 keys, each with a category, type and default) — that every domain read and the tenant seed resolve against. The blueprint ignores it: the extraction contract's settings items are free-form, the prompts invite made-up keys, the commit falls back to an invented category: "onboarding" and an untyped value, and the review panel edits every setting through a single plain-text box. This is stub 4 of the onboarding-blueprint-alignment epic (initiative Build the Bridge, objective Q2-2026 O1 — Establish Product-Market Fit with Vendor Partners): the blueprint must configure a vendor tenant into the platform's real shapes, so a smooth, trustworthy setup gets vendors onto paid tiers.

Proposed change

Constrain the settings section to a curated, onboarding-relevant subset of the canonical registry, so the blueprint only ever proposes registry keys in their registry-correct shape.

  • Curated subset (the operator-facing operating defaults): default_currency, timezone (general), sla_business_days_only (sla), escalation.defaultEnabled (escalation), commercial.vendorRevenuePercent, commercial.pricingRulesSet (commercial). The internal SDM analytics thresholds — matching.minFitScore, all coverage.*, and marketSignals.bandMarginPct — are excluded from onboarding: they keep their seeded registry defaults and are tuned later in the admin Settings tab. The subset is a single explicit allow-list (derived from the registry, not a re-typed copy) so adding/removing a key is one edit.
  • Seed then adjust: the section is pre-populated with the curated seedable entries at their registry defaults (currency EUR, timezone Europe/Brussels, business-days-only true, escalation default false, pricing-rules-set false). The AI adjusts a value only where the source supports it and only within the entry's registry type; the operator confirms or edits each before launch. commercial.vendorRevenuePercent has no registry default — it appears for the operator to optionally supply a value and is committed only if a valid number is given.
  • Registry-shaped contract & prompts: the extraction contract's settings items carry the registry key, its category, a value typed to the entry's type, and the registry description; keys outside the curated subset are rejected/dropped. The concierge prompt and the analyse-source system prompt describe settings as this fixed key list (with categories and types), instructing the model to adjust values, never to invent keys.
  • Registry-correct commit: the settings branch of commit.ts writes each item with the registry key, category and description, and a value coerced to the registry type. It upserts by (tenantId, key) rather than blind-creating, so a commit is idempotent and never collides with a tenant whose defaults were already seeded. The category: "onboarding" fallback is removed.
  • Right control per type: the review panel resolves each settings item's editor control from its registry type — a text input for string, a number input for number, and a new boolean control (switch/checkbox) for boolean — instead of the single plain-text box. This adds a "boolean" variant to EditableField and its renderer in blueprint-item.tsx.

Acceptance criteria

  • The settings section proposes only keys in the curated registry subset (default_currency, timezone, sla_business_days_only, escalation.defaultEnabled, commercial.vendorRevenuePercent, commercial.pricingRulesSet) — no invented settings and no excluded SDM-threshold keys.
  • Each settings item carries the registry-correct key, category, a value typed to the entry's registry type, and the registry description.
  • The section seeds from the curated registry defaults; the AI may adjust a seeded value only where the source supports it and within type; the operator can then confirm or edit each.
  • Committing writes valid tenant_settings rows — one per key, unique on (tenantId, key), with a registry-typed value that passes the key pattern — upserting so an already-seeded tenant does not collide. commercial.vendorRevenuePercent commits only when a valid number is supplied.
  • The review panel edits each settings value with the control matching its registry type: text for string, number input for number, and a boolean toggle for boolean.

Out of scope

  • Adding to, removing from, or otherwise changing entries in SETTINGS_REGISTRY itself.
  • The internal SDM-threshold settings (matching.minFitScore, coverage.*, marketSignals.*) — kept at their seeded registry defaults and managed in the admin Settings tab, not surfaced at onboarding.
  • The admin Settings CRUD tabs (the existing settings hub is unchanged).
  • The other blueprint sections (profile, products, services, skills, sla) — each aligned by its own epic stub.
  • The one-shot-vs-progressive interview flow (epic stub 6, conversational-progressive-flow).
  • Re-onboarding or migrating existing tenants — this refines the fresh-tenant flow only.

Open questions

  • none.

02_build/output/notes.md

Build notes: settings-registry-alignment

  • commits: feat: settings-registry-alignment — constrain the onboarding settings section to the registry

What changed

  • packages/services/src/db/services/tenant-setting/registry.ts: added the curated onboarding allow-list (ONBOARDING_SETTING_KEYS, onboardingSettings(), isOnboardingSettingKey, onboardingSettingLabel) and a shared coerceSettingValue(value, type) typing helper. The curated six are the operator-facing operating defaults; the internal SDM thresholds (matching.*, coverage.*, marketSignals.*) are excluded. Registry entries themselves are untouched.
  • packages/services/src/ai/onboarding/contract.ts: settings now use a registry-constrained DraftSettingsItemSchemakey is a z.enum of the curated keys, value a string|number|boolean matching the key's type. DraftBlueprintSchema.settings uses it (the satisfies guard is relaxed to z.ZodTypeAny so it still fails to compile if a section key is missing). category/description are not the model's to set.
  • packages/services/src/ai/onboarding/analyse-source.ts: the system prompt lists the fixed known settings (key/type/description) and forbids invented keys. New buildSettingsItems seeds every curated setting at its registry default and overlays a type-coerced model value where the source supports one — each item's data carries the registry key/category/value/description. The section is always written (seed-then-adjust); seeded defaults don't count toward the thin/empty signal.
  • packages/services/src/ai/onboarding/prompt.ts: the concierge's settings section is described as the fixed known list (seeded at defaults, adjust-only), so the chat path can't invent settings either.
  • packages/services/src/db/services/onboarding-blueprint/commit.ts: the settings branch now resolves each item against the registry, coerces the value to the registry type, skips off-registry keys and unset values (so an unset commercial.vendorRevenuePercent never commits as 0), and upserts by (tenantId, key) via the model — idempotent, and no unique-index collision on an already-seeded tenant. The invented category: "onboarding" fallback is gone.
  • apps/web/components/onboarding/types.ts: EditableField gains a "boolean" control type; added a client-safe ONBOARDING_SETTING_TYPES map and settingsEditableFields(item) that resolves a settings item's control from its registry key.
  • apps/web/components/onboarding/blueprint-item.tsx: settings items resolve their edit fields per item (heterogeneous types); added a Switch control for boolean, with matching save-time coercion.
  • Barrel: exported DraftSettingsItemSchema / DraftSettingsItem from ai/onboarding.

Acceptance criteria status

  • Only curated registry keys proposed — enforced at the extraction contract (z.enum), buildSettingsItems, the concierge prompt, and as a commit backstop (isOnboardingSettingKey).
  • Each item carries registry-correct key/category/typed value/description — stamped from the registry in buildSettingsItems and written from the registry at commit.
  • Seeds from curated defaults, AI adjusts within type, operator confirms/edits — buildSettingsItems seeds+overlays; review panel edits per type; only approved/edited items commit.
  • Commit writes valid tenant_settings rows, unique per key, upserting; vendorRevenuePercent commits only with a valid number — findOneAndUpdate upsert + coerceSettingValue null-skip.
  • Review panel uses the right control per type — text/number/boolean (Switch) resolved from the item's registry key.

Verify result

  • mechanical checks (format · lint · typecheck · build) run in CI + the Vercel preview, not here. Release reads them back from the PR's check runs.

Notes for review

  • The client keeps a small hand-maintained mirror of the curated settings' value types (ONBOARDING_SETTING_TYPES) because the onboarding client components must not import the server (Mongoose) barrel — same pattern as the existing client-safe mirrors in types.ts. It must stay in lockstep with ONBOARDING_SETTING_KEYS / SETTINGS_REGISTRY.
  • The settings baseline seeds via analyse-source (website/description path). A pure-chat onboarding (no source analysed) relies on the concierge prompt to draft the curated settings; the commit is the hard guarantee that only registry-shaped rows land regardless of path.
  • SECTION_META.settings.editableFields is now a fallback only — blueprint-item overrides it per item for settings — so it was left as-is to keep the diff focused.

03_release/output/changelog.md

Changelog: settings-registry-alignment

Live entry: apps/help/app/changelog/2026-07-08-settings-registry-alignment/page.mdx


title: Workspace setup now proposes your real settings, ready to confirm personas: [admin, vendor]

When you set up a new workspace, the settings step no longer invents made-up options. It now offers the platform's real operating settings, pre-filled with sensible defaults for you to confirm or adjust.

  • You see the settings that actually run your workspace — default currency, timezone, whether SLAs count business days only, and your commercial terms — instead of placeholder values that led nowhere.
  • Each setting comes ready-filled with a sensible default and the right control for its kind: a text box, a number, or a simple on/off toggle. If your details are picked up from your website, the assistant adjusts the values for you to check.
  • Everything you confirm lands straight into your workspace settings, so what you review during setup is exactly what takes effect when you launch.

03_release/output/investor-update.md

Vendor onboarding now configures real settings, not placeholders

Who it's for: Admins and vendors setting up a new workspace What shipped: The onboarding assistant's settings step now proposes only the platform's real, correctly-typed settings, seeded from defaults for the operator to confirm — not invented ones. Why it matters: A trustworthy setup helps get vendors onto paid subscription tiers (Build the Bridge · Q2 objective: establish product-market fit with vendor partners).

Confirmed settings now commit as valid workspace configuration.

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

03_release/output/release.md

Release: settings-registry-alignment

  • pr: #617 (https://github.com/sustentus/sustentus/pull/617) · merged: pending (awaits Ready-to-merge tick + green CI)
  • CI: green on the built branch (Quality Project ✓, preview DB migrate ✓, DB audit ✓, Vercel web+marketing previews ✓)
  • technical docs: no technical docs impact — technical/packages/services describes the onboarding concierge at the blueprint-section level; the described architecture (Zod extraction contract → onboarding-blueprint service → commit into the real workspace) is unchanged, this change only constrains the settings section's shape within it.
  • business docs: no business docs impact — platform-overview describes the blueprint's settings section generically and roles already states tenant settings are "backed by a canonical registry"; this change aligns onboarding to that already-documented registry without altering any documented route or flow.
  • release notes: both — changelog entry (apps/help/app/changelog/2026-07-08-settings-registry-alignment/page.mdx) + investor draft in this PR
  • deploy: pending (poll after merge)
  • sent: pending (after green deploy)

Review summary

Ran /code-review medium (complexity: standard) over this run's diff — ENABLE_CLAUDE_REVIEW is off, so the review ran inline. Correctness pass found no bugs: seeded false, unset null (vendor revenue share), and 0 all round-trip correctly through seed → review → commit, with only genuinely-unset null dropped. Cleanup pass found no CONVENTIONS.md violations and three maintainability notes, all accepted (not actioned) with rationale:

  • Client ONBOARDING_SETTING_TYPES map (types.ts) is Record<string, …>, so a future 7th onboarding setting added server-side wouldn't fail the client build — accepted. The suggested fix (stamp type into item.data and drop the map) would regress the concierge-drafted path (its items don't carry a stamped type, so booleans would render as a text control); the map resolves the right control for both the analyse-source and concierge paths, and commit-side coercion is the safety net either way. The lockstep requirement is documented in a comment + build notes.
  • analyse-source seeds the settings baseline before the itemCount === 0 guard, so a thin source still writes the settings section while returning empty — accepted; deliberate (settings seed regardless of source richness; empty only signals the catalogue was thin so the concierge interviews).
  • SECTION_META.settings.editableFields is now a fallback that BlueprintItem overrides per item for settings — accepted; the Record<OnboardingSectionKey, …> type requires the entry to exist, and it's harmless.

Acceptance check (vs spec)

  • Settings section proposes only curated registry keys — z.enum(ONBOARDING_SETTING_KEYS) in the extraction contract, buildSettingsItems emits only onboardingSettings(), the concierge prompt lists the fixed set, and the commit skips off-registry keys (isOnboardingSettingKey).
  • Each item carries registry-correct key/category/typed value/description — stamped from the registry in buildSettingsItems; the commit writes registry category/description and a type-coerced value.
  • Seeds from curated defaults; AI adjusts within type; operator confirms/edits — buildSettingsItems seeds defaults and overlays coerced source values; review panel edits per type; only approved/edited items commit.
  • Commit writes valid, unique-per-key tenant_settings rows, upserting; vendorRevenuePercent only with a valid number — findOneAndUpdate upsert on (tenantId, key) + coerceSettingValue null-skip.
  • Review panel uses the control matching each registry type — text/number/boolean Switch, resolved per item from its registry key.