Skip to content

docs(event-attendee): state the sys_user cascade rationale as a universal, not a count - #1308

Merged
hotlong merged 1 commit into
mainfrom
claude/issue-1188-sys-user-lookup-count-prose
Aug 26, 2026
Merged

hotlong merged 1 commit into
mainfrom
claude/issue-1188-sys-user-lookup-count-prose

Conversation

@hotlong

@hotlong hotlong commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

Description

Two comments justifying deleteBehavior: 'cascade' on crm_event_attendee.sys_user each stated a hand-counted figure — "HotCRM holds 15 lookups on sys_user; the other 14 … are set_null" — one in the object, one in the test. Nothing produced either integer and nothing checked them.

The figure is load-bearing: it is the evidence for "a deleted user's references degrade is already this app's stance", which is why this lookup uses cascade rather than restrict. The reasoning is sound; the number backing it had no producer. It had already drifted to 16 once (when article_feedback gained an owner_id) with nothing reporting it, and it reads correctly today only because removing crm_account.renewal_owner happened to bring the real count back to 15 — by coincidence, not because anything holds it there.

Both sentences now make the universal claim instead: every other sys_user lookup in HotCRM — every owner_id, plus crm_product.product_manager — is set_null. That is what the cascade argument actually rests on, it is stronger than "the other 14" because a universal cannot be falsified by the next field someone adds, and it needs no producer to stay true.

Pinning the integers was the alternative and was rejected: it would add a second hand-maintained number plus a guard whose only effect is to turn silent drift into red on every new sys_user lookup — an author editing that integer changes nothing a user sees.

Type of Change

  • Documentation update (comment prose only)

Related Issues

Closes #1188

Changes Made

src/objects/event_attendee.object.ts (~:257)

-    //    degrade". HotCRM holds 15 lookups on `sys_user`; the other 14 (every
-    //    `owner_id`, plus `product_manager`) are `set_null`
+    //    degrade". EVERY other `sys_user` lookup in HotCRM (every `owner_id`,
+    //    plus `product_manager`) is `set_null`

test/event-attendee-cascade.test.ts (~:70)

- *     HotCRM holds 15 lookups on `sys_user`; the other 14 — every `owner_id`,
- *     plus `crm_product.product_manager` — are
+ *     EVERY other `sys_user` lookup in HotCRM — every `owner_id`, plus
+ *     `crm_product.product_manager` — is

Each file keeps its own existing voice (parentheses in the object comment, em dashes in the JSDoc block) so the diff stays minimal and the line count is unchanged in both.

Nothing else changed. The whole diff is 4 changed lines across the 2 files, and every one of them is a comment line — verified mechanically:

$ git diff -U0 | grep -E '^[+-]' | grep -vE '^(\+\+\+|---)' | ...
COMMENT-OK: -    //    degrade". HotCRM holds 15 lookups on `sys_user`; the other 14 (every
COMMENT-OK: -    //    `owner_id`, plus `product_manager`) are `set_null`
COMMENT-OK: +    //    degrade". EVERY other `sys_user` lookup in HotCRM (every `owner_id`,
COMMENT-OK: +    //    plus `product_manager`) is `set_null`
COMMENT-OK: - *     HotCRM holds 15 lookups on `sys_user`; the other 14 — every `owner_id`,
COMMENT-OK: - *     plus `crm_product.product_manager` — are
COMMENT-OK: + *     EVERY other `sys_user` lookup in HotCRM — every `owner_id`, plus
COMMENT-OK: + *     `crm_product.product_manager` — is

No code, no assertion, no field, no deleteBehavior. The surrounding argumentation — the /api/v1/auth/delete-user → dataEngine.delete(...) path chain, and the attendee_resolves / attendee_type_exclusive rule claims — is untouched.

The universal claim was verified before being written

The count as of this PR's base (cd198f10), re-measured independently:

$ for f in $(git ls-tree -r --name-only HEAD src/objects | grep '\.object\.ts$'); do
    grep -c "Field.lookup('sys_user'" "$f"; done | paste -sd+ | bc
15
$ # control term, same file set — proves the search reached the code
$ for f in $(git ls-tree -r --name-only HEAD src/objects | grep '\.object\.ts$'); do
    grep -c "Field.lookup(" "$f"; done | paste -sd+ | bc
57

15 objects hold one sys_user lookup each. Field names and delete behaviour:

field objects deleteBehavior
owner_id account, article_feedback, campaign, case, contact, contract, event, forecast, knowledge_article, lead, opportunity, quote, task (13) none declared → default
product_manager product (1) none declared → default
sys_user event_attendee (1) 'cascade' — this lookup

And the default is set_null, from the installed spec (@objectstack/spec@17.1.0, src/data/field.zod.ts:906):

deleteBehavior: z.enum(['set_null', 'cascade', 'restrict']).optional().default('set_null')

So "every other sys_user lookup … is set_null" is true on the base commit, and — unlike the count — stays true as fields are added.

Testing

Run at the final commit 126aa43a:

command verdict
pnpm test Test Files 140 passed (140) · Tests 2969 passed | 1 skipped (2970)
pnpm typecheck exit 0, no output
pnpm validate ✓ Validation passed (1586ms)
pnpm build ✓ Build complete (1711ms)
pnpm lint 45 warning(s), 10 suggestion(s) — pre-existing advisory, exit 0
pnpm lint:i18n-gate ✓ i18n lint gate: 0 i18n/missing-* issues
pnpm hygiene ✓ source hygiene clean
pnpm hygiene:tokens ✓ source token ratchet clean

test/event-attendee-cascade.test.ts — the file whose header comment this PR edits — ran in that suite and passed. The token ratchet is unmoved by design: it strips comments before measuring.

  • Unit tests pass (pnpm test)
  • Linting passes (pnpm lint)
  • Build succeeds (pnpm build)
  • Manual testing completed — N/A, no runtime behaviour changes
  • New tests added — N/A, deliberately: see Additional Notes

Screenshots

N/A — no user-visible surface changes.

Checklist

  • I have added a changeset — .changeset/sys-user-lookup-count-out-of-prose.md, with deliberately empty frontmatter. Of the two lawful exemptions this PR chose the empty-frontmatter changeset over the skip-changeset label: it is the sanctioned "this PR releases nothing" declaration that .github/workflows/changeset-check.yml documents in both of its steps, and it travels in the PR itself rather than depending on a label being applied.
  • My code follows the style guidelines of this project
  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation — this PR is the documentation change
  • My changes generate no new warnings
  • I have added tests that prove my fix is effective — N/A, see Additional Notes
  • New and existing unit tests pass locally with my changes
  • Any dependent changes have been merged and published

Additional Notes

Why no guard ships with this. The card offered a second shape — derive the count from the built stack and assert the comment's number. That shape was rejected on ADR-0049 axis ③: it creates one more hand-maintained integer and merely puts a lock on it, converting "silent drift" into "red on every new sys_user lookup" while changing nothing a user sees. Removing the number removes the drift surface outright, which is why there is no new test here — there is no longer a fact that can drift.

Scope. Strictly the two sentences. test/forecast-seeds.test.ts carries the same family and the same fix shape and is deliberately not touched here — it belongs to its own card.


Generated by Claude Code

…rsal, not a count

Two comments justifying `deleteBehavior: 'cascade'` on
`crm_event_attendee.sys_user` each carried a hand-counted figure — "HotCRM
holds 15 lookups on `sys_user`; the other 14 ... are `set_null`" — in
`src/objects/event_attendee.object.ts` and `test/event-attendee-cascade.test.ts`.
Nothing produced either integer and nothing checked them. They had already
drifted to 16 once, when `article_feedback` gained an `owner_id`, with nothing
reporting it; they read correctly today only because removing
`crm_account.renewal_owner` happened to bring the real count back to 15.

Both sentences now make the universal claim instead: every other `sys_user`
lookup in HotCRM — every `owner_id`, plus `crm_product.product_manager` — is
`set_null`. That is what the cascade argument actually rests on, it is stronger
than "the other 14" because it cannot be falsified by the next field someone
adds, and it needs no producer to stay true.

Pinning the integers was the alternative and was rejected: it would have added
a second hand-maintained number plus a guard whose only effect is to turn
silent drift into red on every new `sys_user` lookup, changing nothing a user
sees.

Comment prose only — no code, no assertion, and no other line of either
comment's argument changed. Ships nothing to users, so the changeset carries
deliberately empty frontmatter.

Claude-Session: https://claude.ai/code/session_01R5spVzEKtmowdQQ3r6xNRM
@vercel

vercel Bot commented Aug 26, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
hotcrm Ignored Ignored Aug 26, 2026 5:50am

Request Review

@github-actions github-actions Bot added ci/cd CI plumbing and the verification pipeline metadata Declarative metadata — schema, security posture, UI surfaces labels Aug 26, 2026
@hotlong
hotlong marked this pull request as ready for review August 26, 2026 05:54
@hotlong
hotlong added this pull request to the merge queue Aug 26, 2026
Merged via the queue into main with commit 9677077 Aug 26, 2026
10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ci/cd CI plumbing and the verification pipeline metadata Declarative metadata — schema, security posture, UI surfaces

Projects

None yet

Development

Successfully merging this pull request may close these issues.

The hand-written "15 lookups on sys_user" figure in two event_attendee comments has no guard, and had already drifted to 16

2 participants