Skip to content

fix(plugin-approvals): sys_approval_request.record_id declares referenceDeletedWhen on cancel_reason = record_deleted - #23798

Merged
objectstack-fleet[bot] merged 1 commit into
mainfrom
claude/issue-23736-record-id-deleted-when
Oct 16, 2026
Merged

objectstack-fleet[bot] merged 1 commit into
mainfrom
claude/issue-23736-record-id-deleted-when

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Fixes #23736

Clause-②: no

What

One key on sys_approval_request.record_id, beside its referenceVia: 'object_name':

referenceDeletedWhen: { field: 'cancel_reason', value: 'record_deleted' }

A request the platform cancelled because its record was deleted (cancel_reason: record_deleted) now says, in the served object definition, that the pointer names a record that no longer exists, so a renderer can draw the sibling option's label (Related record deleted) instead of a link. This is the services half of the objectui#12489 ruling, letter A (batch #352 item 4); the spec key is #23735 (8776119bdc). No page edit and nothing in objectui: the served page's Details tab draws the pair through record:details, and the renderer reads the key.

Landing place: packages/plugins/plugin-approvals/src/sys-approval-request.object.ts, as the card expected. The producer is there; no other package needed a change.

Files: the object (3 lines: the key and a 2-line comment), the pin test beside it (sys-approval-request-pointer-pair.test.ts, +28 lines, one new describe with two tests), and .changeset/23736-record-id-deleted-when.md. Line budget: 43 additions, 0 deletions.

Measured assumptions

  1. The line parses. Measured on origin/main d3df3ba (the card cited 0b1179f): the key is field.zod.ts:1494, its field-half refusal :2294 (no referenceVia is refused), and the sibling half is refuseDishonestReferenceDeletedWhen in object.zod.ts:1862 (the sibling must be a declared single-value select with inline options holding value). record_id declares referenceVia: 'object_name'; cancel_reason is Field.select(APPROVAL_CANCEL_REASONS.map(...)) holding record_deleted. ObjectSchema.create parses the object at import and the pin re-parses the exported object with ObjectSchema.safeParse: success.

  2. Clause-② no holds: the emitted declaration closure of @objectstack/plugin-approvals is byte-identical. Built the package before (on the base) and after (on this head), dist/index.d.ts and dist/index.d.mts, the only two declaration files it emits:

     before  8d6525557b9d3cc681e0cf1e06c8dac6f163ebe535b4f36f99d1a87690d88b1e  (both files)
     after   8d6525557b9d3cc681e0cf1e06c8dac6f163ebe535b4f36f99d1a87690d88b1e  (both files)
    

    Positive control: after the second build dist/index.js and dist/index.mjs each carry the referenceDeletedWhen line (1 hit each), so that build did pick the change up. The key is already in the declarations (45 hits in both builds, from the spec's field type), which is why they do not move. No export, type or accept set changes.

The pin, and its ablations

The pin: fields.record_id.referenceDeletedWhen equals { field: 'cancel_reason', value: 'record_deleted' }; cancel_reason is a select declaring the record_deleted option; the exported object parses through ObjectSchema.

Both ablations ran through scripts/ablation-replace.mjs (anchor hit 1 time, blob changed, restore proven: blob equals HEAD and git diff HEAD empty), after the fix was committed. The pin's subject is this package's source (./sys-approval-request.object.js), so no rebuild is in the path. Direction in both: red.

  • Delete the line -> the presence test is red (1 failed, 5 passed):

      AssertionError: expected undefined to deeply equal { field: 'cancel_reason', ...(1) }
      - Expected: { "field": "cancel_reason", "value": "record_deleted" }
      + Received: undefined
    
  • Point value at an option that does not exist (record_gone) -> the spec refuses the parse at import, so the whole file is red (0 tests run) with the located issue:

      ZodError: code custom, path fields.record_id.referenceDeletedWhen.value
      Field `record_id` on object `sys_approval_request` declares `referenceDeletedWhen.value: 'record_gone'`, but `select` field `cancel_reason` declares no option `record_gone`. Its option values: `record_deleted`.
    

Restored tree: the pin file passes 6 of 6 (4 existing pointer-pair tests and the 2 new ones); the whole @objectstack/plugin-approvals suite passes 78 files, 1082 tests (vitest run --maxWorkers=2, under the shared verify lock, exit 0).

Gates (all at head 194e9ef)

  • pnpm --filter @objectstack/plugin-approvals typecheck: exit 0 (the test-layer debt ledger holds at 8 files, 324 errors, 27 signatures: unchanged).
  • eslint --no-inline-config on the two changed source files: exit 0. Control-byte scan of all three changed files: no hits.
  • node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack derived 65 commands from the diff; every one was run with its exit code recorded before any pipe; --ran reconciles: 65 derived, 65 run, 0 NOT-MEASURED, 0 UNRUN. Two families first exited 3 (PREREQUISITE NOT MET: check:i18n and check:dual-build-cjs-loads read built output of packages not yet built). Their prerequisites were built (turbo run build, 61 and 75 tasks) and both were re-run: exit 0 (check:i18n: 10 packages in sync; check:dual-build-cjs-loads: 110 entry points load). The four other dist-reading families (check:dts-closure, check:published-files, check:lean-entry-closure, check:sourcemap-no-sources-content) were re-run on the fully built tree too: exit 0.
  • origin/main moved to 46ca793 during the run (a version-packages commit and two others; under packages/plugins/plugin-approvals it touches only CHANGELOG.md and package.json, the version bump, and nothing under packages/spec/src/data). The three changeset gates were re-run against it: exit 0. No merge of main into this branch.
  • Everything CI runs beyond that set (the full Lint & Repo Gates farm, Test Core, Build Core) is left to the PR's check-runs; none was re-run locally.

Acceptance notes


Generated by Claude Code

…nceDeletedWhen 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>
@github-actions github-actions Bot added size/s documentation Improvements or additions to documentation tests tooling labels Oct 16, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

3 anchor(s) derived from 1 changed package(s); no hand-written page names any of them, so this run has nothing to list — not a clean bill of health. This check sees only pages that NAME a derived anchor: one that documents this change in prose, or enumerates it in an authoring dialect, names none and stays invisible to it on every run.

What this run could not see

Coarse fallback — 6 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json 46ca793edbef645731bebadb1bd7a63564cafba2 → packageMentionDocs.

Which tree this was computed on

This run read content/docs from a47e71ba2d75db160c8a08130237e3066bf92710 — the merge of head 194e9effe787bad775627bd51561c328d562b672 into base 46ca793edbef645731bebadb1bd7a63564cafba2, which is what actions/checkout gives a pull_request run. Not the PR head.

A worktree cut from an older main holds a different content/docs, so re-deriving there can legitimately return a different list — that is a different tree, not a wrong row. To answer on the same tree:

# while this PR is open — GitHub drops the merge commit once it closes
git fetch origin a47e71ba2d75db160c8a08130237e3066bf92710 && git checkout a47e71ba2d75db160c8a08130237e3066bf92710
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 46ca793edbef645731bebadb1bd7a63564cafba2 194e9effe787bad775627bd51561c328d562b672 && git checkout -B drift-repro 46ca793edbef645731bebadb1bd7a63564cafba2 && git merge --no-ff 194e9effe787bad775627bd51561c328d562b672

node scripts/docs-audit/affected-docs.mjs --json 46ca793edbef645731bebadb1bd7a63564cafba2

⚠️ That checkout carried uncommitted changes, so the commit above does not fully identify what was read.

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

席位核对 · ACCEPT · domain:services seat 2 · session_018VqJPGA1NDNSCZsnecsChP · 2026-10-16T03:16Z

Reviewed against GitHub at head 194e9effe7, not against the report (6207234096 on #23736).

  • Form: draft, base main, first line Fixes #23736 (the whole card lands; no other open card's number sits beside a closing keyword). Three files, all on claim 6206736832's surface: sys-approval-request.object.ts (+3), the pin beside it (+28), .changeset/23736-record-id-deleted-when.md (+12). Nothing under content/docs/releases/. @objectstack/plugin-approvals declares no private, so it is published and the patch changeset is owed and present. check-governed-merges.mjs --pr 23798: NOT governed, 43 changed lines, under 3,000.
  • Premise, measured: the diff adds referenceDeletedWhen: { field: 'cancel_reason', value: 'record_deleted' } beside referenceVia: 'object_name', the ruling's line exactly. The pin asserts the key and re-parses the exported object through ObjectSchema.safeParse. Both ablations are in the PR body with their failing output: the line deleted → expected undefined to deeply equal …; the value pointed at a missing option → the spec's located refusal at import. Clause-②: no is backed by byte-identical dist/index.d.ts / .d.mts before and after, with a positive control in the same block (both dist/index.js and .mjs carry the new line).
  • Changeset, sentence by sentence against the diff: the title (the key on record_id, keyed to cancel_reason = record_deleted): matches. "Clause-②: no": matches the claim. "declares referenceDeletedWhen … beside its referenceVia: 'object_name'": matches. "draws that option's label (Related record deleted)": the label is APPROVAL_CANCEL_REASON_LABELS.record_deleted (approval-service.ts:134). "No export, type, route, stored value or request answer moves": accepted as reading data answers; the bullet's own first sentence states the served definition gains the key.
  • Gates, pinned to head 194e9effe7: completed and green so far: Build Core, Type Check · source gates, Type Check · debt ledger, Check Changeset, Lint · PM dispatch-gates, Lint · scoped · tooling self-tests, Lint · scoped · query-options erasure, Governed Surface Queue Guard, the four claim-integrity checks, Dogfood Regression Gate, Check PR Size. Still in_progress: the five Lint · legs, Type Check · workspace, Type Check · consumer gates, Test Core (1–6/6). ⛔ Not flipped until every check is green or an expected skip (the six skips seen are the roster's).
  • Accepted deviations: model-free commit trailers per AGENTS.md; no merge of main before opening (main moved by a version bump that touched only this package's CHANGELOG.md and package.json; the three changeset gates were re-run against it); a stray /build-before.pid outside the repo, container-local.
  • Out of scope: packages/spec/liveness/field.json keeps referenceDeletedWhen planned until the renderer lands. Carrier: objectui#12489 step 4, which the card body names. No card filed.
  • Landing: no contract review is owed (Clause-②: no, not a narrowing, no governed path). When every check is green this seat runs pr_ready + automerge_enable through the relay, then follows the PR to MERGED.

Generated by Claude Code

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review October 16, 2026 03:35
@objectstack-fleet
objectstack-fleet Bot enabled auto-merge October 16, 2026 03:35
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 16, 2026
Merged via the queue into main with commit 74b4362 Oct 16, 2026
41 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/issue-23736-record-id-deleted-when branch October 16, 2026 04:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/s tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

plugin-approvals: sys_approval_request.record_id declares referenceDeletedWhen on cancel_reason = record_deleted (objectui#12489 letter A)

2 participants