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
15 changes: 15 additions & 0 deletions .changeset/20595-metadata-protocol-provenance-anchors.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
---
'@objectstack/metadata-protocol': patch
---

Provenance comments in `@objectstack/metadata-protocol` 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 one now cites the commit in this repository's history that made the decision it describes
(and ADR-0005's design-principle-3 correction where that record exists). Some of these docblocks sit
on exported members, so the reworded text appears in the published `index.d.ts` / `index.d.cts`, and
a few comments that esbuild keeps appear in the JavaScript output.

Comment only: no export, type, error code, status, message text or runtime behaviour changes.
2 changes: 1 addition & 1 deletion packages/metadata-protocol/src/discovery-version.test.ts
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.

/**
* #11235 — the `version` field `getDiscovery()` serves must be DERIVED: an
* Commit 376c70f98 — the `version` field `getDiscovery()` serves must be DERIVED: an
* injected `OS_RUNTIME_VERSION` stamp, falling back to the resolved
* `@objectstack/metadata-protocol` package version — never the `'1.0'` literal
* this producer hardcoded before the fix, and never any other constant.
Expand Down
6 changes: 3 additions & 3 deletions packages/metadata-protocol/src/discovery-version.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* Resolves the value `ObjectStackProtocolImplementation.getDiscovery()` serves
* as the `DiscoverySchema` "System Identity" `version` field (`./protocol.ts`).
*
* ## #11235 — why the literal it replaces was provably not a contract value
* ## Commit 376c70f98 — why the literal it replaces was provably not a contract value
*
* This producer hardcoded `version: '1.0'`. It is the SECOND
* `DiscoverySchema`-conforming producer; the first
Expand All @@ -23,7 +23,7 @@
* `@objectstack/metadata-protocol`, not the reverse, so importing it would
* invert the dependency direction. Hoisting a shared helper into
* `@objectstack/types` or `@objectstack/core` (both already dependencies of
* this package) was considered and declined at #11235 triage: a hoist widens
* this package) was considered and declined when the derivation landed (commit 376c70f98): a hoist widens
* two packages' published surface for ~10 lines serving two call sites.
* Consolidation rides a later card if a third caller ever appears.
*
Expand Down Expand Up @@ -103,7 +103,7 @@ function resolvePackageVersion(): string | undefined {
* 3. `'unknown'` — only if BOTH of the above are unavailable (the package's own
* `package.json` is unreadable). Honest about not knowing, rather than a
* plausible-looking literal a caller could mistake for real identity — the
* exact failure mode #10993 and #11235 exist to close.
* exact failure mode #10993 and commit 376c70f98 close.
*/
export function resolveDiscoveryVersion(): string {
return getEnv('OS_RUNTIME_VERSION') || resolvePackageVersion() || 'unknown';
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.

/**
* [#14907] `getMetaItemLayered` — the THIRD `/meta` read verb — applies the
* [commit e1d4f9e3f] `getMetaItemLayered` — the THIRD `/meta` read verb — applies the
* registry read gate ITSELF, so a caller cannot spend a raw active
* organization on a type that has no per-org read channel.
*
* ── The defect, and why this verb was graded on its own harm ──────────────
*
* The series is #9454 → #14683 (plural `getMetaItems`) → #14770 (singular
* The series is #9454 → commit 96326040f (plural `getMetaItems`) → commit d5cbb44f3 (singular
* `getMetaItem`) → this one. On the plural verb an ungated organization can
* only ADD a row; on the singular verb it SUBSTITUTES the served document.
* Here the affected value is the `overlay` LAYER of a three-layer diagnostic
Expand All @@ -28,19 +28,19 @@
* and it is correct there because the binding already sat AFTER
* `canonicalizeMetaRequestType`. Here the binding sat BEFORE the fold, so the
* one-liner does NOT port: dropping the same expression in place would gate on
* the RAW type. #10340 measured what that costs — `declaresOrgOverride`
* the RAW type. Commit 26f3588fb measured what that costs — `declaresOrgOverride`
* tolerates the MANIFEST plurals but not the URL-only ones (`translations` /
* `email_templates` have no manifest key), so a raw segment splits one item
* across two partitions. The fix is therefore a REORDER, and §3 is what fails
* if a later author moves the binding back above the fold: it asserts that a
* URL-only spelling of an OVERRIDABLE type still reaches its org partition.
*
* ── §4–§5 are the idempotence proof the #14683 ruling made this conditional
* ── §4–§5 are the idempotence proof the ruling behind commit 96326040f made this conditional
* on, discharged over THIS door's caller population ──────────────────────
*
* That ruling makes a callee-side gate conditional on proving no already-gating
* caller is double-scoped or wrongly denied, discharged PER DOOR over that
* door's own callers. #14770's proof covers none of this verb's population, so
* door's own callers. Commit d5cbb44f3's proof covers none of this verb's population, so
* it is re-discharged here: §4 covers `f(t, undefined) === undefined` (the four
* `plugin-security` invocations that name no organization) and §5 covers
* `f(t, f(t, o)) === f(t, o)` over the COMPLETE accepted-spelling population
Expand Down Expand Up @@ -359,13 +359,13 @@ describe('§3 the gate resolves AFTER canonicalizeMetaRequestType', () => {
expect(res.overlayScope, spelling).toBe('org');
// ONLY the org partition: the org row wins, so the `overlay ===
// null` env fallback never runs. Gated on the raw segment this
// list is `[null]` instead — the partition split #10340 measured.
// list is `[null]` instead — the partition split commit 26f3588fb measured.
expect(partitions(findOnes), spelling).toEqual([ORG]);
}
});

it('answers the URL spelling and the canonical spelling identically', async () => {
// The #10340 statement restated as an equality: one item, ONE
// The statement of commit 26f3588fb restated as an equality: one item, ONE
// partition, whichever accepted spelling addresses it.
for (const spelling of URL_ONLY_OVERRIDABLE) {
const canonical = canonicalMetaUrlType(spelling);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.

/**
* [#14770] `getMetaItem` — the SINGULAR verb — applies the registry read gate
* [commit d5cbb44f3] `getMetaItem` — the SINGULAR verb — applies the registry read gate
* ITSELF, so a caller cannot spend a raw active organization on a type that has
* no per-org read channel.
*
* ── The defect, and why the singular verb is the sharper half ─────────────
*
* #14683 (PR #14767) moved {@link organizationIdForMetaRead} INSIDE the PLURAL
* Commit 96326040f (PR #14767) moved {@link organizationIdForMetaRead} INSIDE the PLURAL
* verb, `getMetaItems`, and deliberately did not carry to this one. There, the
* two `queryByOrg` reads are UNIONed, so an ungated organization can only ADD
* rows — the resurrection that card is about. Here the two `findOverlay` reads
Expand Down Expand Up @@ -37,7 +37,7 @@
* The inner precedence rests on two other things: ADR-0005 design principle 3
* stores the ENTIRE item document per overlay row (so a layering has nothing
* to layer), and the field-level patch model that would have given "layering"
* any meaning was retired and deleted whole under ADR-0049 (#13185, PR #13186,
* any meaning was retired and deleted whole under ADR-0049 (ADR-0005 principle 3's correction, commit 9e0ba21a1,
* maintainer ruling 2026-08-29), with ADR-0126 §6 ruling out the phase it was
* held for; and `organizationIdForMetaRead`'s own docblock quotes this very
* expression as the intended shape while defining #9454. ADR-0029 D9 reaches
Expand Down Expand Up @@ -374,7 +374,7 @@ describe('§4 an already-gating caller receives the same scope it did before', (
//
// ⛔ This is why the gate sits AFTER the fold. `declaresOrgOverride`
// tolerates the MANIFEST plurals and not the URL-only ones
// (`translations` / `email_templates` have no manifest key) — #10340
// (`translations` / `email_templates` have no manifest key) — commit 26f3588fb
// measured what that costs when a raw segment reaches the predicate.
for (const spelling of ALL_SPELLINGS) {
const doorGate = organizationIdForMetaRead(canonicalMetaUrlType(spelling), ORG);
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.

/**
* [#14683] `getMetaItems` applies the registry read gate ITSELF, so a sweep
* [commit 96326040f] `getMetaItems` applies the registry read gate ITSELF, so a sweep
* that reads MORE THAN ONE type per request is scoped per type instead of per
* request.
*
Expand Down
2 changes: 1 addition & 1 deletion packages/metadata-protocol/src/meta-overlay-cache.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -578,7 +578,7 @@ describe('[#11967] §7 distinct reads never share an entry', () => {
expect((scoped.items as any[]).map((i) => i.name)).toEqual(['beta']);
});

// ⚠️ [#14683] `view`, NOT `object`, and the type is LOAD-BEARING here in a
// ⚠️ [commit 96326040f] `view`, NOT `object`, and the type is LOAD-BEARING here in a
// way it is not in this section's three siblings. `getMetaItems` now
// resolves its own read scope through `organizationIdForMetaRead`, so a
// type the registry declares NON-overridable has exactly one partition to
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.

/**
* #10382 — this package's live-MySQL suites must not be able to share one
* Commit ee09d2119 — this package's live-MySQL suites must not be able to share one
* database.
*
* ## Why this suite is structural, and what it is a control FOR
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.

/**
* #10382 — the per-file live MySQL database for this package's live suites.
* Commit ee09d2119 — the per-file live MySQL database for this package's live suites.
*
* ## What was actually wrong, which is not what the card said
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@
* message that discloses neither the statement nor the diagnostic — and carries
* the dialect error whole under a non-enumerable `cause`. Read bare, `detail`
* became *"the database refused to run a raw statement"* for every caller that
* stores it. `operatorFacingErrorText` (`@objectstack/types`, #16657) reads the
* stores it. `operatorFacingErrorText` (`@objectstack/types`, commit 5a95b0e93) reads the
* dialect's own words back out of that chain, so a stored record still names
* `no such column: foo`. The envelope itself is left exactly as the driver
* declared it: this is a READ of the cause, never a widening of the disclosure.
Expand Down Expand Up @@ -392,7 +392,7 @@ export async function probeThenReplaceIndex(
} catch (err: unknown) {
// `detail` is the OPERATOR-facing text: the dialect's own prose, read
// out of the `cause` the raw seam attaches when it declares its fault
// (#16019/#16657 — see the module header). Callers STORE it, and a
// (#16019/commit 5a95b0e93 — see the module header). Callers STORE it, and a
// stored record is the only copy its reader ever gets.
// The VERDICT is taken from the error object itself, so a conflict
// reported on `code` / `errno` / `cause` with unhelpful prose is still
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.

/**
* [#16657] The operator records this package stores name the DIALECT, not the
* [commit 5a95b0e93] The operator records this package stores name the DIALECT, not the
* driver's composed refusal.
*
* ## The regression
Expand Down Expand Up @@ -31,7 +31,7 @@
*
* ⚠️ That formula is now the WHOLE record at every site this change touched —
* all nine in this package, fourteen across the repository. It was eight of
* those nine until #17167: `seed-tenancy-backfill`'s ORGANIZATION probe spelled
* those nine until commit dc709b2cf: `seed-tenancy-backfill`'s ORGANIZATION probe spelled
* `operatorFacingErrorText(e) || 'unknown error'` — the file's last fallback —
* so where the channel was EMPTY that record read `'unknown error'` and never
* `''`. Measured at that probe before the removal: a thrown `''`, a thrown
Expand All @@ -44,7 +44,7 @@
* "the probe did not fail", so deleting the placeholder and putting NOTHING in
* its place routes a thrown `''` down the benign `no-organization-yet` path
* instead of the ambiguous one (measured by ablation, both before and after
* #17167) — the "unknown read as zero" confusion #9261 exists to prevent. The
* commit dc709b2cf) — the "unknown read as zero" confusion #9261 exists to prevent. The
* fact now travels in the TYPE (`string | undefined`), so the status arm is
* pinned beside the record arm in the empty-channel case below: a re-added
* placeholder and a lost discrimination each redden one of them.
Expand Down Expand Up @@ -233,7 +233,7 @@ describe('[#16657] seed-tenancy-backfill — the stored operator record', () =>
* injectable refusal so a single run can be pointed at one seam at a time.
*
* `thrown` defaults to the declared raw-statement fault every case below
* asserts against; the empty-channel cases (#17167) pass their own value,
* asserts against; the empty-channel cases (commit dc709b2cf) pass their own value,
* which is why it is a parameter rather than a second fixture.
*/
function seamExec(refuse: (sql: string) => boolean, thrown: unknown = rawStatementFault()) {
Expand Down Expand Up @@ -383,7 +383,7 @@ describe('[#16657] seed-tenancy-backfill — the stored operator record', () =>

it('an UNDECLARED refusal reads its own message channel at every site', async () => {
// No site here carries a fallback any more — the ORGANIZATION probe's
// `|| 'unknown error'` was the last one and #17167 removed it, which
// `|| 'unknown error'` was the last one and commit dc709b2cf removed it, which
// the empty-channel case below pins. This pin drives the duplicates
// warning with a NON-EMPTY message, the shape for which the channel is
// the whole answer at every site.
Expand Down
8 changes: 4 additions & 4 deletions packages/metadata-protocol/src/migrations/read-probe.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@
* than an oversight. Quietening a refusal requires CLASSIFYING it, this repo has
* exactly one predicate for that (`isMissingTableError`, `@objectstack/types`),
* and it needs `readObject` — the name of the thing the caller was reading —
* both to avoid the #13324 fail-open and because
* both to avoid the fail-open commit 4cda78c9b closed and because
* `driver-error-classification.callers.test.ts` fails any in-repo call that
* omits it. The raw path has no such name: `execute()` takes a string, and
* `rawStatementFaultError` declares no targeted table (pinned by
Expand Down Expand Up @@ -111,7 +111,7 @@ export type ReadProbeExec = (sql: string) => Promise<unknown>;
* array (better-sqlite3 through knex), `{ rows }` (pg), and the `[rows, fields]`
* tuple (mysql2). An empty result set in any of those spellings is still a
* result set, and still `true` — that is what keeps a healthy install's
* `no-split` intact, and it is the half of #10789 that stopped it being a
* `no-split` intact, and it is the half of commit 38bc74ed1 that stopped it being a
* rename.
*
* This cannot lose a split that {@link normalizeRows} would have found: every
Expand Down Expand Up @@ -255,7 +255,7 @@ export type TablePresenceVerdict =
| 'absent'
/**
* The seam accepted the statement and returned no result set at all — a
* memory engine's no-op `execute` (#10789). Not a failure and not an answer;
* memory engine's no-op `execute` (commit 38bc74ed1). Not a failure and not an answer;
* each caller maps it the way its own history already ruled.
*/
| 'no-answer'
Expand All @@ -274,7 +274,7 @@ export interface TablePresenceProbe {
/**
* The table whose presence is asked. Also the `readObject` the fallback arm's
* classification compares the dialect's phrase against, so a refusal naming
* some OTHER relation is ⛔ not read as this table's absence (#13324).
* some OTHER relation is ⛔ not read as this table's absence (commit 4cda78c9b).
*/
table: string;
/** The knex client name, when the caller resolved one. */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
* (`_objectstack_sequences`, `sys_organization`) that other live suites also
* use.
*
* That database is DERIVED FROM THIS FILE's path (#10382) rather than named by
* That database is DERIVED FROM THIS FILE's path (commit ee09d2119) rather than named by
* a constant. It used to be the literal `os_metadata_protocol_9381`, which was
* distinct from the sibling suite's only because two authors happened to type
* two different strings — and `afterAll` below issues `drop database`, so a
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@
* fixed platform names (`_objectstack_sequences`, `sys_organization`) that other
* live suites on the same CI server also use. `drop schema … cascade` in
* `afterAll` is what makes a SHARED name destructive rather than merely
* contended (#10382), and `scripts/check-live-db-isolation.mjs` refuses any live
* contended (commit ee09d2119), and `scripts/check-live-db-isolation.mjs` refuses any live
* suite in the tree whose name reaches that DDL as a literal.
*
* ⚠️ The name comes from `currentLiveMysqlDatabase()` — the MySQL-named resolver
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.

/**
* #10789 — `backfillSeedTenancy` reported `no-split` over a driver it never
* Commit 38bc74ed1 — `backfillSeedTenancy` reported `no-split` over a driver it never
* queried, and its own `absent` branch was unreachable on a no-op seam.
*
* ## The defect
Expand All @@ -26,7 +26,7 @@
* three dialect result-set shapes `normalizeRows` flattens — it means "I did not
* run your query", and it was mapped onto "your query returned no rows".
*
* Same class, same consumer-side shape, as #10677 / PR #10788 landed for
* Same class, same consumer-side shape, as #10677 / commit 3a7ec2d3b landed for
* `os migrate duplicates`: judge the seam by whether it returns a RESULT SET,
* not by whether `execute` exists. No driver is named by the implementation.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -881,7 +881,7 @@ describe('#12395 zero organizations is a third state, not the ambiguous one', ()
*
* `organizationProbeThrown` names the value the organization probe throws;
* omitted, it is a normal driver `Error`. It exists so the EMPTY-channel
* shapes (#17167) can be driven through the same seam — and it is compared
* shapes (commit dc709b2cf) can be driven through the same seam — and it is compared
* against `undefined` rather than reached through `??`, because `''` is not
* nullish and is exactly the value under test.
*/
Expand Down
Loading
Loading