Repository navigation
docs(skills): a user field's declared deleteBehavior is taught in the data skill's field-types and relationships rules - #29781
Conversation
…eteBehavior takes effect when the user is deleted The `user` row of the field-types rule lists `deleteBehavior` with the walk's behaviour (no default; declared, it acts as on a lookup; undeclared, the field keeps the deleted user's id), and the delete-behaviours section of the relationships rule names `lookup`, `tree` and a declaring `user` field among the reference types the walk reads. One eval case exercises it. Each ceiling is paid in place: the field-types Summary (Roll-up) block (restated by the relationships rule's Roll-up Summaries) and the relationships "When to Use lookup vs master_detail" bullets (restated by the Relationship Types table and the Incorrect vs Correct section; its lookup-to-user example contradicted the package's own eval) are deleted. Claude-Session: https://claude.ai/code/session_01J8VVAkZCePk1V8qh465qcT Co-authored-by: Claude <noreply@anthropic.com>
|
domain:skills#1 · huangyiirene · session_01J8VVAnMUWA9rufXzFaEh4J Contract-review-claim: session_01J8VVAnMUWA9rufXzFaEh4J · head dceca37 · 2026-11-03T04:25Z The seat review runs at CONTRACT_REVIEW_TIER in an isolated subagent (the seat session is not at that tier). |
|
domain:skills#1 · huangyiirene · session_01J8VVAnMUWA9rufXzFaEh4J Contract reviewServed-tier: Reviewer: an isolated contract reviewer at CONTRACT_REVIEW_TIER, read-only on GitHub (REST reads only; nothing written, pushed or commented), fed the card body and comments, the PR body, the net diff against the merge-base, the head's check-runs, AGENTS.md, the pm-dispatch SKILL.md contract-review section, contract-review.md, review-checklist.md and lanes/skills.md. The dev gate record was read as input, not as conclusions; every count and every claim about the engine below is this review's own reading of the head tree. No environment variable value is printed anywhere in this record. ① Derived judgments
② Semver levelNone. No published package, no ③ Boundary flags
④ Design judgmentPer hits file, against the card's scenario (an AI authoring metadata from the skill must know that
Must-fix: M1 — in relationships.md L179–180 (head) the sentence " Check-runs at record time (2026-11-03T04:37Z): 45 check-runs on dceca37, 26 success, 18 skipped, 0 failure, 1 in progress on the third read (Test Core 1/8); three reads over seven minutes went 19 → 26 → 26 success with no non-success completion at any read (the first read, at 43 runs, had six in progress: Test Core 1/8 and 2/8, Lint · repo & tooling gates, Lint · docs & code guards, Lint · CI, release & engine gates, Type Check · source gates; all but Test Core 1/8 completed success). Success: Lint & Repo Gates, Lint · repo & tooling gates, Lint · CI, release & engine gates, Lint · PM dispatch-gates, Lint · docs & code guards, Lint · ESLint, Lint · scoped · tooling self-tests, Lint · scoped · slot lookup & corpus, Lint · scoped · query-options erasure, TypeScript Type Check, Type Check · workspace / source gates / debt ledger / consumer gates, Build Core, Governed Surface Queue Guard, Dogfood Regression Gate, Test UI, Test Core (2/8), Plan Test Core shards, Live E2E UI, PR-body guards, Check Changeset, Check PR Size, Auto Label, filter. Skipped by path filter or opt-in: Build Docs, Build UI Deps, Build & E2E UI, Lint UI, Type Check UI, UI Published Artifacts, Dogfood Verify CLI, Temporal Conformance, Schedule gate, 18.x upgrade harness, Packed-tarball smoke, and the matrix-templated placeholder rows for Test UI and the Dogfood shards plus the duplicate Check Changeset / Check PR Size / Auto Label rows from a second workflow trigger. Combined status Implemented-by: VERDICT: FAIL Generated by Claude Code |
…se, and the eval index line reads as a sentence A required `multiple: true` field declaring `set_null` refuses the delete only when removing the member would empty the set; otherwise the member is removed and the delete goes through. The relationships rule's delete-behaviours paragraph now says so, since it binds a `user` field to the lookup behaviour. Claude-Session: https://claude.ai/code/session_01J8VVAkZCePk1V8qh465qcT Co-authored-by: Claude <noreply@anthropic.com>
|
domain:skills#1 · huangyiirene · session_01J8VVAnMUWA9rufXzFaEh4J Contract-review-claim: session_01J8VVAnMUWA9rufXzFaEh4J · head 5039eb2 · 2026-11-03T04:50Z The seat review runs at CONTRACT_REVIEW_TIER in an isolated subagent (the seat session is not at that tier). |
|
domain:skills#1 · huangyiirene · session_01J8VVAnMUWA9rufXzFaEh4J Contract review handed to triage · head
|
|
triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U Contract-review-claim: session_01AavokzJ5DndAwitDXvKy4U · head 5039eb2 · 2026-11-03T06:21Z Taken on the maintainer's word in the triage session, 2026-11-03: 「接,这个座位没有fable了」. This is a one-off exception to #29667 for this delta, as handoff 6595797481 asks. Generated by Claude Code |
|
triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U Contract reviewServed-tier: Taken by triage as a one-off exception to #29667, on the maintainer's word in the triage session: 「接,这个座位没有fable了」. The handoff is 6595797481, after the seat's tier answered 429. ① Derived judgments
② Semver levelNone. These are skills docs and an eval index; ③ Boundary flags
④ Design judgment
Check-runs on Implemented-by: VERDICT: PASS Generated by Claude Code Generated by Claude Code |
|
domain:skills#1 · huangyiirene · session_01J8VVAnMUWA9rufXzFaEh4J 维护者速读
席位核对 — ACCEPT · domain:skills#1 · head
|
|
triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U Path: none Triage: correction to my PASS record on this PR · 2026-11-03T14:28Z⛔ Not a new review of a new head. This corrects ② of triage's delta PASS on
Generated by Claude Code |
domain:skills#1 · huangyiirene · session_01J8VVAnMUWA9rufXzFaEh4J
Requested by Irene · project thread
Fixes #29400
Clause-②: no
Metadata-protocol: none
维护者速读
user字段上作者写明的deleteBehavior在删除该用户时真的生效」:field-types 的user行列出deleteBehavior(无默认;写了就按 lookup 的方式 cascade / set_null / restrict,没写则保留被删用户的 id);relationships 的 Delete Behaviors 节点名删除联动读lookup、tree,以及只在声明了值时才读的user字段。加一条 eval 用例练它。deleteBehavioron auserfield parses but the delete walk never reads it, sorestrict/cascadeon a user field are not enforced #29253),其Skills-docs: follow-up行欠下本卡;技能文本此前只说 user 字段「存储同 lookup」,没说删除用户时会发生什么,AI 写元数据时会把deleteBehavior当摆设或漏写。skills/objectstack-data/rules/relationships.md新段(Delete Behaviors 节末)与被删的 When to Use 节;skills/objectstack-data/rules/field-types.md的user行与被删的 Summary (Roll-up) 块;skills/objectstack-data/evals/objects-fields-relationships.json的用例 6。node scripts/check-skills-token-ratchet.mjs,预期 field-types.md 3183/3207、relationships.md 3844/3873,全绿。skills/**),需要GOVERNED_APPROVERS的批准;PR 保持 draft。What changes
Facts are taken from current
main, not the card text alone:content/docs/data-modeling/fields.mdxas corrected by PR #29712,statesDeleteSemantics/cascadeRelationBehaviorinpackages/objectql/src/engine.ts, and the pins inpackages/objectql/src/engine-cascade-delete-user-field.test.ts.skills/objectstack-data/rules/field-types.md, theuserrow. The Key Config cell addsdeleteBehavior(no default — declared, deleting the user acts as onlookup:cascadedeletes the holders,set_nullclears the field or drops that member,restrictrefuses with409 DELETE_RESTRICTED; undeclared, the field keeps the deleted user's id). Everything else in the row is byte-identical.skills/objectstack-data/rules/relationships.md, Delete Behaviors. One new paragraph after themaster_detailcode block: the walk also readslookupandtree(set_nullby default — the field is cleared; amultiplefield loses only that member — or an authoredcascade/restrict), and auserfield only when it declares a value: deleting the user then cascades, clears or refuses exactly as alookupdoes; undeclared, the field keeps the deleted user's id.restrict, andset_nullon arequiredfield (amultipleone only when the removal would empty it), refuse the delete with409 DELETE_RESTRICTED— the multi-valued narrowing is the engine's (cascadeDeleteRelationsescalates unconditionally only for a single-valued field and otherwise refuses only the rows the removal would empty; pinned inengine-cascade-delete.test.ts). The existingmaster_detailtext above it is byte-identical.skills/objectstack-data/evals/objects-fields-relationships.json. Case 6: a requiredowneruser field that must block deleting a held user (restrict) and a multi-valuedwatchersfield whose deleted member should simply drop out (set_null). Must-contain pins the twodeleteBehaviorvalues,type: 'user',multiple: trueandrequired: true; must-not-contain refusesreference: 'user'anddeleteBehavior: 'cascade'. The eval README's index line names theusercase ("deleteBehavior, on auserfield too"). Eval fixtures are measured, not ratcheted.Token ratchet, paid in place (no ceiling row moved, no re-wrap)
rules/field-types.mdrules/relationships.mdevals/objects-fields-relationships.json(not ratcheted)evals/README.md(not ratcheted)skills/objectstack-data(17 files)The two payments, each a deletion of text restated elsewhere, with its home:
field-types.md→ the Summary (Roll-up) block under Common Field Configurations (14 lines, 361 bytes). Home:rules/relationships.md→ Roll-up Summaries carries the samesummaryOperationsblock with the same keys and comments plus thefilter/relationshipFieldteaching; the Calculated table row in field-types.md keeps the key shape ({ object, field?, function, relationshipField?, filter? }). The deleted block's own last comment pointed atrules/relationships.md.relationships.md→ the When to Use lookup vs master_detail section (14 lines) and its link in the table of contents (613 bytes together). Home: the Relationship Types table directly above it (Lifecycle, Required, Use Case columns), the Incorrect vs Correct section (lookup vs master_detail on invoice line items), and the Patterns examples. Its one line not restated there —set_nullis thelookupdefault — moves into the new Delete Behaviors paragraph. Itstask.assigned_to → userexample taught alookupto auserobject, which this package's own eval 2 forbids (reference: 'user'is must-not-contain; auserobject does not exist), so that line was also stale.The package grows by about 270 tokens: the eval fixture and its index +319 (not loaded into a customer session), the two loaded rule files −24 and −25. Their ceiling rows could be lowered to 3183 and 3844 in a follow-up; this card's file surface does not include
scripts/check-skills-token-ratchet.mjs.Why
PR #29368 (card #29253) made a
userfield's declareddeleteBehaviortake effect when the user is deleted, and itsSkills-docs: follow-upline owed this card. Before this change the data skill said only that auserfield is "stored identically tolookup", and its Delete Behaviors section spoke ofmaster_detailalone, so an AI authoring metadata from the skill had no way to know thatdeleteBehavioron auserfield is now enforced, that it has no default, or that arequiredfield'sset_nullrefuses the delete.Tests
node scripts/check-skills-token-ratchet.mjs: exit 0 —57 authored bundle file(s) within their ceilings;rules/field-types.md is 3183 tokens (ceiling 3207; headroom 24),rules/relationships.md is 3844 tokens (ceiling 3873; headroom 29).must_containstring of case 6 appears in itsexpected_output.Clause-②: no, no!in the title).Acceptance notes
claude/project-thread-5yjezo) in this container's own checkout rather than in a sibling worktree: the branch can be checked out in one place only and the container is this card's alone. Noted as a deviation from the worktree-first spelling, not from its purpose.master_detailtext in both files is left exactly as it was; the card asked for theuserteaching only.lookuprow of field-types.md still listsdeleteBehaviorwithout its default; relationships.md now states the default once. Not expanded further (small feature, small text).set_nullsentence now carries the multi-valued qualifier; S1, the eval README index line reads as a sentence. Nothing else moved.content/docs/data-modeling/fields.mdxcarries the same unqualified sentence for auserfield and is the docs lane's follow-up, not this PR's.Verification
Head
5039eb23(two commits: dceca37 the change, 5039eb2 the review's M1 + S1). No code changed, so no package test or typecheck is owed; the derived gate set is the verification.Local gates on 5039eb2: 32 derived, 31 run with exit 0, 1 NOT MEASURED (`dispatch-gates --ran`); the same 32 on dceca37 read the same
node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstackderived 32 commands for the four changed files, the same list on both heads. All 32 ran on each head in a tree withpnpm install --frozen-lockfiledone and@objectstack/formula,@objectstack/lint,@objectstack/specand@objectstack/objectqlbuilt, each with a 900 s timeout, each exit code captured before any pipe.check-skills-token-ratchet.mjs(and its--self-test),check-comment-mask-corpus.mjs,check:doc-authoring,check:pm-skill-id-lint,check:skill-compatibility,check:skill-frame-sync,check:skill-identifier-liveness,check:role-word,check:nul-bytes,check:pm-governed-merges,check:dts-closureandcheck:lean-entry-closure(on dceca37 the last exited 3 before the@objectstack/objectqlbuild and 0 after it).pnpm check:dual-build-cjs-loadsexited 3:PREREQUISITE NOT MET — this gate reads built output, and some package has no dist/(91 packages; it needs a fullpnpm build). Not a finding: this diff touches nopackages/**file, and the gate's own text says nothing was measured. CI'sLint & Repo Gatesrun on the PR is its reading.--ranprinted:32 derived famil(ies) accounted for — 31 run, 1 NOT-MEASURED (1 DERIVED from a recorded exit 3).node scripts/check-commit-card-trailers.mjs --range origin/main..HEAD: exit 0 on both heads (two commits, no card relation, model-free trailer pair).successor skipped by path filter or opt-in, including the required Lint & Repo Gates, TypeScript Type Check, Test Core, Build Core, Governed Surface Queue Guard, Test UI and Live E2E UI. CI on 5039eb2: in progress at the time of writing.git push×3 (the bare branch pointer, dceca37, 5039eb2); one relaypr_createcarryingskip-changeset; no GitHub MCP write.Human Minimum
packages/specholds no runtime logic. (No schema touched.)pnpm lintis CI's repo-wide run.main, one pull request.skip-changesetrides the create; both declaration lines are above.packages/specnot touched.content/docs/references/,packages/*/CHANGELOG.mdorcontent/docs/releases/.🤖 Generated with Claude Code
https://claude.ai/code/session_01J8VVAkZCePk1V8qh465qcT
Generated by Claude Code