@objectstack/platform-objects 11.0.0 → 11.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -18,6 +18,8 @@ var SysUser = ObjectSchema.create({
18
18
  },
19
19
  description: "User accounts for authentication",
20
20
  displayNameField: "name",
21
+ nameField: "name",
22
+ // [ADR-0079] canonical primary-title pointer (mirrors deprecated displayNameField)
21
23
  titleFormat: "{name}",
22
24
  compactLayout: ["name", "email", "email_verified"],
23
25
  // Custom actions — generic CRUD is suppressed because user accounts are
@@ -422,6 +424,25 @@ var SysUser = ObjectSchema.create({
422
424
  group: "Admin",
423
425
  description: "When set and in the future, sign-in is rejected (brute-force lockout). Auto-clears past this time; an admin can clear it early via Unlock."
424
426
  }),
427
+ // ADR-0069 D1 — last password change; drives password-expiry enforcement.
428
+ // Stamped on sign-up / change-password / reset-password. Null = never
429
+ // expires (until the user next changes their password).
430
+ password_changed_at: Field.datetime({
431
+ label: "Password Changed At",
432
+ required: false,
433
+ readonly: true,
434
+ group: "Admin",
435
+ description: "Timestamp of the last password change. Backs password_expiry_days; system-managed."
436
+ }),
437
+ // ADR-0069 D3 — when enforced MFA first applied to this user; starts the
438
+ // grace clock. Stamped lazily at session validation; system-managed.
439
+ mfa_required_at: Field.datetime({
440
+ label: "MFA Required At",
441
+ required: false,
442
+ readonly: true,
443
+ group: "Admin",
444
+ description: "When enforced MFA first applied to this user (grace-period clock). Backs mfa_required; system-managed."
445
+ }),
425
446
  ai_access: Field.boolean({
426
447
  label: "AI Access",
427
448
  defaultValue: false,
@@ -523,6 +544,8 @@ var SysSession = ObjectSchema.create({
523
544
  },
524
545
  description: "Active user sessions",
525
546
  displayNameField: "user_id",
547
+ nameField: "user_id",
548
+ // [ADR-0079] canonical primary-title pointer (mirrors deprecated displayNameField)
526
549
  titleFormat: "Session \u2014 {user_id}",
527
550
  compactLayout: ["user_id", "ip_address", "expires_at"],
528
551
  // Custom actions — sessions are managed by better-auth (generic CRUD
@@ -594,6 +617,29 @@ var SysSession = ObjectSchema.create({
594
617
  required: true,
595
618
  group: "Session"
596
619
  }),
620
+ // ── ADR-0069 D4 — session controls (idle / absolute / revoke) ──
621
+ last_activity_at: Field.datetime({
622
+ label: "Last Activity At",
623
+ required: false,
624
+ readonly: true,
625
+ group: "Session",
626
+ description: "Timestamp of the last request on this session; drives idle-timeout. System-managed."
627
+ }),
628
+ revoked_at: Field.datetime({
629
+ label: "Revoked At",
630
+ required: false,
631
+ readonly: true,
632
+ group: "Session",
633
+ description: "When set, this session was revoked (idle / absolute-max / concurrent-cap / admin). System-managed."
634
+ }),
635
+ revoke_reason: Field.text({
636
+ label: "Revoke Reason",
637
+ required: false,
638
+ maxLength: 64,
639
+ readonly: true,
640
+ group: "Session",
641
+ description: "Why the session was revoked (idle_timeout, absolute_max, concurrent_cap, \u2026)."
642
+ }),
597
643
  // ── Active context (multi-org/team) ──────────────────────────
598
644
  active_organization_id: Field.lookup("sys_organization", {
599
645
  label: "Active Organization",
@@ -946,6 +992,8 @@ var SysOrganization = ObjectSchema.create({
946
992
  },
947
993
  description: "Organizations for multi-tenant grouping",
948
994
  displayNameField: "name",
995
+ nameField: "name",
996
+ // [ADR-0079] canonical primary-title pointer (mirrors deprecated displayNameField)
949
997
  titleFormat: "{name}",
950
998
  compactLayout: ["name", "slug"],
951
999
  // Custom actions — generic CRUD is suppressed (better-auth-managed),
@@ -1114,6 +1162,17 @@ var SysOrganization = ObjectSchema.create({
1114
1162
  description: "JSON-serialized organization metadata",
1115
1163
  group: "Configuration"
1116
1164
  }),
1165
+ // ADR-0069 D3 — per-org MFA tightening above the global floor. When true,
1166
+ // members of this org must enrol TOTP to access data (enforced at the
1167
+ // session-validation gate). An org can only tighten, never loosen, the
1168
+ // global `mfa_required` setting.
1169
+ require_mfa: Field.boolean({
1170
+ label: "Require Multi-Factor Auth",
1171
+ required: false,
1172
+ defaultValue: false,
1173
+ group: "Configuration",
1174
+ description: "When true, every member of this organization must enroll an authenticator app to access data."
1175
+ }),
1117
1176
  // ── System ───────────────────────────────────────────────────
1118
1177
  id: Field.text({
1119
1178
  label: "Organization ID",
@@ -1557,6 +1616,8 @@ var SysTeam = ObjectSchema.create({
1557
1616
  },
1558
1617
  description: "Teams within organizations for fine-grained grouping",
1559
1618
  displayNameField: "name",
1619
+ nameField: "name",
1620
+ // [ADR-0079] canonical primary-title pointer (mirrors deprecated displayNameField)
1560
1621
  titleFormat: "{name}",
1561
1622
  compactLayout: ["name", "organization_id"],
1562
1623
  // Custom actions calling better-auth's team endpoints. Generic CRUD is
@@ -1803,6 +1864,8 @@ var SysBusinessUnit = ObjectSchema.create({
1803
1864
  managedBy: "platform",
1804
1865
  description: "Canonical Business Unit tree \u2014 hierarchical org/data-partition node (company / division / department / region / office). ADR-0057 D2.",
1805
1866
  displayNameField: "name",
1867
+ nameField: "name",
1868
+ // [ADR-0079] canonical primary-title pointer (mirrors deprecated displayNameField)
1806
1869
  titleFormat: "{name}",
1807
1870
  compactLayout: ["name", "kind", "parent_business_unit_id", "manager_user_id"],
1808
1871
  listViews: {
@@ -2076,6 +2139,8 @@ var SysApiKey = ObjectSchema.create({
2076
2139
  },
2077
2140
  description: "API keys for programmatic access",
2078
2141
  displayNameField: "name",
2142
+ nameField: "name",
2143
+ // [ADR-0079] canonical primary-title pointer (mirrors deprecated displayNameField)
2079
2144
  titleFormat: "{name}",
2080
2145
  compactLayout: ["name", "prefix", "user_id", "expires_at", "revoked"],
2081
2146
  // Custom actions — sys_api_key is managed-by 'better-auth' but the
@@ -2419,6 +2484,8 @@ var SysDeviceCode = ObjectSchema.create({
2419
2484
  docsUrl: "https://docs.objectstack.ai/adr/0010-metadata-protection"
2420
2485
  },
2421
2486
  description: "OAuth 2.0 Device Authorization Grant (RFC 8628) pending requests",
2487
+ nameField: "user_code",
2488
+ // [ADR-0079] canonical primary-title pointer (single-field titleFormat)
2422
2489
  titleFormat: "{user_code}",
2423
2490
  compactLayout: ["user_code", "status", "client_id", "expires_at"],
2424
2491
  fields: {
@@ -2512,6 +2579,8 @@ var SysUserPreference = ObjectSchema.create({
2512
2579
  // surface in Setup is a support/diagnostic view only.
2513
2580
  managedBy: "system",
2514
2581
  description: "Per-user key-value preferences (theme, locale, etc.)",
2582
+ nameField: "key",
2583
+ // [ADR-0079] canonical primary-title pointer (single-field titleFormat)
2515
2584
  titleFormat: "{key}",
2516
2585
  compactLayout: ["user_id", "key"],
2517
2586
  listViews: {
@@ -2607,6 +2676,8 @@ var SysOauthApplication = ObjectSchema.create({
2607
2676
  },
2608
2677
  description: "Registered OAuth/OIDC client applications",
2609
2678
  displayNameField: "name",
2679
+ nameField: "name",
2680
+ // [ADR-0079] canonical primary-title pointer (mirrors deprecated displayNameField)
2610
2681
  titleFormat: "{name}",
2611
2682
  compactLayout: ["name", "client_id", "type", "disabled"],
2612
2683
  // Custom actions — all OAuth-application mutations are routed through
@@ -3298,6 +3369,22 @@ var SysSsoProvider = ObjectSchema.create({
3298
3369
  icon: "shield-check",
3299
3370
  isSystem: true,
3300
3371
  managedBy: "better-auth",
3372
+ // ADR-0024 — env-global, ADMIN-ONLY identity config. Two orthogonal controls:
3373
+ // • `tenancy.enabled: false` — the env IS the tenant; providers are env-wide,
3374
+ // not org-partitioned. Opting out of multi-tenancy lets a platform admin's
3375
+ // `viewAllRecords` superuser bypass see every provider (without it, the
3376
+ // `member_default` wildcard `tenant_isolation` RLS denies every row, since
3377
+ // better-auth writes via its adapter with no tenantId → `organization_id`
3378
+ // is never stamped).
3379
+ // • `requiredPermissions: ['manage_platform_settings']` — object-level
3380
+ // capability gate (ADR-0066 D3) so ordinary members are denied entirely
3381
+ // (without it, tenancy-disabled + `member_default`'s `'*': allowRead` would
3382
+ // leak providers to every authenticated user).
3383
+ // Together: admins see all env providers; non-admins get 403. better-auth's
3384
+ // own endpoints already read via a system context. (Env-only object — no
3385
+ // control-plane cross-tenant risk.)
3386
+ tenancy: { enabled: false, strategy: "shared" },
3387
+ requiredPermissions: ["manage_platform_settings"],
3301
3388
  // ADR-0010 §3.7 — managed by better-auth; tenants may not edit schema.
3302
3389
  protection: {
3303
3390
  lock: "full",
@@ -3306,6 +3393,8 @@ var SysSsoProvider = ObjectSchema.create({
3306
3393
  },
3307
3394
  description: "External SSO identity providers (OIDC / SAML) this environment federates login to",
3308
3395
  displayNameField: "provider_id",
3396
+ nameField: "provider_id",
3397
+ // [ADR-0079] canonical primary-title pointer (mirrors deprecated displayNameField)
3309
3398
  titleFormat: "{provider_id}",
3310
3399
  compactLayout: ["provider_id", "issuer", "domain"],
3311
3400
  // All mutations go through @better-auth/sso's endpoints under
@@ -3321,14 +3410,101 @@ var SysSsoProvider = ObjectSchema.create({
3321
3410
  locations: ["list_toolbar"],
3322
3411
  type: "api",
3323
3412
  method: "POST",
3324
- target: "/api/v1/auth/sso/register",
3413
+ // Routed through the env-side bridge (plugin-auth `auth-plugin.ts`), which
3414
+ // reshapes these FLAT form fields into the nested `oidcConfig` body that
3415
+ // `@better-auth/sso`'s /sso/register requires, then re-dispatches to it
3416
+ // (so the admin gate + discovery hydration run). Posting straight to
3417
+ // /sso/register would drop clientId/clientSecret (top-level → Zod-stripped)
3418
+ // and persist an unusable `oidc_config = null` provider.
3419
+ target: "/api/v1/auth/admin/sso/register",
3325
3420
  refreshAfter: true,
3326
3421
  params: [
3327
3422
  { name: "providerId", label: "Provider ID", type: "text", required: true, helpText: 'Stable identifier, e.g. "okta" or "acme-entra".' },
3328
- { name: "issuer", label: "Issuer URL", type: "text", required: true, helpText: "IdP issuer / discovery base, e.g. https://acme.okta.com." },
3329
- { name: "domain", label: "Email Domain", type: "text", required: true, helpText: "Users with this email domain are routed to this IdP." },
3330
- { name: "clientId", label: "Client ID", type: "text", required: true },
3331
- { name: "clientSecret", label: "Client Secret", type: "text", required: true }
3423
+ { name: "issuer", label: "Issuer URL", type: "text", required: true, helpText: "IdP issuer, e.g. https://acme.okta.com. Discovery is fetched from here unless an explicit URL is given below." },
3424
+ { name: "domain", label: "Email Domain", type: "text", required: true, helpText: "Users with this email domain are routed to this IdP, e.g. acme.com." },
3425
+ { name: "clientId", label: "Client ID", type: "text", required: true, helpText: "OAuth client ID issued by the IdP for this environment." },
3426
+ { name: "clientSecret", label: "Client Secret", type: "text", required: true, helpText: "OAuth client secret (stored encrypted by better-auth)." },
3427
+ { name: "discoveryEndpoint", label: "Discovery URL", type: "text", required: false, helpText: "Optional. OIDC discovery document URL. Leave blank to derive `<issuer>/.well-known/openid-configuration`." },
3428
+ { name: "scopes", label: "Scopes", type: "text", required: false, placeholder: "openid email profile", helpText: 'Optional. Space- or comma-separated OAuth scopes. Defaults to "openid email profile".' },
3429
+ { name: "mapId", label: "Map: User ID claim", type: "text", required: false, placeholder: "sub", helpText: 'Optional. ID-token claim mapped to the user ID. Defaults to "sub".' },
3430
+ { name: "mapEmail", label: "Map: Email claim", type: "text", required: false, placeholder: "email", helpText: 'Optional. Claim mapped to email. Defaults to "email".' },
3431
+ { name: "mapName", label: "Map: Name claim", type: "text", required: false, placeholder: "name", helpText: 'Optional. Claim mapped to display name. Defaults to "name".' }
3432
+ ]
3433
+ },
3434
+ {
3435
+ name: "register_saml_provider",
3436
+ label: "Register SAML Provider",
3437
+ icon: "shield",
3438
+ variant: "primary",
3439
+ mode: "create",
3440
+ locations: ["list_toolbar"],
3441
+ type: "api",
3442
+ method: "POST",
3443
+ // SAML 2.0 via @better-auth/sso (samlify-backed). Routed through the
3444
+ // env-side bridge (plugin-auth `auth-plugin.ts` → register-saml), which
3445
+ // reshapes these FLAT IdP fields into the nested `samlConfig` body that
3446
+ // @better-auth/sso's /sso/register requires (entryPoint/cert/callbackUrl/
3447
+ // spMetadata/identifierFormat), derives the per-provider ACS URL, and
3448
+ // re-dispatches to /sso/register (admin gate runs). The response returns
3449
+ // the SP ACS + metadata URLs to configure on the IdP.
3450
+ target: "/api/v1/auth/admin/sso/register-saml",
3451
+ refreshAfter: true,
3452
+ params: [
3453
+ { name: "providerId", label: "Provider ID", type: "text", required: true, helpText: 'Stable identifier, e.g. "acme-saml".' },
3454
+ { name: "issuer", label: "IdP Entity ID", type: "text", required: true, helpText: "The IdP\u2019s SAML EntityID (issuer), e.g. https://saml.acme.com/entityid." },
3455
+ { name: "domain", label: "Email Domain", type: "text", required: true, helpText: "Users with this email domain are routed to this IdP, e.g. acme.com." },
3456
+ { name: "entryPoint", label: "IdP SSO URL", type: "text", required: true, helpText: "The IdP\u2019s SAML single sign-on (redirect) endpoint that receives the SAMLRequest." },
3457
+ { name: "cert", label: "IdP Signing Certificate", type: "textarea", required: true, helpText: "The IdP\u2019s X.509 signing certificate (PEM body). Used to verify assertion signatures." },
3458
+ { name: "identifierFormat", label: "NameID Format", type: "text", required: false, placeholder: "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress", helpText: "Optional. Requested SAML NameID format. Defaults to the IdP\u2019s configured format." }
3459
+ ]
3460
+ },
3461
+ {
3462
+ name: "request_domain_verification",
3463
+ label: "Request Domain Verification",
3464
+ icon: "globe",
3465
+ variant: "secondary",
3466
+ locations: ["list_item", "record_header"],
3467
+ type: "api",
3468
+ method: "POST",
3469
+ // ADR-0024 ② (opt-in OS_SSO_DOMAIN_VERIFICATION). Asks @better-auth/sso
3470
+ // for a one-time DNS-TXT challenge and reveals the ready-to-paste record
3471
+ // ONCE via `resultDialog`. Routed through the env bridge (plugin-auth /
3472
+ // cloud AuthProxyPlugin) which reshapes the `{domainVerificationToken}`
3473
+ // response into the `{ data: { dnsRecordName, dnsRecordValue } }` envelope
3474
+ // the dialog reads. When the feature is OFF the bridge returns a clear
3475
+ // "not enabled for this environment" error instead of a bare 404.
3476
+ target: "/api/v1/auth/admin/sso/request-domain-verification",
3477
+ params: [
3478
+ { name: "providerId", field: "provider_id", defaultFromRow: true, required: true },
3479
+ { name: "domain", field: "domain", defaultFromRow: true, required: false }
3480
+ ],
3481
+ resultDialog: {
3482
+ title: "Verify your domain",
3483
+ description: "Add the DNS TXT record below at your domain\u2019s DNS provider, then run \u201CVerify Domain\u201D. The token is shown once.",
3484
+ acknowledge: "Done",
3485
+ fields: [
3486
+ { path: "data.dnsRecordType", label: "Record type", format: "text" },
3487
+ { path: "data.dnsRecordName", label: "Name / Host", format: "secret" },
3488
+ { path: "data.dnsRecordValue", label: "Value", format: "secret" }
3489
+ ]
3490
+ }
3491
+ },
3492
+ {
3493
+ name: "verify_domain",
3494
+ label: "Verify Domain",
3495
+ icon: "shield-check",
3496
+ variant: "secondary",
3497
+ locations: ["list_item", "record_header"],
3498
+ type: "api",
3499
+ method: "POST",
3500
+ // ADR-0024 ②. Re-checks the DNS-TXT record and flips `domain_verified`
3501
+ // on success. Routed through the env bridge, which maps @better-auth/sso's
3502
+ // empty 204 / 502 into a clear success/error toast.
3503
+ target: "/api/v1/auth/admin/sso/verify-domain",
3504
+ successMessage: "Domain ownership verified",
3505
+ refreshAfter: true,
3506
+ params: [
3507
+ { name: "providerId", field: "provider_id", defaultFromRow: true, required: true }
3332
3508
  ]
3333
3509
  },
3334
3510
  {
@@ -3355,9 +3531,17 @@ var SysSsoProvider = ObjectSchema.create({
3355
3531
  name: "all",
3356
3532
  label: "All",
3357
3533
  data: { provider: "object", object: "sys_sso_provider" },
3358
- columns: ["provider_id", "issuer", "domain", "created_at"],
3534
+ columns: ["provider_id", "issuer", "domain", "domain_verified", "created_at"],
3359
3535
  sort: [{ field: "provider_id", order: "asc" }],
3360
- pagination: { pageSize: 50 }
3536
+ pagination: { pageSize: 50 },
3537
+ // Per-object empty state — the shared identity-object copy ("created
3538
+ // automatically … cannot be added here") is wrong for this object, which
3539
+ // HAS a "Register SSO Provider" action. Point admins at it instead.
3540
+ emptyState: {
3541
+ title: "No SSO providers yet",
3542
+ message: "Register your organization\u2019s external IdP \u2014 OIDC (Okta, Entra, Auth0, \u2026) with \u201CRegister SSO Provider\u201D, or SAML 2.0 with \u201CRegister SAML Provider\u201D. Members whose email domain matches can then sign in through it.",
3543
+ icon: "log-in"
3544
+ }
3361
3545
  }
3362
3546
  },
3363
3547
  fields: {
@@ -3384,6 +3568,13 @@ var SysSsoProvider = ObjectSchema.create({
3384
3568
  description: "Email domain routed to this IdP (e.g. acme.com)",
3385
3569
  group: "Identity"
3386
3570
  }),
3571
+ domain_verified: Field.boolean({
3572
+ label: "Domain Verified",
3573
+ defaultValue: false,
3574
+ readonly: true,
3575
+ description: "Whether DNS ownership of the email domain has been proven (ADR-0024 \u2461). Set by \u201CVerify Domain\u201D after the DNS TXT record resolves. Managed by better-auth \u2014 not directly editable. Only enforced when domain verification is enabled for the environment.",
3576
+ group: "Identity"
3577
+ }),
3387
3578
  oidc_config: Field.textarea({
3388
3579
  label: "OIDC Config",
3389
3580
  required: false,
@@ -3444,6 +3635,8 @@ var SysScimProvider = ObjectSchema.create({
3444
3635
  },
3445
3636
  description: "SCIM 2.0 connections (bearer tokens) external IdPs use to provision/deprovision this environment's users",
3446
3637
  displayNameField: "provider_id",
3638
+ nameField: "provider_id",
3639
+ // [ADR-0079] canonical primary-title pointer (mirrors deprecated displayNameField)
3447
3640
  titleFormat: "{provider_id}",
3448
3641
  compactLayout: ["provider_id", "organization_id"],
3449
3642
  listViews: {
@@ -3518,6 +3711,8 @@ var SysNotification = ObjectSchema.create({
3518
3711
  managedBy: "system",
3519
3712
  description: "Notification events \u2014 one row per emit() (ADR-0030 Layer 2 ingress)",
3520
3713
  displayNameField: "topic",
3714
+ nameField: "topic",
3715
+ // [ADR-0079] canonical primary-title pointer (mirrors deprecated displayNameField)
3521
3716
  titleFormat: "{topic}",
3522
3717
  compactLayout: ["topic", "severity", "source_object", "created_at"],
3523
3718
  listViews: {
@@ -3744,6 +3939,8 @@ var SysEmail = ObjectSchema.create({
3744
3939
  managedBy: "append-only",
3745
3940
  description: "Outbound email delivery log",
3746
3941
  displayNameField: "subject",
3942
+ nameField: "subject",
3943
+ // [ADR-0079] canonical primary-title pointer (mirrors deprecated displayNameField)
3747
3944
  titleFormat: "{subject}",
3748
3945
  compactLayout: ["subject", "to", "status", "sent_at"],
3749
3946
  fields: {
@@ -3892,6 +4089,8 @@ var SysEmailTemplate = ObjectSchema.create({
3892
4089
  managedBy: "config",
3893
4090
  description: "Outbound email template (subject + body + variables) resolved by name+locale",
3894
4091
  displayNameField: "label",
4092
+ nameField: "label",
4093
+ // [ADR-0079] canonical primary-title pointer (mirrors deprecated displayNameField)
3895
4094
  titleFormat: "{label}",
3896
4095
  compactLayout: ["name", "label", "category", "locale", "active"],
3897
4096
  fields: {
@@ -4028,6 +4227,8 @@ var SysSavedReport = ObjectSchema.create({
4028
4227
  managedBy: "platform",
4029
4228
  description: "Persisted ObjectQL report definition \u2014 re-runnable and schedulable",
4030
4229
  displayNameField: "name",
4230
+ nameField: "name",
4231
+ // [ADR-0079] canonical primary-title pointer (mirrors deprecated displayNameField)
4031
4232
  titleFormat: "{name}",
4032
4233
  compactLayout: ["name", "object_name", "format", "owner_id", "updated_at"],
4033
4234
  fields: {
@@ -4246,6 +4447,8 @@ var SysJob = ObjectSchema.create({
4246
4447
  managedBy: "system",
4247
4448
  description: "Catalogue of registered background jobs",
4248
4449
  displayNameField: "name",
4450
+ nameField: "name",
4451
+ // [ADR-0079] canonical primary-title pointer (mirrors deprecated displayNameField)
4249
4452
  titleFormat: "{name}",
4250
4453
  compactLayout: ["name", "schedule_type", "active", "last_run_at", "last_status"],
4251
4454
  fields: {
@@ -4314,6 +4517,8 @@ var SysJobRun = ObjectSchema.create({
4314
4517
  managedBy: "append-only",
4315
4518
  description: "Background job execution audit trail",
4316
4519
  displayNameField: "job_name",
4520
+ nameField: "job_name",
4521
+ // [ADR-0079] canonical primary-title pointer (mirrors deprecated displayNameField)
4317
4522
  titleFormat: "{job_name} @ {started_at}",
4318
4523
  compactLayout: ["job_name", "status", "started_at", "duration_ms", "attempt"],
4319
4524
  fields: {
@@ -4366,6 +4571,8 @@ var SysJobQueue = ObjectSchema.create({
4366
4571
  managedBy: "system",
4367
4572
  description: "Durable job/message queue including dead letters",
4368
4573
  displayNameField: "queue",
4574
+ nameField: "queue",
4575
+ // [ADR-0079] canonical primary-title pointer (mirrors deprecated displayNameField)
4369
4576
  titleFormat: "{queue} #{id}",
4370
4577
  compactLayout: ["queue", "status", "attempts", "scheduled_for", "last_error"],
4371
4578
  fields: {
@@ -4476,6 +4683,8 @@ var SysSetting = ObjectSchema.create({
4476
4683
  managedBy: "system",
4477
4684
  description: "Generic K/V store backing the SettingsManifest contract.",
4478
4685
  displayNameField: "key",
4686
+ nameField: "key",
4687
+ // [ADR-0079] canonical primary-title pointer (mirrors deprecated displayNameField)
4479
4688
  titleFormat: "{namespace}.{key}",
4480
4689
  compactLayout: ["namespace", "key", "scope", "updated_at"],
4481
4690
  listViews: {