Skip to content

Commit ca74de1

Browse files
committed
Merge remote-tracking branch 'origin/main' into claude/issue-20583-conversions-lint-and-refusal
2 parents f7968d9 + 7510663 commit ca74de1

32 files changed

Lines changed: 258 additions & 116 deletions
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
---
2+
'@objectstack/spec': patch
3+
---
4+
5+
feat(spec): the protocol-18 migration step records the html-tier `div` refusal as the semantic entry `ui-html-page-div-refused` (#20592)
6+
7+
Clause-②: no
8+
9+
`MIGRATIONS_BY_MAJOR[18].semantic` gains one entry, `ui-html-page-div-refused`.
10+
It records the narrowing `@objectstack/cli` takes on when its JSX page gate
11+
reaches the SDUI component manifest that `@objectstack/console` ships: a project
12+
with no `sdui.manifest.json` of its own has its `kind: 'html'` pages checked
13+
against that manifest, which does not declare `div`, so `objectstack validate`,
14+
`compile` and `lint` refuse a `div` there (`jsx-forbidden-tag`,
15+
`jsx-unknown-component`). The entry prescribes `box` for a plain wrapper, names
16+
the layout containers to reach for instead only when their layout is wanted, and
17+
says how to prove the rewrite done.
18+
19+
What moves for a consumer of this package: `MIGRATIONS_BY_MAJOR` carries the
20+
entry, and `objectstack migrate meta` prints it, because its default range
21+
already runs to protocol 18, the highest major with a step. Nothing else does.
22+
The protocol-18 step is not cut yet, so `spec-changes.json` and the protocol
23+
upgrade guide, which project the steps up to the current protocol major, are
24+
unchanged, and no schema accepts or refuses anything it did not before.
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
---
2+
'@objectstack/service-messaging': patch
3+
---
4+
5+
Provenance comments in `service-messaging` were re-anchored
6+
7+
Comment and docblock lines under `src/` that cited tracker numbers which no
8+
longer resolve on GitHub now cite the commit in this repository's history that
9+
decided the matter, and say in their own words what was decided. Comments
10+
only: no type, schema, export, refusal text or runtime behaviour changes.

‎packages/services/service-messaging/src/delivery-update-tenant-audit.integration.test.ts‎

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
* tenant-classification contract this file pins (#10740) is unchanged —
1919
* threaded `tenantId`, never `bypassTenantAudit`.
2020
*
21-
* [#11453] `SqlNotificationOutbox.ack` has since made the SAME move for the
21+
* [commit 1a47a5368] `SqlNotificationOutbox.ack` has since made the SAME move for the
2222
* same reason: its new status precondition ("this row must still be
2323
* `in_flight`") is a compare-and-set, and a predicate on the by-id path is
2424
* silently discarded, so it rides `multi: true` too. Its audit op is
@@ -89,10 +89,10 @@ let driver: SqlDriver;
8989
let warns: Array<{ msg: string; meta: any }>;
9090
/** Every `options` bag that reached `SqlDriver.update` — the `update` op only. */
9191
let driverUpdates: Array<{ object: string; id: unknown; options: any }>;
92-
/** Every `options` bag that reached `SqlDriver.updateMany` — `redeliver`'s op since #11009, the notification `ack`'s since #11453, and the HTTP `ack`'s since #17634. */
92+
/** Every `options` bag that reached `SqlDriver.updateMany` — `redeliver`'s op since #11009, the notification `ack`'s since commit 1a47a5368, and the HTTP `ack`'s since #17634. */
9393
let driverUpdateManys: Array<{ object: string; where: unknown; options: any }>;
9494

95-
/** The audit line for the PREDICATE op — `redeliver`'s write since #11009, the notification `ack`'s since #11453. */
95+
/** The audit line for the PREDICATE op — `redeliver`'s write since #11009, the notification `ack`'s since commit 1a47a5368. */
9696
const auditedUpdateMany = (object: string): boolean =>
9797
warns.some((w) => w.msg.includes(`[tenant-audit] updateMany on tenant-scoped object "${object}"`));
9898

@@ -226,7 +226,7 @@ describe('ack — the two dispatcher sites are a classified global sweep (update
226226
//
227227
// [#17634] The dispatcher's ack hands the claim credential, so it is a
228228
// compare-and-set on the predicate path and the reading moves to the
229-
// `updateMany` spy — the move the notification ack made in #11453. The
229+
// `updateMany` spy — the move the notification ack made in commit 1a47a5368. The
230230
// claim path writes there too (its reap and its atomic claim), so the
231231
// filter names what an ACK write looks like: a scalar id bound to
232232
// `in_flight` AND to the claiming node. That predicate IS the
@@ -283,7 +283,7 @@ describe('ack — the two dispatcher sites are a classified global sweep (update
283283
]);
284284
// ② Declared global, for both organizations' rows.
285285
//
286-
// [#11453] The ack's op is `updateMany` now, so the reading moves to
286+
// [commit 1a47a5368] The ack's op is `updateMany` now, so the reading moves to
287287
// that spy. The claim path writes there too (its reap and its atomic
288288
// claim), so the filter names what an ACK write looks like — and that
289289
// predicate is not incidental: `{ id: <scalar>, status: 'in_flight' }`

‎packages/services/service-messaging/src/dispatcher.ts‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,7 @@ export class NotificationDispatcher {
194194
//
195195
// No partition lock is needed, and none was ever in force: the reap only
196196
// moves rows already past their timeout, a claim only takes `pending`
197-
// rows, and an ack whose claim was reaped matches nothing (#11859) — while
197+
// rows, and an ack whose claim was reaped matches nothing (commit d9cf78eaa) — while
198198
// the per-claim reap, run under partition p's lock, was already rewriting
199199
// rows in every other partition.
200200
//
@@ -368,7 +368,7 @@ export class NotificationDispatcher {
368368
}
369369

370370
/**
371-
* [#11453] Record one attempt's outcome, tolerating the ONE refusal a
371+
* [commit 1a47a5368] Record one attempt's outcome, tolerating the ONE refusal a
372372
* correct dispatcher can legitimately provoke.
373373
*
374374
* `ack()` now refuses a row that is not `in_flight`, and this loop can meet
@@ -389,7 +389,7 @@ export class NotificationDispatcher {
389389
*/
390390
private async ackAttempt(row: ClaimedDeliveryRecord, result: AckResult): Promise<void> {
391391
try {
392-
// [#11859] The record is handed back WHOLE: its (claimedBy,
392+
// [commit d9cf78eaa] The record is handed back WHOLE: its (claimedBy,
393393
// claimedAt) pair is the claim credential the store stamped, and
394394
// the ack's compare-and-set binds it — this loop never needs to
395395
// know or repeat its own nodeId.

‎packages/services/service-messaging/src/email-channel.test.ts‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ describe('email channel', () => {
144144
it('END TO END: one emit, one outbox, one dispatcher tick — the row lands `dead`, ⛔ not `success`', async () => {
145145
// The unit assertions above are about a return value; THIS is the
146146
// reading the card is written against — what an operator sees on
147-
// `sys_notification_delivery`. Before #18424 this row read
147+
// `sys_notification_delivery`. Before commit 879b51270 this row read
148148
// `status: 'success'`, which is the silent half of the defect.
149149
const data = fakeData();
150150
const outbox = new MemoryNotificationOutbox(1);
@@ -580,7 +580,7 @@ describe('email channel', () => {
580580
});
581581
});
582582

583-
// ── #11741 — organization threading. This channel is the producer the
583+
// ── Commit b706af987 — organization threading. This channel is the producer the
584584
// ruling names as HOLDING an organization (`delivery.notification
585585
// .organizationId`, the tenant stamp the outbox snapshots per delivery),
586586
// so it threads that value into the email service's input on BOTH of its

‎packages/services/service-messaging/src/email-channel.ts‎

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ export interface EmailSenderSurface {
3535
html?: string;
3636
text?: string;
3737
/**
38-
* Structural mirror of `SendEmailInput.organizationId` (#11741) —
38+
* Structural mirror of `SendEmailInput.organizationId` (commit b706af987) —
3939
* the tenant stamp for `sys_email.organization_id`, threaded from
4040
* `delivery.notification.organizationId` when the delivery holds
4141
* one. Optional: an org-less delivery sends without it.
@@ -56,7 +56,7 @@ export interface EmailSenderSurface {
5656
to: string | string[];
5757
data?: Record<string, unknown>;
5858
locale?: string;
59-
/** Same tenant stamp as on `send` (#11741) — forwarded by the email service into the send it performs. */
59+
/** Same tenant stamp as on `send` (commit b706af987) — forwarded by the email service into the send it performs. */
6060
organizationId?: string;
6161
}): Promise<{ id?: string; status?: string; error?: string } | unknown>;
6262
/**
@@ -81,7 +81,7 @@ export interface EmailChannelOptions {
8181
* Resolve the email service; `undefined` ⇒ there is no transport, which
8282
* {@link MessagingChannel.isAvailable} reports as
8383
* `transport_not_configured` and {@link MessagingChannel.send} REFUSES with
84-
* the same reason (#18424). ⛔ Not a no-op success: a delivery nothing was
84+
* the same reason (commit 879b51270). ⛔ Not a no-op success: a delivery nothing was
8585
* sent for is never reported as delivered.
8686
*/
8787
getEmail(): EmailSenderSurface | undefined;
@@ -112,7 +112,7 @@ export interface EmailChannelOptions {
112112

113113
/**
114114
* The ONE token both members of this channel use for "there is no transport"
115-
* (#18424) — the reason `isAvailable()` already returns, reused verbatim so the
115+
* (commit 879b51270) — the reason `isAvailable()` already returns, reused verbatim so the
116116
* refusal `send()` writes onto the delivery row and the suppression fan-out
117117
* records on `sys_notification.suppressed_channels` name the same condition.
118118
*
@@ -160,7 +160,7 @@ const EMAIL_SHAPE = (s: string): boolean => {
160160
* path) use that one resolution. A producer-set `payload.locale` — the
161161
* pre-ruling single value for the whole notification — is no longer consulted.
162162
*
163-
* Failure is always REPORTED, never absorbed (#18424): no email service ⇒ a
163+
* Failure is always REPORTED, never absorbed (commit 879b51270): no email service ⇒ a
164164
* refusal carrying the declared `transport_not_configured` reason, graded
165165
* `permanent` so the row dead-letters on attempt one; a recipient with no
166166
* resolvable address ⇒ a reported failure. Either way the delivery row shows
@@ -273,7 +273,7 @@ export function createEmailChannel(opts: EmailChannelOptions): MessagingChannel
273273
async send(ctx: MessagingChannelContext, delivery: Delivery): Promise<SendResult> {
274274
const email = opts.getEmail();
275275
if (!email) {
276-
// [#18424] The SAME condition `isAvailable()` answers above, so
276+
// [commit 879b51270] The SAME condition `isAvailable()` answers above, so
277277
// it gets the same answer — a refusal naming
278278
// `transport_not_configured`, ⛔ never `{ ok: true }`.
279279
//
@@ -340,7 +340,7 @@ export function createEmailChannel(opts: EmailChannelOptions): MessagingChannel
340340
to: address,
341341
...(data !== undefined ? { data } : {}),
342342
...(templateLocale ? { locale: templateLocale } : {}),
343-
// #11741 — this channel HOLDS the organization (the
343+
// Commit b706af987 — this channel HOLDS the organization (the
344344
// tenant stamp the outbox snapshots per delivery), so
345345
// it threads it for the sys_email.organization_id
346346
// stamp. Absent stays absent — never fabricated.
@@ -380,7 +380,7 @@ export function createEmailChannel(opts: EmailChannelOptions): MessagingChannel
380380
subject: rendered.subject,
381381
...(rendered.html !== undefined ? { html: rendered.html } : {}),
382382
...(rendered.text !== undefined ? { text: rendered.text } : {}),
383-
// #11741 — same threading as the template arm above.
383+
// Commit b706af987 — same threading as the template arm above.
384384
...(n.organizationId ? { organizationId: n.organizationId } : {}),
385385
});
386386
const id = result?.id;
@@ -399,7 +399,7 @@ export function createEmailChannel(opts: EmailChannelOptions): MessagingChannel
399399
// are `IEmailService.sendTemplate`'s own error codes plus this
400400
// channel's missing-capability refusal above.
401401
//
402-
// [#18424] `transport_not_configured` joins them, and the grade is
402+
// [commit 879b51270] `transport_not_configured` joins them, and the grade is
403403
// driven rather than assumed: in the shipping composition the same
404404
// condition already terminates a claimed row at once — the mount
405405
// gate unmounts the channel and the dispatcher acks

‎packages/services/service-messaging/src/http-dispatcher.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -269,7 +269,7 @@ export class HttpDispatcher {
269269
* [#17634] Record one attempt's outcome with the claim credential this
270270
* node's `claim()` stamped on the row, tolerating the ONE refusal a correct
271271
* dispatcher can legitimately provoke — `NotificationDispatcher.ackAttempt`'s
272-
* shape (#11453, #11859).
272+
* shape (commits 1a47a5368, d9cf78eaa).
273273
*
274274
* A send slower than `claimTtlMs` lets the visibility-timeout reap return the
275275
* row to `pending`, and another node — or this one, on a later tick —

‎packages/services/service-messaging/src/http-outbox-ack-claim-ownership.integration.test.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
* #17634 — an HTTP ack proves OWNERSHIP of the claim it completes, not just
55
* that a row id exists: a late ack from a claim the visibility-timeout reap
66
* took back must not overwrite the live re-claim on `sys_http_delivery`. The
7-
* notification outbox closed the same shape in #11859; this file pins the HTTP
7+
* notification outbox closed the same shape in commit d9cf78eaa; this file pins the HTTP
88
* outbox against the same sequence.
99
*
1010
* ## The reachable sequence this file replays — for real

‎packages/services/service-messaging/src/http-outbox.ts‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -363,7 +363,7 @@ export type HttpAckResult = HttpAckSuccess | HttpAckFailure;
363363
* stamps on a row when {@link IHttpOutbox.claim} takes it — handed back to
364364
* {@link IHttpOutbox.ack} so the outcome is written only while that claim still
365365
* holds the row. It is exactly the pair the notification outbox's
366-
* `ClaimedDeliveryRecord` guarantees for `INotificationOutbox.ack` (#11859), with
366+
* `ClaimedDeliveryRecord` guarantees for `INotificationOutbox.ack` (commit d9cf78eaa), with
367367
* the same meaning:
368368
*
369369
* - ownership is proven by ROUND-TRIPPING what `claim()` returned, never by the
@@ -649,7 +649,7 @@ export interface IHttpOutbox {
649649
*
650650
* [#17634] **Pass `claimed`** — the claim credential on the row {@link claim}
651651
* returned (the row itself will do). With it, `ack` is the ownership-checked
652-
* completion `INotificationOutbox.ack` performs (#11453, #11859):
652+
* completion `INotificationOutbox.ack` performs (commits 1a47a5368, d9cf78eaa):
653653
*
654654
* ⛔ **Precondition: the row MUST still be held by that claim.** Two tests,
655655
* both re-stated IN the conditional write: the row is `in_flight`, AND its

‎packages/services/service-messaging/src/inbox-caller.ts‎

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
/**
44
* Authenticated-caller scoping for the plugin-facing inbox surface
5-
* (ADR-0030 Layer 5) — the write door (#10753) and the read door (#11452).
5+
* (ADR-0030 Layer 5) — the write door (#10753) and the read door (commit 3b5f0360c).
66
*
77
* ## The shape this closes
88
*
@@ -22,7 +22,7 @@
2222
* permission check sees it either. Unconstrained and undeclared, in both
2323
* directions.
2424
*
25-
* The READ side has the same shape (#11452): `listInbox(userId, opts)` keys
25+
* The READ side has the same shape (closed by commit 3b5f0360c): `listInbox(userId, opts)` keys
2626
* its whole read — inbox rows joined with read-state — on the same free
2727
* parameter, so an in-process caller could read ANY user's inbox titles,
2828
* bodies and read-state. Both doors resolve their recipient here.
@@ -83,9 +83,9 @@ import type { ExecutionContext } from '@objectstack/spec/kernel';
8383
* `ExecutionContext` the caller was handed, passed through whole.
8484
*
8585
* Passed WHOLE, deliberately: the measured defect family behind
86-
* `assembleExecutionContext` (#6071, #6206, #6551) is "a field exists on
87-
* `ExecutionContext`, one copy carries it, another silently does not". A
88-
* hand-picked `{ userId }` slice here would be one more such copy.
86+
* `assembleExecutionContext` (#6071, #6551, and the share-link envelope trim
87+
* commit 8e13ca876 undid) is "a field exists on `ExecutionContext`, one copy
88+
* carries it, another silently does not". A hand-picked `{ userId }` slice here would be one more such copy.
8989
*/
9090
export type InboxCaller = ExecutionContext;
9191

0 commit comments

Comments
 (0)