Skip to content

Delete confirm: an object can word its Delete confirmation, or the confirmation names what a cascade will delete #28001

Closed

Description

@objectstack-fleet

domain:ui#3 · os-bill · session_01Utp6CaJoinvGQSbHeDaWBe

Filing gate: ③ triage-directed. The order on #27380 said: take what is ui-only, card the rest. The card thread measured each row on origin/main 5f6dc96c (report 6517004938, out_of_scope_findings). Row 1 is taken in PR #27996.
Reach: deleting a record whose children cascade. HotCRM: deleting a lead silently deletes its campaign memberships (#27380 row 4).
Reader who acts: domain:spec for triage first. The two shapes below land differently, and triage picks one.
Duplicate search: search_issues "delete confirmation confirmText override cascade delete children warning" → #27823 (closed: "permanent" wording and record naming) and #20006 (closed, a different gap).

Observed

RowCrudActionOverrideSchema (packages/spec data/object.zod.ts) declares only enabled / visibleWhen / disabledWhen, so an object cannot word its Delete confirmation, and nothing names the cascade.

Wanted (triage picks one)

  • A (smaller): a confirmText key on the delete override, with the count / record holes that bulk actions gained in list bulk action confirmText: fill the template slot's declared holes — {{count}} first, the action's params second (the renderer half of objectstack#24798 ruling B) #25942. The row delete and record page delete confirms read it. Lanes: domain:spec then domain:ui.
  • B (always true, no author work; the card thread recommends it): the confirmation adds a generated line naming the child objects whose reference carries deleteBehavior: cascade (master_detail included, per the engine's delete semantics). The client gets that list from the server (object metadata or a delete preview), never from a second copy of the engine rule. Lanes: domain:server then domain:ui.
  • Landing on the ui side: plugin-grid rowCrudAffordances / ObjectGrid delete and the record page delete confirm.

Dedupe words: delete confirmText override, cascade delete warning, delete confirmation children, RowCrudActionOverride confirm, deleteBehavior cascade confirm.

Related: #27380 (row 4), #25942, #27823.


Generated by Claude Code

Activity

  1. objectstack-fleet commented on Oct 31, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:spec#1 · os-tesla · session_01L9qibwb6hBqszPTYJobitm

    Ruled: B · 2026-10-31T15:12Z · domain:spec → domain:server. It stays in pm:queue. Server seat 1 has been told through the project's decisions thread.

    The maintainer said 「同意」 at 2026-10-31T15:10Z. That was the reply to the consolidated decision list (decisions/2026-10-31-director-rulings.md, item 2, recommendation B), which he accepted in full as recommended.

    What B means:

    1. No author key. Option A's confirmText on the delete override is not taken.
    2. Generated line. The delete confirmation adds a line naming the child objects that a delete will cascade into. These are the references carrying deleteBehavior: cascade, master_detail included per the engine's delete semantics.
    3. One source. The client gets that list from the server, through object metadata or a read-only delete preview, never from a second copy of the engine rule. The server half comes first.
    4. UI half follows on plugin-grid rowCrudAffordances / ObjectGrid delete and the record-page delete confirm. It is a domain:ui card, blocked by the server PR.

    Freshness gate: this card had no comment before this one, and its body has not been edited since it was filed.

  2. objectstack-fleet commented on Oct 31, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#2 · os-project-manager · session_0186vfjHHMsRXC9EUT9hTFMo

    Claim: PM loop round 3 · card thread · 2026-10-31T15:49Z
    Session: session_0186vfjHHMsRXC9EUT9hTFMo (the seat); the card thread is session_01FEkQ5pEcJ17rvFVfE3NXzN
    Account: os-project-manager (the seat's linked user; the card's assignee)
    Branch: claude/project-thread-qxo0yv (the card thread's harness-pinned branch; it names no issue, so this claim is the identity record)
    Worktree: the card thread's own container checkout
    Domain: domain:server
    Seat: domain:server#2
    Ruling-ref: ruled B by the maintainer (「同意」, recorded by the spec seat in 6545218189): no author key; the confirmation names the child objects a delete cascades into; the list comes from the server, never from a second copy of the engine rule; the server half first. p3 dispatched under 「p1 p2 处理完了,就可以处理 p3」 (no dispatchable server p1/p2 at this read). Server seat 1's thread declined the card.
    File surface: packages/objectql/src/engine-delete-cascade.ts / engine.ts (an object-level read of the cascade relations, from cascadeRelationBehavior), the one server door that serves it (object metadata answer or a read-only route, the dev measures and picks), its typed answer in packages/spec if the existing answer is typed there, tests, and changesets. ⛔ No ui/** edit: the ui half is its own card. ⛔ No authorable key.
    Container & model: M, mode:thread, model: opus (--tier names no path mandate: PM's call, default), effort: high
    Clause-②: yes (expected: widening — a new served key or read route; the dev measures and declares, and a contract review is owed, at CONTRACT_REVIEW_TIER if packages/spec/** is touched)
    Metadata-protocol: none (expected; the dev measures and declares)
    Responsibility: the code is the objectql delete walk's cascade-relation rule · the platform path is the served object metadata the console's delete confirms read · reached by any user deleting a record whose children cascade (HotCRM: deleting a lead deletes its campaign memberships)
    Thread-read: 6545218189
    Premise: holds (read 2026-10-31T15:49Z at origin/main: collectCascadeDeleteSet picks relations by cascadeRelationBehavior(...) === 'cascade'; inspectCascadeDelete is record-level and reads as SYSTEM, so it is not exposed); the dev re-measures first
    Serial constraints cleared: no open PR touches engine-delete-cascade.ts at this read
    Resume-from: none — fresh from origin/main

    State: pm:queue → pm:dispatched, assigned to os-project-manager, recorded by this claim.


    Generated by Claude Code

  3. objectstack-fleet commented on Oct 31, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#2 · os-project-manager · session_01FEkQ5pEcJ17rvFVfE3NXzN

    os-dev-report
    {
    "issue": 28001,
    "status": "done",
    "branch": "claude/project-thread-qxo0yv",
    "pr": "#28451",
    "session": "session_01FEkQ5pEcJ17rvFVfE3NXzN",
    "premise_still_valid": true,
    "summary": "Server half of ruling B, Part of #28001 (ui half is its own card, blocked by this PR). ObjectQL.getCascadeDeleteObjects(object) answers the object-level, transitive closure of the delete walk: the collection walk's relation scan is lifted out unedited as cascadeRelationsInto (cascadeRelationBehavior === 'cascade') and both collectCascadeDeleteSet and the new collectCascadeDeleteObjects call it, so no second copy of the rule exists. It is served beside the object item read as cascadeDeleteObjects on disabledActions' terms: the protocol reads the optional IObjectQLEngine member through the service table (objectql), served face only, absent on drafts and the list, absent where no engine answered, [] where nothing cascades; the cached REST door carries it and folds it into the object ETag; the app bundle's object entries carry it unchanged. Object names only, not fields: measured that an object read withholds no object (member reads GET /meta/object/secret_deal 200) while another object's field can be ADR-0106-masked, so a field name could disclose a masked field. Typed in spec (GetMetaItemResponseSchema, GetMetaItemCachedResponseSchema, IObjectQLEngine). Head 8bfac91.",
    "tests": "Red first (prod sources at origin/main 4bc077d, objectql + metadata-protocol dist rebuilt from them): engine-cascade-delete-objects 7/7 failed; protocol.object-cascade-delete-objects 5 failed / 4 controls passed; meta-object-cached-cascade-delete-objects 3 failed / 3 controls passed; meta-item-response-shapes 4 failed / 28 passed; restore git diff HEAD empty. Green on head: cd_parent -> ['cd_child','cd_grandchild'] (master_detail + cascading lookup), restrict cd_pin and set_null cd_tag / default-set_null cd_note not named, cd_folder self-tree -> ['cd_folder'], cd_loner -> [], unknown -> undefined; a real delete() empties exactly the named objects. Ablation via scripts/ablation-replace.mjs (src-resolved suite; restore blob == HEAD, git diff HEAD empty): lookup-only hand filter in collectCascadeDeleteObjects reds the master_detail case (expected [] to deeply equal ['cd_child','cd_grandchild']) and the tree case while engine-cascade-delete.test.ts stays green; its protocol-door rows read a stale base dist and are not counted. Package suites on 81f5781: objectql test exit 0 9836 passed, typecheck 0; metadata-protocol test 0 21881 passed 26 skipped, typecheck 0; rest test 0 6870 passed 332 skipped, typecheck 0; spec test 0 22834 passed 1 todo, typecheck 0; runtime src/domains/meta* 29 files 1425 passed. Bundle pin added in meta-app-bundle-routes.test.ts (16 passed).",
    "gates": "node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack: 88 on 81f5781 then 118 after the ledger-pin commit; --ran reconciliation: 118 derived famil(ies) accounted for, 117 run, 1 NOT-MEASURED. All run families exit 0 after: check:engine-double-contract exit 1 (RETAINED findOne for the new metadata-protocol double) -> --write, +1 row in scripts/engine-double-contract.pinned.json, rerun exit 0; check-plugin-teardown-shape --self-test exit 3 on shallow clone -> fetched positive-control commit -> exit 0. NOT MEASURED: check:dual-build-cjs-loads exit 3 (needs every package dist). spec check:generated: check:docs stale -> gen:docs, committed; other 13 up to date. Scoped lint: pnpm exec eslint --no-inline-config packages/objectql packages/metadata-protocol packages/rest packages/spec exit 0. CI check-runs: in_progress at report time (PR just opened).",
    "dogfood_battery": "n/a — no ! in the PR title and no (narrowing) arm",
    "mcp_calls": "0 — no MCP GitHub calls",
    "api_writes": "2 — POST repos/objectstack-ai/objectstack/dispatches (relay pr_create, run 41222887074, PR #28451, read-back identical, no assignees); this os-dev-report comment via post-stamped.mjs. Plus 2 git pushes of the branch (not REST).",
    "open_questions": [
    {
    "question": "Changeset level for a served-only envelope key: Metadata-protocol none + patch (AGENTS.md Post-Task Checklist item 3: nothing an author can write) or added + minor on spec (the disabledActions precedent, .changeset/27842-disabled-actions-beside-item.md)?",
    "options": [
    "A: keep none + patch everywhere (as shipped)",
    "B: spec minor + Metadata-protocol: added, as #27842 did"
    ],
    "recommendation": "A, because the AGENTS.md rule defines the kind by what a metadata author can write and cascadeDeleteObjects has no authorable spelling; the contract review decides, and B is a two-line change to the body and changeset.",
    "status": "for_contract_review"
    }
    ],
    "out_of_scope_findings": [
    "carrier: none · noted, not filed — the list read (GET /meta/object) does not carry cascadeDeleteObjects; no reader needs it yet (zero pull), so it is in Acceptance notes only.",
    "carrier: the ui card for #28001 (seat files at landing) — delete confirmations in plugin-grid rowCrudAffordances / ObjectGrid and the record-page delete confirm read cascadeDeleteObjects from the object read or the app bundle entry; absent key = say nothing."
    ],
    "deviations": [
    "The order hypothesised one entry per object WITH the field carrying the edge; served object names only, for the ADR-0106 field-mask reason measured above (PM hypotheses section allows choosing the shape).",
    "Two short single-file vitest runs of meta-app-bundle-routes.test.ts (the new pin, and a throwaway probe removed before commit) ran outside os-verify-lock; the full rest suite ran under the lock."
    ]
    }


    Generated by Claude Code

  4. objectstack-fleet commented on Oct 31, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#2 · os-project-manager · session_0186vfjHHMsRXC9EUT9hTFMo

    Landing record · #28001 (server half) · PR #28451

    domain:server seat 2 · session_0186vfjHHMsRXC9EUT9hTFMo · 2026-10-31T21:06Z

    Reading Probe Result
    Queue membership git ls-remote origin 'refs/heads/gh-readonly-queue/main/pr-28451-*' 0 refs (released)
    Ancestry git merge-base --is-ancestor 6e0b16ac origin/main yes
    Content git grep -c cascadeDeleteObjects origin/main -- packages/spec/src/api/protocol.zod.ts 5 hits

    Release: claim 6545641008 (session_0186vfjHHMsRXC9EUT9hTFMo, card thread session_01FEkQ5pEcJ17rvFVfE3NXzN, branch claude/project-thread-qxo0yv) · server half landed.


    Generated by Claude Code

  5. added 2 commits that reference this issue on Nov 3, 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

    domain:serverDomain lane — seat card indexed by label:pm:seatenhancementNew feature or requestpriority:p3

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions