From 7bcf2f613f595e00d9ede5742fffa52263842abf Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 2 Oct 2026 03:10:01 +0000 Subject: [PATCH] fix(mcp, email, knowledge, queue, sms, storage, trigger, i18n): runtime strings state each decision in words instead of a tracker number Stage 2 of the services lane: every ledgered tracker-number occurrence in connector-mcp, plugin-email, service-storage, service-knowledge, service-queue, service-sms, trigger-record-change and service-i18n (22 occurrences in 21 string sites) is gone. Where the sentence already said what was decided, only the citation goes; where it leaned on the number, it now says the decision in words. The change-email notice template description changes in all four locales. The doc-authoring ledger is recomputed with --census-ledger: the 14 file blocks of these packages are deleted and no other row moves. Four test assertions that pinned an id now pin the sentence that replaced it. Text only: no status, error code, field, route or control flow moves. Claude-Session: https://claude.ai/code/session_01DiCSbmJrkzNhuEAier4VoJ Co-authored-by: Claude --- ...vices-strings-stage2-state-the-decision.md | 25 +++++++++++ .../connector-mcp/src/mcp-provider.ts | 6 ++- .../plugins/plugin-email/src/email-service.ts | 3 +- .../src/internal-header-readback.ts | 5 ++- .../src/templates/auth-templates.ts | 8 ++-- .../service-i18n/src/i18n-route-ledger.ts | 4 +- .../__tests__/event-sync-data-events.test.ts | 4 +- .../src/knowledge-service-plugin.ts | 5 ++- .../src/knowledge-service.ts | 3 +- .../service-queue/src/db-queue-adapter.ts | 6 ++- .../service-queue/src/queue-service-plugin.ts | 7 +-- .../service-sms/src/sms-daily-quota.ts | 3 +- .../services/service-sms/src/sms-plugin.ts | 2 +- .../src/attachment-lifecycle.ts | 3 +- .../src/storage-route-ledger.ts | 4 +- .../src/array-form-refusal-end-to-end.test.ts | 2 +- .../src/record-change-trigger.test.ts | 2 +- .../src/record-change-trigger.ts | 3 +- scripts/doc-authoring-prose-id.baseline.json | 44 ------------------- 19 files changed, 66 insertions(+), 73 deletions(-) create mode 100644 .changeset/20751-services-strings-stage2-state-the-decision.md diff --git a/.changeset/20751-services-strings-stage2-state-the-decision.md b/.changeset/20751-services-strings-stage2-state-the-decision.md new file mode 100644 index 00000000000..6a008c57251 --- /dev/null +++ b/.changeset/20751-services-strings-stage2-state-the-decision.md @@ -0,0 +1,25 @@ +--- +'@objectstack/connector-mcp': patch +'@objectstack/plugin-email': patch +'@objectstack/service-knowledge': patch +'@objectstack/service-queue': patch +'@objectstack/service-sms': patch +'@objectstack/service-storage': patch +'@objectstack/trigger-record-change': patch +--- + +MCP stdio, email, knowledge, queue, SMS, storage and record-trigger refusals, warnings and template descriptions no longer cite tracker numbers; each one states the decision behind it in words + +Clause-②: no + +Some strings these seven packages show to operators, administrators and flow authors pointed at an issue-tracker number for the reason behind them. The number goes; where the sentence did not already say what was decided, it now does. + +- `@objectstack/connector-mcp`: the declarative stdio refusals say a stdio transport launches a local process, so stack metadata may only name a command the host's own code allows, and that an http transport is not gated by this policy. +- `@objectstack/plugin-email`: the built-in change-email notice template's description, in all four locales, says the notice goes to the previous address so a hijacked session cannot move the account identity unannounced; the internal-headers refusal says a missing header does not announce itself, so the send would succeed while silently deviating from what was authored; the over-limit attachments line says the storage capability holds large content outside the row while the row keeps a reference and the attachment's audit metadata. +- `@objectstack/service-knowledge`: the no-identity retrieval warning says a missing identity is not a grant of authority, so retrieval fails closed rather than searching the whole corpus unscoped; the predicate-write warning says the lifecycle reap guard de-indexes retention-swept rows before they are deleted. +- `@objectstack/service-queue`: the missing-retention refusal says the one platform reaper sweeps completed rows by that declaration, so the adapter does not sweep the table itself; the rejected-floor error says the floor is what makes the lifecycle service refuse an override below the idempotency window. +- `@objectstack/service-sms`: the unreadable-counter warning says a quota the platform cannot count must not refuse the one-time codes users sign in with; the counter store's lines name the daily SMS send quota without a number. +- `@objectstack/service-storage`: the reclamation-gate line says deleting bytes cannot be undone, so it waits for a verified migration with no deviation on record, while reversible work carries on. +- `@objectstack/trigger-record-change`: the array-trigger warning says multi-event arrays are deferred until two independent projects need a combination other than created-or-updated. + +Text only: no status, error code, field, route or control flow moves. A client or log filter that matches the old text (for example a tracker-number suffix) needs the new spelling. diff --git a/packages/connectors/connector-mcp/src/mcp-provider.ts b/packages/connectors/connector-mcp/src/mcp-provider.ts index 0f8484abe85..73687749231 100644 --- a/packages/connectors/connector-mcp/src/mcp-provider.ts +++ b/packages/connectors/connector-mcp/src/mcp-provider.ts @@ -138,14 +138,16 @@ function assertDeclarativeStdioAllowed( throw new Error( `connector-mcp provider: connector '${connectorName}' declares a stdio transport with command '${command}', ` + `which is not in the host's declarativeStdio allowlist [${policy.join(', ')}]. ` + - `Add the command to new ConnectorMcpPlugin({ declarativeStdio: [...] }) if this server is trusted (#3055).`, + `Add the command to new ConnectorMcpPlugin({ declarativeStdio: [...] }) if this server is trusted: a stdio ` + + `transport launches a local process, so stack metadata may only name a command the host's own code allows.`, ); } throw new Error( `connector-mcp provider: connector '${connectorName}' declares a stdio transport (command '${command}'), ` + `but declarative stdio transports are disabled by default — a stdio transport launches a local process ` + `from stack metadata (including runtime Studio publishes). If this server is trusted, opt in deliberately: ` + - `new ConnectorMcpPlugin({ declarativeStdio: ['${command}'] }) — or use an http transport (#3055, ADR-0024 §4).`, + `new ConnectorMcpPlugin({ declarativeStdio: ['${command}'] }) — or use an http transport, which this policy ` + + `does not gate (ADR-0024 §4).`, ); } diff --git a/packages/plugins/plugin-email/src/email-service.ts b/packages/plugins/plugin-email/src/email-service.ts index 1e99bd5e2bc..42fd31f756a 100644 --- a/packages/plugins/plugin-email/src/email-service.ts +++ b/packages/plugins/plugin-email/src/email-service.ts @@ -922,7 +922,8 @@ export class EmailService implements IEmailService { `EmailService: queue delivery skipped for one message — its attachments total ` + `${encodedAttachments.totalBytes} bytes, over the ${SYS_EMAIL_ATTACHMENT_LIMIT_BYTES}-byte limit a ` + 'sys_email row carries, so the message was delivered inline (in-process retries only) rather than ' - + 'queued without them. Content that large is queueable through the storage capability (#5172), ' + + 'queued without them. Content that large is queueable through the storage capability, which holds ' + + 'it outside the row while the row keeps a reference and the attachment\'s audit metadata, ' + `but ${encodedAttachments.storageDetail ?? 'that path was not attempted for this message'}. ` + 'Fix: mount the storage capability (@objectstack/service-storage) so large attachments are ' + 'stored out of the row and the message can be delivered durably.', diff --git a/packages/plugins/plugin-email/src/internal-header-readback.ts b/packages/plugins/plugin-email/src/internal-header-readback.ts index 2eff2d5de42..95d7a34a553 100644 --- a/packages/plugins/plugin-email/src/internal-header-readback.ts +++ b/packages/plugins/plugin-email/src/internal-header-readback.ts @@ -124,8 +124,9 @@ export async function readInternalHeadersJson( throw new Error( `EmailService: ${SYS_EMAIL_OBJECT}.${HEADERS_COLUMN} is declared \`internal: true\`, but this ` + 'data engine does not implement resolveInternalField() — the custom headers this message was ' - + 'authored with are stored but cannot be recovered, and a message must not be sent missing them ' - + '(#8149). The row stays `queued`: the queue retry or the next boot outbox sweep delivers it ' + + 'authored with are stored but cannot be recovered, and a message must not be sent missing them: ' + + 'a missing header does not announce itself, so the send would succeed while silently deviating ' + + 'from what was authored. The row stays `queued`: the queue retry or the next boot outbox sweep delivers it ' + 'intact once an engine that implements the privileged accessor is mounted.', ); } diff --git a/packages/plugins/plugin-email/src/templates/auth-templates.ts b/packages/plugins/plugin-email/src/templates/auth-templates.ts index 3f402005551..6dab2a5e508 100644 --- a/packages/plugins/plugin-email/src/templates/auth-templates.ts +++ b/packages/plugins/plugin-email/src/templates/auth-templates.ts @@ -570,7 +570,7 @@ to end other sessions.`, variables: EMAIL_CHANGE_NOTICE_VARIABLES, active: true, isSystem: true, - description: 'Sent to the PREVIOUS address when a change-email request is accepted (#8019). Notification only — never gates the change.', + description: 'Sent to the PREVIOUS address when a change-email request is accepted, so a hijacked session cannot move the account identity unannounced. Notification only — never gates the change.', }; export const AUTH_EMAIL_CHANGE_NOTICE_TEMPLATE_ZH_CN: EmailTemplate = { @@ -598,7 +598,7 @@ export const AUTH_EMAIL_CHANGE_NOTICE_TEMPLATE_ZH_CN: EmailTemplate = { 如果这不是您本人的操作,您的账号可能已被入侵。请立即联系您的 {{appName}} 管理员 或支持团队,并修改密码以结束其他会话。`, - description: '在接受变更邮箱请求时发送至原邮箱地址(#8019)。仅为通知,绝不阻断变更流程。', + description: '在接受变更邮箱请求时发送至原邮箱地址,使被劫持的会话无法在原邮箱不知情的情况下转移账号身份。仅为通知,绝不阻断变更流程。', }; export const AUTH_EMAIL_CHANGE_NOTICE_TEMPLATE_JA_JP: EmailTemplate = { @@ -627,7 +627,7 @@ export const AUTH_EMAIL_CHANGE_NOTICE_TEMPLATE_JA_JP: EmailTemplate = { 心当たりがない場合、アカウントが不正利用されている可能性があります。直ちに {{appName}} の管理者またはサポートへご連絡のうえ、パスワードを変更して他の セッションを終了してください。`, - description: '変更メールの要求が受理された際に変更前のアドレスへ送信されます(#8019)。通知のみで、変更を妨げることはありません。', + description: '変更メールの要求が受理された際に変更前のアドレスへ送信され、乗っ取られたセッションが元のアドレスに知られないままアカウントの識別情報を移せないようにします。通知のみで、変更を妨げることはありません。', }; export const AUTH_EMAIL_CHANGE_NOTICE_TEMPLATE_ES_ES: EmailTemplate = { @@ -657,7 +657,7 @@ aprobación. Si no has solicitado este cambio, tu cuenta podría estar comprometida. Ponte en contacto de inmediato con el administrador o el equipo de soporte de {{appName}} y cambia tu contraseña para cerrar las demás sesiones.`, - description: 'Se envía a la dirección ANTERIOR cuando se acepta una solicitud de cambio de correo (#8019). Solo notificación; nunca bloquea el cambio.', + description: 'Se envía a la dirección ANTERIOR cuando se acepta una solicitud de cambio de correo, para que una sesión secuestrada no pueda trasladar la identidad de la cuenta sin aviso. Solo notificación; nunca bloquea el cambio.', }; /** diff --git a/packages/services/service-i18n/src/i18n-route-ledger.ts b/packages/services/service-i18n/src/i18n-route-ledger.ts index c49555a8864..c7b15ec1559 100644 --- a/packages/services/service-i18n/src/i18n-route-ledger.ts +++ b/packages/services/service-i18n/src/i18n-route-ledger.ts @@ -95,7 +95,7 @@ export interface I18nRouteLedgerEntry { export const I18N_ROUTE_LEDGER: readonly I18nRouteLedgerEntry[] = [ { route: 'GET /api/v1/i18n/locales', family: 'i18n', disposition: 'sdk', client: 'i18n.getLocales' }, { route: 'GET /api/v1/i18n/translations/:locale', family: 'i18n', disposition: 'sdk', client: 'i18n.getTranslations', - note: 'was a wire-level 404 — the client sent /translations?locale=xx, a shape no server mounts (the dispatcher domain body accepts it, but nothing routes a bare /translations to that body). Since #3636 the client sends the path form the spec declares.' }, + note: 'was a wire-level 404 — the client sent /translations?locale=xx, a shape no server mounts (the dispatcher domain body accepts it, but nothing routes a bare /translations to that body). The client now sends the path form the spec declares.' }, { route: 'GET /api/v1/i18n/labels/:object/:locale', family: 'i18n', disposition: 'sdk', client: 'i18n.getFieldLabels', - note: 'ditto — the client sent /labels/:object?locale=xx against a two-path-param mount (#3636).' }, + note: 'ditto — the client sent /labels/:object?locale=xx against a two-path-param mount; it now sends both the object and the locale in the path.' }, ]; diff --git a/packages/services/service-knowledge/src/__tests__/event-sync-data-events.test.ts b/packages/services/service-knowledge/src/__tests__/event-sync-data-events.test.ts index 67ae69cd8cc..c6ed8cefb74 100644 --- a/packages/services/service-knowledge/src/__tests__/event-sync-data-events.test.ts +++ b/packages/services/service-knowledge/src/__tests__/event-sync-data-events.test.ts @@ -163,8 +163,8 @@ describe('#4626 — KnowledgeServicePlugin event sync on data.record.*', () => { await deliver(BULK_DELETED); const [message] = harness.ctx.logger.warn.mock.calls.at(-1) as [string]; - expect(message).toContain('#4639'); - expect(message).toContain('lifecycle reap guard (#4672)'); + expect(message).toContain('cannot be repaired from the event stream'); + expect(message).toContain('the lifecycle reap guard de-indexes those rows before they are deleted'); expect(message).toContain('application-level predicate writes are not'); }); }); diff --git a/packages/services/service-knowledge/src/knowledge-service-plugin.ts b/packages/services/service-knowledge/src/knowledge-service-plugin.ts index 495cad16410..2b206a6b6e2 100644 --- a/packages/services/service-knowledge/src/knowledge-service-plugin.ts +++ b/packages/services/service-knowledge/src/knowledge-service-plugin.ts @@ -217,8 +217,9 @@ export class KnowledgeServicePlugin implements Plugin { `KnowledgeServicePlugin: '${object}' had a predicate write (${type}) affecting ` + `${typeof matched === 'number' ? matched : 'an unreported number of'} record(s). ` + 'A bulk event carries a count, not records, so the knowledge index for this object ' + - 'may now be stale and cannot be repaired from the event stream (#4639). ' + - 'Retention-sweep deletes are covered separately by the lifecycle reap guard (#4672); ' + + 'may now be stale and cannot be repaired from the event stream. ' + + 'Retention-sweep deletes are covered separately: the lifecycle reap guard de-indexes those rows ' + + 'before they are deleted; ' + 'application-level predicate writes are not, and need an explicit reindexSource.', { object, type, matched }, ); diff --git a/packages/services/service-knowledge/src/knowledge-service.ts b/packages/services/service-knowledge/src/knowledge-service.ts index 89f124770ba..93069e83cae 100644 --- a/packages/services/service-knowledge/src/knowledge-service.ts +++ b/packages/services/service-knowledge/src/knowledge-service.ts @@ -318,7 +318,8 @@ export class KnowledgeService implements IKnowledgeService { // No identity → fail closed on object-backed hits (keep file/http hits). if (!ctx) { this.options.logger?.warn?.( - '[knowledge] retrieval with no ExecutionContext — dropping object-source hits to stay safe (#2981). ' + + '[knowledge] retrieval with no ExecutionContext — dropping object-source hits to stay safe: a missing ' + + 'identity is not a grant of authority, so retrieval fails closed rather than searching the whole corpus unscoped. ' + 'Pass the caller identity (or an explicit system context) to retrieve object-backed knowledge.', ); return hits.filter((h) => !h.sourceRecordId); diff --git a/packages/services/service-queue/src/db-queue-adapter.ts b/packages/services/service-queue/src/db-queue-adapter.ts index 255e60a71f2..67be673ab50 100644 --- a/packages/services/service-queue/src/db-queue-adapter.ts +++ b/packages/services/service-queue/src/db-queue-adapter.ts @@ -89,8 +89,10 @@ export function completedRetentionWindowMs(): number { if (!maxAge) { throw new Error( '[service-queue] sys_job_queue no longer declares lifecycle.retention — DbQueueAdapter dedups against ' - + 'terminal rows by `created_at` window and relies on that declared retention to keep them (ADR-0057, #5179). ' - + 'Restore the declaration in @objectstack/platform-objects rather than sweeping the table from here.', + + 'terminal rows by `created_at` window and relies on that declared retention to keep them (ADR-0057). ' + + 'Restore the declaration in @objectstack/platform-objects rather than sweeping the table from here: ' + + 'the one platform reaper sweeps completed rows by that declaration, and a sweeper here would be a ' + + 'second copy of the window, free to drift from the first.', ); } return lifecycleDurationMs(maxAge); diff --git a/packages/services/service-queue/src/queue-service-plugin.ts b/packages/services/service-queue/src/queue-service-plugin.ts index a387b06d39f..b8759353021 100644 --- a/packages/services/service-queue/src/queue-service-plugin.ts +++ b/packages/services/service-queue/src/queue-service-plugin.ts @@ -157,9 +157,10 @@ export class QueueServicePlugin implements Plugin { // A floor the service refused is a wiring bug in THIS plugin, not a // degraded deployment — but it must not stop the queue from coming up. ctx.logger.error( - 'QueueServicePlugin: the lifecycle service rejected the sys_job_queue retention floor. A `lifecycle` ' - + 'settings override may now shorten sys_job_queue.retention below the idempotency window, in which case ' - + 'publish would silently re-accept duplicates (#5195). Fix the floor registration, or keep ' + 'QueueServicePlugin: the lifecycle service rejected the sys_job_queue retention floor, and that floor is ' + + 'what makes it refuse an override below the idempotency window. A `lifecycle` settings override may now ' + + 'shorten sys_job_queue.retention below that window, in which case publish would silently re-accept ' + + 'duplicates. Fix the floor registration, or keep ' + 'lifecycle.retention_overrides.sys_job_queue unset.', err as any, ); diff --git a/packages/services/service-sms/src/sms-daily-quota.ts b/packages/services/service-sms/src/sms-daily-quota.ts index 42a78823623..c10a26e3d97 100644 --- a/packages/services/service-sms/src/sms-daily-quota.ts +++ b/packages/services/service-sms/src/sms-daily-quota.ts @@ -324,7 +324,8 @@ export class SmsDailyQuota { '[sms] daily SMS quota counter is unreadable (' + String((err as Error)?.message ?? err) + ') — the gate is FAILING OPEN and today\'s spend is unbounded until the counter store recovers. ' + - 'Sign-in is deliberately not taken down with it (#2814).', + 'Sign-in is deliberately not taken down with it: a quota the platform cannot count must not ' + + 'refuse the one-time codes users sign in with.', ); } return { ok: true }; diff --git a/packages/services/service-sms/src/sms-plugin.ts b/packages/services/service-sms/src/sms-plugin.ts index 9f5b7d5fe1b..9b0353c84ff 100644 --- a/packages/services/service-sms/src/sms-plugin.ts +++ b/packages/services/service-sms/src/sms-plugin.ts @@ -167,7 +167,7 @@ export class SmsServicePlugin implements Plugin { resolveCache, logger: ctx.logger, logPrefix: '[sms]', - subject: 'daily SMS send quota (#2814)', + subject: 'daily SMS send quota', degradedImpact: 'The ceiling is still enforced, but PER NODE: an N-node deployment can spend up to N× the ' + 'configured number of PAID SMS per day, which is exactly the total-cost hole this gate exists to close', diff --git a/packages/services/service-storage/src/attachment-lifecycle.ts b/packages/services/service-storage/src/attachment-lifecycle.ts index 0500fd5eacf..44cdd52eb37 100644 --- a/packages/services/service-storage/src/attachment-lifecycle.ts +++ b/packages/services/service-storage/src/attachment-lifecycle.ts @@ -526,7 +526,8 @@ export function createSysFileReapGuard( `file-as-reference migration is not verified, or a deviation has been observed since it ` + `was (a value an OS_ALLOW_LAX_* escape hatch admitted against the migration's own ` + `contract). Either way: fix the data, then run \`os migrate files-to-references --apply\`. ` + - `See sys_migration.verified_at / deviation_observed_at (ADR-0104 / #4797)`, + `See sys_migration.verified_at / deviation_observed_at (ADR-0104): deleting bytes cannot be undone, so ` + + `it waits for a verified migration with no deviation on record, while reversible work carries on.`, ); } return confirmed; diff --git a/packages/services/service-storage/src/storage-route-ledger.ts b/packages/services/service-storage/src/storage-route-ledger.ts index 7ac07f80379..7857e81c281 100644 --- a/packages/services/service-storage/src/storage-route-ledger.ts +++ b/packages/services/service-storage/src/storage-route-ledger.ts @@ -110,13 +110,13 @@ export const STORAGE_ROUTE_LEDGER: readonly StorageRouteLedgerEntry[] = [ // ── download ────────────────────────────────────────────────────────────── { route: 'GET /api/v1/storage/files/:fileId/url', family: 'download', disposition: 'sdk', client: 'storage.getDownloadUrl', - note: 'JSON { success: true, data: { url } } — the authorization-gated signed-URL resolve the dispatcher ledger points at (#3584); the bare { url } it used to answer moved into the declared envelope in #3689' }, + note: 'JSON { success: true, data: { url } } — the authorization-gated signed-URL resolve. The dispatcher ledger points here because this protocol, not a dispatcher route, is the canonical storage surface the SDK speaks; the bare { url } it used to answer moved into the declared envelope' }, { route: 'GET /api/v1/storage/files/:fileId', family: 'download', disposition: 'server-only', note: 'stable 302 to the same signed URL. This is the value objectql stamps into file/image field payloads (engine.ts buildFileValue), followed verbatim by / — a browser URL, not an SDK call. The SDK resolves via the /url sibling above.' }, // ── local-driver loopback (LocalStorageAdapter presign targets) ─────────── { route: 'PUT /api/v1/storage/_local/raw/:token', family: 'local-driver', disposition: 'server-only', - note: 'the uploadUrl LocalStorageAdapter mints for its own presign tokens. storage.upload does PUT it, but opaquely — via fetchImpl on whatever uploadUrl came back, exactly as it would an S3 presigned URL. HMAC-token-authorized, adapter-internal, never a named SDK method. Answers { success: true, data: { key } }; the { ok: true, key } it used to answer retired in #3689, `ok` being a private second word for `success`.' }, + note: 'the uploadUrl LocalStorageAdapter mints for its own presign tokens. storage.upload does PUT it, but opaquely — via fetchImpl on whatever uploadUrl came back, exactly as it would an S3 presigned URL. HMAC-token-authorized, adapter-internal, never a named SDK method. Answers { success: true, data: { key } }; the { ok: true, key } it used to answer retired when every storage route moved to the declared envelope, `ok` being a private second word for `success`.' }, { route: 'GET /api/v1/storage/_local/raw/:token', family: 'local-driver', disposition: 'server-only', note: 'ditto for download: the target of getPresignedDownload/getSignedUrl on the local adapter, handed to the browser as an opaque signed link.' }, ]; diff --git a/packages/triggers/trigger-record-change/src/array-form-refusal-end-to-end.test.ts b/packages/triggers/trigger-record-change/src/array-form-refusal-end-to-end.test.ts index b47d26a4de9..5d8e326e9ce 100644 --- a/packages/triggers/trigger-record-change/src/array-form-refusal-end-to-end.test.ts +++ b/packages/triggers/trigger-record-change/src/array-form-refusal-end-to-end.test.ts @@ -113,7 +113,7 @@ describe('[#14328] array-form triggerType still draws the record-change trigger /NOT bound|never fire/i, ); expect(msg, 'steers to the supported single token').toMatch(/record-after-write/); - expect(msg, 'cites the standing decision').toMatch(/#3457/); + expect(msg, 'states the standing decision').toMatch(/multi-event arrays are deferred until/); // The refusal is a refusal: nothing was armed for this flow. expect(hooks, 'no lifecycle hook registered for an array-form flow').toHaveLength(0); diff --git a/packages/triggers/trigger-record-change/src/record-change-trigger.test.ts b/packages/triggers/trigger-record-change/src/record-change-trigger.test.ts index 3e3ee3ea9ce..0b89418f722 100644 --- a/packages/triggers/trigger-record-change/src/record-change-trigger.test.ts +++ b/packages/triggers/trigger-record-change/src/record-change-trigger.test.ts @@ -180,7 +180,7 @@ describe('RecordChangeTrigger', () => { expect(msg).toMatch(/task_assigned_notify/); expect(msg).toMatch(/array/i); expect(msg).toMatch(/record-after-write/); - expect(msg).toMatch(/#3457/); + expect(msg).toMatch(/multi-event arrays are deferred until/); }); it('keeps the generic unsupported-event warning for a non-array bad token', () => { diff --git a/packages/triggers/trigger-record-change/src/record-change-trigger.ts b/packages/triggers/trigger-record-change/src/record-change-trigger.ts index f3235a962fc..561545dd9a9 100644 --- a/packages/triggers/trigger-record-change/src/record-change-trigger.ts +++ b/packages/triggers/trigger-record-change/src/record-change-trigger.ts @@ -236,7 +236,8 @@ export class RecordChangeTrigger implements FlowTrigger { this.logger.warn( `[record-change] flow '${binding.flowName}' has an ARRAY trigger event ${JSON.stringify(rawTriggerType)} — ` + `multi-event arrays are not supported, so the flow is NOT bound and will never fire. ` + - `For "created or updated" use a single 'record-after-write'; for any other combination author one flow per event (#3457).`, + `For "created or updated" use a single 'record-after-write'; for any other combination author one flow per event — ` + + `multi-event arrays are deferred until two independent projects need a combination other than created-or-updated.`, ); } else { this.logger.warn( diff --git a/scripts/doc-authoring-prose-id.baseline.json b/scripts/doc-authoring-prose-id.baseline.json index caeb9f43722..414cfbc70fc 100644 --- a/scripts/doc-authoring-prose-id.baseline.json +++ b/scripts/doc-authoring-prose-id.baseline.json @@ -1,7 +1,4 @@ { - "packages/connectors/connector-mcp/src/mcp-provider.ts": { - "#3055": 2 - }, "packages/lint/src/authoring-rules.ts": { "#4463": 3, "#4716": 2, @@ -176,15 +173,6 @@ "packages/plugins/plugin-audit/src/translations/zh-CN.objects.generated.ts": { "#11507": 1 }, - "packages/plugins/plugin-email/src/email-service.ts": { - "#5172": 1 - }, - "packages/plugins/plugin-email/src/internal-header-readback.ts": { - "#8149": 1 - }, - "packages/plugins/plugin-email/src/templates/auth-templates.ts": { - "#8019": 4 - }, "packages/plugins/plugin-security/src/bootstrap-system-capabilities.ts": { "#5876": 1, "#8552": 2 @@ -343,37 +331,5 @@ "packages/services/service-datasource/src/external-datasource-service.ts": { "#10997": 1, "#11000": 1 - }, - "packages/services/service-i18n/src/i18n-route-ledger.ts": { - "#3636": 2 - }, - "packages/services/service-knowledge/src/knowledge-service-plugin.ts": { - "#4639": 1, - "#4672": 1 - }, - "packages/services/service-knowledge/src/knowledge-service.ts": { - "#2981": 1 - }, - "packages/services/service-queue/src/db-queue-adapter.ts": { - "#5179": 1 - }, - "packages/services/service-queue/src/queue-service-plugin.ts": { - "#5195": 1 - }, - "packages/services/service-sms/src/sms-daily-quota.ts": { - "#2814": 1 - }, - "packages/services/service-sms/src/sms-plugin.ts": { - "#2814": 1 - }, - "packages/services/service-storage/src/attachment-lifecycle.ts": { - "#4797": 1 - }, - "packages/services/service-storage/src/storage-route-ledger.ts": { - "#3584": 1, - "#3689": 2 - }, - "packages/triggers/trigger-record-change/src/record-change-trigger.ts": { - "#3457": 1 } }