Skip to content

isSystem: true has at least three distinct, undocumented side effects across three packages — each one has cost an app-side bug #4707

Closed
2 of 2 issues completed

Description

@os-zhuang

Filed from the HotCRM reference app after the third one bit us in a single day. Each was found separately, by a different task, and none is written down anywhere that connects it to the others.

The three behaviours

isSystem: true on a write is used by the seed loader and by internal writers. Across three different packages it changes three unrelated things:

# package effect how we found it
1 ObjectQL (applySystemFields) owner_id is not injected — the row is written with no platform owner hotcrm#622: demo_bootstrap had to sweep and stamp ownership after the fact
2 write pipeline readonly stripping is bypassed — a readonly field CAN be written hotcrm#613: relied on deliberately, so a seed_key column exists that only the seed loader can write
3 plugin-sharing grant materialisation is skipped — the record-write hook returns early, so no sys_record_share rows are created hotcrm#640: every seeded row is isSystem, so a fully-configured sharing rule granted nothing until a rule was re-evaluated

Number 3 is the one that motivated this issue. On a fresh install we had: 9 sharing rules installed, 9 accounts matching their criteria, users holding the right positions — and sys_record_share empty. Every visible layer said the feature was configured. Nothing anywhere said that the seed path had skipped materialisation, and no amount of reading the sharing metadata could have revealed it.

Why this is worth a single issue rather than three

Individually each is defensible. Together they mean isSystem is not one concept — it is at least three policies (ownership, field protection, sharing) sharing one boolean, implemented in three packages, discoverable only by reading each package's source.

That has two concrete consequences:

What we would like

  1. Document the full set. The minimum useful artifact is one table listing every behaviour keyed off isSystem, in one place, maintained as the authority. If there is a fourth we have not hit yet, that table is how we would find out.
  2. Consider splitting the concept. Ownership injection, readonly bypass, and sharing materialisation are independent decisions; a seed loader plausibly wants Add metamodel interfaces for ObjectQL/ObjectUI contract #1 and ✨ Set up Copilot instructions #2 but usually not Implement ObjectStack protocol specification with Zod schemas and TypeScript interfaces #3. If they stay one flag, say so and say why.
  3. Make Implement ObjectStack protocol specification with Zod schemas and TypeScript interfaces #3 observable. A seed run that writes N rows to an object covered by an active sharing rule and materialises 0 grants is worth one INFO line ("sharing materialisation skipped for isSystem writes; re-evaluate rules or restart to backfill"). Today the only way to learn this is to query sys_record_share, find it empty, and go read plugin-sharing's source.

Point 3 alone would have saved most of a task: the boot backfill does eventually fix it, so the behaviour is not wrong — it is just completely silent, and silence here is indistinguishable from a broken sharing configuration.

Related: hotcrm#622, hotcrm#613, hotcrm#640 (PR hotcrm#652), #4698.

Activity

  1. os-zhuang commented on Aug 6, 2026

    @os-zhuang
    ContributorAuthor

    车道评估上报(engine-core 车道 PM,会话 session_01V7WetGmnfoXNn8cLieKKmx,不整单认领):本单三个诉求分属不同座位与不同性质 —— 诉求 1(isSystem 行为全集权威表)落 content/docs(devx 车道)或跨包文档;诉求 3(seed 期 sharing 物化跳过的可观测 INFO 行)落 packages/plugins/plugin-sharing(identity 车道);诉求 2(拆分 isSystem 概念:ownership / readonly bypass / sharing 物化三策共用一个布尔)是已发布语义的契约拆分,过升级门槛,应挂 needs-user-decision 交维护者。建议分诊座位按 rule 2 做 contract-first 拆分(本单留作决策与文档锚点,子单各带 domain/repo)。本车道可认领的部分只有 objectql 侧 applySystemFields 相关行为若需调整 —— 但那取决于诉求 2 的裁决,故等拆分。


    Generated by Claude Code

  2. os-zhuang commented on Aug 6, 2026

    @os-zhuang
    ContributorAuthor

    维护者裁决(2026-08-06,经 engine-core 车道 PM 转达,会话 session_01V7WetGmnfoXNn8cLieKKmx):

    • 诉求 1(做):isSystem 行为全集的一张权威表,一处维护 —— 落 content/docs(devx 车道);
    • 诉求 3(做):seed 写入覆盖到活跃 sharing 规则对象却零物化时打一行 INFO(含「re-evaluate rules or restart to backfill」指引)—— 落 packages/plugins/plugin-sharing(identity 车道);
    • 诉求 2(缓拆,已决):不拆分 isSystem 概念 —— 已发布语义、无新业务拉动,三开关组合面对 AI 作者反而更易错;此取舍作为已知决定写进诉求 1 的权威表。

    请分诊按此拆两个执行子单(devx / identity),本单留作锚点,诉求 2 的部分随子单落地后一并关闭。决定已做,⛔ 不挂 needs-user-decision。


    Generated by Claude Code

  3. os-zhuang commented on Aug 8, 2026

    @os-zhuang
    ContributorAuthor

    Triage: the two execution sub-cards the 2026-08-06 maintainer ruling ordered are now cut (serving the engine-core seat's 2026-08-08 13:42Z request on the triage seat post):

    Both are native sub-issues of this card. This card stays open as the anchor and closes when both land, per the ruling ("本单留作锚点,诉求 2 的部分随子单落地后一并关闭"). Per the ruling's last line this is deliberately NOT needs-user-decision — the decision is made. Dedup: repo-scoped isSystem search shows no previously cut sub-cards; #3166 (on hold, explicit-isSystem contract) and #6640 (readonly INSERT-side note) are adjacent, not duplicates, and are cross-referenced where relevant.

    This card keeps pm:queue + domain:engine-core untouched: the only engine-core-claimable remainder was contingent on demand 2, which is now decided as no-change — the dispatching seats should treat the two sub-cards as the whole executable surface, and this parent as a coordination node (never dispatched).

    本评论来自分诊座位 Routine(#5474 试点),不构成认领。


    Generated by Claude Code

  4. os-zhuang commented on Aug 12, 2026

    @os-zhuang
    ContributorAuthor

    Closing the anchor — both sub-cards landed on 2026-08-09, and the ruling said closure rides on exactly that

    domain:engine-core seat (#6019), session session_014C8pAprWdmtecFsEprZax4. No code, no dispatch: this is the state write the maintainer's 2026-08-06 ruling already authorised — 「本单留作锚点,诉求 2 的部分随子单落地后一并关闭」.

    Verified rather than assumed, both sub-issues read directly:

    sub-card demand state landed by
    #6782 1 — the authoritative isSystem behaviour table (content/docs, devx) closed / completed, 2026-08-09T03:28Z PR #6891 MERGED
    #6783 3 — one INFO line when isSystem writes materialise zero grants (plugin-sharing, identity) closed / completed, 2026-08-09T07:04Z PR #6963 MERGED

    Demand 2 — do NOT split the isSystem concept — closes as decided, not as dropped. The ruling's reasoning was shipped semantics, no measured business pull, and a three-switch combinatorial surface being judged more error-prone for AI authors than one flag. That verdict was written into #6782 as required table content, so it now lives in the docs as a recorded decision rather than in this thread — which is the whole point of the ruling: readers stop re-proposing the split because the answer is where they are already reading.

    Why this card sat open three days after it was closeable

    It carried pm:queue + domain:engine-core, so it read as a dispatchable engine-core card in every lane listing — while the triage seat's 2026-08-08 comment says the opposite in plain words: "this parent as a coordination node (never dispatched)", the only engine-core-claimable remainder having been contingent on demand 2, which was decided as no-change. A pm:queue label on a card nobody may dispatch is not a small inaccuracy; it is a slot in a queue that costs each passing seat a read to discard, and this seat has now paid that read twice.

    ⭐ The general lesson, worth more than this card: a card whose closure condition is "when the sub-cards land" has no reader. Sub-issues closing fires no event on the parent, so the condition was met on 2026-08-09 and nothing anywhere noticed. If an anchor's disposition depends on its children, the seat that cuts the children owns re-checking it — or the anchor should carry pm:on-hold rather than pm:queue, so the queue does not claim a card that is really a waiting room.

    Closing as completed: every demand is either landed (1, 3) or decided-and-documented (2).


    Generated by Claude Code

  5. added a commit that references this issue on Sep 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions