Repository navigation
Commit 6091136
fix(mcp, email, knowledge, queue, sms, storage, trigger, i18n): runtime strings state each decision in words instead of a tracker number (stage 2) (#21311)
Part of #20751
Clause-②: no
**Stage 2 of the `domain:services` lane under the maintainer's A / A
ruling (5902360492): the `connector-mcp`, `plugin-email`,
`service-storage`, `service-knowledge`, `service-queue`, `service-sms`,
`trigger-record-change` and `service-i18n` strings.** The card stays
open for the later stages, so this PR carries no closing keyword. Text
only: no status, error `code`, field, route, export or control flow
moves (AST-skeleton proof below, 14 of 14 files SAME).
## What this does
These eight packages' refusals, warnings, log lines, route-ledger notes
and the built-in change-email notice template description (in all four
locales) sent the reader to a tracker number for the reason behind them.
In form D, as stage 1 applied it, the number goes. 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.
All 22 ledgered occurrences in the eight packages (claim `5944824425`),
re-derived from the ledger on `origin/main` at `383a00c7`, not from the
card's table: `plugin-email` 6, `service-storage` 4, `service-knowledge`
3, `connector-mcp` 2, `service-queue` 2, `service-sms` 2, `service-i18n`
2, `trigger-record-change` 1. The card's table reads the same 22. They
sit in 21 string sites (one storage note carried two ids).
### Rewritten in words
Author- and administrator-visible text first, log lines last. Every
cited card was read through REST (body and every comment) before its
string was rewritten.
| Where (head line) | Cited | The text now says | Decision read from |
|---|---|---|---|
| `connector-mcp` `mcp-provider.ts:142`, allowlist-miss refusal (fatal
at boot, skipped and logged on a runtime publish) | 3055 | "a stdio
transport launches a local process, so stack metadata may only name a
command the host's own code allows" | card body: declarative stdio is
default-deny and allowlisted by host code, because metadata (a runtime
Studio publish included) could otherwise launch commands on the server |
| `connector-mcp` `mcp-provider.ts:149`, default-deny refusal | 3055 |
"or use an http transport, which this policy does not gate (ADR-0024
§4)" | card body: http transports stay unaffected |
| `plugin-email` `templates/auth-templates.ts:573`, `:601`, `:630`,
`:660`, the `auth.email_change_notice` description seeded into
`sys_email_template` (en-US, zh-CN, ja-JP, es-ES) | 8019 | "so a
hijacked session cannot move the account identity unannounced", and the
same decision in each locale | maintainer ruling 5271034556: notify the
old address, do not gate |
| `plugin-email` `internal-header-readback.ts:128`, thrown when the
engine cannot dereference `headers_json` | 8149 | "a missing header does
not announce itself, so the send would succeed while silently deviating
from what was authored" | the card adopts its blocker's landed remedy;
unlock comment 5277987462, point 4 (fail closed, because a missing
header is not self-announcing) |
| `service-queue` `db-queue-adapter.ts:93`, thrown when `sys_job_queue`
loses its retention declaration | 5179 | "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" | ACCEPT 5176594402:
declarative retention swept by the one platform reaper (ADR-0057 §3.3),
read from the declaration rather than copied |
| `trigger-record-change` `record-change-trigger.ts:240`, array-trigger
warning to the flow author | 3457 | "multi-event arrays are deferred
until two independent projects need a combination other than
created-or-updated" | closing comment 5076318442 (not planned; its
restart clause is two independent real projects) |
| `service-knowledge` `knowledge-service.ts:321`, no-identity retrieval
warning | 2981 | "a missing identity is not a grant of authority, so
retrieval fails closed rather than searching the whole corpus unscoped"
| card body: fail closed on a missing identity; pass-through only for an
explicit system context (ADR-0096) |
| `service-knowledge` `knowledge-service-plugin.ts:221`, predicate-write
warning | 4672 | "the lifecycle reap guard de-indexes those rows before
they are deleted" | maintainer ruling 5194621834 (plan C) |
| `service-queue` `queue-service-plugin.ts:160`, rejected-floor error |
5195 | "that floor is what makes it refuse an override below the
idempotency window" | ACCEPT 5177937633: an override below a consumer's
registered floor is refused |
| `service-sms` `sms-daily-quota.ts:327`, unreadable-counter warning |
2814 | "a quota the platform cannot count must not refuse the one-time
codes users sign in with" | card body, ask 4: a counter-store failure
fails open with a warning, and the quota gate must not take sign-in down
|
| `service-storage` `attachment-lifecycle.ts:529`, reclamation-gate line
| 4797 | "deleting bytes cannot be undone, so it waits for a verified
migration with no deviation on record, while reversible work carries on"
| maintainer ruling 5202265919 / 5203575604 (conditional B: withdraw the
irreversible authority, keep the reversible) |
| `plugin-email` `email-service.ts:925`, over-limit attachments line |
5172 | "queueable through the storage capability, which holds it outside
the row while the row keeps a reference and the attachment's audit
metadata" | maintainer ruling in the card body (content through storage;
the row keeps the reference and permanent audit metadata) |
| `service-storage` `storage-route-ledger.ts:113`, download-URL row note
(guard data, not shipped) | 3584 | "The dispatcher ledger points here
because this protocol, not a dispatcher route, is the canonical storage
surface the SDK speaks" | landing commit `0bab8bb45` (the
service-storage protocol stays canonical), and the later retirement of
the dispatcher bridge recorded in `packages/runtime/src/route-ledger.ts`
|
### Citation only (the sentence already stated the decision)
- `service-knowledge` `knowledge-service-plugin.ts:220` (4639, "A bulk
event carries a count, not records, ... cannot be repaired from the
event stream": the honest aggregate event).
- `service-sms` `sms-plugin.ts:170` (2814): the counter store's
`subject` is now "daily SMS send quota". It is prose only, interpolated
into the store's bind and fallback log lines, never a key.
- `service-i18n` `i18n-route-ledger.ts:98`, `:100` (3636, "The client
now sends the path form the spec declares"; "it now sends both the
object and the locale in the path", checked against
`i18n.getTranslations` / `i18n.getFieldLabels` in
`packages/client/src/index.ts`).
- `service-storage` `storage-route-ledger.ts:113` (second half) and
`:119` (3689, "moved into the declared envelope"; "retired when every
storage route moved to the declared envelope, `ok` being a private
second word for `success`").
### Translations
The change-email notice description is the only string here with locale
variants. Each of the zh-CN, ja-JP and es-ES rows carries the same
decision as the en-US row ("使被劫持的会话无法在原邮箱不知情的情况下转移账号身份",
"乗っ取られたセッションが元のアドレスに知られないままアカウントの識別情報を移せないようにします", "para que una sesión
secuestrada no pueda trasladar la identidad de la cuenta sin aviso") and
no number. They are template rows, not i18n bundle keys, so no bundle or
digest moves. `check:i18n` reads "all bundles in sync". The built-in
seeder upserts by name and locale on every boot, so a deployment's
existing rows take the new description.
## Ledger (`scripts/doc-authoring-prose-id.baseline.json`)
Recomputed with `node scripts/check-doc-authoring.mjs --census-ledger`
(exit 0, no growth refusal) into a scratch file, then copied into place.
The diff deletes 44 lines and adds none: exactly the 14 file blocks of
the eight packages. A scripted comparison of every other key: 0 moved, 0
added.
| | before (`383a00c7`) | after |
|---|---|---|
| `plugin-email` | 6 occurrences, 3 pairs, 3 files | 0 |
| `service-storage` | 4 occurrences, 3 pairs, 2 files | 0 |
| `service-knowledge` | 3 occurrences, 3 pairs, 2 files | 0 |
| `connector-mcp` | 2 occurrences, 1 pair, 1 file | 0 |
| `service-queue` | 2 occurrences, 2 pairs, 2 files | 0 |
| `service-sms` | 2 occurrences, 2 pairs, 2 files | 0 |
| `service-i18n` | 2 occurrences, 1 pair, 1 file | 0 |
| `trigger-record-change` | 1 occurrence, 1 pair, 1 file | 0 |
| whole ledger | 283 occurrences, 205 pairs, 86 files | 261 occurrences,
189 pairs, 72 files |
`pnpm check:doc-authoring` at the head: "236 pinned site(s) across 72
file(s), 86149 string(s) read in 1248 parsed source(s), no growth, no
burn-down unrecorded" (the census before the change read 257 sites). No
gate is added or loosened; `scripts/check-doc-authoring.mjs` is
untouched.
## Changeset
`.changeset/20751-services-strings-stage2-state-the-decision.md`:
`patch` for `@objectstack/connector-mcp`, `@objectstack/plugin-email`,
`@objectstack/service-knowledge`, `@objectstack/service-queue`,
`@objectstack/service-sms`, `@objectstack/service-storage` and
`@objectstack/trigger-record-change`.
Measured after building at the head:
- Each new sentence named above is in its package's built output (2
files each; `plugin-email`'s four descriptions read back from the built
module's exported `AUTH_EMAIL_CHANGE_NOTICE_TEMPLATES`, all four
id-free).
- A TypeScript scan of every string literal and template text in the
eight packages' built `.js`/`.mjs`/`.cjs` finds 0 tracker ids. Control:
the same scan reads 86 in `plugin-security`'s built output and 66 in
`service-automation`'s.
- `@objectstack/service-i18n` is deliberately NOT in the changeset. Its
only change is the route ledger, which no entry imports:
`I18N_ROUTE_LEDGER` and the new note are in 0 files of its `dist/`
(positive control: `registerRoutes` in 4). The same holds for
`service-storage`'s ledger (`STORAGE_ROUTE_LEDGER` in 0, control `reap
guard: kept` in 2), so that package's patch is for
`attachment-lifecycle.ts` alone.
## Text-only proof
A TypeScript-AST skeleton of each changed non-test `.ts` file: every
string literal and template text is a placeholder, a run of adjacent
string operands of a `+` chain is one string (embedded expressions kept
in order), identifiers and numbers are kept, and comments are never
read. The walk visits every child (no early exit). `383a00c7` against
the head: 14 of 14 SAME, node counts identical per file. Control on
scratch copies of `mcp-provider.ts`: renaming one identifier reads DIFF;
changing only a string's text reads SAME; re-splitting one template into
two `+` operands reads SAME.
## Pins
Four assertions named a tracker id and now name the sentence that
replaced it:
- `service-knowledge` `__tests__/event-sync-data-events.test.ts:166`,
`:167`: `toContain('cannot be repaired from the event stream')` and
`toContain('the lifecycle reap guard de-indexes those rows before they
are deleted')` instead of the two ids. The third half-pin
(`application-level predicate writes are not`) is unchanged, so both
halves of the honest line stay pinned.
- `trigger-record-change` `array-form-refusal-end-to-end.test.ts:116`
and `record-change-trigger.test.ts:183`: `toMatch(/multi-event arrays
are deferred until/)` instead of the id. The label now reads "states the
standing decision".
No `code` or `status` assertion was touched; none of these strings is a
coded refusal. Every other old fragment was searched repo-wide: no other
test, doc or fixture quotes them.
## Tests
All at head `7bcf2f613`, through `scripts/pm/os-verify-lock.sh`, each
`VERDICT command-exit 0`:
- Build: `turbo run build` over the eight packages and their dependency
closure (37/37), then `turbo run build --filter=./packages/*
--filter=./packages/*/*` (71/71) for the dist-reading gates.
- `vitest run --maxWorkers=2` per package: `plugin-email` 31 files, 510
tests; `service-storage` 41 files, 629 tests; `trigger-record-change` 10
files, 101 tests; `service-queue` 5 files, 77 tests; `service-i18n` 5
files, 74 tests; `service-sms` 5 files, 74 tests; `service-knowledge` 4
files, 44 tests; `connector-mcp` 3 files, 23 tests. All passed. The
three re-pinned files were also run with the verbose reporter, which
names the three cases holding the four re-pinned assertions as passed.
- `typecheck` for all eight, exit 0, including `check:test-typecheck:
OK` where the package wires it.
## Gates
- `node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack
--commands` (no paths) at `7bcf2f613`: 72 commands over 19 paths, run
one at a time from the worktree, each exit code recorded before any
pipe. `--ran`: "72 derived famil(ies) accounted for — 72 run, 0
NOT-MEASURED (a DERIVED zero — all 72 recorded an exit code and none of
them is 3)".
- `check:dual-build-cjs-loads` and `check:i18n` first answered
`PREREQUISITE NOT MET` (exit 3, only the closure was built). After the
full package build they and the other dist-reading gates were re-run,
all exit 0: 105 require entry points across 66 packages load; "all
bundles in sync"; `check:dts-closure` 71 built packages, 167/167
declaration files; `check:sourcemap-no-sources-content` 68 packages, 522
maps; `check:lean-entry-closure` and `check:published-files` green.
- `check-issue-citations`: "no issue citations added against 383a00c
(14 file(s) read)"; `check:nul-bytes`: OK, 9609 files, no raw ASCII
control bytes; `check:i18n-stale-fill`: "no new stale fills";
`check:type-check-debt`: "none above its recorded number";
`check:engine-double-contract`: OK.
- Outside the derived set, all exit 0: the eleven declared
wide-population families (`check:init-service-contract`,
`check:live-db-isolation`, `check:meta-type-normalized`,
`check:optional-error-sink`, `check:resume-authority-declared`,
`check:route-envelope`, `check:runner-env-posture`,
`check:settings-bind-window`, `check:startup-registry-verdict`,
`check:verify-stand-in`, `check:wildcard-fallthrough`) and the
artifact-roster families whose roster sits under one of this PR's
directories (`check-changeset-fixed`, `check-published-list-mirrors` and
its self-test, `check:authz-resolver`, `check:console-injection`,
`check:error-code-casing`, `check:filter-alias-parity`,
`check:published-readme-exports`, `check-dts-references --self-test`).
The path-scheduled CI jobs and the type-check lanes are CI's.
- `pnpm lint` (`eslint . --no-inline-config`, repo-wide, not narrowed)
at `7bcf2f613`: exit 0, 128 s under the lock.
- No gate run appended anything to `AGENTS.md`; the tree stayed clean
throughout.
## Acceptance notes
- `origin/main` moved 8 commits to `393ae878` after the branch point.
None touches any of this PR's 19 paths or any of the eight packages, and
the ledger is not among them, so the recomputed ledger stands on the
merged tree. The branch was not merged with `main`; the queue rebuilds
it there.
- Comments and test titles in these packages still cite numbers: a
comment is the sanctioned home for an internal anchor, and the ledger
does not read test files. Two conformance tests carry an id in their
failure text (`storage-route-ledger.conformance.test.ts:74`,
`i18n-route-ledger.conformance.test.ts:91`); they are test files,
outside the ledger and this stage.
- `packages/lint/src/validate-flow-trigger-readiness.ts:557` carries the
same 3457 deferral in the lint hint, and its test pins the id
(`validate-flow-trigger-readiness.test.ts:738`). `packages/lint` is not
in this stage. The trigger warning now uses the lint hint's own phrase
("multi-event arrays are deferred"), so the two read alike once that
stage lands.
- The remaining packages of this lane's share are later stages.
---
_Generated by [Claude
Code](https://claude.ai/code/session_01DiCSbmJrkzNhuEAier4VoJ)_
Co-authored-by: Claude <noreply@anthropic.com>1 parent 1c52a5e commit 6091136
19 files changed
Lines changed: 66 additions & 73 deletions
File tree
- .changeset
- packages
- connectors/connector-mcp/src
- plugins/plugin-email/src
- templates
- services
- service-i18n/src
- service-knowledge/src
- __tests__
- service-queue/src
- service-sms/src
- service-storage/src
- triggers/trigger-record-change/src
- scripts
Lines changed: 25 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
138 | 138 | | |
139 | 139 | | |
140 | 140 | | |
141 | | - | |
| 141 | + | |
| 142 | + | |
142 | 143 | | |
143 | 144 | | |
144 | 145 | | |
145 | 146 | | |
146 | 147 | | |
147 | 148 | | |
148 | | - | |
| 149 | + | |
| 150 | + | |
149 | 151 | | |
150 | 152 | | |
151 | 153 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
922 | 922 | | |
923 | 923 | | |
924 | 924 | | |
925 | | - | |
| 925 | + | |
| 926 | + | |
926 | 927 | | |
927 | 928 | | |
928 | 929 | | |
| |||
Lines changed: 3 additions & 2 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
124 | 124 | | |
125 | 125 | | |
126 | 126 | | |
127 | | - | |
128 | | - | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
129 | 130 | | |
130 | 131 | | |
131 | 132 | | |
| |||
Lines changed: 4 additions & 4 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
570 | 570 | | |
571 | 571 | | |
572 | 572 | | |
573 | | - | |
| 573 | + | |
574 | 574 | | |
575 | 575 | | |
576 | 576 | | |
| |||
598 | 598 | | |
599 | 599 | | |
600 | 600 | | |
601 | | - | |
| 601 | + | |
602 | 602 | | |
603 | 603 | | |
604 | 604 | | |
| |||
627 | 627 | | |
628 | 628 | | |
629 | 629 | | |
630 | | - | |
| 630 | + | |
631 | 631 | | |
632 | 632 | | |
633 | 633 | | |
| |||
657 | 657 | | |
658 | 658 | | |
659 | 659 | | |
660 | | - | |
| 660 | + | |
661 | 661 | | |
662 | 662 | | |
663 | 663 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
95 | 95 | | |
96 | 96 | | |
97 | 97 | | |
98 | | - | |
| 98 | + | |
99 | 99 | | |
100 | | - | |
| 100 | + | |
101 | 101 | | |
Lines changed: 2 additions & 2 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
163 | 163 | | |
164 | 164 | | |
165 | 165 | | |
166 | | - | |
167 | | - | |
| 166 | + | |
| 167 | + | |
168 | 168 | | |
169 | 169 | | |
170 | 170 | | |
| |||
Lines changed: 3 additions & 2 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
217 | 217 | | |
218 | 218 | | |
219 | 219 | | |
220 | | - | |
221 | | - | |
| 220 | + | |
| 221 | + | |
| 222 | + | |
222 | 223 | | |
223 | 224 | | |
224 | 225 | | |
| |||
Lines changed: 2 additions & 1 deletion
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
318 | 318 | | |
319 | 319 | | |
320 | 320 | | |
321 | | - | |
| 321 | + | |
| 322 | + | |
322 | 323 | | |
323 | 324 | | |
324 | 325 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
89 | 89 | | |
90 | 90 | | |
91 | 91 | | |
92 | | - | |
93 | | - | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
94 | 96 | | |
95 | 97 | | |
96 | 98 | | |
| |||
0 commit comments