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

Provenance comments in `@objectstack/runtime` 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 route, error code, refusal text, type, export or runtime behaviour
changes.
2 changes: 1 addition & 1 deletion packages/runtime/src/action-declarative-update.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -336,7 +336,7 @@ describe('#15079 point 3 — the authorization point: a caller who cannot read o
expect(res.body.error.code).toBe('RECORD_NOT_FOUND');
expect(res.body.error.message).toContain(RECORD_ID);
expect(res.body.error.message).toContain(OBJECT);
// ⛔ The #14143 class: a swallowed load must never become an implicit
// ⛔ The class commit f19475c0a closed: a swallowed load must never become an implicit
// grant. The verdict is CONSUMED — no write was even attempted.
expect(rig.updates).toHaveLength(0);
expect(rig.row.status).toBe('open');
Expand Down
6 changes: 3 additions & 3 deletions packages/runtime/src/action-door-record-load-denied.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
*
* 1. **Both doors × both surfaces.** The flow door and the script/body door,
* on the REST `/actions` route and on the MCP `run_action` bridge. A rule
* implemented at one door is the failure class #14143 and #15168 each paid
* implemented at one door is the failure class commit f19475c0a and #15168 each paid
* for on this exact seam, so every case below is asserted on all four.
* 2. **⛔ No run, no body.** The refusal lands BEFORE `automation.execute`
* (no persisted run) and BEFORE `executeAction` (no trusted, RLS-bypassing
Expand All @@ -46,7 +46,7 @@
* 4. **Record-less and new-record actions are byte-for-byte unchanged.** An
* object-less action key never attempts a load, so its verdict is never
* `true` and it still receives the `recordId` stamp — the regression that
* #14143 deliberately kept and that this card must not take away.
* commit f19475c0a deliberately kept and that this card must not take away.
* 5. **A load that SUCCEEDS still runs.** The owner reaches the flow and the
* handler exactly as before; this is the firing control that stops every
* zero above from being a rig that dispatches nothing.
Expand Down Expand Up @@ -450,7 +450,7 @@ describe('[#16370] refuseDeniedSubjectLoad — the rule, isolated from every doo

it('returns silently when the verdict is `false` — and the stamp is NOT the predicate', () => {
// ⛔ `record.id` is truthy in BOTH cases; re-deriving the verdict from
// it is the #14143 defect verbatim, so this pair is what says the
// it is the defect commit f19475c0a fixed, verbatim, so this pair is what says the
// implementation reads the flag and nothing else.
expect(() => refuseDeniedSubjectLoad(OBJECT, RECORD_ID,
{ record: { id: RECORD_ID }, recordLoadDenied: false })).not.toThrow();
Expand Down
24 changes: 12 additions & 12 deletions packages/runtime/src/action-execution.ts
Original file line number Diff line number Diff line change
Expand Up @@ -900,14 +900,14 @@ export function isFlowActionRefusal(e: unknown): e is FlowActionRefusal {
* and a downstream reader that had to tell them apart could only infer.
*
* [#15168] **The wiring takes the subject LOAD, not a bare record.** The flow
* face of #14143's signal (`AutomationContext.recordLoadDenied`, declared by
* face of commit f19475c0a's signal (`AutomationContext.recordLoadDenied`, declared by
* #14244) is derived here, once, from {@link loadActionSubjectRecord}'s
* outcome — so a caller cannot hand this door a record while dropping the
* verdict that says the caller could not read it. Both call sites already held
* that outcome and were passing `subject.record` out of it; taking the whole
* `subject` removes the second de-facto source rather than adding a key beside
* it, and makes the omission a compile error instead of a silent inertness one
* door over (the shape #14143 was filed for).
* door over (the shape commit f19475c0a fixed).
*/
export async function dispatchFlowAction(deps: ActionExecutionDeps,
requestContext: HttpProtocolContext,
Expand Down Expand Up @@ -1685,10 +1685,10 @@ export function buildActionEngineFacade(_deps: ActionExecutionDeps, ql: any, ec?

/**
* The subject-record load's outcome, as the two action doors hand it to a
* handler (#14143).
* handler (commit f19475c0a).
*/
export interface ActionSubjectRecordLoad {
/** What the handler receives as `ctx.record`. Unchanged by #14143. */
/** What the handler receives as `ctx.record`. Unchanged by commit f19475c0a. */
record: Record<string, unknown>;
/**
* `true` exactly when a caller-scope load was ATTEMPTED and did not deliver
Expand All @@ -1700,7 +1700,7 @@ export interface ActionSubjectRecordLoad {

/**
* Load an action's subject record IN THE CALLER'S OWN SCOPE, and report whether
* that load actually delivered the row (#14143). ONE producer for both action
* that load actually delivered the row (commit f19475c0a). ONE producer for both action
* doors — the MCP `run_action` bridge below and the REST `/actions` route
* (`domains/actions.ts`) — because the signal it emits is documented to app
* authors, and a signal only one of two doors sets is an authorization guard
Expand Down Expand Up @@ -1793,7 +1793,7 @@ export function actionRecordLoadSignal(load: ActionSubjectRecordLoad): { recordL
* reading it left open ("whether the automation engine acts on it … is a
* separate reading") is this function. A swallowed load must never become an
* implicit grant — the rule is #15079's, and a rule implemented at one of three
* doors is the failure class #14143 and #15168 each already paid for here.
* doors is the failure class commit f19475c0a and #15168 each already paid for here.
*
* ## The predicate is the LOAD's verdict — ⛔ never the action's `locations`
*
Expand Down Expand Up @@ -1906,11 +1906,11 @@ function declarativeUpdateRefusal(message: string, status: number): Error {
* 'update'` + `patch` (#14092, maintainer ruling 2026-09-01, quoted on the
* card). ONE implementation, called by BOTH action doors.
*
* ## Shared on purpose, for the #14143 reason
* ## Shared on purpose, for the reason of commit f19475c0a
*
* The REST `/actions` door and the MCP `run_action` bridge are two doors onto
* one action model, and this repo has now paid twice for a rule implemented at
* one of them: #14143 (a `recordLoadDenied` signal only one door set) and
* one of them: commit f19475c0a (a `recordLoadDenied` signal only one door set) and
* #15168 (a flow face with no populator at all). An authorization rule is the
* worst possible thing to fork, and contract point 3 is an authorization rule
* — so the branch each door owns is three lines, and everything that decides
Expand Down Expand Up @@ -1944,7 +1944,7 @@ function declarativeUpdateRefusal(message: string, status: number): Error {
* the caller's own scope actually delivered it. A caller who cannot read the
* row is refused HERE, before any write is attempted, on
* `subject.recordLoadDenied`. Re-deriving that from `subject.record` is the
* #14143 defect verbatim: the door stamps `record.id = recordId` on a refused
* defect commit f19475c0a fixed, verbatim: the door stamps `record.id = recordId` on a refused
* load, so `record.id` is truthy either way and `if (!record?.id)` is false on
* a row the caller cannot see. A swallowed load must never become an implicit
* grant.
Expand Down Expand Up @@ -2202,7 +2202,7 @@ export async function invokeBusinessAction(deps: ActionExecutionDeps,

// Load the subject record under RLS when row-context (engages the same
// permission path as get_record — an unseen record reads as not-found).
// [#14143] Through the ONE shared producer, so this door and the REST
// [commit f19475c0a] Through the ONE shared producer, so this door and the REST
// `/actions` door emit the same `recordLoadDenied` signal to handlers.
const subject = await loadActionSubjectRecord(objectName, recordId, () =>
callData('get', { object: objectName, id: recordId }, driver, envId, ec));
Expand All @@ -2213,7 +2213,7 @@ export async function invokeBusinessAction(deps: ActionExecutionDeps,
// first, and an action with no handler has no body to elevate for. The
// shared executor the REST `/actions` door also calls, so the two doors
// cannot disagree about the identity the write carries — the failure class
// #14143 and #15168 each paid for once, on this exact seam.
// commit f19475c0a and #15168 each paid for once, on this exact seam.
if (isDeclarativeUpdateAction(action)) {
const result = await executeDeclarativeUpdateAction(deps, action, {
objectName, actionName: name, subject, recordId, params, ec, driver, envId, callData,
Expand Down Expand Up @@ -2281,7 +2281,7 @@ export async function invokeBusinessAction(deps: ActionExecutionDeps,
);
const actionContext: any = {
record,
// [#14143] The caller-scope load's verdict, on the same context the
// [commit f19475c0a] The caller-scope load's verdict, on the same context the
// record rides. `ctx.record.id` is present either way (the stamp is
// load-bearing for record-less actions), so this is the ONLY thing that
// tells a handler its subject row did not resolve for THIS caller —
Expand Down
18 changes: 9 additions & 9 deletions packages/runtime/src/action-governance-scope-divergence.test.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.

/**
* #14423 (a) — the AGREEMENT, where this file used to pin the divergence.
* Commit a56baa2bd — the AGREEMENT, where this file used to pin the divergence.
*
* The two sites the card names:
* The two sites that diverged:
* - the AUDIT, `runActionGovernanceInventory` (`packages/objectql/src/
* action-governance.ts`), whose metadata-plane sources are now
* `loadStandaloneActionsKeyed = () => meta.loadManyKeyed('action')` and the
Expand All @@ -13,7 +13,7 @@
* whose third rung is `meta.loadDiagnosed('action', name)` — resolved per
* request off `deps.resolveService(requestContext, 'metadata', envId)`.
*
* PR #14421 closed the registry rung. This file measured the remaining one and
* Commit bd8795ea1 closed the registry rung. This file measured the remaining one and
* reproduced it four ways; the measurement is now the fix's pin, case for
* case, with the same harness. **C1 and C5 are unchanged controls** — they
* were green before and must stay green, because a "fix" that simply stopped
Expand Down Expand Up @@ -184,7 +184,7 @@ function auditAccused(warnings: Array<{ message: string; meta?: Record<string, u
* ONE handler registered and NO object-embedded declaration and NO registry
* item — so the metadata plane is the only source that can clear it.
*
* [#14423] Transcribed from the plugin's wiring after the fix, including its
* [commit a56baa2bd] Transcribed from the plugin's wiring after the fix, including its
* fallbacks: the keyed plural read when the plane offers one, the unkeyed one
* otherwise, and the by-name rung preferring `loadDiagnosed` over `load`
* exactly as `resolveRouteActionDeclaration` does. Keeping the branches rather
Expand All @@ -206,7 +206,7 @@ async function runAudit(meta: any) {
loadStandaloneActionsKeyed: meta && typeof loadManyKeyed === 'function'
? () => loadManyKeyed.call(meta, 'action')
: undefined,
lookupRegistryAction: () => undefined, // rung 2 holds nothing — #14421's rung is not the one under test
lookupRegistryAction: () => undefined, // rung 2 holds nothing — commit bd8795ea1's rung is not the one under test
lookupMetadataAction: meta && typeof loadDiagnosed === 'function'
? async (name: string) => (await loadDiagnosed.call(meta, 'action', name))?.data
: (meta && typeof load === 'function' ? (name: string) => load.call(meta, 'action', name) : undefined),
Expand Down Expand Up @@ -323,7 +323,7 @@ describe('#14423 (a) — the audit and the router now answer from one identity a
// The keyed enumeration is short for the same reason — keying is not a
// cure for an unreachable loader, and does not claim to be.
expect(await meta.loadManyKeyed<any>('action')).toEqual([]);
// [#14423 item 1] ...and the sibling enumeration no longer THROWS where
// [commit a56baa2bd] ...and the sibling enumeration no longer THROWS where
// its two siblings merely came back short.
await expect(meta.listNames('action')).resolves.toEqual([]);

Expand All @@ -335,7 +335,7 @@ describe('#14423 (a) — the audit and the router now answer from one identity a
});

/**
* C4 — a BOUNDARY, not a defect, and pinned as one. NARROWER since #16610.
* C4 — a BOUNDARY, not a defect, and pinned as one. NARROWER since commit 316a20fc5.
*
* `metadata` is registered `SCOPED`, so `PluginLoader.getService` mints one
* instance per `scopeId`. The kernel's RAW SYNCHRONOUS accessor
Expand All @@ -348,7 +348,7 @@ describe('#14423 (a) — the audit and the router now answer from one identity a
* plane. That asymmetry is what every assertion below exercises — the
* accessor, directly, never the plugin's wiring around it.
*
* ## What the PLUGIN does with that accessor, after #16610
* ## What the PLUGIN does with that accessor, after commit 316a20fc5
*
* `ObjectQLPlugin.resolveGovernanceMetadataService` no longer calls the
* synchronous accessor alone, so the throw is no longer swallowed into
Expand All @@ -363,7 +363,7 @@ describe('#14423 (a) — the audit and the router now answer from one identity a
* ⚠ So do NOT read the paragraph above as a live defect in `plugin.ts`.
* It describes the rung the plugin now reaches for SECOND, and this case
* pins that rung's behaviour — which is why its assertions stay green and
* stay true across #16610.
* stay true across commit 316a20fc5.
*
* ## Why this stays accused, and why that is CORRECT
*
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.

/**
* "Object-less" has ONE answer inside `action-execution.ts` (#14864).
* "Object-less" has ONE answer inside `action-execution.ts` (commit 066dd3bd0).
*
* `isObjectLessActionKey` (`@objectstack/objectql`) is the canonical predicate:
* the routed object is object-less when it is the canonical
Expand Down
18 changes: 9 additions & 9 deletions packages/runtime/src/action-owner-key-single-source.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.

/**
* The standalone-action owner-key ladder has ONE spelling (#14422).
* The standalone-action owner-key ladder has ONE spelling (commit dc7c226b9).
*
* `action.objectName` -> `action.object` -> the object-less
* `GLOBAL_ACTION_OBJECT_KEY` decides which engine key a standalone `action`
Expand All @@ -26,16 +26,16 @@
* B reads this package's own source and fails if the ladder grows a second
* body here.
*
* Half C closes the same hole one level down (#14678). #14422 converged the
* Half C closes the same hole one level down (commit 73ad0bba7). Commit dc7c226b9 converged the
* LADDER, and the runtime kept three bare `'global'` spellings elsewhere in
* `action-execution.ts` that the ladder check could not see: a live comparison
* in `seedFlowActionParams`, a warn-once log key in `enforceActionParams`, and
* a docblock. All three were equal in value and invisible to every test in the
* repo, which is the whole shape #14422 was filed to remove — so the same
* repo, which is the whole shape commit dc7c226b9 was written to remove — so the same
* convergence needed the same weld, or the next reader re-inlines one and
* nothing says so.
*
* Half D (#14878) is the odd one out and says so at its own section below: it
* Half D (commit 29db3cd2a) is the odd one out and says so at its own section below: it
* is not about this package's source at all. It is the TREE-scoped absence pin
* for the plugin member this convergence deleted, carried here as well as in
* `@objectstack/objectql` so that losing either copy still leaves a guard.
Expand Down Expand Up @@ -157,14 +157,14 @@ describe('standalone-action owner key — half C: no bare literal (#14678)', ()
// file was written to replace. Both controls are positive assertions
// against text the converged file must carry.
//
// [#14864] The second control used to be the `seedFlowActionParams`
// [commit 066dd3bd0] The second control used to be the `seedFlowActionParams`
// comparison `objectName !== GLOBAL_ACTION_OBJECT_KEY`. That guard is
// gone — it was one of the two rival answers to "is this route
// object-less", and it now delegates to `isObjectLessActionKey` like
// its neighbours. Re-anchored rather than deleted, and deliberately
// onto a site this file's own subject does not move: the warn-once log
// key in `enforceActionParams`, which is the SECOND of the three bare
// literals #14678 converged and is untouched by the predicate work.
// literals commit 73ad0bba7 converged and is untouched by the predicate work.
// ⛔ Do not re-anchor a control onto the thing the next change is most
// likely to edit — a control that moves with its subject stops being a
// control.
Expand All @@ -184,9 +184,9 @@ describe('standalone-action owner key — half C: no bare literal (#14678)', ()
});

/**
* ── Half D [#14878]: the absence assertion is TREE-scoped, not FILE-scoped ───
* ── Half D [commit 29db3cd2a]: the absence assertion is TREE-scoped, not FILE-scoped ───
*
* #14667 deleted a private `actionObjectKey` member from `ObjectQLPlugin` and
* Commit dc7c226b9 deleted a private `actionObjectKey` member from `ObjectQLPlugin` and
* DID write a guard for it — `not.toContain(...)` against `plugin.ts`. The kind
* of guard was right; its SCOPE was the defect. A pin written by the deleting PR
* can only look where its author thought to look, and the whole failure mode is
Expand Down Expand Up @@ -260,7 +260,7 @@ describe('standalone-action owner key — half C: no bare literal (#14678)', ()
*/

/**
* The member #14667 deleted from `ObjectQLPlugin`. Held as DATA: naming a symbol
* The member commit dc7c226b9 deleted from `ObjectQLPlugin`. Held as DATA: naming a symbol
* in a string cannot resurrect it, and this file is excluded from its own scan
* precisely so it may carry the name.
*/
Expand Down
2 changes: 1 addition & 1 deletion packages/runtime/src/action-params-enforcement.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

/**
* `enforceActionParams` — the ADR-0104 D2 gate itself, not its validator
* (#14864).
* (commit 066dd3bd0).
*
* ## What was measured, and why this file exists
*
Expand Down
8 changes: 4 additions & 4 deletions packages/runtime/src/action-record-load-denied.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.

/**
* [#14143] A handler must be able to tell "the caller cannot read this row"
* [commit f19475c0a] A handler must be able to tell "the caller cannot read this row"
* from "this action legitimately has no record".
*
* ## The defect
Expand Down Expand Up @@ -493,7 +493,7 @@ describe('[#15168] the FLOW door and its verdict — MCP run_action', () => {
* `AutomationContext.recordLoadDenied` is a declared spec key
* (`contracts/automation-service.ts`, pinned in `packages/spec`), the
* dispatcher is its ONE populator, and a populator that quietly stopped
* populating would be exactly the inert-signal shape #14143 was filed for. So
* populating would be exactly the inert-signal shape commit f19475c0a fixed. So
* the assertions #15168 wrote at the doors are re-pinned HERE, on the
* dispatcher itself, where a denied subject can still be constructed.
*
Expand Down Expand Up @@ -556,8 +556,8 @@ describe('[#15168] dispatchFlowAction derives the verdict from the subject load'

/**
* [#15168] The convergence itself. A per-door assertion is satisfied by two
* copies of a rule, and two copies drifting apart is the defect #14143 was
* filed for and the reason this card had to move both doors in one stroke — so
* copies of a rule, and two copies drifting apart is the defect commit f19475c0a
* fixed, and the reason this card had to move both doors in one stroke — so
* the SAME caller against the SAME row is driven through both doors and the
* signal is compared as a set.
*/
Expand Down
Loading
Loading