Part of objectstack-ai/hotcrm#700
Found during HotCRM's 17.0 GA acceptance sweep on @objectstack/* 17.0.0-rc.2, flow phase. Same root shape as the ctx.api action-body stamping gap HotCRM hit earlier (hotcrm#548), but a different writer: the automation engine's create_record node under runAs:'system'. Filed separately so the fix isn't assumed covered by whichever patch closes the action-path variant.
Reproduction (observed live, timestamps from the acceptance run's devserver2.log)
- 08:13:03 — manual trigger of a
runAs:'system' scheduled sweep (contract_renewal) created a task and an opportunity via create_record nodes.
- Both rows:
owner_id = NULL, organization_id = NULL, created_by = NULL (the app-level owner lookup gets a value; the platform columns do not).
- 08:19 — as admin:
DELETE → 403 FORBIDDEN; repair PATCH {owner_id:…} → 403 as well.
- 08:20 — a demo-scaffolding flow's 10-minute claim pass stamped
owner_id on the rows.
- After that: the same PATCH/DELETE → 200.
Step 4/5 is the tell: nothing about the caller changed between the 403 and the 200 — only the row's owner_id did.
Impact
Demo scaffolding is a crutch real installs don't have: every record a scheduled sweep creates is born admin-untouchable — renewal tasks/opportunities, stalled-deal tasks, snapshot rows. Nobody can edit, complete, reassign, or delete them without DB-level intervention.
organization_id = NULL additionally leaves such rows outside the org partition: unique indexes on (organization_id, …) don't bite across the NULL boundary and org-scoped queries may not see the rows (this produced a duplicate visible-number pair in hotcrm#698's evidence).
Expected
ADR-0118 (#4608) defines the non-user-actor contract: explicit isSystem, fail-closed, no NULL ambiguity. The engine knows the org and could stamp it; for ownership, stamp a system/service identity or the flow's declared operating identity — anything but NULL.
Refs (checked for duplicates): #4608 (the contract ADR, closed), #5378 (author-time validator vs system columns — different defect). Sibling app-side report: objectstack-ai/hotcrm#700.
Part of objectstack-ai/hotcrm#700
Found during HotCRM's 17.0 GA acceptance sweep on
@objectstack/*17.0.0-rc.2, flow phase. Same root shape as thectx.apiaction-body stamping gap HotCRM hit earlier (hotcrm#548), but a different writer: the automation engine'screate_recordnode underrunAs:'system'. Filed separately so the fix isn't assumed covered by whichever patch closes the action-path variant.Reproduction (observed live, timestamps from the acceptance run's devserver2.log)
runAs:'system'scheduled sweep (contract_renewal) created a task and an opportunity viacreate_recordnodes.owner_id = NULL,organization_id = NULL,created_by = NULL(the app-levelownerlookup gets a value; the platform columns do not).DELETE→ 403 FORBIDDEN; repairPATCH {owner_id:…}→ 403 as well.owner_idon the rows.Step 4/5 is the tell: nothing about the caller changed between the 403 and the 200 — only the row's
owner_iddid.Impact
Demo scaffolding is a crutch real installs don't have: every record a scheduled sweep creates is born admin-untouchable — renewal tasks/opportunities, stalled-deal tasks, snapshot rows. Nobody can edit, complete, reassign, or delete them without DB-level intervention.
organization_id = NULLadditionally leaves such rows outside the org partition: unique indexes on(organization_id, …)don't bite across the NULL boundary and org-scoped queries may not see the rows (this produced a duplicate visible-number pair in hotcrm#698's evidence).Expected
ADR-0118 (#4608) defines the non-user-actor contract: explicit
isSystem, fail-closed, no NULL ambiguity. The engine knows the org and could stamp it; for ownership, stamp a system/service identity or the flow's declared operating identity — anything but NULL.Refs (checked for duplicates): #4608 (the contract ADR, closed), #5378 (author-time validator vs system columns — different defect). Sibling app-side report: objectstack-ai/hotcrm#700.