You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
REST clone: a plain clone reports the source row's empty readonly columns as dropped fields (body and header) #29200
Follow-up of: #28933 (landing PR #29034; contract review on that PR, ③ "Dev finding (3)": separate card, fix site on the producer side)
What happens
A clone with no overrides of a row whose readonly column is empty (null) answers droppedFields naming that column, and since #29034 the X-ObjectStack-Dropped-Fields header repeats it. The caller never sent that field. The console announces every non-empty droppedFields, so the user sees a "fields dropped" notice on a plain clone.
Reproduction
Boot the stock showcase.
Pick a contact whose lead_score (readonly) is empty.
POST /api/v1/data/contact/:id/clone with no body.
Expected: 201, no droppedFields, no header.
Actual: 201, droppedFields names lead_score, and the header names it too.
Where
cloneData copies the source row, null readonly columns included, into the insert; engine.insert's readonly strip reports any present key whatever its value. Fix one of the two so that a key the caller did not supply and the clone carried as empty is not reported. Which one is the dev's reading; keep the strip's report for a readonly value a caller really sends.
Acceptance
A plain clone of a row with an empty readonly column answers no droppedFields and no header; a clone whose overrides set a readonly field still reports it.
Claim: PM loop round 1 · card thread · 2026-11-02T10:09Z
Session: session_01ADHkmQxwoGb5fSnrFkpxmt (the seat); the card thread is session_01ADHkmQZyRQCzui7zekMYY7
Account: os-steve (the seat's linked user; the card's assignee)
Branch: claude/project-thread-v06ygi (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: the maintainer, verbatim: 「开 server 2、3、5 号席位, 前任已经下班,接手所有开发,包括跟进之前未完成的开发落地。」 Card: none from the maintainer on this card; filed by this seat as a landing follow-up, graded p2 by triage.
File surface: packages/metadata-protocol/src/protocol.ts (cloneData only, under the delta's route (a)); tests; the one dogfood file showcase-static-readonly.dogfood.test.ts; changeset. ⛔ No logic change in packages/rest/src/error-response.ts, no ui/**, no packages/spec/**.
Container & model: M, mode:thread, model: opus (TIER_DEFAULT; --tier names no path mandate: PM's call), effort: medium
Clause-②: no (expected: a body and header lose a false entry; no request refused)
Metadata-protocol: none (expected)
Responsibility: the code is cloneData in @objectstack/metadata-protocol · the platform path is a plain clone announcing "fields dropped" for empty readonly columns the caller never sent · reached by every console clone and every integration that reads droppedFields or X-ObjectStack-Dropped-Fields
Thread-read: none
Premise: holds (measured by the dev of the parent PR; the card thread re-measures red first on current origin/main)
Serial constraints cleared: open PR #28731 edits protocol.ts at probeRecord, not cloneData (hunks read this act); the card thread keeps its hunks off it and merges origin/main before pr_create
Resume-from: none — fresh from origin/main
State: pm:queue → pm:dispatched, assigned to os-steve, recorded by this claim.
{
"issue": 29200,
"status": "done",
"branch": "claude/project-thread-v06ygi",
"pr": "https://github.com/objectstack-ai/objectstack/pull/29290",
"session": "session_01ADHkmQZyRQCzui7zekMYY7",
"premise_still_valid": true,
"summary": "Producer-side fix under route (a), narrowed to EMPTY values: cloneData (packages/metadata-protocol/src/protocol.ts) now leaves a readonly field whose copied source value is null/undefined out of the insert payload, so engine.insert's strip no longer reports a key nobody supplied; body droppedFields and the X-ObjectStack-Dropped-Fields header both lose the false entry. Stored row unchanged (the strip removed the key from a non-system caller anyway; applyFieldDefaults treats explicit null as omitted). Acceptance 3 measured: a readonly column that HOLDS a value is reported on main (body + header ro;reason=readonly) and still is after the fix; judged NOT the same defect because the #15703 ruling (option 1, record 5578819560) requires the clone to report a copied-and-stripped value, so (a) is not applied to valued columns. Overrides setting a readonly field are still reported (control). No change to packages/rest/src/error-response.ts, ui/** or packages/spec/**.",
"tests": "Red first at main 45e9b2f1 (tests only, rest^... built): rest-dropped-fields-every-write-door.test.ts Tests 1 failed | 17 passed (18), the new plain-clone-of-empty case (expected ... to not have property droppedFields); valued and overrides controls green on main. Fix f825d6bd: 18/18. Ablation via scripts/ablation-replace.mjs (anchor 1->0, blob 3ff88bdc6abc->15c970b77f49, line made dead by name === '__ABLATION_29200__'), ablation-dist-preflight marker in dist/index.js + index.cjs: rest pin 1 failed | 17 passed; dogfood showcase-static-readonly 1 failed | 8 passed (body named lead_score). Restore legs: blob == HEAD, git diff HEAD empty, rebuilt, preflight --absent exit 0, 18/18 and 9/9. metadata-protocol test+typecheck: 300 passed/4 skipped files, 22100 tests passed, VERDICT command-exit 0. rest test+typecheck: 367/367 files, 6966 passed/332 skipped, VERDICT command-exit 0. After merging origin/main (85ebba60): install, rest^... + dogfood^... closure build, spec check:generated VERDICT command-exit 0; rest pin 18/18, dogfood file 9/9.",
"gates": "At 85ebba60: dispatch-gates --commands derived 65; all 65 recorded exit 0 (check:dual-build-cjs-loads first exit 3 PREREQUISITE NOT MET, re-run exit 0 after turbo build ./packages/**). dispatch-gates --ran: 65 run, 0 NOT-MEASURED, 0 UNRUN (DERIVED zero). Ran list: /mnt/project-files/server-seat2/29200-gates-ran.txt. Lint: no package lint script; root eslint --no-inline-config on the 3 touched files exit 0. CI check-runs on #29290: not awaited (in_progress at report time).",
"dogfood_battery": "n/a — no ! in the PR title and no (narrowing) arm; the one touched dogfood file was run: Tests 9 passed (9) at 85ebba60",
"line_budget": "+78 / -6 over 4 files (protocol.ts +12/-0, rest test +38/-3, dogfood +13/-3, changeset +15/-0)",
"files_changed": [
"packages/metadata-protocol/src/protocol.ts (cloneData only)",
"packages/rest/src/rest-dropped-fields-every-write-door.test.ts",
"packages/qa/dogfood/test/showcase-static-readonly.dogfood.test.ts",
".changeset/29200-clone-empty-readonly-not-dropped.md"
],
"deviations": [
"Route (a) applied to EMPTY readonly values only, not 'do not carry a readonly column at all' as the delta words it: dropping valued readonly columns would silence the #15703-ruled report (acceptance 3 decision, stated in the PR body).",
"Forced test edits: dogfood clone-with-overrides header toContain -> toBe('lead_score;reason=readonly') plus comment; rest test comment above CLONE_OBJECT rewritten and one seed row src_empty added (no assertion moved). Both named in the PR body."
],
"mcp_calls": "3 — issue_read x3 (29200 get, 29200 get_comments, 28933 get_comments); no GitHub write tool. Other reads were gh api REST GETs (29200 comments, 29034 review comment 6570720694, #15703 and its comments, PR 29034 body).",
"api_writes": "2 — fleet-write pr_create (#29290, run 41434211047, 9352 bytes sent = stored); this os-dev-report comment on #29200 via post-stamped. git push is not a REST write.",
"open_questions": [],
"out_of_scope_findings": []
}
Activity
objectstack-fleet commentedon Nov 2, 2026
triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U
Path: none
Triage: p2 ·
domain:server·area:api·pm:queue· 2026-11-02T09:57Z⛔ Not a claim.
droppedFields(rest: PATCH /data 对被静默剥离的写入字段无任何回传 — onFieldsDropped 通道未接线(follow-up #3407/#3413) #3431 contract; REST: clone and createMany report a readonly strip in the body's droppedFields but send no X-ObjectStack-Dropped-Fields header, unlike single create and update #28933 is a sibling).packages/rest, p2.Generated by Claude Code
objectstack-fleet commentedon Nov 2, 2026
domain:server#2 · os-steve · session_01ADHkmQxwoGb5fSnrFkpxmt
Claim: PM loop round 1 · card thread · 2026-11-02T10:09Z
Session:
session_01ADHkmQxwoGb5fSnrFkpxmt(the seat); the card thread issession_01ADHkmQZyRQCzui7zekMYY7Account:
os-steve(the seat's linked user; the card's assignee)Branch:
claude/project-thread-v06ygi(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:serverSeat:
domain:server#2Ruling-ref: the maintainer, verbatim: 「开 server 2、3、5 号席位, 前任已经下班,接手所有开发,包括跟进之前未完成的开发落地。」 Card: none from the maintainer on this card; filed by this seat as a landing follow-up, graded p2 by triage.
File surface:
packages/metadata-protocol/src/protocol.ts(cloneDataonly, under the delta's route (a)); tests; the one dogfood fileshowcase-static-readonly.dogfood.test.ts; changeset. ⛔ No logic change inpackages/rest/src/error-response.ts, noui/**, nopackages/spec/**.Container & model:
M,mode:thread,model: opus(TIER_DEFAULT;--tiernames no path mandate: PM's call),effort: mediumClause-②: no (expected: a body and header lose a false entry; no request refused)
Metadata-protocol: none (expected)
Responsibility: the code is
cloneDatain@objectstack/metadata-protocol· the platform path is a plain clone announcing "fields dropped" for empty readonly columns the caller never sent · reached by every console clone and every integration that readsdroppedFieldsorX-ObjectStack-Dropped-FieldsThread-read: none
Premise: holds (measured by the dev of the parent PR; the card thread re-measures red first on current
origin/main)Serial constraints cleared: open PR #28731 edits
protocol.tsatprobeRecord, notcloneData(hunks read this act); the card thread keeps its hunks off it and mergesorigin/mainbefore pr_createResume-from: none — fresh from
origin/mainState:
pm:queue→pm:dispatched, assigned toos-steve, recorded by this claim.Generated by Claude Code
objectstack-fleet commentedon Nov 2, 2026
domain:server#2 · os-steve · session_01ADHkmQZyRQCzui7zekMYY7
os-dev-report
{ "issue": 29200, "status": "done", "branch": "claude/project-thread-v06ygi", "pr": "https://github.com/objectstack-ai/objectstack/pull/29290", "session": "session_01ADHkmQZyRQCzui7zekMYY7", "premise_still_valid": true, "summary": "Producer-side fix under route (a), narrowed to EMPTY values: cloneData (packages/metadata-protocol/src/protocol.ts) now leaves a readonly field whose copied source value is null/undefined out of the insert payload, so engine.insert's strip no longer reports a key nobody supplied; body droppedFields and the X-ObjectStack-Dropped-Fields header both lose the false entry. Stored row unchanged (the strip removed the key from a non-system caller anyway; applyFieldDefaults treats explicit null as omitted). Acceptance 3 measured: a readonly column that HOLDS a value is reported on main (body + header ro;reason=readonly) and still is after the fix; judged NOT the same defect because the #15703 ruling (option 1, record 5578819560) requires the clone to report a copied-and-stripped value, so (a) is not applied to valued columns. Overrides setting a readonly field are still reported (control). No change to packages/rest/src/error-response.ts, ui/** or packages/spec/**.", "tests": "Red first at main 45e9b2f1 (tests only, rest^... built): rest-dropped-fields-every-write-door.test.ts Tests 1 failed | 17 passed (18), the new plain-clone-of-empty case (expected ... to not have property droppedFields); valued and overrides controls green on main. Fix f825d6bd: 18/18. Ablation via scripts/ablation-replace.mjs (anchor 1->0, blob 3ff88bdc6abc->15c970b77f49, line made dead by name === '__ABLATION_29200__'), ablation-dist-preflight marker in dist/index.js + index.cjs: rest pin 1 failed | 17 passed; dogfood showcase-static-readonly 1 failed | 8 passed (body named lead_score). Restore legs: blob == HEAD, git diff HEAD empty, rebuilt, preflight --absent exit 0, 18/18 and 9/9. metadata-protocol test+typecheck: 300 passed/4 skipped files, 22100 tests passed, VERDICT command-exit 0. rest test+typecheck: 367/367 files, 6966 passed/332 skipped, VERDICT command-exit 0. After merging origin/main (85ebba60): install, rest^... + dogfood^... closure build, spec check:generated VERDICT command-exit 0; rest pin 18/18, dogfood file 9/9.", "gates": "At 85ebba60: dispatch-gates --commands derived 65; all 65 recorded exit 0 (check:dual-build-cjs-loads first exit 3 PREREQUISITE NOT MET, re-run exit 0 after turbo build ./packages/**). dispatch-gates --ran: 65 run, 0 NOT-MEASURED, 0 UNRUN (DERIVED zero). Ran list: /mnt/project-files/server-seat2/29200-gates-ran.txt. Lint: no package lint script; root eslint --no-inline-config on the 3 touched files exit 0. CI check-runs on #29290: not awaited (in_progress at report time).", "dogfood_battery": "n/a — no ! in the PR title and no (narrowing) arm; the one touched dogfood file was run: Tests 9 passed (9) at 85ebba60", "line_budget": "+78 / -6 over 4 files (protocol.ts +12/-0, rest test +38/-3, dogfood +13/-3, changeset +15/-0)", "files_changed": [ "packages/metadata-protocol/src/protocol.ts (cloneData only)", "packages/rest/src/rest-dropped-fields-every-write-door.test.ts", "packages/qa/dogfood/test/showcase-static-readonly.dogfood.test.ts", ".changeset/29200-clone-empty-readonly-not-dropped.md" ], "deviations": [ "Route (a) applied to EMPTY readonly values only, not 'do not carry a readonly column at all' as the delta words it: dropping valued readonly columns would silence the #15703-ruled report (acceptance 3 decision, stated in the PR body).", "Forced test edits: dogfood clone-with-overrides header toContain -> toBe('lead_score;reason=readonly') plus comment; rest test comment above CLONE_OBJECT rewritten and one seed row src_empty added (no assertion moved). Both named in the PR body." ], "mcp_calls": "3 — issue_read x3 (29200 get, 29200 get_comments, 28933 get_comments); no GitHub write tool. Other reads were gh api REST GETs (29200 comments, 29034 review comment 6570720694, #15703 and its comments, PR 29034 body).", "api_writes": "2 — fleet-write pr_create (#29290, run 41434211047, 9352 bytes sent = stored); this os-dev-report comment on #29200 via post-stamped. git push is not a REST write.", "open_questions": [], "out_of_scope_findings": [] }Stamped 2026-11-02T11:09Z.
Generated by Claude Code