Skip to content

Commit 7bcf2f6

Browse files
committed
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 <noreply@anthropic.com>
1 parent 383a00c commit 7bcf2f6

19 files changed

Lines changed: 66 additions & 73 deletions
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
---
2+
'@objectstack/connector-mcp': patch
3+
'@objectstack/plugin-email': patch
4+
'@objectstack/service-knowledge': patch
5+
'@objectstack/service-queue': patch
6+
'@objectstack/service-sms': patch
7+
'@objectstack/service-storage': patch
8+
'@objectstack/trigger-record-change': patch
9+
---
10+
11+
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
12+
13+
Clause-②: no
14+
15+
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.
16+
17+
- `@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.
18+
- `@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.
19+
- `@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.
20+
- `@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.
21+
- `@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.
22+
- `@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.
23+
- `@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.
24+
25+
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.

‎packages/connectors/connector-mcp/src/mcp-provider.ts‎

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -138,14 +138,16 @@ function assertDeclarativeStdioAllowed(
138138
throw new Error(
139139
`connector-mcp provider: connector '${connectorName}' declares a stdio transport with command '${command}', ` +
140140
`which is not in the host's declarativeStdio allowlist [${policy.join(', ')}]. ` +
141-
`Add the command to new ConnectorMcpPlugin({ declarativeStdio: [...] }) if this server is trusted (#3055).`,
141+
`Add the command to new ConnectorMcpPlugin({ declarativeStdio: [...] }) if this server is trusted: a stdio ` +
142+
`transport launches a local process, so stack metadata may only name a command the host's own code allows.`,
142143
);
143144
}
144145
throw new Error(
145146
`connector-mcp provider: connector '${connectorName}' declares a stdio transport (command '${command}'), ` +
146147
`but declarative stdio transports are disabled by default — a stdio transport launches a local process ` +
147148
`from stack metadata (including runtime Studio publishes). If this server is trusted, opt in deliberately: ` +
148-
`new ConnectorMcpPlugin({ declarativeStdio: ['${command}'] }) — or use an http transport (#3055, ADR-0024 §4).`,
149+
`new ConnectorMcpPlugin({ declarativeStdio: ['${command}'] }) — or use an http transport, which this policy ` +
150+
`does not gate (ADR-0024 §4).`,
149151
);
150152
}
151153

‎packages/plugins/plugin-email/src/email-service.ts‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -922,7 +922,8 @@ export class EmailService implements IEmailService {
922922
`EmailService: queue delivery skipped for one message — its attachments total `
923923
+ `${encodedAttachments.totalBytes} bytes, over the ${SYS_EMAIL_ATTACHMENT_LIMIT_BYTES}-byte limit a `
924924
+ 'sys_email row carries, so the message was delivered inline (in-process retries only) rather than '
925-
+ 'queued without them. Content that large is queueable through the storage capability (#5172), '
925+
+ 'queued without them. Content that large is queueable through the storage capability, which holds '
926+
+ 'it outside the row while the row keeps a reference and the attachment\'s audit metadata, '
926927
+ `but ${encodedAttachments.storageDetail ?? 'that path was not attempted for this message'}. `
927928
+ 'Fix: mount the storage capability (@objectstack/service-storage) so large attachments are '
928929
+ 'stored out of the row and the message can be delivered durably.',

‎packages/plugins/plugin-email/src/internal-header-readback.ts‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -124,8 +124,9 @@ export async function readInternalHeadersJson(
124124
throw new Error(
125125
`EmailService: ${SYS_EMAIL_OBJECT}.${HEADERS_COLUMN} is declared \`internal: true\`, but this `
126126
+ 'data engine does not implement resolveInternalField() — the custom headers this message was '
127-
+ 'authored with are stored but cannot be recovered, and a message must not be sent missing them '
128-
+ '(#8149). The row stays `queued`: the queue retry or the next boot outbox sweep delivers it '
127+
+ 'authored with are stored but cannot be recovered, and a message must not be sent missing them: '
128+
+ 'a missing header does not announce itself, so the send would succeed while silently deviating '
129+
+ 'from what was authored. The row stays `queued`: the queue retry or the next boot outbox sweep delivers it '
129130
+ 'intact once an engine that implements the privileged accessor is mounted.',
130131
);
131132
}

‎packages/plugins/plugin-email/src/templates/auth-templates.ts‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -570,7 +570,7 @@ to end other sessions.`,
570570
variables: EMAIL_CHANGE_NOTICE_VARIABLES,
571571
active: true,
572572
isSystem: true,
573-
description: 'Sent to the PREVIOUS address when a change-email request is accepted (#8019). Notification only — never gates the change.',
573+
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.',
574574
};
575575

576576
export const AUTH_EMAIL_CHANGE_NOTICE_TEMPLATE_ZH_CN: EmailTemplate = {
@@ -598,7 +598,7 @@ export const AUTH_EMAIL_CHANGE_NOTICE_TEMPLATE_ZH_CN: EmailTemplate = {
598598
599599
如果这不是您本人的操作,您的账号可能已被入侵。请立即联系您的 {{appName}} 管理员
600600
或支持团队,并修改密码以结束其他会话。`,
601-
description: '在接受变更邮箱请求时发送至原邮箱地址(#8019)。仅为通知,绝不阻断变更流程。',
601+
description: '在接受变更邮箱请求时发送至原邮箱地址,使被劫持的会话无法在原邮箱不知情的情况下转移账号身份。仅为通知,绝不阻断变更流程。',
602602
};
603603

604604
export const AUTH_EMAIL_CHANGE_NOTICE_TEMPLATE_JA_JP: EmailTemplate = {
@@ -627,7 +627,7 @@ export const AUTH_EMAIL_CHANGE_NOTICE_TEMPLATE_JA_JP: EmailTemplate = {
627627
心当たりがない場合、アカウントが不正利用されている可能性があります。直ちに
628628
{{appName}} の管理者またはサポートへご連絡のうえ、パスワードを変更して他の
629629
セッションを終了してください。`,
630-
description: '変更メールの要求が受理された際に変更前のアドレスへ送信されます(#8019)。通知のみで、変更を妨げることはありません。',
630+
description: '変更メールの要求が受理された際に変更前のアドレスへ送信され、乗っ取られたセッションが元のアドレスに知られないままアカウントの識別情報を移せないようにします。通知のみで、変更を妨げることはありません。',
631631
};
632632

633633
export const AUTH_EMAIL_CHANGE_NOTICE_TEMPLATE_ES_ES: EmailTemplate = {
@@ -657,7 +657,7 @@ aprobación.
657657
Si no has solicitado este cambio, tu cuenta podría estar comprometida. Ponte en
658658
contacto de inmediato con el administrador o el equipo de soporte de {{appName}}
659659
y cambia tu contraseña para cerrar las demás sesiones.`,
660-
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.',
660+
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.',
661661
};
662662

663663
/**

‎packages/services/service-i18n/src/i18n-route-ledger.ts‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ export interface I18nRouteLedgerEntry {
9595
export const I18N_ROUTE_LEDGER: readonly I18nRouteLedgerEntry[] = [
9696
{ route: 'GET /api/v1/i18n/locales', family: 'i18n', disposition: 'sdk', client: 'i18n.getLocales' },
9797
{ route: 'GET /api/v1/i18n/translations/:locale', family: 'i18n', disposition: 'sdk', client: 'i18n.getTranslations',
98-
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.' },
98+
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.' },
9999
{ route: 'GET /api/v1/i18n/labels/:object/:locale', family: 'i18n', disposition: 'sdk', client: 'i18n.getFieldLabels',
100-
note: 'ditto — the client sent /labels/:object?locale=xx against a two-path-param mount (#3636).' },
100+
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.' },
101101
];

‎packages/services/service-knowledge/src/__tests__/event-sync-data-events.test.ts‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -163,8 +163,8 @@ describe('#4626 — KnowledgeServicePlugin event sync on data.record.*', () => {
163163
await deliver(BULK_DELETED);
164164

165165
const [message] = harness.ctx.logger.warn.mock.calls.at(-1) as [string];
166-
expect(message).toContain('#4639');
167-
expect(message).toContain('lifecycle reap guard (#4672)');
166+
expect(message).toContain('cannot be repaired from the event stream');
167+
expect(message).toContain('the lifecycle reap guard de-indexes those rows before they are deleted');
168168
expect(message).toContain('application-level predicate writes are not');
169169
});
170170
});

‎packages/services/service-knowledge/src/knowledge-service-plugin.ts‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -217,8 +217,9 @@ export class KnowledgeServicePlugin implements Plugin {
217217
`KnowledgeServicePlugin: '${object}' had a predicate write (${type}) affecting ` +
218218
`${typeof matched === 'number' ? matched : 'an unreported number of'} record(s). ` +
219219
'A bulk event carries a count, not records, so the knowledge index for this object ' +
220-
'may now be stale and cannot be repaired from the event stream (#4639). ' +
221-
'Retention-sweep deletes are covered separately by the lifecycle reap guard (#4672); ' +
220+
'may now be stale and cannot be repaired from the event stream. ' +
221+
'Retention-sweep deletes are covered separately: the lifecycle reap guard de-indexes those rows ' +
222+
'before they are deleted; ' +
222223
'application-level predicate writes are not, and need an explicit reindexSource.',
223224
{ object, type, matched },
224225
);

‎packages/services/service-knowledge/src/knowledge-service.ts‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -318,7 +318,8 @@ export class KnowledgeService implements IKnowledgeService {
318318
// No identity → fail closed on object-backed hits (keep file/http hits).
319319
if (!ctx) {
320320
this.options.logger?.warn?.(
321-
'[knowledge] retrieval with no ExecutionContext — dropping object-source hits to stay safe (#2981). ' +
321+
'[knowledge] retrieval with no ExecutionContext — dropping object-source hits to stay safe: a missing ' +
322+
'identity is not a grant of authority, so retrieval fails closed rather than searching the whole corpus unscoped. ' +
322323
'Pass the caller identity (or an explicit system context) to retrieve object-backed knowledge.',
323324
);
324325
return hits.filter((h) => !h.sourceRecordId);

‎packages/services/service-queue/src/db-queue-adapter.ts‎

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -89,8 +89,10 @@ export function completedRetentionWindowMs(): number {
8989
if (!maxAge) {
9090
throw new Error(
9191
'[service-queue] sys_job_queue no longer declares lifecycle.retention — DbQueueAdapter dedups against '
92-
+ 'terminal rows by `created_at` window and relies on that declared retention to keep them (ADR-0057, #5179). '
93-
+ 'Restore the declaration in @objectstack/platform-objects rather than sweeping the table from here.',
92+
+ 'terminal rows by `created_at` window and relies on that declared retention to keep them (ADR-0057). '
93+
+ 'Restore the declaration in @objectstack/platform-objects rather than sweeping the table from here: '
94+
+ 'the one platform reaper sweeps completed rows by that declaration, and a sweeper here would be a '
95+
+ 'second copy of the window, free to drift from the first.',
9496
);
9597
}
9698
return lifecycleDurationMs(maxAge);

0 commit comments

Comments
 (0)