Skip to content

Commit 194e9ef

Browse files
committed
fix(plugin-approvals): sys_approval_request.record_id declares referenceDeletedWhen on cancel_reason = record_deleted
One key on the pointer's id half, beside referenceVia: a request cancelled because its record was deleted says so in the served object definition, so a renderer draws the option's label instead of a link. Pinned beside the object, together with the object's parse through ObjectSchema. Claude-Session: https://claude.ai/code/session_018VqJPGA1NDNSCZsnecsChP Co-authored-by: Claude <noreply@anthropic.com>
1 parent d3df3ba commit 194e9ef

3 files changed

Lines changed: 43 additions & 0 deletions

File tree

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
---
2+
'@objectstack/plugin-approvals': patch
3+
---
4+
5+
fix(plugin-approvals): `sys_approval_request.record_id` declares that the record it names was deleted when `cancel_reason` is `record_deleted`
6+
7+
Clause-②: no
8+
9+
- **The pointer carries its own tombstone predicate.** `record_id` declares `referenceDeletedWhen: { field: 'cancel_reason', value: 'record_deleted' }` beside its `referenceVia: 'object_name'`. A request the platform cancelled because its record was deleted (`status: cancelled`, `cancel_reason: record_deleted`) therefore says, in the served object definition, that the pointer names a record that no longer exists. A renderer draws that option's label (`Related record deleted`) in place of a link, and reads nothing.
10+
- **Nothing is accepted or answered differently.** The sibling is the existing `cancel_reason` select, which already holds `record_deleted`, so the spec's parse-time refusals of the key are satisfied by the shipped object. No export, type, route, stored value or request answer moves.
11+
12+
A patch: one key on one platform object's served field definition.

‎packages/plugins/plugin-approvals/src/sys-approval-request-pointer-pair.test.ts‎

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.
22

33
import { describe, it, expect } from 'vitest';
4+
import { ObjectSchema } from '@objectstack/spec/data';
45
import { SysApprovalRequest } from './sys-approval-request.object.js';
56

67
/**
@@ -49,3 +50,30 @@ describe('sys_approval_request — declared pointer pair (#11386)', () => {
4950
expect(fields.object_name.required).toBe(true);
5051
});
5152
});
53+
54+
/**
55+
* The pointer's tombstone predicate — objectui#12489 ruling, letter A.
56+
*
57+
* When the platform voids a `pending` request because its record was deleted
58+
* (`cancelled` + `cancel_reason: 'record_deleted'`), `record_id` names a record
59+
* that no longer exists. `referenceDeletedWhen` is the object's own assertion of
60+
* that, so a renderer draws the sibling option's label instead of a link to
61+
* nothing. The spec refuses the declaration at parse unless the sibling is a
62+
* single-value `select` holding the option, so the parse is half of the pin.
63+
*/
64+
describe('sys_approval_request — record_id tombstone predicate', () => {
65+
const fields = SysApprovalRequest.fields as Record<string, Record<string, unknown>>;
66+
67+
it('declares record_id as deleted when cancel_reason holds record_deleted', () => {
68+
expect(fields.record_id.referenceDeletedWhen).toEqual({ field: 'cancel_reason', value: 'record_deleted' });
69+
});
70+
71+
it('names a sibling select that declares that option, and the object parses through ObjectSchema', () => {
72+
expect(fields.cancel_reason.type).toBe('select');
73+
expect(fields.cancel_reason.options).toEqual(
74+
expect.arrayContaining([expect.objectContaining({ value: 'record_deleted' })]),
75+
);
76+
const parsed = ObjectSchema.safeParse(SysApprovalRequest);
77+
expect(parsed.success, parsed.success ? '' : JSON.stringify(parsed.error.issues)).toBe(true);
78+
});
79+
});

‎packages/plugins/plugin-approvals/src/sys-approval-request.object.ts‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,9 @@ export const SysApprovalRequest = ObjectSchema.create({
218218
// dropped-deferral error, and `success: false` — and since commit 1cba33f16 the
219219
// loader also WARNS at load time, before the engine rejects the row.
220220
referenceVia: 'object_name',
221+
// A `cancelled` request whose `cancel_reason` is `record_deleted` names a
222+
// record the platform deleted: a renderer draws that option's label, not a link.
223+
referenceDeletedWhen: { field: 'cancel_reason', value: 'record_deleted' },
221224
}),
222225

223226
submitter_id: Field.lookup('sys_user', {

0 commit comments

Comments
 (0)