Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions .changeset/20595-platform-objects-provenance-anchors.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
---
'@objectstack/platform-objects': patch
---

Provenance comments in `@objectstack/platform-objects` cite the commits that decided them, not tracker numbers that no longer resolve

Clause-②: no

Docblocks and comments across the package cited issue-tracker numbers that now answer 404 on GitHub.
Each now cites the commit in this repository's history that made the decision it describes, except one
that cites ADR-0104's 2026-09-05 addendum, the record of that ruling. Some of these docblocks sit on
exported members, so the reworded text appears in the published declaration files (`apps`, `identity`,
`metadata-translations` and `system` `index.d.ts` / `index.d.mts`), and the field comments esbuild keeps
appear in the JavaScript output (`index`, `apps`, `audit`, `identity` and `plugin`, `.js` / `.mjs`).

Comment only: no export, type, error code, status, message text or runtime behaviour changes.
4 changes: 2 additions & 2 deletions packages/platform-objects/scripts/i18n-extract.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
* --out=packages/platform-objects/src/apps/translations
*
* `--source-hashes` also emits `<locale>.source-hashes.generated.ts` — the
* provenance companion from maintainer ruling #12069 Option A (#11671). Without
* provenance companion from maintainer ruling #12069 Option A (commit 09b4f4e4e). Without
* it, a leaf filled from the source and then left behind when the source was
* revised is indistinguishable BY VALUE from a real translation, so it publishes
* a superseded draft under a green `check:i18n` forever. This package opts in;
Expand Down Expand Up @@ -280,7 +280,7 @@ const config: ObjectStackDefinition = defineStack({
SysSsoProvider,
// Stable @better-auth/scim 1.7.x model set + the ObjectStack-owned
// credential store (#3653). The rc.1-era SysScimProvider retired under
// #11757.
// commit 4d25d22d4.
SysScimConnectionBinding,
SysScimConnectionCredential,
SysScimGroup,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ const OWNED_OBJECTS = new Set([
// identity — external SSO provider (admin-facing, better-auth-managed)
'sys_sso_provider',
// identity — stable @better-auth/scim 1.7.x model set + the ObjectStack-owned
// credential store (#3653; the rc.1-era sys_scim_provider retired under #11757)
// credential store (#3653; the rc.1-era sys_scim_provider retired under commit 4d25d22d4)
'sys_scim_connection_binding', 'sys_scim_connection_credential', 'sys_scim_group',
'sys_scim_group_member', 'sys_scim_identity_tombstone', 'sys_scim_projection_grant',
'sys_scim_subject', 'sys_scim_user',
Expand Down
2 changes: 1 addition & 1 deletion packages/platform-objects/src/apps/translations/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -276,7 +276,7 @@ export const en: TranslationData = {
// The platform's own record pages (`@objectstack/platform-objects/pages`,
// contributed by plugin-auth and plugin-security). Their page-level `label`
// is the only key the extractor reaches -- everything else on them is
// authored as an inline locale map under `slots.*` (#14817). English
// authored as an inline locale map under `slots.*` (commit 5529a374e). English
// mirrors the literal in the page metadata, as above.
sys_user_detail: { label: 'User' },
sys_organization_detail: { label: 'Organization' },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ import { withSourceFallback } from './source-hash.js';
*
* A leaf with NO recorded hash is legacy-trusted and served verbatim.
*
* ## The generated half joined this seam in #11671
* ## The generated half joined this seam in commit 09b4f4e4e
*
* The fourth argument is the GENERATED provenance table
* (`<locale>.source-hashes.generated.ts`, written by `os i18n extract
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,7 @@ describe('the shipped bundles', () => {
});

// ───────────────────────────────────────────────────────────────────────────
// The GENERATED half (#11671, maintainer ruling #12069 Option A)
// The GENERATED half (commit 09b4f4e4e, maintainer ruling #12069 Option A)
//
// Same mechanism, different predicate: these leaves got their text by being
// COPIED from the source, so the bytes are evidence and the record only says
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@
*
* ## Scope: BOTH halves — and the correction that put the generated half here
*
* This note used to end with a claim that is FALSE, and #11671 is its
* This note used to end with a claim that is FALSE, and the defect commit 09b4f4e4e fixed is its
* counterexample. It read:
*
* > `objects` and `metadataForms` are GENERATED (`*.generated.ts`) and are
Expand Down Expand Up @@ -211,7 +211,7 @@ export const HAND_AUTHORED_SECTIONS = ['apps', 'dashboards', 'pages'] as const;
* judged by {@link findStaleFills}. Their recorded digests live in the
* generated `<locale>.source-hashes.generated.ts`.
*
* These were excluded from this module until #11671 measured that the hole it
* These were excluded from this module until commit 09b4f4e4e acted on the measurement that the hole it
* closes occurs here too — see the module note for the claim that was wrong and
* why it was wrong.
*
Expand Down Expand Up @@ -567,7 +567,7 @@ function setDeep(target: Record<string, unknown>, path: string, value: string):
* optional `filledFrom` judges the generated ones ({@link findStaleFills}), over
* whatever sections `filledFrom` itself records.
* Omitting `filledFrom` leaves the generated sections entirely legacy-trusted,
* which is what every caller did before #11671 and is still the honest default
* which is what every caller did before commit 09b4f4e4e and is still the honest default
* for a bundle with no committed `<locale>.source-hashes.generated.ts`.
*
* The input is never mutated. Returns the same reference when nothing is stale,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ export const SysImportJob = ObjectSchema.create({
// ── lifecycle timestamps ──
started_at: Field.datetime({ label: 'Started At', required: false, group: 'State' }),
completed_at: Field.datetime({ label: 'Completed At', required: false, group: 'State' }),
// [#11374 route A] The value is `context.userId`, stamped by the rest-server
// [commit 4805b5619, route A] The value is `context.userId`, stamped by the rest-server
// import route (`String(context?.userId ?? context?.user?.id ?? '')` in
// `rest-server.ts`) — i.e. a `sys_user.id`. The bound is derived by
// referenced-column transitivity from three converging in-repo producers,
Expand Down
2 changes: 1 addition & 1 deletion packages/platform-objects/src/identity/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ export { SysSsoProvider } from './sys-sso-provider.object.js';
// ── SCIM 2.0 provisioning (@better-auth/scim) ──────────────────────
// The stable 1.7.x model set (#3653) — seven library-managed tables plus the
// ObjectStack-owned credential store the app-owned verifyBearerToken uses.
// (The rc.1-era `SysScimProvider` connection row retired under #11757.)
// (The rc.1-era `SysScimProvider` connection row retired under commit 4d25d22d4.)
export { SysScimConnectionBinding } from './sys-scim-connection-binding.object.js';
export { SysScimConnectionCredential } from './sys-scim-connection-credential.object.js';
export { SysScimGroup } from './sys-scim-group.object.js';
Expand Down
6 changes: 3 additions & 3 deletions packages/platform-objects/src/identity/sys-account.object.ts
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ export const SysAccount = ObjectSchema.create({
provider_id: Field.text({
label: 'Provider ID',
required: true,
// [#11374] Transitive bound: SSO-registered providers are the widest
// [commit 3954fb7df] Transitive bound: SSO-registered providers are the widest
// producer, and sys_sso_provider.provider_id declares maxLength: 255;
// better-auth's built-in social providers are short fixed slugs.
maxLength: 255,
Expand All @@ -177,7 +177,7 @@ export const SysAccount = ObjectSchema.create({
account_id: Field.text({
label: 'Provider Account ID',
required: true,
// [#11374] Bound from the identity-provider norms for the two federated
// [commit 3954fb7df] Bound from the identity-provider norms for the two federated
// shapes this column stores: an OIDC `sub` MUST NOT exceed 255 ASCII
// chars (OIDC Core §2) and a SAML persistent/transient NameID MUST NOT
// exceed 256 chars (SAML Core 2.0 §8.3.7/§8.3.8) — 256 is the wider of
Expand Down Expand Up @@ -276,7 +276,7 @@ export const SysAccount = ObjectSchema.create({
required: false,
}),

// [#8676] `internal: true` — never returned on the generic data path.
// [commit d6e80b28b] `internal: true` — never returned on the generic data path.
// Both columns below are one-way password hashes (ADR-0100's third
// channel), and BOTH serialized on `/api/v1/data/sys_account` before this
// flag: to an admin for every user's row, and to a member for their own
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { describe, it, expect } from 'vitest';
import { SysApiKey } from './sys-api-key.object.js';
import * as specIdentity from '@objectstack/spec/identity';

// ─── [#8715] `sys_api_key` has exactly ONE declaration — this object ────────
// ─── [commit 2c86fe3ea] `sys_api_key` has exactly ONE declaration — this object ────────
//
// `@objectstack/spec/identity` used to publish an `ApiKeySchema` that
// documented better-auth's `apiKey` PLUGIN shape — a plugin this platform
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -292,7 +292,7 @@ export const SysApiKey = ObjectSchema.create({
key: Field.text({
label: 'Hashed Key',
required: true,
// [#11374] Exact producer bound: the only writer is
// [commit 3954fb7df] Exact producer bound: the only writer is
// `packages/core/src/security/api-key.ts` (`hashApiKey` — "sha256(raw)
// hex — store this in sys_api_key.key"), a fixed 64-hex-char digest.
// better-auth's apiKey plugin is not loaded, so no other producer exists.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ export const SysDeviceCode = ObjectSchema.create({
device_code: Field.text({
label: 'Device Code',
required: true,
// [#11374] Upstream hard cap: better-auth 1.7.1's device-authorization
// [commit 3954fb7df] Upstream hard cap: better-auth 1.7.1's device-authorization
// plugin refuses ANY generated code — custom generators included — longer
// than 191 chars at runtime (`validateGeneratedCode`), and its
// `deviceCodeLength` option schema is `max(191)` (default 40). Nothing
Expand All @@ -87,7 +87,7 @@ export const SysDeviceCode = ObjectSchema.create({
user_code: Field.text({
label: 'User Code',
required: true,
// [#11374] Same upstream hard cap as device_code: `validateGeneratedCode`
// [commit 3954fb7df] Same upstream hard cap as device_code: `validateGeneratedCode`
// refuses > 191 chars and `userCodeLength` is `max(191)` (default 8).
maxLength: 191,
description: 'Short user-facing code (e.g. ABCD-EFGH)',
Expand All @@ -110,7 +110,7 @@ export const SysDeviceCode = ObjectSchema.create({
status: Field.text({
label: 'Status',
required: true,
// [#11374] The value domain is the closed literal set the plugin's own
// [commit 3954fb7df] The value domain is the closed literal set the plugin's own
// routes write — 'pending' | 'approved' | 'denied', 8 chars at the
// widest. 64 follows the landed machine-vocabulary precedent
// (sys_session.revoke_reason, maxLength: 64) so a future status word can
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ export const SysOauthAccessToken = ObjectSchema.create({
client_id: Field.text({
label: 'Client ID',
required: true,
// [#11374] Bound from the referenced column: this is a foreign key to
// [commit 3954fb7df] Bound from the referenced column: this is a foreign key to
// sys_oauth_application.client_id, which declares maxLength: 255 (and
// upstream @better-auth/oauth-provider's oauthClient.clientId is a
// unique string — varchar(255) on MySQL). A referencing column takes the
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ export const SysOauthClientResource = ObjectSchema.create({
client_id: Field.text({
label: 'Client ID',
required: true,
// [#11374] Bound from the referenced column: this is a foreign key to
// [commit 3954fb7df] Bound from the referenced column: this is a foreign key to
// sys_oauth_application.client_id, which declares maxLength: 255 (and
// upstream @better-auth/oauth-provider's oauthClient.clientId is a
// unique string — varchar(255) on MySQL). A referencing column takes the
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ export const SysOauthConsent = ObjectSchema.create({
client_id: Field.text({
label: 'Client ID',
required: true,
// [#11374] Bound from the referenced column: this is a foreign key to
// [commit 3954fb7df] Bound from the referenced column: this is a foreign key to
// sys_oauth_application.client_id, which declares maxLength: 255 (and
// upstream @better-auth/oauth-provider's oauthClient.clientId is a
// unique string — varchar(255) on MySQL). A referencing column takes the
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ export const SysOauthRefreshToken = ObjectSchema.create({
client_id: Field.text({
label: 'Client ID',
required: true,
// [#11374] Bound from the referenced column: this is a foreign key to
// [commit 3954fb7df] Bound from the referenced column: this is a foreign key to
// sys_oauth_application.client_id, which declares maxLength: 255 (and
// upstream @better-auth/oauth-provider's oauthClient.clientId is a
// unique string — varchar(255) on MySQL). A referencing column takes the
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import { ObjectSchema, Field } from '@objectstack/spec/data';
* `plugin-auth/src/scim-connection-service.ts`.
*
* Deliberately NOT a resurrection of `sys_scim_provider` (which retires under
* #11757): a row here is one bearer credential FOR a connection, not the
* commit 4d25d22d4): a row here is one bearer credential FOR a connection, not the
* connection itself. Several rows may authenticate the same `connection_id`
* (staged rotation); the connection's durable lifecycle state lives in
* `sys_scim_connection_binding`, written by the library.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@
// [#7826] `sys_session`'s ADR-0057 lifecycle declaration, at the SPEC tier.
//
// This is the third control on the card: the declaration parses, and neither
// of #10165's two `ttl.onlyWhen` conflict refines fires for it.
// of commit 801296050's two `ttl.onlyWhen` conflict refines fires for it.
//
// ⚠️ "Neither refine fires" is worth nothing as a bare absence — `sys_session`
// declares no `archive` and no rotation `storage`, so of course they do not
// fire, and the same green would be printed by a build in which both refines
// had been deleted. So each is measured against its own counterfactual: the
// exact declaration plus the conflicting block must be REFUSED, with #10165's
// exact declaration plus the conflicting block must be REFUSED, with commit 801296050's
// own message. That turns "no refine fired" into a statement about live rules.
//
// The sweep behaviour these keys buy — the tombstone-sparing and positive
Expand Down Expand Up @@ -55,7 +55,7 @@ describe('[#7826] sys_session lifecycle declaration', () => {
expect((SysDeviceCode as any).lifecycle.ttl.expireAfter).toBe(lifecycle.ttl.expireAfter);
});

// ── #10165's two refines: not fired here, and proved to be live ──────────
// ── commit 801296050's two refines: not fired here, and proved to be live ──────────

it('declares neither conflicting block, so neither #10165 refine fires', () => {
expect(lifecycle.archive).toBeUndefined();
Expand Down
4 changes: 2 additions & 2 deletions packages/platform-objects/src/identity/sys-session.object.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ export const SysSession = ObjectSchema.create({
// ordinary row that looks MAXIMALLY expired. A `ttl` keyed on `expires_at`
// without this filter would therefore reap the ADR-0069 D4 audit records
// FIRST AND HARDEST — the very rows it exists to preserve — and no existing
// test would go red. The canonical null predicate (`{$null: true}`, #10165)
// test would go red. The canonical null predicate (`{$null: true}`, commit 801296050)
// is what lets that exclusion be declared HERE, in the object file, instead
// of hiding in a plugin registration one package away.
//
Expand Down Expand Up @@ -262,7 +262,7 @@ export const SysSession = ObjectSchema.create({
token: Field.text({
label: 'Session Token',
required: true,
// [#11374] Bound from better-auth 1.7.1's own MySQL schema: a unique
// [commit 3954fb7df] Bound from better-auth 1.7.1's own MySQL schema: a unique
// string column is emitted as varchar(255) (get-migration.mjs), and the
// producer writes generateId(32) — 32 chars. 255 admits everything the
// upstream schema admits, and lets the unique index exist on MySQL.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.

/**
* #19249 — the `set_user_manager` row action on `sys_user`, pinned against the
* Commit 74fb2f7a8 — the `set_user_manager` row action on `sys_user`, pinned against the
* three things about it that are decisions rather than code.
*
* `sys_user.manager_id` drives the approvals `manager` rung and the ADR-0057
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ export const SysVerification = ObjectSchema.create({
label: 'Verification Token',
required: true,
internal: true,
// [#11374/#11701] Deliberately UNBOUNDED: better-auth's oauth-provider
// [commit 3954fb7df and #11701] Deliberately UNBOUNDED: better-auth's oauth-provider
// writes OIDC authorization-code payloads here as a JSON blob, so no
// bound provably admits every value it may write. That is only
// survivable because the column carries no index — see the `indexes`
Expand All @@ -123,7 +123,7 @@ export const SysVerification = ObjectSchema.create({
label: 'Identifier',
required: true,
internal: true,
// [#11374] Bound from better-auth 1.7.1's own MySQL schema: the
// [commit 3954fb7df] Bound from better-auth 1.7.1's own MySQL schema: the
// verification model declares `identifier` with `index: true`, and the
// upstream migration emits an indexed string column as varchar(255)
// (get-migration.mjs) — every better-auth flow that writes this table,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ const enSource = { metadataForms: enMetadataForms };
* preserves existing translations (via `--merge`) and only fills newly
* added schema keys per `--fill=default`.
*
* ## Staleness (#11671)
* ## Staleness (commit 09b4f4e4e)
*
* "Only fills newly added keys" is exactly the sticky drift the source-hash
* mechanism exists for: a leaf filled from the source and then left behind when
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@ import * as PlatformObjects from './index';
/**
* #11701 — a NON-UNIQUE declared index over a text column MySQL cannot key.
*
* ## What used to be here, and where it went (#12147)
* ## What used to be here, and where it went (commit 945e91a13)
*
* This file also carried route A's own rule — "every text-family column a
* declared index keys on declares a `maxLength`" (#11374) — enumerated over
* declared index keys on declares a `maxLength`" (commit 3954fb7df) — enumerated over
* this package's exports, with a vacuity control, an `UNBOUNDABLE` allowlist
* and a synthetic control driving that allowlist's two branches. All of it is
* now `scripts/check-keyed-text-bounds.mjs`, which walks EVERY `*.object.ts` in
Expand Down Expand Up @@ -79,7 +79,7 @@ const platformObjects: AnyObject[] = Object.values(PlatformObjects)
/**
* #11701 — a NON-UNIQUE index over a text column MySQL cannot key.
*
* See this file's header for why this is a different defect from #11374's:
* See this file's header for why this is a different defect from the one commit 3954fb7df fixed:
* a UNIQUE index over an unkeyable column is EXPRESSIBLE after #11627 (it moves
* onto a SHA-256 hash-shadow column), but a non-unique one is not — hashing
* destroys the ordering and prefix structure an access path is for, so there is
Expand Down
Loading
Loading