Repository navigation
Commit e47355b
fix(auth, webhooks, messaging): runtime strings state each decision in words instead of a tracker number (stage 1) (#21218)
Part of #20751
Clause-②: no
**Stage 1 of the `domain:services` lane under the maintainer's A / A
ruling (5902360492): the `plugin-auth`, `plugin-webhooks` and
`service-messaging` 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. One module-private
interface loses the two members that held the citations as data (see
Text-only proof).
## What this does
The three packages' refusals, thrown errors, boot and log lines, auth
route-ledger notes and the `sys_http_delivery` field help (with its
`es-ES`, `ja-JP` and `zh-CN` variants) sent the reader to a tracker
number for the reason behind them. In form D, as the earlier stages of
the other lanes 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 84 ledgered occurrences in the three packages (claim `5936032644`),
re-derived from the ledger on `origin/main` at `5e5ce48c`: `plugin-auth`
28 (21 pairs, 13 files), `plugin-webhooks` 33 (19 pairs, 8 files),
`service-messaging` 23 (15 pairs, 8 files). They sit in 70 string sites.
### Rewritten in words
Author- and administrator-visible text first, log lines last.
| Where (head line) | Cited | The text now says | Decision read from |
|---|---|---|---|
| `service-messaging` `http-delivery.object.ts:237`, `attempts` help,
and its three locale leaves | 8069 | "never sent, and not redeliverable,
because it carries no signature" | the dev report on the card (the
refusal keys on `attempts === 0` because a parked row has no signature)
under the maintainer's minimal-cut ruling |
| `service-messaging` `http-outbox.ts:514`, `DELIVERY_NEVER_SENT`
message | 7799, 8069 | "so the delivery would go out UNSIGNED — which is
never allowed" | 8069 ruling (redelivery never sends unsigned) |
| `service-messaging` `http-outbox.ts:547`, `DELIVERY_NOT_ELIGIBLE`
message | 8069 | "refusing rather than sending, because a check that
could not run never reads as allowed" | 8069 (a guard that throws
refuses: fail-closed) |
| `service-messaging` `outbox.ts:187`, `ack()` refusal | 11453 | "there
is deliberately no ack() spelling: cancelling a pending row is not part
of this contract until a live consumer needs it" | triage scope ruling
5400898843 (no cancel member; deferred until a live consumer pulls on
it) |
| `plugin-webhooks` `auto-enqueuer.ts:536`, the parked row's `error`
(shown in Setup) | 7799 or 7986, 8069 | "recording it here rather than
delivered unsigned [or: without the headers it was authored with], and
rather than discarding it without a trace" | 8069 ruling (a discarded
event leaves a dead row, never an unsigned send) |
| `plugin-webhooks` `redeliver-guard.ts:90` and `:123`, redeliver
refusals | 8069; 7799, 8069 | "so it is refused rather than risk going
out unsigned"; "refusing rather than sending it unsigned" | same |
| `plugin-auth` `audience-posture.ts:245`, boot refusal | 5205 |
"Refused, never coerced: an unrecognised value must not fall through to
a more permissive posture than the one intended." | maintainer ruling on
the card (shape 2) and ACCEPT 5181815596: an out-of-vocabulary value is
refused loudly, never read as the permissive branch |
| `plugin-auth` `internal-field-readback.ts:336`, thrown composition
error | 8118 | "Wire the ObjectQL engine, which provides the accessor
(an `internal` column is withheld from every ordinary read and recovered
only through it)" | triage routing decision 5269775253 (route 3: redact
on read, recover through a privileged accessor) |
| `plugin-auth` `walled-owner-verification-path.ts:367`, boot warning |
12751 | "a declared owner is stamped verified only when an
operator-provisioned path CREATES the account" | card title and body |
| `plugin-auth` `objectql-adapter.ts:400`, warning | 5814 | "`$ieq` is
deliberately deferred until there is demonstrated pull for it" |
maintainer ruling 2026-08-09, option 3 |
| `plugin-auth` `auth-route-ledger.ts:146`, change-email note | 7735,
8019 | "change-email was ruled table stakes, so it is wired rather than
de-booked ... the OLD address also gets an auth.email_change_notice, so
a hijacked session cannot move the account identity unannounced" |
ruling 5261746059, and the 2026-08-12 ruling on 8019 (notify the old
address, do not gate) |
| `plugin-auth` `auth-route-ledger.ts:157`, delete-user note | 7735 |
"self-service deletion needs a deliberate B2B design first, because it
reaches record ownership and tenant data (maintainer ruling 2026-08-12)"
| ruling 5261746059 |
| `plugin-auth` `auth-route-ledger.ts:234`, import-users note | 2766 |
"A dedicated identity-import door rather than the generic sys_user
import, because a direct ObjectQL write would bypass better-auth's
password hashing and mint no sys_account credential" | the decided items
in the card body |
| `plugin-auth` `auth-route-ledger.ts:236`, `:237`, SSO register notes |
9653 | "platform-admin gated ahead of the delegation (ADR-0068 D4), so
registering an identity provider never rests on the vendor's own check
alone" | triage ruling 5331642704 |
| `plugin-auth` `auth-route-ledger.ts:254`, add-member note | 9941 | "on
multi-org it is the only UI path that attaches an existing user to an
organization" | card body (the admin-guarded wrapper over
`auth.api.addMember`) |
| `plugin-auth` `last-admin-guard.ts:795`, standing-key exclusion reason
| 10982 | "The resolver now applies isGrantActive ..." (the sentence
already carries the ruling: a lapsed membership is NO membership) |
maintainer ruling A recorded on the card |
| `plugin-auth` `last-admin-guard.ts:840`, standing-key exclusion reason
| 11663 (L2) | "now that deployment config names the platform-admin
holder, from the configured-and-verified email pair" | ruled direction
in the card body |
| `plugin-webhooks` `auto-enqueuer.ts:517`, error | 8069 | "DISCARDED
WITH NO TRACE in sys_http_delivery — the very loss a parked
subscription's dead row exists to prevent" | 8069 ruling |
| `plugin-webhooks` `auto-enqueuer.ts:833`, warning | 3196 | "there is
no manual fire path (the 'api' trigger was removed because nothing could
fire it)" | card body (trim, since no fire path exists) |
| `plugin-webhooks` `webhook-outbox-plugin.ts:343`, error | 7799, 8069 |
"may send a delivery that can no longer be authenticated (an unsigned
send, which webhook delivery otherwise never makes)" | 8069 ruling |
| `plugin-webhooks` `bootstrap-declared-webhooks.ts:266`, warning | 7799
| "its signing secret cannot be stored encrypted, and it is never stored
in cleartext instead" | card ACCEPT (refuse rather than fall back to
cleartext) |
| `plugin-webhooks` `migrate-webhook-secrets.ts:134`, `:135`, warnings |
7799, 7986 | "no CryptoProvider to move them into their encrypted
columns"; "row left unchanged, still cleartext in definition_json" |
both cards (the credentials move out of `definition_json` into encrypted
columns) |
| `plugin-auth` `two-factor-reenrollment-verified-reset.ts:130`, `:149`,
`:170`, error lines | 10700 | "so a re-enrolled TOTP secret may be live
at sign-in without ever having been confirmed" | triage route 5367978973
(reset `verified` so the new secret is inert until confirmed) |
| `plugin-auth` `member-role-canonical.ts:259` (debug) and `:473`
(warning) | 8317 | "so ObjectStack and better-auth read one role"; "the
owner-versus-member inversion" | maintainer ruling, option A, and the
card title |
### Citation only (the sentence already stated the decision)
- `plugin-auth`: `adopt-membership.ts:246` (7725, "adopted the existing
sys_member row instead of inserting a second one ... unique by
declaration"); `auth-manager.ts:2837` (3585, "will now 500 every
/get-session instead of degrading to a missing set-auth-jwt header");
`auth-plugin.ts:507`, `:511` (2780, 4790, the budget's name and its
per-node impact); `last-admin-guard.ts:1215` (6084, "the state a DELETED
'admin_full_access' permission-set row leaves behind: it un-makes every
platform admin at once"); `member-role-canonical.ts:463`, `:484` (8317);
`objectql-adapter.ts:492` (5813, "refusing the query rather than
dropping the predicate"); `otp-send-guard.ts:174` (4808, "bounded rather
than silently truncated"); `sso-client-secret.ts:339` (8009, now naming
the move out of cleartext `oidc_config`).
- `plugin-webhooks`: `auto-enqueuer.ts:543`, `:764` (8022, "re-arms on
registration and at the next periodic refresh"), `:742` (the say-once
repeat line), `:756`, `:759` (7799 or 7986, 8069);
`webhook-headers-gate.ts:155` (7986, 8558, 8566, "checked at the write
door because one step later there is nothing left to check");
`webhook-headers.ts:253`, `:324`, `:335` (7986, 8558);
`webhook-secret.ts:291`, `:301` (7799, 8542, "this is NOT an unsigned
webhook, and delivering it unsigned would strip the receiver of its only
proof of origin").
- `service-messaging`: `http-delivery.object.ts:176` (8118, "never
returned on the generic data path ... recovers it through the engine's
privileged accessor") and `:260` (8069); `http-outbox.ts:573` (8069);
`sql-http-outbox.ts:327` (8118); `outbox.ts:210` (11453, 11859).
Every cited card was read (REST, open or closed) before its string was
rewritten. Two answer 404 on the issue itself, 11453 and 11859, and were
read through their comment threads, which carry the scope ruling and the
decision analysis.
### Translations
- `attempts` help: the `es-ES`, `ja-JP` and `zh-CN` leaves were real
translations; each now carries the same decision ("porque no lleva
firma", "署名を持たないため", "因不带签名") and no number.
- `headers_json` and `error` help: in all three locales these leaves
were byte copies of the English source. They are now copies of the
revised English, and `node scripts/check-i18n-bundles.mjs --write
--filter=service-messaging` recorded their new digests in the three
`*.source-hashes.generated.ts` companions. No digest was hand-edited;
the `en` bundle was already byte-identical to what the extractor writes.
- Tracker numbers left in the package's locale bundles: 0.
## 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 113 lines and adds none: exactly the 29 file blocks of
the three packages. Every other row is byte-identical (a scripted
comparison of every other key: 0 moved).
| | before (`5e5ce48c`) | after |
|---|---|---|
| `plugin-auth` | 28 occurrences, 21 pairs, 13 files | 0 |
| `plugin-webhooks` | 33 occurrences, 19 pairs, 8 files | 0 |
| `service-messaging` | 23 occurrences, 15 pairs, 8 files | 0 |
| whole ledger | 550 occurrences, 375 pairs, 147 files | 466
occurrences, 320 pairs, 118 files |
`pnpm check:doc-authoring` at the head: "393 pinned site(s) across 118
file(s), 85833 string(s) read in 1245 parsed source(s), no growth, no
burn-down unrecorded" (before: 463 sites across 147 files). No gate is
added or loosened; `scripts/check-doc-authoring.mjs` is untouched.
## Changeset
`.changeset/20751-services-strings-state-the-decision.md`: `patch` for
`@objectstack/plugin-auth`, `@objectstack/plugin-webhooks` and
`@objectstack/service-messaging`. Measured after building the three
packages at the head: each new author-visible sentence named above is in
its package's `dist/index.js` and `dist/index.cjs` (2 files each). A
TypeScript scan of every string literal and template in the three
packages' built `.js`/`.cjs` finds 0 tracker ids (the ids left in `dist`
are in comments). Control: the same scan reads 86 in `plugin-security`'s
built output and 66 in `service-automation`'s. The auth route ledger
does not ship: `AUTH_ROUTE_LEDGER` is in 0 files under
`packages/plugins/plugin-auth/dist`.
## Text-only proof
A TypeScript-AST skeleton of each changed non-test `.ts` file, where
every string literal and template text is a placeholder, a run of
adjacent string operands of a `+` chain is one string (only its embedded
expressions are kept), and comments and JSDoc are never read. `5e5ce48c`
against the head `0bb7a64f`: 31 of 32 SAME, token counts identical per
file. The one DIFF is `auto-enqueuer.ts`, and it is exactly the
citations-as-data: the module-private `DropReason` interface loses its
`issue` and `issues` members, the two credential constants lose those
two properties, and three interpolations (`credential.issue` twice,
`credential.issues` once) leave three templates. Nothing else in the
file's skeleton moves. Control: the first draft of the tool compared
only each file's first statement and reported `auto-enqueuer.ts` SAME;
that miss is what exposed it, and the corrected tool reports the DIFF
above.
## Pins
Three assertions named the old strings and now name the new substance:
- `auth-plugin.test.ts:1706`: `per-number OTP send budget bound to the
kernel cache service`.
- `last-admin-guard.test.ts:1905`, `:1908`: `toThrow(/state a DELETED
'admin_full_access' permission-set row leaves behind/)` instead of the
bare citation. The sibling test still asserts `code:
'PERMISSION_DENIED'`, `status: 403` on the same refusal.
- `scim-case-insensitive-identifier.test.ts:357`: `` `$ieq` is
deliberately deferred until there is demonstrated pull `` instead of the
citation.
Every other old fragment was searched repo-wide. The remaining hits are
comments, test titles and assertion labels, and two test files
(`rate-limit-storage.test.ts`, `otp-send-guard.test.ts`) that pass their
OWN subject string into the counter store and assert it back; none reads
production text. No `code` or `status` assertion was touched.
## Tests
All at head `0bb7a64f`, through `scripts/pm/os-verify-lock.sh`, every
verdict `VERDICT command-exit 0`:
- Build: `turbo run build` over the closure of the three packages and
`@objectstack/cli` (59/59 tasks), then the three packages rebuilt at the
head, then `turbo run build --filter=./packages/*
--filter=./packages/*/*` (71/71).
- `@objectstack/plugin-auth`: 116 files, 2484 tests passed.
- `@objectstack/plugin-webhooks`: 13 files, 160 tests passed.
`@objectstack/service-messaging`: 46 files, 507 tests passed.
- `typecheck` for all three, including `check:test-typecheck: OK` for
`plugin-auth` and `plugin-webhooks`.
## Gates
- `node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack
--commands` (no paths) at `0bb7a64f`: 75 commands, run one at a time
from the worktree, each exit code recorded before any pipe. `--ran`: "75
derived famil(ies) accounted for — 75 run, 0 NOT-MEASURED (a DERIVED
zero — all 75 recorded an exit code and none of them is 3)".
- `check:dual-build-cjs-loads` first answered `PREREQUISITE NOT MET`
(exit 3: only the closure was built). After the full package build it
and the other dist-reading gates were re-run, all exit 0: 105 require
entry points across 66 packages load; `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:i18n`: "all bundles in sync"; `check:i18n-stale-fill`: "0
stale-fill leaf/leaves"; `check:issue-citations`: "no issue citations
added against 5e5ce48 (32 file(s) read)"; `check:nul-bytes`: OK, 9850
files; `check:type-check-debt`: "none above its recorded number".
- Outside the derived set, all exit 0: the ten 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: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 `0bb7a64f`: exit 0, 101 s under the lock.
## Acceptance notes
- `origin/main` moved to `862f12c0` (four commits) after the branch
point. None touches any of this PR's 37 paths, and none adds or removes
an id-bearing line in a non-test source under `packages/`, so the
recomputed ledger stands on the merged tree. The branch was not merged
with `main`; the queue rebuilds it there.
- Comments in the three packages still cite numbers: a comment is the
sanctioned home for an internal anchor, and dead citations in this
lane's comments are a sibling card's work.
- `docs/qa/platform-checklist/areas/integration-system.json:572` cites
7799 and 8069 in its own clause prose; it quotes none of the text
changed here.
- 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 c6b6889 commit e47355b
37 files changed
Lines changed: 113 additions & 206 deletions
File tree
- .changeset
- packages
- plugins
- plugin-auth/src
- plugin-webhooks/src
- services/service-messaging/src
- objects
- translations
- scripts
| 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 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
243 | 243 | | |
244 | 244 | | |
245 | 245 | | |
246 | | - | |
| 246 | + | |
247 | 247 | | |
248 | 248 | | |
249 | 249 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
242 | 242 | | |
243 | 243 | | |
244 | 244 | | |
245 | | - | |
| 245 | + | |
246 | 246 | | |
247 | 247 | | |
248 | 248 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
2834 | 2834 | | |
2835 | 2835 | | |
2836 | 2836 | | |
2837 | | - | |
| 2837 | + | |
2838 | 2838 | | |
2839 | 2839 | | |
2840 | 2840 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1703 | 1703 | | |
1704 | 1704 | | |
1705 | 1705 | | |
1706 | | - | |
| 1706 | + | |
1707 | 1707 | | |
1708 | 1708 | | |
1709 | 1709 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
504 | 504 | | |
505 | 505 | | |
506 | 506 | | |
507 | | - | |
| 507 | + | |
508 | 508 | | |
509 | 509 | | |
510 | 510 | | |
511 | | - | |
| 511 | + | |
512 | 512 | | |
513 | 513 | | |
514 | 514 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
143 | 143 | | |
144 | 144 | | |
145 | 145 | | |
146 | | - | |
| 146 | + | |
147 | 147 | | |
148 | 148 | | |
149 | 149 | | |
| |||
154 | 154 | | |
155 | 155 | | |
156 | 156 | | |
157 | | - | |
| 157 | + | |
158 | 158 | | |
159 | 159 | | |
160 | 160 | | |
| |||
231 | 231 | | |
232 | 232 | | |
233 | 233 | | |
234 | | - | |
| 234 | + | |
235 | 235 | | |
236 | | - | |
237 | | - | |
| 236 | + | |
| 237 | + | |
238 | 238 | | |
239 | 239 | | |
240 | 240 | | |
| |||
251 | 251 | | |
252 | 252 | | |
253 | 253 | | |
254 | | - | |
| 254 | + | |
255 | 255 | | |
256 | 256 | | |
257 | 257 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
333 | 333 | | |
334 | 334 | | |
335 | 335 | | |
336 | | - | |
| 336 | + | |
| 337 | + | |
337 | 338 | | |
338 | 339 | | |
339 | 340 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1902 | 1902 | | |
1903 | 1903 | | |
1904 | 1904 | | |
1905 | | - | |
| 1905 | + | |
1906 | 1906 | | |
1907 | 1907 | | |
1908 | | - | |
| 1908 | + | |
1909 | 1909 | | |
1910 | 1910 | | |
1911 | 1911 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
792 | 792 | | |
793 | 793 | | |
794 | 794 | | |
795 | | - | |
| 795 | + | |
796 | 796 | | |
797 | 797 | | |
798 | 798 | | |
| |||
837 | 837 | | |
838 | 838 | | |
839 | 839 | | |
840 | | - | |
| 840 | + | |
| 841 | + | |
841 | 842 | | |
842 | 843 | | |
843 | 844 | | |
| |||
1211 | 1212 | | |
1212 | 1213 | | |
1213 | 1214 | | |
1214 | | - | |
| 1215 | + | |
1215 | 1216 | | |
1216 | 1217 | | |
1217 | 1218 | | |
| |||
0 commit comments