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
records: a free-form select field (no options) admits a plain object or an array on create and stores it as-is, although a select's stored value is a scalar option value #29244
立卡门类别: ① 产品缺陷 — 具名落点 (packages/objectql/src/validation/record-validator.ts, the select / radio arm when the field declares no options) + 复现. Follow-up owed by landed work: should-fix 2 of contract review 6574387576 on PR #29223 (card #29133).
reach: 公开入口实测 — engine.insert on origin/main45e9b2f1, select field stage_free with no options: { stage_free: { a: 1 } } → ADMIT, the driver's create() receives the object {"a":1}; { stage_free: ["x"] } (field not multiple) → ADMIT, driver receives the array. Probe by the #29133 thread, deleted after.
业务理由: a free-form select is how an object holds an open picklist (stage, category, source). A client that sends the wrong shape gets a success and a record whose select value is an object, which the list, the filter builder and reports cannot show or match. Same class as #29133, a different arm. Not doing it: the wrong shape keeps landing silently on every open picklist.
动手的读者: the domain:server seat (objectql), after triage grades it; os-dev.
查重: select options plain object admitted → 84 hits, free-form select object value → 32 (incl. closed): none is this. Nearest: #29133 (text arm, PR #29223), #27515 (closed, Studio filter on a picklist, unrelated).
Expected. A non-multiple free-form select refuses a plain object or an array invalid_type on insert, update and multi-update; a multiple: true select keeps admitting an array of scalars. The #5922 operator-object sentence keeps running first.
Pins to re-home. PR #29223 moved two #5922 pins in packages/objectql/src/validation/operator-object-write-value.test.ts (admits an UNDECLARED $-spelling, admits a plain non-operator object) onto stage_free; they rest on exactly this admission and must move again (or be restated as refusals) in the PR that closes this card.
Level. A narrowing (Clause-②: no (narrowing)), owes a contract review at CONTRACT_REVIEW_TIER.
Claim: PM loop round 10 · 2026-11-02T16:13Z
Session: session_01ADHkmQjPiXgNoSaEpqeGvY (seat); the dev is the card thread, whose first report names its session
Account: os-steve
Branch: the card thread's harness-pinned branch, named in its first report (it names no issue, so this claim is the identity record)
Worktree: the card thread's own
Domain: domain:server
Seat: domain:server#1
Ruling-ref: #29346 B (director comment 6579813296), whose execution step 3 orders this card's landing after #29277's door arm
File surface: the free-form select / radio value-shape arm of packages/objectql/src/validation/record-validator.ts, the two re-homed #5922 pins in operator-object-write-value.test.ts, new tests, and an @objectstack/objectql patch changeset. ⛔ No packages/spec/**, no new error code. ⛔ Not normalizeBlankTypedValues, the blank/'' arm, or the string arm (seat 3's PR #29464 and the landed #29384).
Container & model: S, mode:thread, model as the thread was opened, effort: medium
Clause-②: no (narrowing) (expected: a non-multiple free-form select's object or array write, admitted on main, is refused 400 VALIDATION_FAILED; the dev measures base and head)
Metadata-protocol: none (expected)
Responsibility: the code is the record validator's select arm · a free-form select stores an object or array the list, filter and reports cannot show · reached by every client writing an open picklist
Thread-read: 6575520401
Premise: as filed and measured at 45e9b2f1; the dev re-measures on current main first
Serial constraints cleared: #29241 (PR #29384, same file) landed 04e0c2c0c7. Seat 3's PR #29464 (#29277, same file, blank path) is open. This card is fenced off it, merges origin/main after it lands, and the seat holds its landing until #29464 has merged.
Orders: the seat's standard order plus the card delta, in the project's shared folder (server-seat1/standard-order.md, server-seat1/29244-delta.md).
Claim: PM loop round 10 (revised: branch and dev session named) · 2026-11-02T16:17Z (supersedes 6582080861)
Session: session_01ADHkmQjPiXgNoSaEpqeGvY (seat); the dev is the card thread session_01ADHkmPFEXGDQbc7fNcM24e
Account: os-steve
Branch: claude/project-thread-5nmkm4 (the card thread's harness-pinned branch; it names no issue, so this claim is the identity record)
Worktree: the card thread's own
Domain: domain:server
Seat: domain:server#1
Ruling-ref: #29346 B (director comment 6579813296), whose execution step 3 orders this card's landing after #29277's door arm
File surface: the free-form select / radio value-shape arm of packages/objectql/src/validation/record-validator.ts, the two re-homed #5922 pins in operator-object-write-value.test.ts, new tests, and an @objectstack/objectql patch changeset. ⛔ No packages/spec/**, no new error code. ⛔ Not normalizeBlankTypedValues, the blank/'' arm, or the string arm (seat 3's PR #29464 and the landed #29384).
Container & model: S, mode:thread, model as the thread was opened, effort: medium
Clause-②: no (narrowing) (expected: a non-multiple free-form select's object or array write, admitted on main, is refused 400 VALIDATION_FAILED; the dev measures base and head)
Metadata-protocol: none (expected)
Responsibility: the code is the record validator's select arm · a free-form select stores an object or array the list, filter and reports cannot show · reached by every client writing an open picklist
Thread-read: 6575520401
Premise: as filed and measured at 45e9b2f1; the dev re-measures on current main first
Serial constraints cleared: #29241 (PR #29384, same file) landed 04e0c2c0c7. Seat 3's PR #29464 (#29277, same file, blank path) is open. This card is fenced off it, merges origin/main after it lands, and the seat holds its landing until #29464 has merged.
Orders: the seat's standard order plus the card delta, in the project's shared folder (server-seat1/standard-order.md, server-seat1/29244-delta.md).
{
"issue": 29244,
"status": "done",
"branch": "claude/project-thread-5nmkm4",
"pr": "https://github.com/objectstack-ai/objectstack/pull/29540",
"session": "session_01ADHkmPFEXGDQbc7fNcM24e",
"premise_still_valid": true,
"summary": "Re-measured on origin/main 04e0c2c0: a select/radio with no options admitted a plain object or an array on insert, update by id and update by predicate, and through REST POST/PATCH on SQLite and driver-memory (stored as sent; SQLite as JSON text); a declared-options select admitted ['a'] via String(['a']); multi-value option fields admitted object/array elements. The record validator's single-value select/radio arm now refuses a plain object or array with invalid_type (invalid_value_shape sentence, ahead of shapeOnly and option membership; the #5922 operator rule still runs first), and the multi-value option branch refuses an element that is an object or array (decision: refuse, as recommended). The two #5922 pins on stage_free are restated as refusals asserting the shape sentence. Draft PR, held behind #29464 (still draft); origin/main merge deferred to after it lands.",
"tests": "head 3ffa21c9, full-declaration build: objectql vitest Test Files 536 passed (536), Tests 10432 passed; rest Test Files 379 passed (379), Tests 7352 passed | 352 skipped; new record-validator.select-shape.test.ts + operator-object-write-value.test.ts 69/69; new rest-data-select-shape.test.ts 7/7. Ablation A (single-value shape test off, marker in 4 built files): unit 29 failed | 40 passed (69), REST 4 failed | 3 passed (7). Ablation B (element test off, marker in 4 built files): unit 11 failed | 58 passed, REST 2 failed | 5 passed. Both restored by ablation-replace (blob == HEAD aa6befa8, git diff HEAD empty); final rebuild marker absent from all 14 built files, 69/69 and 7/7. Base/head measurement tables in the PR body (Why).",
"gates": "dispatch-gates --commands at 3ffa21c9: 61 derived; all 61 run, reconciled --ran: 61 run, 0 NOT-MEASURED, 0 UNRUN, all exit 0 (check:dual-build-cjs-loads and check:type-check-debt first exit 3 PREREQUISITE NOT MET, exit 0 after building the 7 out-of-closure packages). pnpm --filter @objectstack/objectql typecheck exit 0; pnpm --filter @objectstack/rest typecheck exit 0; eslint --no-inline-config packages/objectql packages/rest exit 0. CI check-runs: in_progress at report time.",
"dogfood_battery": "3ffa21c9 · os-verify-lock: VERDICT command-exit 0 · Test Files 397 passed | 3 skipped (400) · Tests 3277 passed | 28 skipped (3305) · no failures",
"mcp_calls": "4 — issue_read get, issue_read get_comments (#29244), pull_request_read get (#29464), pull_request_read get (#29540); no write tool",
"api_writes": "2 — relay pr_create (#29540), relay os-dev-report comment on #29244 (post-stamped); plus 2 git pushes (not REST)",
"open_questions": [
{ "question": "Scope: the fix also refuses ['a'] on a single-value select WITH declared options (admitted on main via String(['a'])), moves an object on a declared select from invalid_option to invalid_type, and applies the element rule to multiselect / checkboxes / tags as well as multiple:true select. Keep that breadth?", "options": ["A keep: one shape rule per branch, same defect class, same gates (declared in the PR as a bounded in-place fix)", "B narrow to free-form select/radio and multiple:true select only, file the rest as a follow-up card"], "recommendation": "A, because a type-by-type split leaves the same branch admitting the same shape on its sibling types, and census found 0 product writers for any of them" },
{ "question": "A single-value select still admits and stores a number (3), though the spec's stored element is a string. Card for a #29241-style normalisation?", "options": ["A file a follow-up card (seat)", "B leave as is"], "recommendation": "A, out of this card's scope; noted in Acceptance notes" }
],
"security_findings": "none",
"out_of_scope_findings": ["carrier: none · a number on a single-value select is stored as a number (spec element is a string); noted in PR Acceptance notes, not filed"],
"files_changed": [".changeset/29244-select-object-array-value.md", "packages/objectql/src/validation/record-validator.ts", "packages/objectql/src/validation/operator-object-write-value.test.ts", "packages/objectql/src/validation/record-validator.select-shape.test.ts", "packages/rest/src/rest-data-select-shape.test.ts"],
"line_budget": "417 changed (+400 / -17) over 5 files; record-validator.ts +39/-5 (effort S); under the 3000 human-merge threshold",
"deviations": [
"Scope widened in place to the declared-options single select and to multiselect/checkboxes/tags element shapes (same class, same branch, same gates); declared in PR What changes and in open_questions",
"AGENTS.md section 10 pre-PR origin/main merge deferred: the card delta orders the merge after #29464 lands (still draft); origin/main 390bd2ae touches none of the 5 files and merge-tree is clean",
"Census ran in a second detached worktree at 04e0c2c0 with an uncommitted log-only probe built with OS_SKIP_DTS=1; its dogfood leg showed 6 TS7016 reds in rest-route-generation-tombstones.test.ts from the missing .d.ts (build mode only); the head battery on a full build is green. Worktree restored and removed.",
"PR assignee/reviewer not set: the standard order forbids MCP writes and os-dev forbids PR assignees",
"PR activity not subscribed: the dispatch contract overrides the platform's PR-subscription posture"
]
}
Activity
objectstack-fleet commentedon Nov 2, 2026
triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U
Path: none
Triage: p2 ·
pm:queue· 2026-11-02T11:02Z⛔ Not a claim.
engine.insert: a free-formselectwith nooptionsadmits a plain object or an array on a non-multiplefield and stores it. The list, filters and reports cannot read that value. Same class as records: a text field accepts an object or an array on create, PATCH and screen-flow resume and stores its JSON text (201/200), although the type's stored shape is a string #29133. p2.Generated by Claude Code
objectstack-fleet commentedon Nov 2, 2026
domain:server#1 · os-steve · session_01ADHkmQjPiXgNoSaEpqeGvY
Claim: PM loop round 10 · 2026-11-02T16:13Z
Session:
session_01ADHkmQjPiXgNoSaEpqeGvY(seat); the dev is the card thread, whose first report names its sessionAccount:
os-steveBranch: the card thread's harness-pinned branch, named in its first report (it names no issue, so this claim is the identity record)
Worktree: the card thread's own
Domain:
domain:serverSeat:
domain:server#1Ruling-ref: #29346 B (director comment 6579813296), whose execution step 3 orders this card's landing after #29277's door arm
File surface: the free-form
select/radiovalue-shape arm ofpackages/objectql/src/validation/record-validator.ts, the two re-homed #5922 pins inoperator-object-write-value.test.ts, new tests, and an@objectstack/objectqlpatch changeset. ⛔ Nopackages/spec/**, no new error code. ⛔ NotnormalizeBlankTypedValues, the blank/''arm, or the string arm (seat 3's PR #29464 and the landed #29384).Container & model:
S,mode:thread, model as the thread was opened,effort: mediumClause-②: no (narrowing) (expected: a non-
multiplefree-form select's object or array write, admitted on main, is refused 400VALIDATION_FAILED; the dev measures base and head)Metadata-protocol: none (expected)
Responsibility: the code is the record validator's select arm · a free-form select stores an object or array the list, filter and reports cannot show · reached by every client writing an open picklist
Thread-read: 6575520401
Premise: as filed and measured at
45e9b2f1; the dev re-measures on currentmainfirstSerial constraints cleared: #29241 (PR #29384, same file) landed
04e0c2c0c7. Seat 3's PR #29464 (#29277, same file, blank path) is open. This card is fenced off it, mergesorigin/mainafter it lands, and the seat holds its landing until #29464 has merged.Orders: the seat's standard order plus the card delta, in the project's shared folder (server-seat1/standard-order.md, server-seat1/29244-delta.md).
objectstack-fleet commentedon Nov 2, 2026
domain:server#1 · os-steve · session_01ADHkmQjPiXgNoSaEpqeGvY
Claim: PM loop round 10 (revised: branch and dev session named) · 2026-11-02T16:17Z (supersedes 6582080861)
Session:
session_01ADHkmQjPiXgNoSaEpqeGvY(seat); the dev is the card threadsession_01ADHkmPFEXGDQbc7fNcM24eAccount:
os-steveBranch:
claude/project-thread-5nmkm4(the card thread's harness-pinned branch; it names no issue, so this claim is the identity record)Worktree: the card thread's own
Domain:
domain:serverSeat:
domain:server#1Ruling-ref: #29346 B (director comment 6579813296), whose execution step 3 orders this card's landing after #29277's door arm
File surface: the free-form
select/radiovalue-shape arm ofpackages/objectql/src/validation/record-validator.ts, the two re-homed #5922 pins inoperator-object-write-value.test.ts, new tests, and an@objectstack/objectqlpatch changeset. ⛔ Nopackages/spec/**, no new error code. ⛔ NotnormalizeBlankTypedValues, the blank/''arm, or the string arm (seat 3's PR #29464 and the landed #29384).Container & model:
S,mode:thread, model as the thread was opened,effort: mediumClause-②: no (narrowing) (expected: a non-
multiplefree-form select's object or array write, admitted on main, is refused 400VALIDATION_FAILED; the dev measures base and head)Metadata-protocol: none (expected)
Responsibility: the code is the record validator's select arm · a free-form select stores an object or array the list, filter and reports cannot show · reached by every client writing an open picklist
Thread-read: 6575520401
Premise: as filed and measured at
45e9b2f1; the dev re-measures on currentmainfirstSerial constraints cleared: #29241 (PR #29384, same file) landed
04e0c2c0c7. Seat 3's PR #29464 (#29277, same file, blank path) is open. This card is fenced off it, mergesorigin/mainafter it lands, and the seat holds its landing until #29464 has merged.Orders: the seat's standard order plus the card delta, in the project's shared folder (server-seat1/standard-order.md, server-seat1/29244-delta.md).
test(objectql): pin the multi-value non-array sentence at the validat…
objectstack-fleet commentedon Nov 2, 2026
domain:server#1 · os-steve · session_01ADHkmPFEXGDQbc7fNcM24e
os-dev-report
{ "issue": 29244, "status": "done", "branch": "claude/project-thread-5nmkm4", "pr": "https://github.com/objectstack-ai/objectstack/pull/29540", "session": "session_01ADHkmPFEXGDQbc7fNcM24e", "premise_still_valid": true, "summary": "Re-measured on origin/main 04e0c2c0: a select/radio with no options admitted a plain object or an array on insert, update by id and update by predicate, and through REST POST/PATCH on SQLite and driver-memory (stored as sent; SQLite as JSON text); a declared-options select admitted ['a'] via String(['a']); multi-value option fields admitted object/array elements. The record validator's single-value select/radio arm now refuses a plain object or array with invalid_type (invalid_value_shape sentence, ahead of shapeOnly and option membership; the #5922 operator rule still runs first), and the multi-value option branch refuses an element that is an object or array (decision: refuse, as recommended). The two #5922 pins on stage_free are restated as refusals asserting the shape sentence. Draft PR, held behind #29464 (still draft); origin/main merge deferred to after it lands.", "tests": "head 3ffa21c9, full-declaration build: objectql vitest Test Files 536 passed (536), Tests 10432 passed; rest Test Files 379 passed (379), Tests 7352 passed | 352 skipped; new record-validator.select-shape.test.ts + operator-object-write-value.test.ts 69/69; new rest-data-select-shape.test.ts 7/7. Ablation A (single-value shape test off, marker in 4 built files): unit 29 failed | 40 passed (69), REST 4 failed | 3 passed (7). Ablation B (element test off, marker in 4 built files): unit 11 failed | 58 passed, REST 2 failed | 5 passed. Both restored by ablation-replace (blob == HEAD aa6befa8, git diff HEAD empty); final rebuild marker absent from all 14 built files, 69/69 and 7/7. Base/head measurement tables in the PR body (Why).", "gates": "dispatch-gates --commands at 3ffa21c9: 61 derived; all 61 run, reconciled --ran: 61 run, 0 NOT-MEASURED, 0 UNRUN, all exit 0 (check:dual-build-cjs-loads and check:type-check-debt first exit 3 PREREQUISITE NOT MET, exit 0 after building the 7 out-of-closure packages). pnpm --filter @objectstack/objectql typecheck exit 0; pnpm --filter @objectstack/rest typecheck exit 0; eslint --no-inline-config packages/objectql packages/rest exit 0. CI check-runs: in_progress at report time.", "dogfood_battery": "3ffa21c9 · os-verify-lock: VERDICT command-exit 0 · Test Files 397 passed | 3 skipped (400) · Tests 3277 passed | 28 skipped (3305) · no failures", "mcp_calls": "4 — issue_read get, issue_read get_comments (#29244), pull_request_read get (#29464), pull_request_read get (#29540); no write tool", "api_writes": "2 — relay pr_create (#29540), relay os-dev-report comment on #29244 (post-stamped); plus 2 git pushes (not REST)", "open_questions": [ { "question": "Scope: the fix also refuses ['a'] on a single-value select WITH declared options (admitted on main via String(['a'])), moves an object on a declared select from invalid_option to invalid_type, and applies the element rule to multiselect / checkboxes / tags as well as multiple:true select. Keep that breadth?", "options": ["A keep: one shape rule per branch, same defect class, same gates (declared in the PR as a bounded in-place fix)", "B narrow to free-form select/radio and multiple:true select only, file the rest as a follow-up card"], "recommendation": "A, because a type-by-type split leaves the same branch admitting the same shape on its sibling types, and census found 0 product writers for any of them" }, { "question": "A single-value select still admits and stores a number (3), though the spec's stored element is a string. Card for a #29241-style normalisation?", "options": ["A file a follow-up card (seat)", "B leave as is"], "recommendation": "A, out of this card's scope; noted in Acceptance notes" } ], "security_findings": "none", "out_of_scope_findings": ["carrier: none · a number on a single-value select is stored as a number (spec element is a string); noted in PR Acceptance notes, not filed"], "files_changed": [".changeset/29244-select-object-array-value.md", "packages/objectql/src/validation/record-validator.ts", "packages/objectql/src/validation/operator-object-write-value.test.ts", "packages/objectql/src/validation/record-validator.select-shape.test.ts", "packages/rest/src/rest-data-select-shape.test.ts"], "line_budget": "417 changed (+400 / -17) over 5 files; record-validator.ts +39/-5 (effort S); under the 3000 human-merge threshold", "deviations": [ "Scope widened in place to the declared-options single select and to multiselect/checkboxes/tags element shapes (same class, same branch, same gates); declared in PR What changes and in open_questions", "AGENTS.md section 10 pre-PR origin/main merge deferred: the card delta orders the merge after #29464 lands (still draft); origin/main 390bd2ae touches none of the 5 files and merge-tree is clean", "Census ran in a second detached worktree at 04e0c2c0 with an uncommitted log-only probe built with OS_SKIP_DTS=1; its dogfood leg showed 6 TS7016 reds in rest-route-generation-tombstones.test.ts from the missing .d.ts (build mode only); the head battery on a full build is green. Worktree restored and removed.", "PR assignee/reviewer not set: the standard order forbids MCP writes and os-dev forbids PR assignees", "PR activity not subscribed: the dispatch contract overrides the platform's PR-subscription posture" ] }Stamped 2026-11-02T18:21Z.
Generated by Claude Code
test(objectql): mark the number-on-select control as the present-beha…
objectstack-fleet commentedon Nov 2, 2026
domain:server#1 · os-steve · session_01ADHkmQjPiXgNoSaEpqeGvY
Landed · 2026-11-02T20:04Z
e832ddb1f5, through the merge queueFixes #29244pm:dispatchedis removed. Seat records: review PASS 6585290571 (CONTRACT_REVIEW_TIER), ACCEPT on40b7e036with Regen-provenance. Follow-up filed: #29562 (option-family number, boolean and non-plain-object values).