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

Provenance comments in `@objectstack/core` 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
three source comments: one in `resolve-authz-context.ts` that quotes a maintainer ruling now cites
ADR-0131's 2026-09-17 amendment, which records that ruling verbatim, and two on the unpack-time
integrity re-verification leg, which pointed at a tracker for work that was never built, now say in
words that the leg is unbuilt. One test comment named a maintainer-ruling comment that also answers
404; it now cites ADR-0025 §3.7, which records that ruling's effect. Some of these docblocks sit on
exported members, so the reworded text appears in the published declaration files (`index.d.ts` /
`index.d.cts`), and the comments esbuild keeps appear in the JavaScript output (`index.js` /
`index.cjs`).

Comment only: no export, type, error code, status, message text or runtime behaviour changes.
2 changes: 1 addition & 1 deletion packages/core/src/artifact-packages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@
* not disagree.
*
* ⛔ The other half of that reason — "and Zod strips undeclared keys" — is GONE,
* not merely reworded. `ManifestSchema` is `strictObject` since #14192 and
* not merely reworded. `ManifestSchema` is `strictObject` since commit 4d0d9445a and
* `AssembledPackageBodySchema` inherits the closed posture through `.extend()`,
* so an undeclared key on an entry is REFUSED by this very parse, by name, and
* never reaches a clone to be dropped from. Defaults are what still move bytes;
Expand Down
4 changes: 2 additions & 2 deletions packages/core/src/hot-reload.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -260,7 +260,7 @@ describe('[#12340] stateStrategy refusal', () => {
expect(m).toContain('were removed');
expect(m).toContain("Use 'memory'");
expect(m).toContain('p'); // locates the offending plugin
// The negative twin (#13179's strip): the prescription anchors on the
// The negative twin (commit fd289be45's strip): the prescription anchors on the
// ADR and the version — never on a tracker id the refused author
// cannot resolve. Mirrors the spec-side door's own pin.
expect(m).not.toMatch(/(?<![#&])#\d{3,5}(?![0-9A-Za-z])/);
Expand Down Expand Up @@ -385,7 +385,7 @@ describe('[#12428] startWatching refusal and the watch-handle removal', () => {
expect(m).toContain('never watched');
expect(m).toContain('scheduleReload');
expect(m).toContain('p'); // locates the offending plugin
// The negative twin (#13179's strip, extended to this door's sibling id):
// The negative twin (commit fd289be45's strip, extended to this door's sibling id):
// anchored on the ADR and the migration call, never on a tracker id.
expect(m).not.toMatch(/(?<![#&])#\d{3,5}(?![0-9A-Za-z])/);
});
Expand Down
6 changes: 3 additions & 3 deletions packages/core/src/lite-kernel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,14 +38,14 @@ export class LiteKernel extends ObjectKernelBase {
* A plugin object the DECLARED plugin contract refuses is refused here,
* with `PLUGIN_CONTRACT_VIOLATION` — the same check, the same envelope,
* that `ObjectKernel.use()` runs through `PluginLoader` (`plugin-contract.ts`
* is the one statement both kernels call; #16721, maintainer ruling
* is the one statement both kernels call; commit 51ae73123 landed maintainer ruling
* 2026-09-08, option A under #9864's precedent that the kernels converge).
*
* This method used to write the object straight into the registry, so the
* same plugin was accepted by this kernel and refused by `ObjectKernel` —
* and `AGENTS.md` names THIS kernel for tests, so a plugin could be green
* in vitest and refused at production boot. Measured before converging
* (#16721 step 1): of 813 `LiteKernel.use()` calls reachable in this
* (step 1, before commit 51ae73123): of 813 `LiteKernel.use()` calls reachable in this
* repository's suites, 807 were accepted by the schema unchanged and the
* six refusals came from three test-local fixture objects, none of them
* product code.
Expand All @@ -69,7 +69,7 @@ export class LiteKernel extends ObjectKernelBase {
use(plugin: Plugin): this {
this.validateIdle();

// Same check, same envelope, as `ObjectKernel.use()` (#16721).
// Same check, same envelope, as `ObjectKernel.use()` (commit 51ae73123).
assertPluginContract(plugin);

registerPluginByName(this.plugins, plugin, this.logger);
Expand Down
4 changes: 2 additions & 2 deletions packages/core/src/metadata-service-contract.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@
* - `check:meta-type-normalized` (`scripts/check-meta-type-normalized.mjs`)
* is the CI gate whose whole job is to refuse a DECISION made on the
* un-normalized `:type` — its header carries the three authorization
* bypasses (#3984, #5881, #6241) that made the direction a rule. Its scan
* bypasses (#3984, #5881, the one commit 83a3b1f2e closed) that made the direction a rule. Its scan
* surface is `packages/rest/src`; what this module converges with is its
* DIRECTION: normalize once, at the entry, and let every decision — here,
* every store key — read the normalized value;
Expand All @@ -65,7 +65,7 @@
* Row 3: a `data` that is not a plain object cannot be a metadata document.
* The pre-ruling `MetadataFacade` accepted such a write and filed it under the
* literal key `undefined` — readable back through no member (silent loss, the
* #6725 family) — and the interim fix coerced it into a `{ name, content }`
* same family as the defect commit 1507ba356 fixed) — and the interim fix coerced it into a `{ name, content }`
* box, which collides with `content` being a REAL authorable field on live
* metadata types (`doc`, `knowledge_document`). The ruling forbids both:
* refuse, do not coerce into storability. `null` and arrays are refused with
Expand Down
16 changes: 8 additions & 8 deletions packages/core/src/plugin-contract-enforcement.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@

/**
* `kernel.use()` enforces the DECLARED plugin contract (#16049) — on BOTH
* published kernels (#16721).
* published kernels (commit 51ae73123).
*
* WHICH KERNEL. Groups A–F drive `ObjectKernel.use()`, the path #16049 wired
* (`PluginLoader.validatePluginContract`). Group G drives `LiteKernel.use()`,
* which #16721 converged onto the SAME check — `assertPluginContract` in
* which commit 51ae73123 converged onto the SAME check — `assertPluginContract` in
* `plugin-contract.ts`, the one statement both kernels call. G is not a copy
* of A–F: it pins the cases whose answer DIFFERED between the kernels before
* #16721, the parity of the envelope for one input, and the two orderings
* commit 51ae73123, the parity of the envelope for one input, and the two orderings
* `LiteKernel.use()` owes (state before contract, contract before registry).
*
* WHY THIS FILE EXISTS. `PluginSchema` (`@objectstack/spec`,
Expand Down Expand Up @@ -424,7 +424,7 @@ describe('E — `version` is the NINTH enforced key, and admitting it refused no

describe('G — the SAME contract on LiteKernel.use() (#16721)', () => {
/**
* Before #16721 every refusal above had an accepting twin on this kernel:
* Before commit 51ae73123 every refusal above had an accepting twin on this kernel:
* `LiteKernel.use()` wrote the object straight into its registry, so the
* object group A refuses mounted routes here. `AGENTS.md` names this
* kernel for tests, so "green in vitest, refused at boot" was the shape
Expand Down Expand Up @@ -469,7 +469,7 @@ describe('G — the SAME contract on LiteKernel.use() (#16721)', () => {
['staticPath', { name: '@os-fixture/lite-ui-no-static-path', type: 'ui', slug: 'lite-ui-no-static-path' }],
['slug', { name: '@os-fixture/lite-ui-no-slug', type: 'ui', staticPath: UI_STATIC_PATH }],
] as const)('refuses a `ui` plugin with no `%s`, naming the key and the spec code (#16334 reaches this kernel now)', (key, overrides) => {
// The two inputs #16721 was filed on: refused by `ObjectKernel` (group F),
// The two inputs behind commit 51ae73123: refused by `ObjectKernel` (group F),
// and until now stored verbatim here — the hono auto-discovery pin's
// group F carried the accepting readings and was rewritten with this.
const kernel = makeLiteKernel();
Expand Down Expand Up @@ -542,7 +542,7 @@ describe('G — the SAME contract on LiteKernel.use() (#16721)', () => {
// was excluded from the schema check it was the ONE declared key this
// kernel did not judge at all: `version: 'v1.0.0'` registered here and
// was refused by `ObjectKernel` at boot — precisely the green-in-vitest,
// refused-in-production split #16721 converged the other eight keys to
// refused-in-production split commit 51ae73123 converged the other eight keys to
// close. It now travels the ordinary envelope.
const kernel = makeLiteKernel();
const bad = fixture({ name: 'com.example.lite-bad-version', version: 'v1.0.0' });
Expand All @@ -567,7 +567,7 @@ describe('G — the SAME contract on LiteKernel.use() (#16721)', () => {
// "An author gets ONE refusal, with the same code and message shape,
// from either kernel." `ObjectKernel.use()` re-wraps a failed load as
// `Failed to load plugin: <name> - <message>` for EVERY load failure —
// its existing wrapper, untouched by #16721 — so the parity to pin is
// its existing wrapper, untouched by commit 51ae73123 — so the parity to pin is
// that the LiteKernel message is exactly what follows that prefix.
const make = () => fixture({ name: '@os-fixture/parity', type: 'ui', staticPath: UI_STATIC_PATH, slug: 'Not A Slug' });

Expand Down Expand Up @@ -596,7 +596,7 @@ describe('G — the SAME contract on LiteKernel.use() (#16721)', () => {
});

it('ORDER — state is checked before the contract: after bootstrap the refusal is the idle one', async () => {
// `validateIdle()` first, then the contract — the wiring #16721 step 1
// `validateIdle()` first, then the contract — the wiring step 1 (before commit 51ae73123)
// measured with. A kernel that can no longer register plugins says so,
// and does not run the schema over an object it would not store anyway.
const kernel = makeLiteKernel();
Expand Down
6 changes: 3 additions & 3 deletions packages/core/src/plugin-contract.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import type { Plugin } from './types.js';
* The DECLARED plugin contract, enforced at `use()` on BOTH kernels — one
* statement, shared by `LiteKernel.use()` and by
* `PluginLoader.validatePluginContract` on the `ObjectKernel.use()` path
* (#16721, maintainer ruling 2026-09-08, option A).
* (maintainer ruling 2026-09-08, option A, landed as commit 51ae73123).
*
* ## Why it is written down here rather than in each kernel
*
Expand Down Expand Up @@ -113,7 +113,7 @@ import type { Plugin } from './types.js';
* line, and the first violated key is the one to fix.
*
* The code is spelled the ADR-0112 way and is REGISTERED in
* `ERROR_CODE_LEDGER` under `@objectstack/core` (#16649, under the #16404
* `ERROR_CODE_LEDGER` under `@objectstack/core` (commit 613bfbd3d, under the #16404
* door-or-no-door rule), exactly like `SERVICE_NOT_REGISTERED_CODE` one module
* over. `door: 'none'` on this tree — it is raised while the kernel is still
* assembling itself, before any HTTP boundary exists. If a transport ever
Expand Down Expand Up @@ -152,7 +152,7 @@ import type { Plugin } from './types.js';
* version` message, not `PLUGIN_CONTRACT_VIOLATION`; that ordering is
* unchanged and is pinned. `LiteKernel` has never run `validatePluginStructure`
* and still does not — so on that kernel a malformed `version` is refused for
* the first time here, by the schema, which is exactly the convergence #16721
* the first time here, by the schema, which is exactly the convergence landed in commit 51ae73123 as
* ruled for the other eight keys.
*/
const PLUGIN_CONTRACT_VIOLATION_CODE = 'PLUGIN_CONTRACT_VIOLATION';
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/plugin-loader.retired-fields.pin.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
// `@objectstack/core` "has no `typecheck` script (type-check DEBT ledger
// entry)", making a `@ts-expect-error` here a phantom pin
// `check:type-check-coverage` refuses. False on this tree in BOTH halves:
// #14613 split a `tsconfig.test.json` out of the build config,
// Commit 81208086a split a `tsconfig.test.json` out of the build config,
// `package.json`'s `typecheck` NAMES it (via `check:test-typecheck
// --project`), and this package holds no DEBT entry. A directive here WOULD be
// evaluated — against `./plugin-loader.ts`, this package's own SOURCE, which
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/plugin-loader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -418,7 +418,7 @@ export class PluginLoader {
*
* The check itself — `PluginSchema.safeParse` for validation only, the
* nine keys it reaches and the `PLUGIN_CONTRACT_VIOLATION` envelope —
* lives in `plugin-contract.ts`, because since #16721 it is ONE statement
* lives in `plugin-contract.ts`, because since commit 51ae73123 it is ONE statement
* run by BOTH kernels:
* `LiteKernel.use()` calls it directly, and `ObjectKernel.use()` reaches
* it here, through `loadPlugin`. That module's comment is the authority on
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/plugin-type-closed-set.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
// ⚠️ This used to read as though the split were forced — that
// `@objectstack/core` "has no `typecheck` script (type-check DEBT ledger
// entry)", making a `@ts-expect-error` here a phantom pin
// `check:type-check-coverage` refuses. False on this tree: #14613 split a
// `check:type-check-coverage` refuses. False on this tree: commit 81208086a split a
// `tsconfig.test.json` out of the build config, `package.json`'s `typecheck`
// NAMES it (via `check:test-typecheck --project`), and this package holds no
// DEBT entry. A directive here WOULD be evaluated — against `./types.ts`,
Expand Down
4 changes: 2 additions & 2 deletions packages/core/src/security/admin-standing-surface.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.

/**
* [#8734] The FIRST of the two links that bind `plugin-auth`'s break-glass
* [commit f8eb73601] The FIRST of the two links that bind `plugin-auth`'s break-glass
* standing-key lists to what this resolver actually reads.
*
* This half answers one question mechanically: **which columns does
Expand Down Expand Up @@ -97,7 +97,7 @@ function makeRecordingQl(tables: Record<string, Array<Record<string, unknown>>>,
return raw(row, key) === cond;
}),
);
// [#10978] Enforce the caller's bound — presence, not truthiness, so
// [commit 4c9780c7a] Enforce the caller's bound — presence, not truthiness, so
// `limit: 0` returns nothing rather than everything. Bounding BEFORE the
// Proxy wrap keeps the column-observation ledger honest: a row the real
// read would never have returned must not record column reads either.
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/security/admin-standing-surface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* ADMIN_STANDING_SURFACE — what `resolveAuthzContext` READS when it decides
* who is an administrator, declared beside the resolver that reads it.
*
* ## Why this file exists (#8734)
* ## Why this file exists (commit f8eb73601)
*
* `plugin-auth`'s break-glass guard (`last-admin-guard.ts`, ADR-0135 D5.2)
* decides whether a pending write can empty the administrator population by
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/security/api-key.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import {
/**
* In-memory sys_api_key store exposing the `find` shape the verifier uses.
*
* [#10978] `limit` is ENFORCED — presence, not truthiness, so `limit: 0` returns
* [commit 4c9780c7a] `limit` is ENFORCED — presence, not truthiness, so `limit: 0` returns
* nothing rather than everything. A double that drops the bound makes any limit
* change on this read green by construction; the verifier reads with `limit: 1`.
*/
Expand Down
16 changes: 8 additions & 8 deletions packages/core/src/security/assemble-execution-context.test.ts
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.
//
// #6216 — the ExecutionContext assembly converges onto ONE module, and the
// Commit f586f1a89 — the ExecutionContext assembly converges onto ONE module, and the
// maintainer ruling of 2026-08-08 (Option A) is explicit that **neither surface
// changes runtime behaviour**: what changes is that the anonymous divergence
// becomes named API instead of drift.
//
// A green suite proves nothing about that on its own — the suite was green
// before the change too. So the load-bearing test here is a PARITY PIN: the
// pre-#6216 assembly of each face is transcribed VERBATIM below, frozen, and
// assembly of each face before commit f586f1a89 is transcribed VERBATIM below, frozen, and
// every shape either face can serve is assembled both ways and compared.
//
// ⚠ The two `legacy*` functions are FROZEN TRANSCRIPTIONS of code that no
Expand All @@ -30,9 +30,9 @@ import {
} from './assemble-execution-context.js';
import type { ResolvedAuthzContext } from './resolve-authz-context.js';

// ───────────────────────── frozen pre-#6216 transcriptions ─────────────────────────
// ───────────────────────── frozen transcriptions from before commit f586f1a89 ─────────────────────────

/** Runtime / MCP dispatcher assembly, verbatim, pre-#6216. FROZEN — see header. */
/** Runtime / MCP dispatcher assembly, verbatim, before commit f586f1a89. FROZEN — see header. */
function legacyDispatcherAssembly(
authz: ResolvedAuthzContext,
oauthPrincipal: OAuthTokenProvenance | undefined,
Expand All @@ -49,7 +49,7 @@ function legacyDispatcherAssembly(
if (oauthPrincipal?.clientId) {
ctx.principalKind = 'agent';
ctx.onBehalfOf = { userId: authz.userId, principalKind: 'human' };
// Pre-#6216 these two read `scopesToAgentPermissionSets(oauthPrincipal.scopes)`
// Before commit f586f1a89 these two read `scopesToAgentPermissionSets(oauthPrincipal.scopes)`
// and `oauthPrincipal.scopes?.includes(MCP_OAUTH_SCOPE_ACTIONS)` inline.
// Both are now interpreted at the `/mcp` door and arrive pre-derived; the
// scope→ceiling mapping itself is pinned end-to-end through the real
Expand Down Expand Up @@ -88,7 +88,7 @@ function legacyDispatcherAssembly(
}

/**
* REST `computeExecCtx` assembly, verbatim, pre-#6216. FROZEN — see header.
* REST `computeExecCtx` assembly, verbatim, before commit f586f1a89. FROZEN — see header.
* `authGate` / `__kernel` are outside: at the time this was frozen neither was
* an `ExecutionContext` field, and the REST face added both after assembly.
*
Expand Down Expand Up @@ -280,7 +280,7 @@ describe('#6216 — REST face: byte-for-byte parity with the pre-#6216 assembly'
localization,
requestLocale,
// The named per-face divergence: REST has never carried the
// session bearer, and #6216 preserves that.
// session bearer, and commit f586f1a89 preserves that.
accessToken: undefined,
authGate: undefined,
});
Expand Down Expand Up @@ -494,7 +494,7 @@ describe('#6216 — the field set is CLOSED', () => {
});

describe('#6216 — the measured residual: keys that were present-with-undefined', () => {
// Reported rather than hidden. The pre-#6216 dispatcher assigned
// Reported rather than hidden. The dispatcher before commit f586f1a89 assigned
// `ctx.timezone` / `ctx.locale` unconditionally inside its authenticated
// branch, and the REST literal always spelled `tenantId` / `email` — so both
// faces could emit a key whose value was `undefined`. The shared assembler
Expand Down
Loading
Loading