diff --git a/.changeset/20596-service-storage-provenance-anchors.md b/.changeset/20596-service-storage-provenance-anchors.md new file mode 100644 index 00000000000..a3b1f1f140e --- /dev/null +++ b/.changeset/20596-service-storage-provenance-anchors.md @@ -0,0 +1,10 @@ +--- +'@objectstack/service-storage': patch +--- + +Provenance comments in `service-storage` were re-anchored + +Comment and docblock lines under `src/` that cited tracker numbers which no +longer resolve on GitHub now cite the commit in this repository's history that +decided the matter, and say in their own words what was decided. Comments +only: no type, schema, export, log or refusal text, or runtime behaviour changes. diff --git a/packages/services/service-storage/src/attachment-access-hooks.test.ts b/packages/services/service-storage/src/attachment-access-hooks.test.ts index 4a3117fe77b..4a4281786bd 100644 --- a/packages/services/service-storage/src/attachment-access-hooks.test.ts +++ b/packages/services/service-storage/src/attachment-access-hooks.test.ts @@ -222,7 +222,7 @@ describe('attachment access — beforeDelete (uploader or parent editor)', () => }); // ───────────────────────────────────────────────────────────────────────── -// #10091 — beforeUpdate: uploader or parent editor, + the attach rule on a +// [commit da891e0ef] beforeUpdate: uploader or parent editor, + the attach rule on a // re-point. The delete gate's rule applied to the verb that could otherwise // rewrite it away (the comment kit — derived from this one — has gated // update since #4630; the source kit was missing the limb its derivative @@ -618,7 +618,7 @@ describe('unscoped multi-delete (no id, no where) — #4757 through the wired en }); // ───────────────────────────────────────────────────────────────────────── -// #10091 through the WIRED engine — the update verb. +// Commit da891e0ef's gate through the WIRED engine — the update verb. // // Same rig as the #4757 block above, driving `ql.update('sys_attachment', …)` // end to end: the unscoped refusal reaches the handler through the @@ -763,7 +763,7 @@ describe('unscoped multi-update (no id, no where) — #10091 through the wired e // rebuilt a five-field projection of the caller's execution envelope before // handing it to `ISharingService.canEdit`, whose contract declares the FULL // envelope and whose doc block tells callers they "MUST NOT rebuild a subset -// of it" (#6523 / the #6206 ruling). +// of it" (commit aa4b90d9a, the full-envelope ruling). // ───────────────────────────────────────────────────────────────────────── /** @@ -911,9 +911,9 @@ describe('#7145 — caller envelope forwarded to the sharing gate', () => { ); const forwarded = canEdit.mock.calls[0]![2] as unknown as Record; - // Every principal field survives — the #6523 contract's unit is the - // envelope, and #6206 forbids rebuilding a subset of it. `uploaded_by` - // stamping does not touch the context. + // Every principal field survives — the contract's unit is the envelope + // (commit aa4b90d9a), and the full-envelope ruling forbids rebuilding a + // subset of it. `uploaded_by` stamping does not touch the context. expect(forwarded).toEqual(DELEGATED_PRINCIPAL_FIELDS); // …and every middleware-private key resolved for `sys_attachment` is gone. for (const key of OPERATION_PRIVATE_KEYS) expect(forwarded).not.toHaveProperty(key); diff --git a/packages/services/service-storage/src/attachment-access-hooks.ts b/packages/services/service-storage/src/attachment-access-hooks.ts index c4d5a09cf44..2c3d1dd7197 100644 --- a/packages/services/service-storage/src/attachment-access-hooks.ts +++ b/packages/services/service-storage/src/attachment-access-hooks.ts @@ -25,7 +25,7 @@ import type { * on the parent; v1 enforces read visibility — strictly better than * nothing, edit-parity is a tracked follow-up.) `uploaded_by` is * server-stamped from the session — a client-supplied value never wins. - * - beforeUpdate (#10091): the caller must be the uploader OR hold edit on + * - beforeUpdate (commit da891e0ef): the caller must be the uploader OR hold edit on * the parent record — the delete rule, applied to the verb that could * otherwise rewrite the other two gates away: an ungated update let any * member re-point `parent_id` at a record they cannot see, or rewrite @@ -124,9 +124,9 @@ function asIdList(id: unknown): Array | null { * session snapshot lacks `permissions`, which sharing bypasses need. * * [#7145] Forwarded as the full envelope, which is what `ISharingService` - * declares for every parameter this value is handed to and what the #6206 + * declares for every parameter this value is handed to and what the full-envelope * ruling requires of every caller: they "MUST NOT rebuild a subset of it" - * (#6523). The five-field projection this replaced (`userId` / `tenantId` / + * (commit aa4b90d9a). The five-field projection this replaced (`userId` / `tenantId` / * `positions` / `permissions` / `isSystem`) was doing two jobs at once, and * only one of them was correct — same defect, same kit, one package over from * `comment-access-hooks.ts` (#7141 / PR #7143), which this mirrors: @@ -467,7 +467,7 @@ export function installAttachmentAccessHooks( // the mechanism was ruled onto `beforeUpdate` as well; the refusal stays // PER REGISTRATION. This one carries #4757's delete refusal under its // grandfathered `ATTACHMENT_DELETE_DENIED` envelope; the `beforeUpdate` - // registration above declares the update-verb refusal (#10091) under the + // registration above declares the update-verb refusal (commit da891e0ef) under the // standard catalog code — the same both-verbs pairing the derived // comment kit ships. { object: 'sys_attachment', packageId: PACKAGE_ID, dispatchUnscopedMultiWrite: true }, diff --git a/packages/services/service-storage/src/attachment-lifecycle.ts b/packages/services/service-storage/src/attachment-lifecycle.ts index 2e0349f5425..0500fd5eacf 100644 --- a/packages/services/service-storage/src/attachment-lifecycle.ts +++ b/packages/services/service-storage/src/attachment-lifecycle.ts @@ -367,7 +367,7 @@ export async function findFileHolder( /** * The BATCHED form of {@link findFileHolder} — "which of these files is still - * held?" — for callers holding many rows at once (#11427). + * held?" — for callers holding many rows at once (commit c3c72a4bc). * * Record file-field hydration is such a caller: it must reach the same verdict * the download path reaches (#10246) or one `sys_file` row gets two answers, diff --git a/packages/services/service-storage/src/backfill-sys-file-organizations.ts b/packages/services/service-storage/src/backfill-sys-file-organizations.ts index becbe0a9330..c06b9b8865a 100644 --- a/packages/services/service-storage/src/backfill-sys-file-organizations.ts +++ b/packages/services/service-storage/src/backfill-sys-file-organizations.ts @@ -83,7 +83,7 @@ * `createRecordOrganizationResolver`, and the divergence from the precedent is * the point of this paragraph. That resolver's limb 0 reads * `tenancy.organizationField`, a STAMP-ONLY key whose consumers are scope-pinned - * by the #8778 ruling (widened by name on cloud#1395) to exactly three + * by its ruling (commit 7901b2dd2; widened by name on cloud#1395) to exactly three * platform-row writers; a fourth needs its own maintainer ruling. It would also * be the WRONG question here. That key answers "which column says who this row * is ABOUT"; this sweep needs "which column is this subject WALLED by", because diff --git a/packages/services/service-storage/src/file-read-tenancy-posture-admission.test.ts b/packages/services/service-storage/src/file-read-tenancy-posture-admission.test.ts index edf2a4fb17a..9d0c0781f94 100644 --- a/packages/services/service-storage/src/file-read-tenancy-posture-admission.test.ts +++ b/packages/services/service-storage/src/file-read-tenancy-posture-admission.test.ts @@ -576,10 +576,10 @@ describe('[#15352] §5b — a `tenancy` service that was REGISTERED and FAILED i * the digits are now asserted, and they are the declared ones. * * What happened: the authorizer always re-raised the brand - * (`isAuthzStoreUnavailableError(err) ⇒ throw`, #13279), and + * (`isAuthzStoreUnavailableError(err) ⇒ throw`, commit 6a180e42d), and * `registerStorageRoutes`' `authorizeDownload` absorbed that re-raise one * frame up in `catch { verdict = 'deny' }`, rendering an outage as the gate's - * own capability refusal — the confusion #13279 exists to prevent. That + * own capability refusal — the confusion commit 6a180e42d was made to prevent. That * `catch` now RELAYS the declared envelope instead (⛔ not a bare re-raise: * the route's outer `catch` would answer `500 INTERNAL`, and the shared * render for an escaped envelope is #16545 and has not landed). diff --git a/packages/services/service-storage/src/file-reference-lifecycle.ts b/packages/services/service-storage/src/file-reference-lifecycle.ts index 9850e2684b6..8191e150580 100644 --- a/packages/services/service-storage/src/file-reference-lifecycle.ts +++ b/packages/services/service-storage/src/file-reference-lifecycle.ts @@ -108,7 +108,7 @@ const SYSTEM_CTX = { isSystem: true, [RAW_FILE_VALUES_CONTEXT_KEY]: true } as co * * This mirrors `StorageMetadataStore`'s `StorageWriteContext` threading * (`createFile` #12745, `createSession` #12928, the update/delete halves - * #13178) rather than inventing a second convention: the caller hands the + * in commit f087c376f) rather than inventing a second convention: the caller hands the * engine the organization it is acting in as an execution context, and the * platform's existing insert-side chokepoint decides the rest. ⛔ The * organization is NOT written onto the payload here — whether this object has diff --git a/packages/services/service-storage/src/metadata-store.ts b/packages/services/service-storage/src/metadata-store.ts index dbccbbc4c7d..aaedd7b2240 100644 --- a/packages/services/service-storage/src/metadata-store.ts +++ b/packages/services/service-storage/src/metadata-store.ts @@ -77,7 +77,7 @@ export interface FileRecord { * every walled deployment — a warning naming exactly this defect ("writes will * not be tenant-isolated"). * - * ## What the SAME context does on update / delete (#13178) + * ## What the SAME context does on update / delete (commit f087c376f) * * ⚠️ Not the same thing, and the difference is the whole reason the * `update`/`delete` doors are not a copy-paste of the insert ones. Write-side @@ -350,7 +350,7 @@ export class StorageMetadataStore { /** * Update one `sys_file` row. * - * `context` carries the acting organization (#13178). It is the SAME channel + * `context` carries the acting organization (commit f087c376f). It is the SAME channel * {@link createFile} opened in #12745 — `{ context: { tenantId } }` on the * engine options bag — and this door is the `update` half of that insert * that #12745 did not repair. What the value MEANS differs by verb, and @@ -381,7 +381,7 @@ export class StorageMetadataStore { // stand up a second isolation mechanism outside the driver that owns // the one real one. A no-engine deployment has no wall to be on the // wrong side of (the same sentence `createFile`'s stand-in already - // makes), so this branch is unchanged by #13178. + // makes), so this branch is unchanged by commit f087c376f. this.files.set(id, merged); return merged; } @@ -395,7 +395,7 @@ export class StorageMetadataStore { /** * Delete one `sys_file` row. * - * `context` carries the acting organization (#13178) — the `delete` half of + * `context` carries the acting organization (commit f087c376f) — the `delete` half of * #12745's insert, repaired for the same reason and through the same * channel as {@link updateFile}. See {@link StorageWriteContext} for what * the value does on this verb (it scopes the statement; it stamps nothing). @@ -480,7 +480,7 @@ export class StorageMetadataStore { /** * Update one `sys_upload_session` row. * - * `context` carries the acting organization (#13178) — the `update` half of + * `context` carries the acting organization (commit f087c376f) — the `update` half of * the insert #12928 repaired, the `sys_upload_session` sibling of * {@link updateFile}. Same channel, same chokepoint, and the same split * between stamping and scoping that {@link StorageWriteContext} records. @@ -522,7 +522,7 @@ export class StorageMetadataStore { /** * Delete one `sys_upload_session` row. * - * `context` carries the acting organization (#13178) — the `delete` half of + * `context` carries the acting organization (commit f087c376f) — the `delete` half of * #12928's insert. See {@link StorageWriteContext} for what the value does * on this verb. */ diff --git a/packages/services/service-storage/src/storage-routes.authz-outage-relay.test.ts b/packages/services/service-storage/src/storage-routes.authz-outage-relay.test.ts index 3ed2fec95a7..99fd7683d51 100644 --- a/packages/services/service-storage/src/storage-routes.authz-outage-relay.test.ts +++ b/packages/services/service-storage/src/storage-routes.authz-outage-relay.test.ts @@ -8,12 +8,12 @@ * ## What was measured, and why this row came first * * `buildFileReadAuthorizer` re-raises `AuthzStoreUnavailableError` rather than - * returning `'deny'` (#13279). `registerStorageRoutes`' `authorizeDownload` + * returning `'deny'` (commit 6a180e42d). `registerStorageRoutes`' `authorizeDownload` * then wrapped the whole authorizer call in `catch { verdict = 'deny' }` one * frame up, so the re-raise was absorbed and the outage rendered as * `403 FILE_DOWNLOAD_DENIED` / `403 ATTACHMENT_DOWNLOAD_DENIED`. Fail-CLOSED, * never an admission — but indistinguishable on the wire from a genuine - * refusal, which is the exact confusion #13279 exists to prevent, and the worst + * refusal, which is the exact confusion commit 6a180e42d was made to prevent, and the worst * shape in this card's six-site census (the datasource and settings families * lost the envelope into a 500; this one lost it into a *verdict*). * diff --git a/packages/services/service-storage/src/storage-routes.ts b/packages/services/service-storage/src/storage-routes.ts index b2d3f27491f..44ecc8e64ae 100644 --- a/packages/services/service-storage/src/storage-routes.ts +++ b/packages/services/service-storage/src/storage-routes.ts @@ -39,7 +39,7 @@ export type FileReadVerdict = 'allow' | 'deny' | 'unauthenticated'; * session with no active organization resolves to `undefined` rather than to a * guess. * - * ⚠️ Since #13178 the same value also travels on this door's `updateFile` / + * ⚠️ Since commit f087c376f the same value also travels on this door's `updateFile` / * `updateSession` calls, where it does something DIFFERENT — it scopes the * statement instead of stamping a column, so a row belonging to another * organization is no longer reachable (see `StorageWriteContext`). Two @@ -196,9 +196,9 @@ export function registerStorageRoutes( } catch (err) { // [#15999, ruling item 3] An UNREADABLE authorization store is an outage, // not a verdict. `buildFileReadAuthorizer` already re-raises the brand - // rather than returning `'deny'` (#13279) — and until now this `catch` + // rather than returning `'deny'` (commit 6a180e42d) — and until now this `catch` // absorbed that re-raise one frame up and rendered it as this gate's own - // `403`, which is precisely the confusion #13279 exists to prevent: an + // `403`, which is precisely the confusion commit 6a180e42d was made to prevent: an // outage answered as a capability denial, indistinguishable on the wire // from a genuine refusal. // @@ -452,7 +452,7 @@ export function registerStorageRoutes( // --------------------------------------------------------------------------- httpServer.post(`${basePath}/upload/complete`, async (req: IHttpRequest, res: IHttpResponse) => { try { - // [#13178] Bound, not discarded: this handler already resolved the + // [commit f087c376f] Bound, not discarded: this handler already resolved the // session and threw the value away, which is what left the commit // statement unscoped and raising `[tenant-audit]`. const session = await requireUploadSession(req, res); @@ -592,7 +592,7 @@ export function registerStorageRoutes( // --------------------------------------------------------------------------- httpServer.put(`${basePath}/upload/chunked/:uploadId/chunk/:chunkIndex`, async (req: IHttpRequest, res: IHttpResponse) => { try { - // [#13178] Bound rather than discarded — see the commit door above. + // [commit f087c376f] Bound rather than discarded — see the commit door above. // Named `authSession` because `session` below is the sys_upload_session // ROW; these are two different things and the handler needs both. const authSession = await requireUploadSession(req, res); @@ -680,7 +680,7 @@ export function registerStorageRoutes( // --------------------------------------------------------------------------- httpServer.post(`${basePath}/upload/chunked/:uploadId/complete`, async (req: IHttpRequest, res: IHttpResponse) => { try { - // [#13178] Bound rather than discarded — see the commit door above. + // [commit f087c376f] Bound rather than discarded — see the commit door above. const authSession = await requireUploadSession(req, res); if (authSession === false) return; const writeContext: StorageWriteContext = { organizationId: authSession?.organizationId }; @@ -750,7 +750,7 @@ export function registerStorageRoutes( // --------------------------------------------------------------------------- httpServer.get(`${basePath}/upload/chunked/:uploadId/progress`, async (req: IHttpRequest, res: IHttpResponse) => { try { - // [#13178] Bound rather than discarded — the progress door can WRITE + // [commit f087c376f] Bound rather than discarded — the progress door can WRITE // (`expireIfPastDeadline` statuses the row `expired`), so it owes the // same context the other two write doors do. const authSession = await requireUploadSession(req, res); diff --git a/packages/services/service-storage/src/storage-service-plugin.test.ts b/packages/services/service-storage/src/storage-service-plugin.test.ts index 8ba1a836e00..98d44069776 100644 --- a/packages/services/service-storage/src/storage-service-plugin.test.ts +++ b/packages/services/service-storage/src/storage-service-plugin.test.ts @@ -464,7 +464,7 @@ describe('StorageServicePlugin: sys_file orphan lifecycle wiring (#2755)', () => // Lifecycle hooks (afterDelete/afterInsert, plus afterUpdate since #10171 // gave the update verb its detach leg) + access hooks - // (beforeInsert/beforeUpdate/beforeDelete, #10091 added the update verb) + // (beforeInsert/beforeUpdate/beforeDelete; commit da891e0ef added the update verb) // — see attachment-lifecycle.ts and attachment-access-hooks.ts. // // [#10240] There is exactly ONE `beforeDelete` here, and it is the access diff --git a/packages/services/service-storage/src/storage-service-plugin.ts b/packages/services/service-storage/src/storage-service-plugin.ts index 39edbb24d28..82307bbcee5 100644 --- a/packages/services/service-storage/src/storage-service-plugin.ts +++ b/packages/services/service-storage/src/storage-service-plugin.ts @@ -389,7 +389,7 @@ export class StorageServicePlugin implements Plugin { // deployment flag, fresh — before any byte is deleted. installFileReferenceHooks(engine as any, () => this.storage, ctx.logger); // "Is anything still holding this tombstone?" for RECORD FILE-FIELD - // HYDRATION (#11427). The download path got this question in #10246; + // HYDRATION (commit c3c72a4bc). The download path got this question in #10246; // the record read kept the older `status === 'committed'` rule, so one // `sys_file` row answered 200 at `/files/:id` and a bare id inside a // record payload — which UI and export render as "no attachment". @@ -1045,7 +1045,7 @@ function buildAuthSessionResolver( * SUPPORTED and its behaviour here is exactly what it was. * * The throw is raised inside the authorizer's own `try`, so it takes the - * #13279 relay that block already runs for the identical fault one seam over + * relay that block has run since commit 6a180e42d for the identical fault one seam over * (`isAuthzStoreUnavailableError(err)` re-raises instead of returning * `'deny'`). Deliberately NOT a second net. * @@ -1054,9 +1054,9 @@ function buildAuthSessionResolver( * authorizer in `catch { verdict = 'deny' }`, so the re-raise was absorbed one * frame up and a failed posture read rendered as the download gate's own 403 * refusal — fail-CLOSED, never an admission, but wearing the costume of a - * capability denial, which is the confusion #13279 exists to prevent. That - * flattening was PRE-EXISTING (it had swallowed the #13279 permission-store - * outage at this door since that card landed, out of the same `catch`) and was + * capability denial, which is the confusion commit 6a180e42d was made to prevent. That + * flattening was PRE-EXISTING (it had swallowed the branded permission-store + * outage at this door since commit 6a180e42d landed, out of the same `catch`) and was * repaired by #15999's ruling item 3: the `catch` now RELAYS the declared * `503` / `SERVICE_UNAVAILABLE` envelope. The pin below still asserts the * outage CLASS — never 200, never a minted capability — and its 403 arm retired @@ -1146,7 +1146,7 @@ function buildFileReadAuthorizer( // posture-conditional API-key refusals unreachable here — see // `resolveAdmissionTenancyPosture` above for the classification, and for // why a quiet `catch` at this seam would be the defect rather than the - // fix. Raised INSIDE this `try`, so an outage takes the #13279 relay in + // fix. Raised INSIDE this `try`, so an outage takes the relay (commit 6a180e42d) in // the `catch` below rather than a new net. const tenancyPosture = await resolveAdmissionTenancyPosture(registry); const authz = await resolveAuthzContext({ ql: engine, headers, getSession, tenancyPosture }); @@ -1226,7 +1226,7 @@ function buildFileReadAuthorizer( } return 'deny'; } catch (err) { - // [#13279] A permission-store outage is not a read verdict. Re-raised so + // [commit 6a180e42d] A permission-store outage is not a read verdict. Re-raised so // the file-read door answers the 503 it is instead of a silent 'deny' // indistinguishable from a genuine refusal. if (isAuthzStoreUnavailableError(err)) throw err; diff --git a/packages/services/service-storage/src/sys-file-copy-backfill-organization-stamping.test.ts b/packages/services/service-storage/src/sys-file-copy-backfill-organization-stamping.test.ts index a96595a1ac5..acdf025a76d 100644 --- a/packages/services/service-storage/src/sys-file-copy-backfill-organization-stamping.test.ts +++ b/packages/services/service-storage/src/sys-file-copy-backfill-organization-stamping.test.ts @@ -4,7 +4,7 @@ // // Four doors on this object have been given the acting organization one card // at a time — `createFile` (#12745), `createSession` (#12928), and the -// `update`/`delete` halves (#13178) — and all four run through +// `update`/`delete` halves (commit f087c376f) — and all four run through // `StorageMetadataStore`, which threads a `StorageWriteContext` into // `context.tenantId` so the platform's insert-side chokepoint can stamp the // column. These two bypass that store entirely: diff --git a/packages/services/service-storage/src/tenant-audit-update-delete-half-repairs.test.ts b/packages/services/service-storage/src/tenant-audit-update-delete-half-repairs.test.ts index 2447cfa6853..1ccf0ed344a 100644 --- a/packages/services/service-storage/src/tenant-audit-update-delete-half-repairs.test.ts +++ b/packages/services/service-storage/src/tenant-audit-update-delete-half-repairs.test.ts @@ -1,6 +1,6 @@ // Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license. // -// #13178 — the four HALF-REPAIRS: the `update` / `delete` siblings of the two +// Commit f087c376f — the four HALF-REPAIRS: the `update` / `delete` siblings of the two // inserts #12745 and #12928 repaired. // // ## What was half-repaired diff --git a/packages/services/service-storage/src/tombstone-hydration-download-agreement.test.ts b/packages/services/service-storage/src/tombstone-hydration-download-agreement.test.ts index 9bb43b49fac..ee84cf718a6 100644 --- a/packages/services/service-storage/src/tombstone-hydration-download-agreement.test.ts +++ b/packages/services/service-storage/src/tombstone-hydration-download-agreement.test.ts @@ -1,7 +1,7 @@ // Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license. /** - * #11427 — record file-field hydration and the download path must answer the + * Commit c3c72a4bc — record file-field hydration and the download path must answer the * SAME question about one `sys_file` row. * * ## The defect @@ -317,7 +317,7 @@ describe('#11427 — file-field hydration and the download path agree about one * The batched question and the single-file question must be the SAME question. * * `findHeldFiles` exists only because asking `findFileHolder` per row would be - * N queries per read. The moment the two disagree, the divergence #11427 fixes + * N queries per read. The moment the two disagree, the divergence commit c3c72a4bc fixed * reopens one layer down — hydration and the download path would once again be * reading one row through two predicates. So the equivalence is pinned over a * matrix that exercises both limbs and both of their absences, rather than diff --git a/packages/services/service-storage/src/translations/index.ts b/packages/services/service-storage/src/translations/index.ts index 9bedbe31b26..22621115880 100644 --- a/packages/services/service-storage/src/translations/index.ts +++ b/packages/services/service-storage/src/translations/index.ts @@ -26,7 +26,7 @@ import { esESGeneratedSourceHashes } from './es-ES.source-hashes.generated.js'; * ## The provenance companions are READ here, not merely recorded * * `os i18n extract --source-hashes` writes `.source-hashes.generated.ts` - * beside these bundles (maintainer ruling #12069 Option A, #11671). A record + * beside these bundles (maintainer ruling #12069 Option A, commit 09b4f4e4e). A record * says: "this locale's leaf at that path is still a byte copy of THAT source * revision". Recording alone changes nothing a user sees — the substitution is * what {@link withSourceFallback} does, and until it was wired here this set