From 8a91c554f30a9ba548ef65a816db5f3792504000 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 23 Oct 2026 03:53:38 +0000 Subject: [PATCH 1/4] fix(platform-objects, plugin-security, plugin-approvals, service-messaging, objectql): Setup field help written for administrators; the inert access-review pair hidden Claude-Session: https://claude.ai/code/session_01RsXNmkeex9WY5XsjNwtRYv Co-authored-by: Claude --- .../src/objects/sys-metadata.object.ts | 2 +- .../src/lifecycle/lifecycle-settings.test.ts | 48 +++++++++++++++++++ .../src/lifecycle/lifecycle-settings.ts | 12 ++--- .../src/apps/setup-copy-vocabulary.test.ts | 14 ++++++ .../identity/sys-oauth-access-token.object.ts | 8 ++-- .../sys-oauth-client-resource.object.ts | 4 +- .../src/identity/sys-oauth-consent.object.ts | 4 +- .../sys-oauth-refresh-token.object.ts | 6 +-- .../src/identity/sys-user.object.ts | 18 +++---- .../src/admin-copy-vocabulary.test.ts | 14 ++++++ .../src/sys-approval-delegation.object.ts | 2 +- .../src/objects/admin-copy-vocabulary.test.ts | 25 ++++++++++ .../objects/sys-user-permission-set.object.ts | 20 ++++---- .../src/objects/sys-user-position.object.ts | 16 ++++--- .../src/objects/admin-copy-vocabulary.test.ts | 14 ++++++ .../objects/notification-preference.object.ts | 11 ++++- 16 files changed, 172 insertions(+), 46 deletions(-) create mode 100644 packages/objectql/src/lifecycle/lifecycle-settings.test.ts diff --git a/packages/metadata-core/src/objects/sys-metadata.object.ts b/packages/metadata-core/src/objects/sys-metadata.object.ts index 8fa900fdf5f..65e10436b8b 100644 --- a/packages/metadata-core/src/objects/sys-metadata.object.ts +++ b/packages/metadata-core/src/objects/sys-metadata.object.ts @@ -77,7 +77,7 @@ export const SysMetadataObject = ObjectSchema.create({ label: 'Package Version', required: false, description: - 'Foreign key to sys_package_version (UUID). Null = platform-built-in or env-level override.', + 'The package release this metadata was installed from. Empty for built-in platform metadata and for changes made in this environment.', }), /** Who manages this record: package, platform, or user */ diff --git a/packages/objectql/src/lifecycle/lifecycle-settings.test.ts b/packages/objectql/src/lifecycle/lifecycle-settings.test.ts new file mode 100644 index 00000000000..b70d25916bd --- /dev/null +++ b/packages/objectql/src/lifecycle/lifecycle-settings.test.ts @@ -0,0 +1,48 @@ +// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license. + +/** + * The Data Lifecycle settings page is administrator copy. + * + * Setup renders the manifest's `description` under the page title and each + * specifier's `label` / `description` beside its control. An ADR number, a + * section reference or the name of an internal component tells that reader + * nothing they can act on: the reference belongs in the docblock next to the + * manifest, never in the string. + */ + +import { describe, expect, it } from 'vitest'; +import { lifecycleSettingsManifest } from './lifecycle-settings.js'; + +const DESIGN_RECORD_VOCABULARY: ReadonlyArray = [ + ['an ADR number', /ADR-\d/], + ['a section reference', /§\s?\d/], + ['an internal component', /\bLifecycleService\b|\bReaper\b|\bRotator\b|\bArchiver\b/], +]; + +function vocabularyIn(text: string): string[] { + return DESIGN_RECORD_VOCABULARY.filter(([, re]) => re.test(text)).map(([name]) => name); +} + +type Copy = { label?: string; description?: string; key?: string }; + +describe('lifecycle settings: admin-facing copy carries no design-record vocabulary', () => { + it('the matcher recognises each kind of vocabulary it exists to keep out', () => { + expect(vocabularyIn('Governance for ADR-0057 data lifecycle enforcement.')).toEqual(['an ADR number']); + expect(vocabularyIn('A regulated tenant sets years (§3.2).')).toEqual(['a section reference']); + expect(vocabularyIn('Growth alerts for the LifecycleService (Reaper / Rotator / Archiver).')).toEqual(['an internal component']); + expect(vocabularyIn('How long system data is kept.')).toEqual([]); + }); + + it('the page description and every specifier label and description', () => { + const manifest = lifecycleSettingsManifest as unknown as Copy & { specifiers: Copy[] }; + const strings: Array<[string, string]> = []; + for (const [at, item] of [['lifecycle', manifest] as const, ...manifest.specifiers.map((s) => [`lifecycle.${s.key}`, s] as const)]) { + if (typeof item.label === 'string') strings.push([`${at}.label`, item.label]); + if (typeof item.description === 'string') strings.push([`${at}.description`, item.description]); + } + // Positive control: the walk reaches the page sentence and the retention override help. + expect(strings.map(([at]) => at)).toEqual(expect.arrayContaining(['lifecycle.description', 'lifecycle.retention_overrides.description'])); + const offenders = strings.filter(([, text]) => vocabularyIn(text).length > 0).map(([at, text]) => `${at}: ${text}`); + expect(offenders).toEqual([]); + }); +}); diff --git a/packages/objectql/src/lifecycle/lifecycle-settings.ts b/packages/objectql/src/lifecycle/lifecycle-settings.ts index 7e0142ed772..bf958b2d1ac 100644 --- a/packages/objectql/src/lifecycle/lifecycle-settings.ts +++ b/packages/objectql/src/lifecycle/lifecycle-settings.ts @@ -17,9 +17,9 @@ export const lifecycleSettingsManifest = { label: 'Data Lifecycle', icon: 'Timer', description: - 'Governance for ADR-0057 data lifecycle enforcement: retention window ' + - 'overrides, per-table row quotas, and growth alerts for the platform ' + - 'LifecycleService (Reaper / Rotator / Archiver).', + 'How long system data is kept: override retention windows, cap row counts ' + + 'per table, and get alerted when a table grows unexpectedly. The platform ' + + 'applies these on its periodic cleanup sweep.', scope: 'global', readPermission: 'manage_platform_settings', writePermission: 'manage_platform_settings', @@ -44,9 +44,9 @@ export const lifecycleSettingsManifest = { default: {}, description: 'Per-object window overrides: { "": { "maxAge": "1y", "expireAfter": "30d" } }. ' + - 'Duration literals: h/d/w/y. Tenant-scoped — a regulated tenant sets years while dev keeps days (ADR-0057 §3.2). ' + - 'An override BELOW a retention floor a consumer registered (e.g. the job queue\'s dedup window) is rejected at ' + - 'sweep time and logged at error — the declared window keeps running.', + 'Durations use h/d/w/y. Set per organization, so a regulated organization can keep years while a development one keeps days. ' + + 'An override shorter than the minimum a platform feature needs (for example the job queue\'s duplicate-detection window) ' + + 'is ignored and logged as an error, and the default window keeps applying.', }, { type: 'json', diff --git a/packages/platform-objects/src/apps/setup-copy-vocabulary.test.ts b/packages/platform-objects/src/apps/setup-copy-vocabulary.test.ts index b84198d3e2d..a436bd79022 100644 --- a/packages/platform-objects/src/apps/setup-copy-vocabulary.test.ts +++ b/packages/platform-objects/src/apps/setup-copy-vocabulary.test.ts @@ -61,6 +61,15 @@ const DESIGN_RECORD_VOCABULARY: ReadonlyArray = [ ['a review-finding id', /finding F/], ['an API call', /\bemit\(\)/], ['a runtime internal', /\bkernel\b/i], + // The developer notes a Setup pass met in field help: a column reference, + // a function name, an internal component, a storage-history note and an + // inert-column notice. What the field means and what to do with it is the + // help; where it is stored and which code reads it is the comment. + ['a foreign-key note', /\bforeign key to\b/i], + ['a function name', /\bisGrantActive\b/], + ['an internal component', /\bmiddleware\b|\bbetter-auth is oblivious\b|\bReaper\b/i], + ['a column-history note', /\bNULL only on\b/], + ['an inert-column notice', /\bInert today\b/i], ]; function vocabularyIn(text: string): string[] { @@ -133,6 +142,11 @@ describe('Setup copy carries no design-record vocabulary', () => { expect(vocabularyIn('Bypass rule (finding F2).')).toEqual(['a review-finding id']); expect(vocabularyIn('One row per emit().')).toEqual(['an API call']); expect(vocabularyIn('The Kernel API is additive.')).toEqual(['a runtime internal']); + expect(vocabularyIn('Foreign key to sys_user.id')).toEqual(['a foreign-key note']); + expect(vocabularyIn('Enforced at resolution time via isGrantActive.')).toEqual(['a function name']); + expect(vocabularyIn('Batch cadence (P3 digest middleware).')).toEqual(['an internal component']); + expect(vocabularyIn('NULL only on a grant written before the column existed.')).toEqual(['a column-history note']); + expect(vocabularyIn('Inert today: nothing reads it.')).toEqual(['an inert-column notice']); expect(vocabularyIn('Your company structure, used to scope data and reports.')).toEqual([]); }); diff --git a/packages/platform-objects/src/identity/sys-oauth-access-token.object.ts b/packages/platform-objects/src/identity/sys-oauth-access-token.object.ts index e5ed48767a8..24416665672 100644 --- a/packages/platform-objects/src/identity/sys-oauth-access-token.object.ts +++ b/packages/platform-objects/src/identity/sys-oauth-access-token.object.ts @@ -83,25 +83,25 @@ export const SysOauthAccessToken = ObjectSchema.create({ // unique string — varchar(255) on MySQL). A referencing column takes the // referenced column's bound. maxLength: 255, - description: 'Foreign key to sys_oauth_application.client_id', + description: 'The client ID of the OAuth application this token was issued to.', }), session_id: Field.lookup('sys_session', { label: 'Session', required: false, - description: 'Foreign key to sys_session.id', + description: 'The sign-in session this token was issued in.', }), user_id: Field.lookup('sys_user', { label: 'User', required: false, - description: 'Foreign key to sys_user.id', + description: 'The user this token acts for.', }), refresh_id: Field.lookup('sys_oauth_refresh_token', { label: 'Refresh Token', required: false, - description: 'Foreign key to sys_oauth_refresh_token.id', + description: 'The refresh token this access token was issued from.', }), reference_id: Field.text({ diff --git a/packages/platform-objects/src/identity/sys-oauth-client-resource.object.ts b/packages/platform-objects/src/identity/sys-oauth-client-resource.object.ts index 5feb7377dad..2f4d57988cd 100644 --- a/packages/platform-objects/src/identity/sys-oauth-client-resource.object.ts +++ b/packages/platform-objects/src/identity/sys-oauth-client-resource.object.ts @@ -46,7 +46,7 @@ export const SysOauthClientResource = ObjectSchema.create({ // unique string — varchar(255) on MySQL). A referencing column takes the // referenced column's bound. maxLength: 255, - description: 'Foreign key to sys_oauth_application.client_id', + description: 'The client ID of the OAuth application this grant is for.', }), resource_id: Field.text({ @@ -91,7 +91,7 @@ export const SysOauthClientResource = ObjectSchema.create({ // symmetric: referent and referrer now accept exactly the same domain, // and the silent register-then-never-authorize dead-end is closed. maxLength: 255, - description: 'Foreign key to sys_oauth_resource.identifier', + description: 'The identifier of the protected resource the application may request tokens for.', }), metadata: Field.textarea({ diff --git a/packages/platform-objects/src/identity/sys-oauth-consent.object.ts b/packages/platform-objects/src/identity/sys-oauth-consent.object.ts index f9ff3c1dcf9..21c115a29c4 100644 --- a/packages/platform-objects/src/identity/sys-oauth-consent.object.ts +++ b/packages/platform-objects/src/identity/sys-oauth-consent.object.ts @@ -64,13 +64,13 @@ export const SysOauthConsent = ObjectSchema.create({ // unique string — varchar(255) on MySQL). A referencing column takes the // referenced column's bound. maxLength: 255, - description: 'Foreign key to sys_oauth_application.client_id', + description: 'The client ID of the OAuth application the user consented to.', }), user_id: Field.lookup('sys_user', { label: 'User', required: false, - description: 'Foreign key to sys_user.id', + description: 'The user who gave this consent.', }), reference_id: Field.text({ diff --git a/packages/platform-objects/src/identity/sys-oauth-refresh-token.object.ts b/packages/platform-objects/src/identity/sys-oauth-refresh-token.object.ts index cc6fed93c35..51a3d899002 100644 --- a/packages/platform-objects/src/identity/sys-oauth-refresh-token.object.ts +++ b/packages/platform-objects/src/identity/sys-oauth-refresh-token.object.ts @@ -82,19 +82,19 @@ export const SysOauthRefreshToken = ObjectSchema.create({ // unique string — varchar(255) on MySQL). A referencing column takes the // referenced column's bound. maxLength: 255, - description: 'Foreign key to sys_oauth_application.client_id', + description: 'The client ID of the OAuth application this token was issued to.', }), session_id: Field.lookup('sys_session', { label: 'Session', required: false, - description: 'Foreign key to sys_session.id', + description: 'The sign-in session this token was issued in.', }), user_id: Field.lookup('sys_user', { label: 'User', required: true, - description: 'Foreign key to sys_user.id', + description: 'The user this token acts for.', }), reference_id: Field.text({ diff --git a/packages/platform-objects/src/identity/sys-user.object.ts b/packages/platform-objects/src/identity/sys-user.object.ts index d13bca9b5b3..75ba3273ef9 100644 --- a/packages/platform-objects/src/identity/sys-user.object.ts +++ b/packages/platform-objects/src/identity/sys-user.object.ts @@ -967,13 +967,14 @@ export const SysUser = ObjectSchema.create({ defaultValue: false, readonly: true, // ADR-0092 — a licensed-seat grant; flows through the AiSeatPlugin enforcement path group: 'Admin', + // The framework synthesizes the `ai_seat` capability from this flag + // (plugin-hono-server resolveCtx); the seat cap is enforced by + // @objectstack/security-enterprise AiSeatPlugin. Owned by objectql + // (better-auth is oblivious to this column). description: - 'Whether this user holds an AI seat — grants access to the in-UI AI ' + - 'agents (build / ask). The framework synthesizes the `ai_seat` ' + - 'capability from this flag (plugin-hono-server resolveCtx). Assignment ' + - 'is capped by the licensed / purchased seat count (enforced by ' + - '@objectstack/security-enterprise AiSeatPlugin). Owned by objectql ' + - '(better-auth is oblivious to this column).', + 'Whether this user holds an AI seat, which gives access to the AI ' + + 'assistants in the app (build and ask). How many users can hold one ' + + 'is limited by the seats your license includes.', }), // ── Profile ────────────────────────────────────────────────── @@ -1033,9 +1034,8 @@ export const SysUser = ObjectSchema.create({ maxLength: 35, group: 'Profile', description: - 'Preferred language for notifications, as a BCP-47 tag (e.g. zh-CN, ja-JP). ' + - 'Read per recipient at delivery time; when unset the deployment default applies. ' + - 'Owned by objectql (better-auth is oblivious to this column).', + 'Preferred language for this user\'s notifications, as a language tag such as zh-CN or ja-JP. ' + + 'When unset, the deployment\'s default language applies.', }), // ── Organization ───────────────────────────────────────────── diff --git a/packages/plugins/plugin-approvals/src/admin-copy-vocabulary.test.ts b/packages/plugins/plugin-approvals/src/admin-copy-vocabulary.test.ts index e1afe13877d..842a35432e3 100644 --- a/packages/plugins/plugin-approvals/src/admin-copy-vocabulary.test.ts +++ b/packages/plugins/plugin-approvals/src/admin-copy-vocabulary.test.ts @@ -37,6 +37,15 @@ const DESIGN_RECORD_VOCABULARY: ReadonlyArray = [ ['a review-finding id', /finding F/], ['an API call', /\bemit\(\)/], ['a runtime internal', /\bkernel\b/i], + // The developer notes a Setup pass met in field help: a column reference, + // a function name, an internal component, a storage-history note and an + // inert-column notice. What the field means and what to do with it is the + // help; where it is stored and which code reads it is the comment. + ['a foreign-key note', /\bforeign key to\b/i], + ['a function name', /\bisGrantActive\b/], + ['an internal component', /\bmiddleware\b|\bbetter-auth is oblivious\b|\bReaper\b/i], + ['a column-history note', /\bNULL only on\b/], + ['an inert-column notice', /\bInert today\b/i], ]; function vocabularyIn(text: string): string[] { @@ -93,6 +102,11 @@ describe('approval objects: admin-facing copy carries no design-record vocabular expect(vocabularyIn('Bypass rule (finding F2).')).toEqual(['a review-finding id']); expect(vocabularyIn('One row per emit().')).toEqual(['an API call']); expect(vocabularyIn('Resumed by a rebuilt kernel.')).toEqual(['a runtime internal']); + expect(vocabularyIn('Foreign key to sys_user.id')).toEqual(['a foreign-key note']); + expect(vocabularyIn('Enforced at resolution time via isGrantActive.')).toEqual(['a function name']); + expect(vocabularyIn('Batch cadence (P3 digest middleware).')).toEqual(['an internal component']); + expect(vocabularyIn('NULL only on a grant written before the column existed.')).toEqual(['a column-history note']); + expect(vocabularyIn('Inert today: nothing reads it.')).toEqual(['an inert-column notice']); expect(vocabularyIn('While someone is away, approvals go to their delegate.')).toEqual([]); }); diff --git a/packages/plugins/plugin-approvals/src/sys-approval-delegation.object.ts b/packages/plugins/plugin-approvals/src/sys-approval-delegation.object.ts index fc7b28fbd9d..7c1d42c3692 100644 --- a/packages/plugins/plugin-approvals/src/sys-approval-delegation.object.ts +++ b/packages/plugins/plugin-approvals/src/sys-approval-delegation.object.ts @@ -117,7 +117,7 @@ export const SysApprovalDelegation = ObjectSchema.create({ group: 'Delegation', description: 'Rule is inactive before this instant. Null = active immediately. ' + - 'Enforced at resolution time via isGrantActive — never by a background job.', + 'Checked each time an approval is routed, so the rule starts on time without any scheduled job.', }), valid_until: Field.datetime({ diff --git a/packages/plugins/plugin-security/src/objects/admin-copy-vocabulary.test.ts b/packages/plugins/plugin-security/src/objects/admin-copy-vocabulary.test.ts index d47e1524af4..171170d13cf 100644 --- a/packages/plugins/plugin-security/src/objects/admin-copy-vocabulary.test.ts +++ b/packages/plugins/plugin-security/src/objects/admin-copy-vocabulary.test.ts @@ -39,6 +39,15 @@ const DESIGN_RECORD_VOCABULARY: ReadonlyArray = [ ['a review-finding id', /finding F/], ['an API call', /\bemit\(\)/], ['a runtime internal', /\bkernel\b/i], + // The developer notes a Setup pass met in field help: a column reference, + // a function name, an internal component, a storage-history note and an + // inert-column notice. What the field means and what to do with it is the + // help; where it is stored and which code reads it is the comment. + ['a foreign-key note', /\bforeign key to\b/i], + ['a function name', /\bisGrantActive\b/], + ['an internal component', /\bmiddleware\b|\bbetter-auth is oblivious\b|\bReaper\b/i], + ['a column-history note', /\bNULL only on\b/], + ['an inert-column notice', /\bInert today\b/i], ]; function vocabularyIn(text: string): string[] { @@ -95,6 +104,11 @@ describe('security objects and permission sets: admin-facing copy carries no des expect(vocabularyIn('Bypass rule (finding F2).')).toEqual(['a review-finding id']); expect(vocabularyIn('One row per emit().')).toEqual(['an API call']); expect(vocabularyIn('The Kernel API is additive.')).toEqual(['a runtime internal']); + expect(vocabularyIn('Foreign key to sys_user.id')).toEqual(['a foreign-key note']); + expect(vocabularyIn('Enforced at resolution time via isGrantActive.')).toEqual(['a function name']); + expect(vocabularyIn('Batch cadence (P3 digest middleware).')).toEqual(['an internal component']); + expect(vocabularyIn('NULL only on a grant written before the column existed.')).toEqual(['a column-history note']); + expect(vocabularyIn('Inert today: nothing reads it.')).toEqual(['an inert-column notice']); expect(vocabularyIn('Which positions each user holds, one row per assignment.')).toEqual([]); }); @@ -109,6 +123,17 @@ describe('security objects and permission sets: admin-facing copy carries no des expect(offenders(strings)).toEqual([]); }); + // A field whose help says nothing uses it yet stays off the Setup forms and + // detail pages until something does; the column and its stored values stay. + it.each([ + ['sys_user_permission_set', 'last_certified_at'], ['sys_user_permission_set', 'certified_by'], + ['sys_user_position', 'last_certified_at'], ['sys_user_position', 'certified_by'], + ])('%s.%s, a field nothing reads yet, is hidden from the default UI', (object, field) => { + const def = (Object.values(SecurityObjects) as unknown[]).filter(isObjectSchema).find((o) => o.name === object); + expect(def?.fields[field], `${object}.${field} is declared`).toBeDefined(); + expect((def!.fields[field] as { hidden?: boolean }).hidden).toBe(true); + }); + it('the label and description of every permission set this package ships', () => { const names = defaultPermissionSets.map((ps) => ps.name); for (const name of ['organization_admin', 'organization_admin_no_bypass', 'mcp_agent_data_read', 'mcp_agent_data_write']) { diff --git a/packages/plugins/plugin-security/src/objects/sys-user-permission-set.object.ts b/packages/plugins/plugin-security/src/objects/sys-user-permission-set.object.ts index c16a9940aeb..de72608c5c1 100644 --- a/packages/plugins/plugin-security/src/objects/sys-user-permission-set.object.ts +++ b/packages/plugins/plugin-security/src/objects/sys-user-permission-set.object.ts @@ -81,7 +81,7 @@ export const SysUserPermissionSet = ObjectSchema.create({ user_id: Field.lookup('sys_user', { label: 'User', required: true, - description: 'Foreign key to sys_user.', + description: 'The user who receives this permission set.', }), // [ADR-0131 D4] The permission set this grant holds, BY NAME — the grant's @@ -103,8 +103,8 @@ export const SysUserPermissionSet = ObjectSchema.create({ // a text input where the widget is not registered. widget: 'permission-ref', description: - 'Machine name of the permission set this grant holds, as the security catalog names it. ' + - 'A name the catalog does not hold is refused. NULL only on a grant written before the column existed.', + 'The permission set this assignment gives the user. Pick one of the permission sets defined in Setup; ' + + 'a name that matches no permission set is rejected.', }), organization_id: Field.lookup('sys_organization', { @@ -202,22 +202,24 @@ export const SysUserPermissionSet = ObjectSchema.create({ // has to be told it was wrong, not left to infer it. If D5 is ever // implemented, these two descriptions are what must change with it. // [ADR-0091 D5] The recertification pair. + // [#25331] `hidden` keeps the pair off the Setup forms and detail pages until + // something reads it; the columns and their stored values stay. last_certified_at: Field.datetime({ label: 'Last Certified At', required: false, + hidden: true, description: - 'Reserved for a future access-recertification workflow, which would stamp here when this grant was last attested. ' + - 'Inert today: no platform code writes this column and none reads it — no resolution path, gate or lint consults it, and nothing derives ' + - '"never certified" or "certification stale" from it. Null therefore means the workflow does not exist, not that this grant went unreviewed.', + 'Not in use yet: kept for a future access-review feature. Nothing fills in or checks this date, ' + + 'so an empty value does not mean the assignment went unreviewed.', }), certified_by: Field.lookup('sys_user', { label: 'Certified By', required: false, + hidden: true, description: - 'Reserved for the same future access-recertification workflow: the reviewer who would attest this grant. ' + - 'Inert today: no platform code writes or reads it. A value written here by a client is an unverified annotation — ' + - 'the platform checks nothing about it and grants nothing on the strength of it.', + 'Not in use yet: kept for a future access-review feature. A reviewer entered here is not verified ' + + 'and changes nothing about what the assignment grants.', }), created_at: Field.datetime({ diff --git a/packages/plugins/plugin-security/src/objects/sys-user-position.object.ts b/packages/plugins/plugin-security/src/objects/sys-user-position.object.ts index 321e998858d..f86a671b4b2 100644 --- a/packages/plugins/plugin-security/src/objects/sys-user-position.object.ts +++ b/packages/plugins/plugin-security/src/objects/sys-user-position.object.ts @@ -81,7 +81,7 @@ export const SysUserPosition = ObjectSchema.create({ user_id: Field.lookup('sys_user', { label: 'User', required: true, - description: 'Foreign key to sys_user.', + description: 'The user who holds this position.', }), position: Field.text({ @@ -175,22 +175,24 @@ export const SysUserPosition = ObjectSchema.create({ // workflow built or the columns dropped. Full rationale sits with the // sibling declaration in sys-user-permission-set.object.ts. // [ADR-0091 D5] The recertification pair. + // [#25331] `hidden` keeps the pair off the Setup forms and detail pages until + // something reads it; the columns and their stored values stay. last_certified_at: Field.datetime({ label: 'Last Certified At', required: false, + hidden: true, description: - 'Reserved for a future access-recertification workflow, which would stamp here when this grant was last attested. ' + - 'Inert today: no platform code writes this column and none reads it — no resolution path, gate or lint consults it, and nothing derives ' + - '"never certified" or "certification stale" from it. Null therefore means the workflow does not exist, not that this grant went unreviewed.', + 'Not in use yet: kept for a future access-review feature. Nothing fills in or checks this date, ' + + 'so an empty value does not mean the assignment went unreviewed.', }), certified_by: Field.lookup('sys_user', { label: 'Certified By', required: false, + hidden: true, description: - 'Reserved for the same future access-recertification workflow: the reviewer who would attest this grant. ' + - 'Inert today: no platform code writes or reads it. A value written here by a client is an unverified annotation — ' + - 'the platform checks nothing about it and grants nothing on the strength of it.', + 'Not in use yet: kept for a future access-review feature. A reviewer entered here is not verified ' + + 'and changes nothing about what the assignment grants.', }), created_at: Field.datetime({ diff --git a/packages/services/service-messaging/src/objects/admin-copy-vocabulary.test.ts b/packages/services/service-messaging/src/objects/admin-copy-vocabulary.test.ts index 5e0f6e5012e..a339a703032 100644 --- a/packages/services/service-messaging/src/objects/admin-copy-vocabulary.test.ts +++ b/packages/services/service-messaging/src/objects/admin-copy-vocabulary.test.ts @@ -34,6 +34,15 @@ const DESIGN_RECORD_VOCABULARY: ReadonlyArray = [ ['a review-finding id', /finding F/], ['an API call', /\bemit\(\)/], ['a runtime internal', /\bkernel\b/i], + // The developer notes a Setup pass met in field help: a column reference, + // a function name, an internal component, a storage-history note and an + // inert-column notice. What the field means and what to do with it is the + // help; where it is stored and which code reads it is the comment. + ['a foreign-key note', /\bforeign key to\b/i], + ['a function name', /\bisGrantActive\b/], + ['an internal component', /\bmiddleware\b|\bbetter-auth is oblivious\b|\bReaper\b/i], + ['a column-history note', /\bNULL only on\b/], + ['an inert-column notice', /\bInert today\b/i], ]; function vocabularyIn(text: string): string[] { @@ -90,6 +99,11 @@ describe('messaging objects: admin-facing copy carries no design-record vocabula expect(vocabularyIn('Bypass rule (finding F2).')).toEqual(['a review-finding id']); expect(vocabularyIn('One row per emit().')).toEqual(['an API call']); expect(vocabularyIn('The Kernel API is additive.')).toEqual(['a runtime internal']); + expect(vocabularyIn('Foreign key to sys_user.id')).toEqual(['a foreign-key note']); + expect(vocabularyIn('Enforced at resolution time via isGrantActive.')).toEqual(['a function name']); + expect(vocabularyIn('Batch cadence (P3 digest middleware).')).toEqual(['an internal component']); + expect(vocabularyIn('NULL only on a grant written before the column existed.')).toEqual(['a column-history note']); + expect(vocabularyIn('Inert today: nothing reads it.')).toEqual(['an inert-column notice']); expect(vocabularyIn('Which topics and channels each user receives.')).toEqual([]); }); diff --git a/packages/services/service-messaging/src/objects/notification-preference.object.ts b/packages/services/service-messaging/src/objects/notification-preference.object.ts index f73a77ca0ee..04c3ebfd47c 100644 --- a/packages/services/service-messaging/src/objects/notification-preference.object.ts +++ b/packages/services/service-messaging/src/objects/notification-preference.object.ts @@ -160,13 +160,20 @@ export const NotificationPreference = ObjectSchema.create({ label: 'Digest', required: false, defaultValue: 'none', - description: 'Batch cadence (P3 digest middleware).', + description: + 'Collect these notifications into one summary instead of sending each as it arrives: daily sends it at midnight, ' + + 'weekly on Monday at midnight, none sends each one right away. Critical notifications are always sent at once. ' + + 'Takes effect on a preference whose channel is * (all channels).', }), quiet_hours: Field.json({ label: 'Quiet Hours', required: false, - description: 'Optional { tz, start, end } window (P3 quiet-hours middleware).', + description: + 'A daily window when notifications are held and delivered when it ends, for example ' + + '{ "tz": "Europe/Berlin", "start": "22:00", "end": "07:00" }. Times are HH:MM in that time zone (UTC when tz is left out), ' + + 'and the window may run past midnight. Critical notifications are not held. ' + + 'Takes effect on a preference whose channel is * (all channels).', }), created_at: Field.datetime({ label: 'Created At', readonly: true }), From 3152acce012a76072f30d021b0455fb3709473f5 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 23 Oct 2026 04:01:00 +0000 Subject: [PATCH 2/4] chore(i18n): regenerate the en bundles and translate the rewritten field help in zh-CN, ja-JP and es-ES Claude-Session: https://claude.ai/code/session_01RsXNmkeex9WY5XsjNwtRYv Co-authored-by: Claude --- .../apps/translations/en.objects.generated.ts | 28 +++++++++---------- .../translations/es-ES.objects.generated.ts | 28 +++++++++---------- .../translations/ja-JP.objects.generated.ts | 28 +++++++++---------- .../translations/zh-CN.objects.generated.ts | 28 +++++++++---------- .../src/translations/en.objects.generated.ts | 2 +- .../translations/es-ES.objects.generated.ts | 2 +- .../es-ES.source-hashes.generated.ts | 1 - .../translations/ja-JP.objects.generated.ts | 2 +- .../ja-JP.source-hashes.generated.ts | 1 - .../translations/zh-CN.objects.generated.ts | 2 +- .../src/translations/en.objects.generated.ts | 14 +++++----- .../translations/es-ES.objects.generated.ts | 14 +++++----- .../translations/ja-JP.objects.generated.ts | 14 +++++----- .../translations/zh-CN.objects.generated.ts | 14 +++++----- .../src/translations/en.objects.generated.ts | 4 +-- .../translations/es-ES.objects.generated.ts | 4 +-- .../es-ES.source-hashes.generated.ts | 2 -- .../translations/ja-JP.objects.generated.ts | 4 +-- .../ja-JP.source-hashes.generated.ts | 2 -- .../translations/zh-CN.objects.generated.ts | 4 +-- 20 files changed, 96 insertions(+), 102 deletions(-) diff --git a/packages/platform-objects/src/apps/translations/en.objects.generated.ts b/packages/platform-objects/src/apps/translations/en.objects.generated.ts index 495f45a6171..1d351e2adb8 100644 --- a/packages/platform-objects/src/apps/translations/en.objects.generated.ts +++ b/packages/platform-objects/src/apps/translations/en.objects.generated.ts @@ -86,14 +86,14 @@ export const enObjects: NonNullable = { }, ai_access: { label: "AI Access", - help: "Whether this user holds an AI seat — grants access to the in-UI AI agents (build / ask). The framework synthesizes the `ai_seat` capability from this flag (plugin-hono-server resolveCtx). Assignment is capped by the licensed / purchased seat count (enforced by @objectstack/security-enterprise AiSeatPlugin). Owned by objectql (better-auth is oblivious to this column)." + help: "Whether this user holds an AI seat, which gives access to the AI assistants in the app (build and ask). How many users can hold one is limited by the seats your license includes." }, image: { label: "Profile Image" }, locale: { label: "Locale", - help: "Preferred language for notifications, as a BCP-47 tag (e.g. zh-CN, ja-JP). Read per recipient at delivery time; when unset the deployment default applies. Owned by objectql (better-auth is oblivious to this column)." + help: "Preferred language for this user's notifications, as a language tag such as zh-CN or ja-JP. When unset, the deployment's default language applies." }, manager_id: { label: "Manager", @@ -1582,19 +1582,19 @@ export const enObjects: NonNullable = { }, client_id: { label: "Client ID", - help: "Foreign key to sys_oauth_application.client_id" + help: "The client ID of the OAuth application this token was issued to." }, session_id: { label: "Session", - help: "Foreign key to sys_session.id" + help: "The sign-in session this token was issued in." }, user_id: { label: "User", - help: "Foreign key to sys_user.id" + help: "The user this token acts for." }, refresh_id: { label: "Refresh Token", - help: "Foreign key to sys_oauth_refresh_token.id" + help: "The refresh token this access token was issued from." }, reference_id: { label: "Reference ID", @@ -1646,15 +1646,15 @@ export const enObjects: NonNullable = { }, client_id: { label: "Client ID", - help: "Foreign key to sys_oauth_application.client_id" + help: "The client ID of the OAuth application this token was issued to." }, session_id: { label: "Session", - help: "Foreign key to sys_session.id" + help: "The sign-in session this token was issued in." }, user_id: { label: "User", - help: "Foreign key to sys_user.id" + help: "The user this token acts for." }, reference_id: { label: "Reference ID", @@ -1718,11 +1718,11 @@ export const enObjects: NonNullable = { }, client_id: { label: "Client ID", - help: "Foreign key to sys_oauth_application.client_id" + help: "The client ID of the OAuth application the user consented to." }, user_id: { label: "User", - help: "Foreign key to sys_user.id" + help: "The user who gave this consent." }, reference_id: { label: "Reference ID", @@ -1820,11 +1820,11 @@ export const enObjects: NonNullable = { }, client_id: { label: "Client ID", - help: "Foreign key to sys_oauth_application.client_id" + help: "The client ID of the OAuth application this grant is for." }, resource_id: { label: "Resource ID", - help: "Foreign key to sys_oauth_resource.identifier" + help: "The identifier of the protected resource the application may request tokens for." }, metadata: { label: "Metadata", @@ -3042,7 +3042,7 @@ export const enObjects: NonNullable = { }, package_version_id: { label: "Package Version", - help: "Foreign key to sys_package_version (UUID). Null = platform-built-in or env-level override." + help: "The package release this metadata was installed from. Empty for built-in platform metadata and for changes made in this environment." }, managed_by: { label: "Managed By", diff --git a/packages/platform-objects/src/apps/translations/es-ES.objects.generated.ts b/packages/platform-objects/src/apps/translations/es-ES.objects.generated.ts index fea3218412e..fcfcdc212f0 100644 --- a/packages/platform-objects/src/apps/translations/es-ES.objects.generated.ts +++ b/packages/platform-objects/src/apps/translations/es-ES.objects.generated.ts @@ -86,14 +86,14 @@ export const esESObjects: NonNullable = { }, ai_access: { label: "Acceso a IA", - help: "Indica si este usuario dispone de una plaza de IA: otorga acceso a los agentes de IA integrados en la interfaz (build / ask). El framework sintetiza la capacidad `ai_seat` a partir de este indicador (plugin-hono-server resolveCtx). La asignación está limitada por el número de plazas licenciadas o compradas (aplicado por AiSeatPlugin de @objectstack/security-enterprise). Propiedad de objectql (better-auth ignora esta columna)." + help: "Indica si este usuario dispone de una plaza de IA, que da acceso a los asistentes de IA de la aplicación (build y ask). El número de usuarios que pueden tenerla está limitado por las plazas que incluye su licencia." }, image: { label: "Imagen de perfil" }, locale: { label: "Idioma", - help: "Idioma preferido para las notificaciones, como etiqueta BCP-47 (p. ej. zh-CN, ja-JP). Se lee por destinatario en el momento de la entrega; si no está definido, se aplica el idioma predeterminado del despliegue. Propiedad de objectql (better-auth ignora esta columna)." + help: "Idioma preferido para las notificaciones de este usuario, como una etiqueta de idioma, por ejemplo zh-CN o ja-JP. Si no se define, se aplica el idioma predeterminado del despliegue." }, manager_id: { label: "Gerente", @@ -1582,19 +1582,19 @@ export const esESObjects: NonNullable = { }, client_id: { label: "ID de cliente", - help: "Clave foránea a sys_oauth_application.client_id." + help: "El ID de cliente de la aplicación OAuth a la que se emitió este token." }, session_id: { label: "Sesión", - help: "Clave foránea a sys_session.id." + help: "La sesión de inicio de sesión en la que se emitió este token." }, user_id: { label: "Usuario", - help: "Clave foránea a sys_user.id." + help: "El usuario en cuyo nombre actúa este token." }, refresh_id: { label: "Token de actualización", - help: "Clave foránea a sys_oauth_refresh_token.id." + help: "El token de actualización a partir del cual se emitió este token de acceso." }, reference_id: { label: "ID de referencia", @@ -1646,15 +1646,15 @@ export const esESObjects: NonNullable = { }, client_id: { label: "ID de cliente", - help: "Clave foránea a sys_oauth_application.client_id." + help: "El ID de cliente de la aplicación OAuth a la que se emitió este token." }, session_id: { label: "Sesión", - help: "Clave foránea a sys_session.id." + help: "La sesión de inicio de sesión en la que se emitió este token." }, user_id: { label: "Usuario", - help: "Clave foránea a sys_user.id." + help: "El usuario en cuyo nombre actúa este token." }, reference_id: { label: "ID de referencia", @@ -1718,11 +1718,11 @@ export const esESObjects: NonNullable = { }, client_id: { label: "ID de cliente", - help: "Clave foránea a sys_oauth_application.client_id." + help: "El ID de cliente de la aplicación OAuth a la que el usuario dio su consentimiento." }, user_id: { label: "Usuario", - help: "Clave foránea a sys_user.id." + help: "El usuario que dio este consentimiento." }, reference_id: { label: "ID de referencia", @@ -1820,11 +1820,11 @@ export const esESObjects: NonNullable = { }, client_id: { label: "ID de cliente", - help: "Clave foránea a sys_oauth_application.client_id." + help: "El ID de cliente de la aplicación OAuth a la que corresponde esta concesión." }, resource_id: { label: "ID del recurso", - help: "Clave foránea a sys_oauth_resource.identifier." + help: "El identificador del recurso protegido para el que la aplicación puede solicitar tokens." }, metadata: { label: "Metadatos", @@ -3042,7 +3042,7 @@ export const esESObjects: NonNullable = { }, package_version_id: { label: "Versión del paquete", - help: "Clave foránea a sys_package_version (UUID). Null = incorporado en la plataforma o sobrescritura a nivel de entorno." + help: "La versión del paquete desde la que se instaló estos metadatos. Vacío para los metadatos integrados en la plataforma y para los cambios hechos en este entorno." }, managed_by: { label: "Gestionado por", diff --git a/packages/platform-objects/src/apps/translations/ja-JP.objects.generated.ts b/packages/platform-objects/src/apps/translations/ja-JP.objects.generated.ts index a82570c2582..75fd0ad5df8 100644 --- a/packages/platform-objects/src/apps/translations/ja-JP.objects.generated.ts +++ b/packages/platform-objects/src/apps/translations/ja-JP.objects.generated.ts @@ -86,14 +86,14 @@ export const jaJPObjects: NonNullable = { }, ai_access: { label: "AI アクセス", - help: "このユーザーが AI シートを保有しているかどうか。UI 内 AI エージェント(build / ask)へのアクセスを付与します。フレームワークはこのフラグから `ai_seat` ケイパビリティを合成します(plugin-hono-server resolveCtx)。割り当て数はライセンス/購入済みシート数が上限です(@objectstack/security-enterprise AiSeatPlugin が強制)。objectql が所有します(better-auth はこの列を認識しません)。" + help: "このユーザーが AI シートを保有しているかどうか。保有するとアプリ内の AI アシスタント(build と ask)を利用できます。保有できるユーザー数は、ライセンスに含まれるシート数までです。" }, image: { label: "プロフィール画像" }, locale: { label: "言語", - help: "通知に使用する優先言語(BCP-47 タグ、例: zh-CN、ja-JP)。配信時に受信者ごとに読み取られ、未設定の場合はデプロイのデフォルト言語が適用されます。objectql が所有します(better-auth はこの列を認識しません)。" + help: "このユーザーへの通知に使用する優先言語。zh-CN や ja-JP のような言語タグで指定します。未設定の場合はデプロイのデフォルト言語が適用されます。" }, manager_id: { label: "マネージャー", @@ -1582,19 +1582,19 @@ export const jaJPObjects: NonNullable = { }, client_id: { label: "クライアント ID", - help: "sys_oauth_application.client_id への外部キー" + help: "このトークンの発行先となった OAuth アプリケーションのクライアント ID。" }, session_id: { label: "セッション", - help: "sys_session.id への外部キー" + help: "このトークンが発行されたサインインセッション。" }, user_id: { label: "ユーザー", - help: "sys_user.id への外部キー" + help: "このトークンが代理で操作するユーザー。" }, refresh_id: { label: "リフレッシュトークン", - help: "sys_oauth_refresh_token.id への外部キー" + help: "このアクセストークンの発行元となったリフレッシュトークン。" }, reference_id: { label: "参照 ID", @@ -1646,15 +1646,15 @@ export const jaJPObjects: NonNullable = { }, client_id: { label: "クライアント ID", - help: "sys_oauth_application.client_id への外部キー" + help: "このトークンの発行先となった OAuth アプリケーションのクライアント ID。" }, session_id: { label: "セッション", - help: "sys_session.id への外部キー" + help: "このトークンが発行されたサインインセッション。" }, user_id: { label: "ユーザー", - help: "sys_user.id への外部キー" + help: "このトークンが代理で操作するユーザー。" }, reference_id: { label: "参照 ID", @@ -1718,11 +1718,11 @@ export const jaJPObjects: NonNullable = { }, client_id: { label: "クライアント ID", - help: "sys_oauth_application.client_id への外部キー" + help: "ユーザーが同意した OAuth アプリケーションのクライアント ID。" }, user_id: { label: "ユーザー", - help: "sys_user.id への外部キー" + help: "この同意を行ったユーザー。" }, reference_id: { label: "参照 ID", @@ -1820,11 +1820,11 @@ export const jaJPObjects: NonNullable = { }, client_id: { label: "クライアント ID", - help: "sys_oauth_application.client_id への外部キー" + help: "この許可の対象となる OAuth アプリケーションのクライアント ID。" }, resource_id: { label: "リソース ID", - help: "sys_oauth_resource.identifier への外部キー" + help: "アプリケーションがトークンを要求できる保護リソースの識別子。" }, metadata: { label: "メタデータ", @@ -3042,7 +3042,7 @@ export const jaJPObjects: NonNullable = { }, package_version_id: { label: "パッケージバージョン", - help: "sys_package_version への外部キー(UUID)。null = プラットフォーム組み込みまたは環境レベルの上書き。" + help: "このメタデータのインストール元となったパッケージのリリース。プラットフォーム組み込みのメタデータと、この環境で行った変更では空になります。" }, managed_by: { label: "管理者", diff --git a/packages/platform-objects/src/apps/translations/zh-CN.objects.generated.ts b/packages/platform-objects/src/apps/translations/zh-CN.objects.generated.ts index 82c69565cc9..5bfff19cddf 100644 --- a/packages/platform-objects/src/apps/translations/zh-CN.objects.generated.ts +++ b/packages/platform-objects/src/apps/translations/zh-CN.objects.generated.ts @@ -86,14 +86,14 @@ export const zhCNObjects: NonNullable = { }, ai_access: { label: "AI 访问权限", - help: "该用户是否占用一个 AI 席位——授予界面内 AI 智能体(build / ask)的使用权限。框架会根据此标志合成 `ai_seat` 能力(plugin-hono-server resolveCtx)。可分配数量受许可/已购席位数限制(由 @objectstack/security-enterprise AiSeatPlugin 强制执行)。归 objectql 所有(better-auth 不感知此列)。" + help: "该用户是否占用一个 AI 席位,占用后可使用应用内的 AI 助手(build 和 ask)。可占用席位的用户数受许可证所含席位数限制。" }, image: { label: "头像" }, locale: { label: "语言", - help: "通知使用的首选语言,BCP-47 标签(如 zh-CN、ja-JP)。投递时按收件人读取;未设置时采用部署默认语言。归 objectql 所有(better-auth 不感知此列)。" + help: "该用户接收通知时使用的首选语言,填写语言标签,如 zh-CN 或 ja-JP。未设置时采用部署的默认语言。" }, manager_id: { label: "经理", @@ -1582,19 +1582,19 @@ export const zhCNObjects: NonNullable = { }, client_id: { label: "客户端 ID", - help: "指向 sys_oauth_application.client_id 的外键" + help: "此令牌所签发给的 OAuth 应用的客户端 ID。" }, session_id: { label: "会话", - help: "指向 sys_session.id 的外键" + help: "签发此令牌时所在的登录会话。" }, user_id: { label: "用户", - help: "指向 sys_user.id 的外键" + help: "此令牌代表其执行操作的用户。" }, refresh_id: { label: "刷新令牌", - help: "指向 sys_oauth_refresh_token.id 的外键" + help: "签发此访问令牌所依据的刷新令牌。" }, reference_id: { label: "引用 ID", @@ -1646,15 +1646,15 @@ export const zhCNObjects: NonNullable = { }, client_id: { label: "客户端 ID", - help: "指向 sys_oauth_application.client_id 的外键" + help: "此令牌所签发给的 OAuth 应用的客户端 ID。" }, session_id: { label: "会话", - help: "指向 sys_session.id 的外键" + help: "签发此令牌时所在的登录会话。" }, user_id: { label: "用户", - help: "指向 sys_user.id 的外键" + help: "此令牌代表其执行操作的用户。" }, reference_id: { label: "引用 ID", @@ -1718,11 +1718,11 @@ export const zhCNObjects: NonNullable = { }, client_id: { label: "客户端 ID", - help: "指向 sys_oauth_application.client_id 的外键" + help: "用户已授予同意的 OAuth 应用的客户端 ID。" }, user_id: { label: "用户", - help: "指向 sys_user.id 的外键" + help: "授予此同意的用户。" }, reference_id: { label: "引用 ID", @@ -1820,11 +1820,11 @@ export const zhCNObjects: NonNullable = { }, client_id: { label: "客户端 ID", - help: "指向 sys_oauth_application.client_id 的外键" + help: "此授权所针对的 OAuth 应用的客户端 ID。" }, resource_id: { label: "资源 ID", - help: "指向 sys_oauth_resource.identifier 的外键" + help: "该应用可为其申请令牌的受保护资源的标识符。" }, metadata: { label: "元数据", @@ -3042,7 +3042,7 @@ export const zhCNObjects: NonNullable = { }, package_version_id: { label: "包版本", - help: "指向 sys_package_version(UUID)的外键。Null = 平台内置或环境级覆盖。" + help: "此元数据安装自的软件包版本。平台内置的元数据以及在本环境中所做的更改此项为空。" }, managed_by: { label: "管理方", diff --git a/packages/plugins/plugin-approvals/src/translations/en.objects.generated.ts b/packages/plugins/plugin-approvals/src/translations/en.objects.generated.ts index 5e30c5a8f42..ab7de8b4e72 100644 --- a/packages/plugins/plugin-approvals/src/translations/en.objects.generated.ts +++ b/packages/plugins/plugin-approvals/src/translations/en.objects.generated.ts @@ -362,7 +362,7 @@ export const enObjects: NonNullable = { }, valid_from: { label: "Valid From", - help: "Rule is inactive before this instant. Null = active immediately. Enforced at resolution time via isGrantActive — never by a background job." + help: "Rule is inactive before this instant. Null = active immediately. Checked each time an approval is routed, so the rule starts on time without any scheduled job." }, valid_until: { label: "Valid Until", diff --git a/packages/plugins/plugin-approvals/src/translations/es-ES.objects.generated.ts b/packages/plugins/plugin-approvals/src/translations/es-ES.objects.generated.ts index 60f5ae0efdc..00792b3d56c 100644 --- a/packages/plugins/plugin-approvals/src/translations/es-ES.objects.generated.ts +++ b/packages/plugins/plugin-approvals/src/translations/es-ES.objects.generated.ts @@ -362,7 +362,7 @@ export const esESObjects: NonNullable = { }, valid_from: { label: "Valid From", - help: "Rule is inactive before this instant. Null = active immediately. Enforced at resolution time via isGrantActive — never by a background job." + help: "La regla está inactiva antes de este instante. Vacío = activa de inmediato. Se comprueba cada vez que se enruta una aprobación, por lo que la regla empieza a tiempo sin ninguna tarea programada." }, valid_until: { label: "Valid Until", diff --git a/packages/plugins/plugin-approvals/src/translations/es-ES.source-hashes.generated.ts b/packages/plugins/plugin-approvals/src/translations/es-ES.source-hashes.generated.ts index ebb9da253b0..37e6b847bc4 100644 --- a/packages/plugins/plugin-approvals/src/translations/es-ES.source-hashes.generated.ts +++ b/packages/plugins/plugin-approvals/src/translations/es-ES.source-hashes.generated.ts @@ -36,7 +36,6 @@ export const esESGeneratedSourceHashes: Readonly> = { "objects.sys_approval_delegation.fields.reason.help": "712516f1461dff2b", "objects.sys_approval_delegation.fields.reason.label": "ac399f03d7f2e64d", "objects.sys_approval_delegation.fields.updated_at.label": "aba63dc2a9c79b8d", - "objects.sys_approval_delegation.fields.valid_from.help": "20b8f73a1c3976d6", "objects.sys_approval_delegation.fields.valid_from.label": "02eaf201e57c9eda", "objects.sys_approval_delegation.fields.valid_until.help": "a48ea34e65933e9d", "objects.sys_approval_delegation.fields.valid_until.label": "a68db5bd4fc72a13", diff --git a/packages/plugins/plugin-approvals/src/translations/ja-JP.objects.generated.ts b/packages/plugins/plugin-approvals/src/translations/ja-JP.objects.generated.ts index 65382fdc0e5..02141b6bbc6 100644 --- a/packages/plugins/plugin-approvals/src/translations/ja-JP.objects.generated.ts +++ b/packages/plugins/plugin-approvals/src/translations/ja-JP.objects.generated.ts @@ -362,7 +362,7 @@ export const jaJPObjects: NonNullable = { }, valid_from: { label: "Valid From", - help: "Rule is inactive before this instant. Null = active immediately. Enforced at resolution time via isGrantActive — never by a background job." + help: "この時点より前はルールが無効です。空欄 = 直ちに有効。承認がルーティングされるたびに確認されるため、スケジュールされたジョブなしで予定どおりに有効になります。" }, valid_until: { label: "Valid Until", diff --git a/packages/plugins/plugin-approvals/src/translations/ja-JP.source-hashes.generated.ts b/packages/plugins/plugin-approvals/src/translations/ja-JP.source-hashes.generated.ts index 782f325ca99..16edecc272a 100644 --- a/packages/plugins/plugin-approvals/src/translations/ja-JP.source-hashes.generated.ts +++ b/packages/plugins/plugin-approvals/src/translations/ja-JP.source-hashes.generated.ts @@ -35,7 +35,6 @@ export const jaJPGeneratedSourceHashes: Readonly> = { "objects.sys_approval_delegation.fields.reason.help": "712516f1461dff2b", "objects.sys_approval_delegation.fields.reason.label": "ac399f03d7f2e64d", "objects.sys_approval_delegation.fields.updated_at.label": "aba63dc2a9c79b8d", - "objects.sys_approval_delegation.fields.valid_from.help": "20b8f73a1c3976d6", "objects.sys_approval_delegation.fields.valid_from.label": "02eaf201e57c9eda", "objects.sys_approval_delegation.fields.valid_until.help": "a48ea34e65933e9d", "objects.sys_approval_delegation.fields.valid_until.label": "a68db5bd4fc72a13", diff --git a/packages/plugins/plugin-approvals/src/translations/zh-CN.objects.generated.ts b/packages/plugins/plugin-approvals/src/translations/zh-CN.objects.generated.ts index 7203f97cbb5..2668a86fa1d 100644 --- a/packages/plugins/plugin-approvals/src/translations/zh-CN.objects.generated.ts +++ b/packages/plugins/plugin-approvals/src/translations/zh-CN.objects.generated.ts @@ -362,7 +362,7 @@ export const zhCNObjects: NonNullable = { }, valid_from: { label: "生效时间", - help: "此刻之前规则不生效。留空 = 立即生效。在解析时通过 isGrantActive 强制——绝不依赖后台任务。" + help: "此刻之前规则不生效。留空 = 立即生效。每次路由审批时都会检查,因此规则会按时生效,无需任何定时任务。" }, valid_until: { label: "失效时间", diff --git a/packages/plugins/plugin-security/src/translations/en.objects.generated.ts b/packages/plugins/plugin-security/src/translations/en.objects.generated.ts index 81a27f41254..68c175dc1b6 100644 --- a/packages/plugins/plugin-security/src/translations/en.objects.generated.ts +++ b/packages/plugins/plugin-security/src/translations/en.objects.generated.ts @@ -30,11 +30,11 @@ export const enObjects: NonNullable = { }, user_id: { label: "User", - help: "Foreign key to sys_user." + help: "The user who receives this permission set." }, permission_set: { label: "Permission Set", - help: "Machine name of the permission set this grant holds, as the security catalog names it. A name the catalog does not hold is refused. NULL only on a grant written before the column existed." + help: "The permission set this assignment gives the user. Pick one of the permission sets defined in Setup; a name that matches no permission set is rejected." }, organization_id: { label: "Organization", @@ -58,11 +58,11 @@ export const enObjects: NonNullable = { }, last_certified_at: { label: "Last Certified At", - help: "Reserved for a future access-recertification workflow, which would stamp here when this grant was last attested. Inert today: no platform code writes this column and none reads it — no resolution path, gate or lint consults it, and nothing derives \"never certified\" or \"certification stale\" from it. Null therefore means the workflow does not exist, not that this grant went unreviewed." + help: "Not in use yet: kept for a future access-review feature. Nothing fills in or checks this date, so an empty value does not mean the assignment went unreviewed." }, certified_by: { label: "Certified By", - help: "Reserved for the same future access-recertification workflow: the reviewer who would attest this grant. Inert today: no platform code writes or reads it. A value written here by a client is an unverified annotation — the platform checks nothing about it and grants nothing on the strength of it." + help: "Not in use yet: kept for a future access-review feature. A reviewer entered here is not verified and changes nothing about what the assignment grants." }, created_at: { label: "Created At" @@ -92,7 +92,7 @@ export const enObjects: NonNullable = { }, user_id: { label: "User", - help: "Foreign key to sys_user." + help: "The user who holds this position." }, position: { label: "Position", @@ -128,11 +128,11 @@ export const enObjects: NonNullable = { }, last_certified_at: { label: "Last Certified At", - help: "Reserved for a future access-recertification workflow, which would stamp here when this grant was last attested. Inert today: no platform code writes this column and none reads it — no resolution path, gate or lint consults it, and nothing derives \"never certified\" or \"certification stale\" from it. Null therefore means the workflow does not exist, not that this grant went unreviewed." + help: "Not in use yet: kept for a future access-review feature. Nothing fills in or checks this date, so an empty value does not mean the assignment went unreviewed." }, certified_by: { label: "Certified By", - help: "Reserved for the same future access-recertification workflow: the reviewer who would attest this grant. Inert today: no platform code writes or reads it. A value written here by a client is an unverified annotation — the platform checks nothing about it and grants nothing on the strength of it." + help: "Not in use yet: kept for a future access-review feature. A reviewer entered here is not verified and changes nothing about what the assignment grants." }, created_at: { label: "Created At" diff --git a/packages/plugins/plugin-security/src/translations/es-ES.objects.generated.ts b/packages/plugins/plugin-security/src/translations/es-ES.objects.generated.ts index 5a90466ed39..ae3d98e37ca 100644 --- a/packages/plugins/plugin-security/src/translations/es-ES.objects.generated.ts +++ b/packages/plugins/plugin-security/src/translations/es-ES.objects.generated.ts @@ -30,11 +30,11 @@ export const esESObjects: NonNullable = { }, user_id: { label: "Usuario", - help: "Clave foránea a sys_user." + help: "El usuario que recibe este conjunto de permisos." }, permission_set: { label: "Conjunto de permisos", - help: "Nombre de máquina del conjunto de permisos que otorga esta asignación, tal como lo nombra el catálogo de seguridad. Un nombre que el catálogo no contiene se rechaza. NULL solo en una asignación escrita antes de que existiera la columna." + help: "El conjunto de permisos que esta asignación da al usuario. Elija uno de los conjuntos de permisos definidos en Configuración; un nombre que no coincide con ningún conjunto de permisos se rechaza." }, organization_id: { label: "Organización", @@ -58,11 +58,11 @@ export const esESObjects: NonNullable = { }, last_certified_at: { label: "Última certificación", - help: "Reservada para un futuro flujo de recertificación de accesos, que registraría aquí cuándo se atestiguó por última vez esta concesión. Hoy es inerte: ningún código de la plataforma escribe esta columna ni la lee; ninguna ruta de resolución, control ni regla de lint la consulta, y nada deriva de ella «nunca certificada» ni «certificación obsoleta». Por tanto, un valor nulo significa que el flujo no existe, no que esta concesión no se haya revisado." + help: "Aún no se usa: se reserva para una futura función de revisión de accesos. Nada rellena ni comprueba esta fecha, así que un valor vacío no significa que la asignación no se haya revisado." }, certified_by: { label: "Certificado por", - help: "Reservada para ese mismo futuro flujo de recertificación de accesos: el revisor que atestiguaría esta concesión. Hoy es inerte: ningún código de la plataforma la escribe ni la lee. Un valor escrito aquí por un cliente es una anotación sin verificar; la plataforma no comprueba nada sobre él ni concede nada en función de él." + help: "Aún no se usa: se reserva para una futura función de revisión de accesos. Un revisor indicado aquí no se verifica y no cambia nada de lo que concede la asignación." }, created_at: { label: "Creado el" @@ -92,7 +92,7 @@ export const esESObjects: NonNullable = { }, user_id: { label: "Usuario", - help: "Clave foránea a sys_user." + help: "El usuario que ocupa este puesto." }, position: { label: "Puesto", @@ -128,11 +128,11 @@ export const esESObjects: NonNullable = { }, last_certified_at: { label: "Última certificación", - help: "Reservada para un futuro flujo de recertificación de accesos, que registraría aquí cuándo se atestiguó por última vez esta concesión. Hoy es inerte: ningún código de la plataforma escribe esta columna ni la lee; ninguna ruta de resolución, control ni regla de lint la consulta, y nada deriva de ella «nunca certificada» ni «certificación obsoleta». Por tanto, un valor nulo significa que el flujo no existe, no que esta concesión no se haya revisado." + help: "Aún no se usa: se reserva para una futura función de revisión de accesos. Nada rellena ni comprueba esta fecha, así que un valor vacío no significa que la asignación no se haya revisado." }, certified_by: { label: "Certificado por", - help: "Reservada para ese mismo futuro flujo de recertificación de accesos: el revisor que atestiguaría esta concesión. Hoy es inerte: ningún código de la plataforma la escribe ni la lee. Un valor escrito aquí por un cliente es una anotación sin verificar; la plataforma no comprueba nada sobre él ni concede nada en función de él." + help: "Aún no se usa: se reserva para una futura función de revisión de accesos. Un revisor indicado aquí no se verifica y no cambia nada de lo que concede la asignación." }, created_at: { label: "Creado el" diff --git a/packages/plugins/plugin-security/src/translations/ja-JP.objects.generated.ts b/packages/plugins/plugin-security/src/translations/ja-JP.objects.generated.ts index 2152d45e631..87942f9cff7 100644 --- a/packages/plugins/plugin-security/src/translations/ja-JP.objects.generated.ts +++ b/packages/plugins/plugin-security/src/translations/ja-JP.objects.generated.ts @@ -30,11 +30,11 @@ export const jaJPObjects: NonNullable = { }, user_id: { label: "ユーザー", - help: "sys_user への外部キー。" + help: "この権限セットを付与されるユーザー。" }, permission_set: { label: "権限セット", - help: "この付与が保持する権限セットのマシン名(セキュリティカタログでの名前)。カタログにない名前は拒否されます。この列が存在する前に書き込まれた付与でのみ NULL です。" + help: "この割り当てによってユーザーに付与される権限セット。設定で定義されている権限セットから選択してください。どの権限セットにも一致しない名前は拒否されます。" }, organization_id: { label: "組織", @@ -58,11 +58,11 @@ export const jaJPObjects: NonNullable = { }, last_certified_at: { label: "最終認証日時", - help: "将来のアクセス再認証ワークフロー用に予約された列で、その際にこの付与が最後に証明された日時を記録します。現時点では不活性です。プラットフォームはこの列に書き込まず、読み取るコードもありません。解決経路もゲートも lint も参照せず、「未認証」や「認証が古い」を導出する箇所もありません。したがって Null はワークフローが存在しないことを意味するだけで、この付与が未レビューであることを意味しません。" + help: "まだ使用されていません。将来のアクセスレビュー機能のために確保されています。この日付を入力または確認する機能はないため、空欄でもこの割り当てがレビューされていないことを意味しません。" }, certified_by: { label: "認証者", - help: "同じく将来のアクセス再認証ワークフロー用に予約された列で、その際にこの付与を証明したレビュアーを記録します。現時点では不活性で、プラットフォームは書き込みも読み取りも行いません。クライアントがここに書き込んだ値は検証されない注記にすぎず、プラットフォームはそれを検証せず、それを根拠に権限を与えることもありません。" + help: "まだ使用されていません。将来のアクセスレビュー機能のために確保されています。ここに入力したレビュアーは検証されず、この割り当てが付与する内容は何も変わりません。" }, created_at: { label: "作成日時" @@ -92,7 +92,7 @@ export const jaJPObjects: NonNullable = { }, user_id: { label: "ユーザー", - help: "sys_user への外部キー。" + help: "このポジションを持つユーザー。" }, position: { label: "ポジション", @@ -128,11 +128,11 @@ export const jaJPObjects: NonNullable = { }, last_certified_at: { label: "最終認証日時", - help: "将来のアクセス再認証ワークフロー用に予約された列で、その際にこの付与が最後に証明された日時を記録します。現時点では不活性です。プラットフォームはこの列に書き込まず、読み取るコードもありません。解決経路もゲートも lint も参照せず、「未認証」や「認証が古い」を導出する箇所もありません。したがって Null はワークフローが存在しないことを意味するだけで、この付与が未レビューであることを意味しません。" + help: "まだ使用されていません。将来のアクセスレビュー機能のために確保されています。この日付を入力または確認する機能はないため、空欄でもこの割り当てがレビューされていないことを意味しません。" }, certified_by: { label: "認証者", - help: "同じく将来のアクセス再認証ワークフロー用に予約された列で、その際にこの付与を証明したレビュアーを記録します。現時点では不活性で、プラットフォームは書き込みも読み取りも行いません。クライアントがここに書き込んだ値は検証されない注記にすぎず、プラットフォームはそれを検証せず、それを根拠に権限を与えることもありません。" + help: "まだ使用されていません。将来のアクセスレビュー機能のために確保されています。ここに入力したレビュアーは検証されず、この割り当てが付与する内容は何も変わりません。" }, created_at: { label: "作成日時" diff --git a/packages/plugins/plugin-security/src/translations/zh-CN.objects.generated.ts b/packages/plugins/plugin-security/src/translations/zh-CN.objects.generated.ts index 4b78f73302a..52bc588b197 100644 --- a/packages/plugins/plugin-security/src/translations/zh-CN.objects.generated.ts +++ b/packages/plugins/plugin-security/src/translations/zh-CN.objects.generated.ts @@ -30,11 +30,11 @@ export const zhCNObjects: NonNullable = { }, user_id: { label: "用户", - help: "指向 sys_user 的外键。" + help: "获得此权限集的用户。" }, permission_set: { label: "权限集", - help: "此授权所持有的权限集的机器名,以安全目录中的名称为准。安全目录中不存在的名称将被拒绝。仅在此列存在之前写入的授权中为 NULL。" + help: "此分配授予用户的权限集。请从设置中已定义的权限集中选择;不匹配任何权限集的名称将被拒绝。" }, organization_id: { label: "组织", @@ -58,11 +58,11 @@ export const zhCNObjects: NonNullable = { }, last_certified_at: { label: "最近认证时间", - help: "为未来的访问权限重新认证(定期复核)流程预留:届时用于记录此授予最近一次被认证的时间。当前该列是惰性的:平台不会写入它,也没有任何代码读取它——没有任何解析路径、门禁或 lint 会参考它,也没有任何地方据此推导出「从未认证」或「认证已过期」。因此为空只表示该流程尚不存在,并不表示此授予未经复核。" + help: "尚未启用:为将来的访问复核功能预留。目前没有任何功能会填写或检查此日期,因此为空并不表示该分配未经复核。" }, certified_by: { label: "认证人", - help: "同样为未来的访问权限重新认证流程预留:届时用于记录认证此授予的复核人。当前该列是惰性的:平台既不写入也不读取它。客户端在此写入的值只是一条未经验证的备注——平台不会对它做任何校验,也不会据此授予任何权限。" + help: "尚未启用:为将来的访问复核功能预留。在此填写的复核人不会被验证,也不会改变该分配授予的任何权限。" }, created_at: { label: "创建时间" @@ -92,7 +92,7 @@ export const zhCNObjects: NonNullable = { }, user_id: { label: "用户", - help: "指向 sys_user 的外键。" + help: "担任此岗位的用户。" }, position: { label: "岗位", @@ -128,11 +128,11 @@ export const zhCNObjects: NonNullable = { }, last_certified_at: { label: "最近认证时间", - help: "为未来的访问权限重新认证(定期复核)流程预留:届时用于记录此授予最近一次被认证的时间。当前该列是惰性的:平台不会写入它,也没有任何代码读取它——没有任何解析路径、门禁或 lint 会参考它,也没有任何地方据此推导出「从未认证」或「认证已过期」。因此为空只表示该流程尚不存在,并不表示此授予未经复核。" + help: "尚未启用:为将来的访问复核功能预留。目前没有任何功能会填写或检查此日期,因此为空并不表示该分配未经复核。" }, certified_by: { label: "认证人", - help: "同样为未来的访问权限重新认证流程预留:届时用于记录认证此授予的复核人。当前该列是惰性的:平台既不写入也不读取它。客户端在此写入的值只是一条未经验证的备注——平台不会对它做任何校验,也不会据此授予任何权限。" + help: "尚未启用:为将来的访问复核功能预留。在此填写的复核人不会被验证,也不会改变该分配授予的任何权限。" }, created_at: { label: "创建时间" diff --git a/packages/services/service-messaging/src/translations/en.objects.generated.ts b/packages/services/service-messaging/src/translations/en.objects.generated.ts index a75e6fa4a48..ce9b8d4e9ca 100644 --- a/packages/services/service-messaging/src/translations/en.objects.generated.ts +++ b/packages/services/service-messaging/src/translations/en.objects.generated.ts @@ -222,7 +222,7 @@ export const enObjects: NonNullable = { }, digest: { label: "Digest", - help: "Batch cadence (P3 digest middleware).", + help: "Collect these notifications into one summary instead of sending each as it arrives: daily sends it at midnight, weekly on Monday at midnight, none sends each one right away. Critical notifications are always sent at once. Takes effect on a preference whose channel is * (all channels).", options: { none: "none", daily: "daily", @@ -231,7 +231,7 @@ export const enObjects: NonNullable = { }, quiet_hours: { label: "Quiet Hours", - help: "Optional { tz, start, end } window (P3 quiet-hours middleware)." + help: "A daily window when notifications are held and delivered when it ends, for example { \"tz\": \"Europe/Berlin\", \"start\": \"22:00\", \"end\": \"07:00\" }. Times are HH:MM in that time zone (UTC when tz is left out), and the window may run past midnight. Critical notifications are not held. Takes effect on a preference whose channel is * (all channels)." }, created_at: { label: "Created At" diff --git a/packages/services/service-messaging/src/translations/es-ES.objects.generated.ts b/packages/services/service-messaging/src/translations/es-ES.objects.generated.ts index fc909325bab..ccfb7c95fe9 100644 --- a/packages/services/service-messaging/src/translations/es-ES.objects.generated.ts +++ b/packages/services/service-messaging/src/translations/es-ES.objects.generated.ts @@ -222,7 +222,7 @@ export const esESObjects: NonNullable = { }, digest: { label: "Digest", - help: "Batch cadence (P3 digest middleware).", + help: "Agrupa estas notificaciones en un único resumen en lugar de enviar cada una al llegar: daily lo envía a medianoche, weekly el lunes a medianoche y none envía cada una de inmediato. Las notificaciones críticas siempre se envían al momento. Se aplica en una preferencia cuyo canal es * (todos los canales).", options: { none: "none", daily: "daily", @@ -231,7 +231,7 @@ export const esESObjects: NonNullable = { }, quiet_hours: { label: "Quiet Hours", - help: "Optional { tz, start, end } window (P3 quiet-hours middleware)." + help: "Una franja diaria en la que las notificaciones se retienen y se entregan al terminar, por ejemplo { \"tz\": \"Europe/Berlin\", \"start\": \"22:00\", \"end\": \"07:00\" }. Las horas son HH:MM en esa zona horaria (UTC si se omite tz) y la franja puede pasar de medianoche. Las notificaciones críticas no se retienen. Se aplica en una preferencia cuyo canal es * (todos los canales)." }, created_at: { label: "Created At" diff --git a/packages/services/service-messaging/src/translations/es-ES.source-hashes.generated.ts b/packages/services/service-messaging/src/translations/es-ES.source-hashes.generated.ts index 9f7ec205b7a..44542a05943 100644 --- a/packages/services/service-messaging/src/translations/es-ES.source-hashes.generated.ts +++ b/packages/services/service-messaging/src/translations/es-ES.source-hashes.generated.ts @@ -98,7 +98,6 @@ export const esESGeneratedSourceHashes: Readonly> = { "objects.sys_notification_preference.fields.channel.help": "8c06b7c3f1a65fd7", "objects.sys_notification_preference.fields.channel.label": "979883fe73ee88cb", "objects.sys_notification_preference.fields.created_at.label": "1f02d416befb595b", - "objects.sys_notification_preference.fields.digest.help": "86c9da5370d5c2e3", "objects.sys_notification_preference.fields.digest.label": "76e9b35f27134f0b", "objects.sys_notification_preference.fields.digest.options.daily": "1dd810d2b5a02102", "objects.sys_notification_preference.fields.digest.options.none": "f0ac498bc08c6383", @@ -107,7 +106,6 @@ export const esESGeneratedSourceHashes: Readonly> = { "objects.sys_notification_preference.fields.enabled.help": "94e2727fd8c18296", "objects.sys_notification_preference.fields.enabled.label": "41fb04f20e64dc4f", "objects.sys_notification_preference.fields.id.label": "429cdf8675ce2833", - "objects.sys_notification_preference.fields.quiet_hours.help": "588a4adb4b5013e7", "objects.sys_notification_preference.fields.quiet_hours.label": "fab43f24806d8f6f", "objects.sys_notification_preference.fields.topic.help": "cf04f6b5ad2a5a33", "objects.sys_notification_preference.fields.topic.label": "819afdb3853e9d80", diff --git a/packages/services/service-messaging/src/translations/ja-JP.objects.generated.ts b/packages/services/service-messaging/src/translations/ja-JP.objects.generated.ts index 3b21fdd33c6..df8724f2590 100644 --- a/packages/services/service-messaging/src/translations/ja-JP.objects.generated.ts +++ b/packages/services/service-messaging/src/translations/ja-JP.objects.generated.ts @@ -222,7 +222,7 @@ export const jaJPObjects: NonNullable = { }, digest: { label: "Digest", - help: "Batch cadence (P3 digest middleware).", + help: "これらの通知を 1 件ずつ送らず、1 通のまとめにします。daily は深夜 0 時、weekly は月曜の深夜 0 時に送信し、none は 1 件ずつすぐに送信します。緊急の通知は常にすぐに送信されます。チャネルが *(すべてのチャネル)の設定でのみ有効です。", options: { none: "none", daily: "daily", @@ -231,7 +231,7 @@ export const jaJPObjects: NonNullable = { }, quiet_hours: { label: "Quiet Hours", - help: "Optional { tz, start, end } window (P3 quiet-hours middleware)." + help: "通知を保留し、終了時にまとめて配信する毎日の時間帯。例: { \"tz\": \"Europe/Berlin\", \"start\": \"22:00\", \"end\": \"07:00\" }。時刻はそのタイムゾーンの HH:MM で指定します(tz を省略した場合は UTC)。時間帯は深夜 0 時をまたいでもかまいません。緊急の通知は保留されません。チャネルが *(すべてのチャネル)の設定でのみ有効です。" }, created_at: { label: "Created At" diff --git a/packages/services/service-messaging/src/translations/ja-JP.source-hashes.generated.ts b/packages/services/service-messaging/src/translations/ja-JP.source-hashes.generated.ts index 9addd38cb8f..1e4ae0fdfd4 100644 --- a/packages/services/service-messaging/src/translations/ja-JP.source-hashes.generated.ts +++ b/packages/services/service-messaging/src/translations/ja-JP.source-hashes.generated.ts @@ -97,7 +97,6 @@ export const jaJPGeneratedSourceHashes: Readonly> = { "objects.sys_notification_preference.fields.channel.help": "8c06b7c3f1a65fd7", "objects.sys_notification_preference.fields.channel.label": "979883fe73ee88cb", "objects.sys_notification_preference.fields.created_at.label": "1f02d416befb595b", - "objects.sys_notification_preference.fields.digest.help": "86c9da5370d5c2e3", "objects.sys_notification_preference.fields.digest.label": "76e9b35f27134f0b", "objects.sys_notification_preference.fields.digest.options.daily": "1dd810d2b5a02102", "objects.sys_notification_preference.fields.digest.options.none": "f0ac498bc08c6383", @@ -106,7 +105,6 @@ export const jaJPGeneratedSourceHashes: Readonly> = { "objects.sys_notification_preference.fields.enabled.help": "94e2727fd8c18296", "objects.sys_notification_preference.fields.enabled.label": "41fb04f20e64dc4f", "objects.sys_notification_preference.fields.id.label": "429cdf8675ce2833", - "objects.sys_notification_preference.fields.quiet_hours.help": "588a4adb4b5013e7", "objects.sys_notification_preference.fields.quiet_hours.label": "fab43f24806d8f6f", "objects.sys_notification_preference.fields.topic.help": "cf04f6b5ad2a5a33", "objects.sys_notification_preference.fields.topic.label": "819afdb3853e9d80", diff --git a/packages/services/service-messaging/src/translations/zh-CN.objects.generated.ts b/packages/services/service-messaging/src/translations/zh-CN.objects.generated.ts index 0c275394d03..84c006a09f7 100644 --- a/packages/services/service-messaging/src/translations/zh-CN.objects.generated.ts +++ b/packages/services/service-messaging/src/translations/zh-CN.objects.generated.ts @@ -222,7 +222,7 @@ export const zhCNObjects: NonNullable = { }, digest: { label: "摘要", - help: "批量发送节奏(P3 摘要中间件)。", + help: "将这些通知合并为一份摘要,而不是逐条发送:daily 在午夜发送,weekly 在周一午夜发送,none 则每条立即发送。紧急通知始终立即发送。仅在渠道为 *(所有渠道)的偏好设置上生效。", options: { none: "不摘要", daily: "每日", @@ -231,7 +231,7 @@ export const zhCNObjects: NonNullable = { }, quiet_hours: { label: "免打扰时段", - help: "可选的 { tz, start, end } 时间窗(P3 免打扰中间件)。" + help: "每天的免打扰时段,期间的通知会暂缓,并在时段结束时送达,例如 { \"tz\": \"Europe/Berlin\", \"start\": \"22:00\", \"end\": \"07:00\" }。时间为该时区的 HH:MM(未填 tz 时为 UTC),时段可以跨越午夜。紧急通知不会暂缓。仅在渠道为 *(所有渠道)的偏好设置上生效。" }, created_at: { label: "创建时间" From d798d39ce2d4f814bc2659ff76ed9cdd72de9474 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 23 Oct 2026 04:05:56 +0000 Subject: [PATCH 3/4] fix(platform-objects): keep the ai_seat capability in the AI Access help, the evidence the capability wording is derived from Claude-Session: https://claude.ai/code/session_01RsXNmkeex9WY5XsjNwtRYv Co-authored-by: Claude --- .../src/apps/translations/en.objects.generated.ts | 2 +- .../src/apps/translations/es-ES.objects.generated.ts | 2 +- .../src/apps/translations/ja-JP.objects.generated.ts | 2 +- .../src/apps/translations/zh-CN.objects.generated.ts | 2 +- packages/platform-objects/src/identity/sys-user.object.ts | 6 +++--- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/platform-objects/src/apps/translations/en.objects.generated.ts b/packages/platform-objects/src/apps/translations/en.objects.generated.ts index 1d351e2adb8..92a36b3219a 100644 --- a/packages/platform-objects/src/apps/translations/en.objects.generated.ts +++ b/packages/platform-objects/src/apps/translations/en.objects.generated.ts @@ -86,7 +86,7 @@ export const enObjects: NonNullable = { }, ai_access: { label: "AI Access", - help: "Whether this user holds an AI seat, which gives access to the AI assistants in the app (build and ask). How many users can hold one is limited by the seats your license includes." + help: "Whether this user holds an AI seat. A seat grants the `ai_seat` capability, which opens the AI assistants in the app (build and ask). How many users can hold one is limited by the seats your license includes." }, image: { label: "Profile Image" diff --git a/packages/platform-objects/src/apps/translations/es-ES.objects.generated.ts b/packages/platform-objects/src/apps/translations/es-ES.objects.generated.ts index fcfcdc212f0..b35f8836ce5 100644 --- a/packages/platform-objects/src/apps/translations/es-ES.objects.generated.ts +++ b/packages/platform-objects/src/apps/translations/es-ES.objects.generated.ts @@ -86,7 +86,7 @@ export const esESObjects: NonNullable = { }, ai_access: { label: "Acceso a IA", - help: "Indica si este usuario dispone de una plaza de IA, que da acceso a los asistentes de IA de la aplicación (build y ask). El número de usuarios que pueden tenerla está limitado por las plazas que incluye su licencia." + help: "Indica si este usuario dispone de una plaza de IA. La plaza otorga la capacidad `ai_seat`, que da acceso a los asistentes de IA de la aplicación (build y ask). El número de usuarios que pueden tenerla está limitado por las plazas que incluye su licencia." }, image: { label: "Imagen de perfil" diff --git a/packages/platform-objects/src/apps/translations/ja-JP.objects.generated.ts b/packages/platform-objects/src/apps/translations/ja-JP.objects.generated.ts index 75fd0ad5df8..730144bf88d 100644 --- a/packages/platform-objects/src/apps/translations/ja-JP.objects.generated.ts +++ b/packages/platform-objects/src/apps/translations/ja-JP.objects.generated.ts @@ -86,7 +86,7 @@ export const jaJPObjects: NonNullable = { }, ai_access: { label: "AI アクセス", - help: "このユーザーが AI シートを保有しているかどうか。保有するとアプリ内の AI アシスタント(build と ask)を利用できます。保有できるユーザー数は、ライセンスに含まれるシート数までです。" + help: "このユーザーが AI シートを保有しているかどうか。シートは `ai_seat` ケイパビリティを付与し、アプリ内の AI アシスタント(build と ask)を利用できるようにします。保有できるユーザー数は、ライセンスに含まれるシート数までです。" }, image: { label: "プロフィール画像" diff --git a/packages/platform-objects/src/apps/translations/zh-CN.objects.generated.ts b/packages/platform-objects/src/apps/translations/zh-CN.objects.generated.ts index 5bfff19cddf..e57ce593fa7 100644 --- a/packages/platform-objects/src/apps/translations/zh-CN.objects.generated.ts +++ b/packages/platform-objects/src/apps/translations/zh-CN.objects.generated.ts @@ -86,7 +86,7 @@ export const zhCNObjects: NonNullable = { }, ai_access: { label: "AI 访问权限", - help: "该用户是否占用一个 AI 席位,占用后可使用应用内的 AI 助手(build 和 ask)。可占用席位的用户数受许可证所含席位数限制。" + help: "该用户是否占用一个 AI 席位。席位授予 `ai_seat` 能力,可使用应用内的 AI 助手(build 和 ask)。可占用席位的用户数受许可证所含席位数限制。" }, image: { label: "头像" diff --git a/packages/platform-objects/src/identity/sys-user.object.ts b/packages/platform-objects/src/identity/sys-user.object.ts index 75ba3273ef9..0777b8fdae4 100644 --- a/packages/platform-objects/src/identity/sys-user.object.ts +++ b/packages/platform-objects/src/identity/sys-user.object.ts @@ -972,9 +972,9 @@ export const SysUser = ObjectSchema.create({ // @objectstack/security-enterprise AiSeatPlugin. Owned by objectql // (better-auth is oblivious to this column). description: - 'Whether this user holds an AI seat, which gives access to the AI ' + - 'assistants in the app (build and ask). How many users can hold one ' + - 'is limited by the seats your license includes.', + 'Whether this user holds an AI seat. A seat grants the `ai_seat` ' + + 'capability, which opens the AI assistants in the app (build and ask). ' + + 'How many users can hold one is limited by the seats your license includes.', }), // ── Profile ────────────────────────────────────────────────── From ee5da23123ab035c1916fa9055da5dd748d36373 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 23 Oct 2026 04:16:53 +0000 Subject: [PATCH 4/4] chore(changeset): admin field help and the hidden access-review pair Claude-Session: https://claude.ai/code/session_01RsXNmkeex9WY5XsjNwtRYv Co-authored-by: Claude --- .changeset/25331-admin-field-help.md | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 .changeset/25331-admin-field-help.md diff --git a/.changeset/25331-admin-field-help.md b/.changeset/25331-admin-field-help.md new file mode 100644 index 00000000000..895e700ca6f --- /dev/null +++ b/.changeset/25331-admin-field-help.md @@ -0,0 +1,24 @@ +--- +'@objectstack/platform-objects': patch +'@objectstack/metadata-core': patch +'@objectstack/plugin-security': patch +'@objectstack/plugin-approvals': patch +'@objectstack/service-messaging': patch +'@objectstack/objectql': patch +--- + +fix: Setup field help is written for administrators, and the two access-review fields nothing uses yet are hidden from the Setup forms + +Clause-②: no +Metadata-protocol: none + +A Setup pass met field help written for the platform's developers: "Foreign key to sys_user.id", "NULL only on a grant written before the column existed", "via isGrantActive", "(P3 digest middleware)", "Owned by objectql (better-auth is oblivious to this column)", "LifecycleService (Reaper / Rotator / Archiver)" and ADR numbers. Each now says what the field means and what to do with it, in every shipped locale (`en` regenerated from the source; `zh-CN`, `ja-JP` and `es-ES` translated): + +- the user, OAuth application, session and refresh-token fields of the permission-set and position assignments and of the OAuth token, consent and client-resource objects, and the package-version field of stored metadata; +- the permission set picked on an assignment; +- the start time of an approval delegation; +- the digest cadence and quiet hours of a notification preference, which now say how each value behaves; +- the AI Access and Locale fields of a user; +- the Data Lifecycle settings page and its retention overrides. + +`Last Certified At` and `Certified By` on `sys_user_permission_set` and `sys_user_position` are reserved for a future access-review feature and nothing writes or reads them. They are now `hidden: true`, so the Setup forms and detail pages no longer show them. The columns and any stored values stay.