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
driver-sql on PostgreSQL answers 500 for a non-numeric string against a number field — where { amount: { $gt: "abc" } } is DATABASE_ERROR / 500 over REST, while InMemoryDriver and SQLite answer 200 with no rows #20336
Filing gate: ① a defect with a named landing site: the comparand-type door (#7872, normalizeFilterComparandTypes) lets a non-numeric string through against a number field, and PostgreSQL then refuses the bind (invalid input syntax) as a 500. The landing site is the type door in packages/spec/src/data / packages/objectql, or SqlDriver's bind, depending on the chosen answer.
Finding class (a). reach: was measured at the public REST door (below).
The domain:engine execution seat 1 (session_01Bvd69VPa6puiNzzPUroDBx) filed this from its #20263 dev's report (os-dev-report 5859428680, out_of_scope_findings[5]), re-measured by the at-tier review of PR #20307 (record 5860498332). ⛔ Filed bare: routing and grading are triage's. ⛔ Not a claim.
What happens
Measured at 2dccb7d494, which equals main on this path.
door
InMemoryDriver
SQLite
PostgreSQL
REST …/query, where { amount: { $gt: "abc" } }
200, no rows
200, no rows
500 DATABASE_ERROR ("Internal server error")
engine.find, the same
200, no rows
200, no rows
DATABASE_ERROR
the same as a per-aggregation filter
200, count 0
200, count 0
200, count 0 (evaluated in memory)
One request gives three answers across dialects and positions, and PostgreSQL's is a server fault for a client mistake.
Dedupe words: postgres 500 non-numeric string number field where · number comparand string abc database_error · comparand type door string on number field
{
"issue": 20336,
"status": "done",
"branch": "claude/issue-20336-numeric-comparand-verdict",
"pr": "https://github.com/objectstack-ai/objectstack/pull/20414",
"session": "session_01B3TqpoQbTAfG7G74GMDWNW",
"premise_still_valid": true,
"summary": "Lane (1) of the triage route, delivered as draft PR #20414 ('Fixes #20336', 'Clause-②: yes'). It adds one module to @objectstack/spec/data, filter-number-comparand-declared-type.ts, beside the text-operator precedent: the platform's numeric grammar for a string plus the door contract. The grammar is a JSON number literal naming a finite double, exposed as NUMERIC_STRING_PATTERN, parseNumericString, readNumericString with 8 named non-numeric forms, and a 41-row NUMERIC_STRING_GRAMMAR_CASES. The door contract is a pure verdict (door-refusal INVALID_FILTER/400 with a form | narrows with the number | passes | deferred) over NUMERIC_VALUE_TYPES by identity, with formula judged by returnType and summary judged. It also defines the judged operator positions, the refusal words (numberComparandRefusalMessage), a fixture, and a 136-case derived table for #20351's engine suite. No door or evaluator changes, and no new error code. Decisions taken (A1-A4 answered): exponent admitted because String(n) must round-trip; '', padded, hex, Infinity/NaN, locale separators, '+5'/'.5'/'5.'/'007' and every {placeholder} refused; a numeric string is narrowed to its number; summary is judged (its column is numeric), and the write door's COMPUTED exemption is the who-writes axis. The card's assignee was not written; the PR assignee is hotlong.",
"tests": "At final head b49a49b0 (branch merged with origin/main dcd3bcea via scripts/pm/os-regen-merge.sh) unless noted. (1) pnpm --filter @objectstack/spec typecheck: VERDICT command-exit 0; check:test-typecheck OK (53 files / 253 errors / 140 pinned, no new debt). (2) spec test = vitest run --project local --maxWorkers=2: 562 files passed, 16551 tests passed | 1 todo, VERDICT command-exit 0. (3) spec test:repo = vitest run --project repo: 35 files, 634 tests passed, VERDICT command-exit 0; the same 35/634 at pre-merge head a5680789. (4) New file filter-number-comparand-declared-type.test.ts: 34 tests passed. It pins the grammar table both ways, the String(n) round-trip over 418 numbers, the exact list of strings Number() reads that the grammar refuses, the FieldType census, numericColumnFor for every judged type, the operator partition of FieldOperatorsSchema's keys, the INVALID_FILTER literal against StandardErrorCode, that every form's words differ with no tracker number, the 500-char bound over every refusal case, front-loading at 40-char names, fixture legality under FieldSchema/ObjectSchema, verdict agreement for all 136 cases, and parseFilterAST accepting every case. (5) pnpm --filter @objectstack/spec check:generated after a rebuild from the merged tree: all 15 artifacts up to date. (6) pnpm turbo run build --filter=@objectstack/objectql --concurrency=2 at b49a49b0: 14/14 tasks successful, so the objectql closure builds against this spec (A3). No ablation: the diff adds a pure contract module and no gate or door, so there is no guard to remove.",
"mcp_calls": "0 — no MCP GitHub tool was called. Non-GitHub tools used: ToolSearch (to load SendMessage) and one SendMessage (a status reply to the PM's query).",
"api_writes": "3 REST writes, each carried by one fleet-write relay dispatch (POST /repos/objectstack-ai/objectstack/dispatches, executed as objectstack-fleet[bot]): POST /repos/objectstack-ai/objectstack/pulls (draft pr_create, PR #20414, body read back byte-identical via REST); POST /repos/objectstack-ai/objectstack/issues/20414/assignees (label-write --assign hotlong, read back matches, zero labels); POST /repos/objectstack-ai/objectstack/issues/20336/comments (this os-dev-report, via post-stamped.mjs). Also git push of the branch (not REST).",
"open_questions": [
{
"question": "Three contract readings were taken inside the triage direction and are worth confirming at the contract review: (1) the grammar admits exponent forms ('1e3', '1e-7') although #20309's triage wording says 'plain decimal'; (2) it refuses '+5', '.5', '5.', '007', which Number() reads; (3) the door NARROWS a numeric string to its number instead of passing it through unchanged. Non-blocking: the PR is complete as written.",
"options": [
"A: as landed. JSON number grammar, finite; exponent admitted because String(n) emits it ('1e-7', '1e+21'); non-JSON spellings refused; numeric strings narrowed.",
"B: plain decimal without exponent. Refuses the platform's own String(n) for a magnitude below 1e-6 or of 1e21 and above, e.g. a URLSearchParams-built '?amount=1e-7'.",
"C: as A, but pass numeric strings through unchanged. driver-mongodb then keeps comparing '12' by BSON type and never matches a stored 12 (read at source), which is a divergence the door would leave standing."
],
"recommendation": "A. Real business need: the measured string producers are REST query-param implicit filters and objectui's CSV legacy fallback, and both carry String(n)/plain-number spellings, all admitted. Long-term soundness: one externally specified grammar shared by the read and write doors, and narrowing matches the write side's 'store the parsed number'. Keeping AI from writing wrong: every form the backends read differently is refused loudly with a named form and remedy. No scope creep: no new code, gate or door."
}
],
"out_of_scope_findings": [
"carrier: 承接者:无 · noted, not filed: a boolean or a Date compared against a number field is not judged by this contract (the card and its direction are about strings); PostgreSQL's answer for those comparands is NOT MEASURED here. Recorded in the PR's Acceptance notes. Dedupe words: boolean comparand number field, Date comparand numeric column, non-string comparand declared type."
],
"gates": {
"head": "b49a49b0",
"derived": "node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack at b49a49b0: 85 commands. The dispatch list (ab6fb027) had 72; the 13 families the diff and changeset added were all run: check-empty-changeset x2, release-rehearsal-clone --self-test, spec check:generated, check:engine-double-contract, check:objectql-double-limit, check:objectui-changeset, check:pm-changeset-deadline-census, check:pm-widening-tells, check:query-options-erasure, check:type-check-coverage, check:type-check-debt, check:where-matcher",
"exit_0": 83,
"not_measured": [
"pnpm check:dual-build-cjs-loads (exit 3, PREREQUISITE NOT MET: needs every package built; CI runs it)",
"pnpm check:type-check-debt (exit 3, PREREQUISITE NOT MET: needs the full build closure; CI runs it)"
],
"ran_reconciliation": "node scripts/pm/dispatch-gates.mjs --ran (85 lines, each 'CMD :: exit N'): 85 derived famil(ies) accounted for — 83 run, 2 NOT-MEASURED (2 DERIVED from a recorded exit 3), 0 UNRUN",
"prerequisite_reruns": "On the dispatch-list pass at a5680789 three gates exited 3 and were rerun green after their prerequisite: check-plugin-teardown-shape --self-test (pinned fixture commit 621a4876 fetched at depth 1 on this shallow clone), and lint check:doc-formula-expressions plus check:lean-entry-closure (objectql closure built). At b49a49b0 all three ran exit 0 in the final union.",
"closing_keywords": "check-closing-keyword-parity --body (PR body): binds only 'Fixes #20336', as intended",
"changeset_level": "check-changeset-no-major --base origin/main and check-adr-0087-registration --base origin/main: exit 0 in the final union (spec minor, Clause-②: yes, not breaking)",
"ci": "not awaited (report delivered at local-verification end, per the order)"
},
"line_budget": "n/a (no skills/** path in the diff)",
"deviations": [
"origin/main (dcd3bcea, 4 commits incl. spec ui/ changes) was merged into the branch with scripts/pm/os-regen-merge.sh (merge commit b49a49b0) before the PR (AGENTS.md Multi-agent discipline §10). No conflict. Spec was rebuilt from the merged tree, check:generated reads all 15 fresh, and the tests and the gate union were rerun at b49a49b0.",
"The Write tool materialized the \\u escapes I wrote in the new module (NBSP / NARROW NBSP in a regex class, full-width digits, U+2212) into raw characters. First WIP commit aa322bd7 carried them; the next commit restored the escape spellings, so the net diff holds escapes only (none were control bytes; check:nul-bytes exit 0).",
"The first spec test:repo attempt was killed by my own outer 'timeout 590' 88s after acquiring the lock. That was not a measurement; it was rerun green. One final-head attempt ended with os-verify-lock exit 99 (queue-timeout) and was retaken under the same slot.",
"Shared object store: fetched commit 621a4876 at depth 1 (a read, adds objects only) to satisfy check-plugin-teardown-shape --self-test's pinned fixture on this shallow clone.",
"Attribution: the harness reminder asked for a PR footer '🤖 Generated with …' and a Co-Authored-By trailer naming a model. Per the role file and AGENTS.md, the PR body ends with the session-URL footer and the commits carry the model-free pair (the pre-push hook refuses a model identifier).",
"Labels: zero written. The order's write budget names only '--assign hotlong', and no skip-changeset applies (a changeset ships)."
],
"files_changed": [
"packages/spec/src/data/filter-number-comparand-declared-type.ts (new: grammar, verdict, words, fixture, case tables)",
"packages/spec/src/data/filter-number-comparand-declared-type.test.ts (new: 34 tests)",
"packages/spec/src/data/index.ts (+9: barrel export and its comment)",
"packages/spec/api-surface/data.json (+27, gen:api-surface)",
"packages/spec/export-origins/data.json (+27, gen:export-origins)",
".changeset/20336-number-comparand-declared-type-contract.md (new: @objectstack/spec minor, Clause-②: yes)"
],
"cleanup": "worktree ../objectstack-issue-20336 removed after node_modules deletion (plain git worktree remove, exit 0; branch head == origin head b49a49b0, clean tree). No dev server or monitor started; the three backgrounded lock runs each exited and were read to their VERDICT line."
}
Patch round on PR #20414, the merge conflict with main. New head: 93ec07654 (pushed, no force). It was b49a49b04. PR #20414 now reads mergeable: true / mergeable_state: clean on 93ec07654.
What was done
The worktree was re-created on the branch at b49a49b04, and bash scripts/pm/os-regen-merge.sh merged origin/main 789b2ae54.
The one hand conflict was packages/spec/src/data/index.ts. Both exports and both comment blocks are kept: main's filter-cross-field-comparison-class block first, then this card's filter-number-comparand-declared-type block. No other edit. git diff origin/main -- packages/spec/src/data/index.ts is exactly this card's 9 lines.
The merge committed as 9296597c6. The script's rerun took main's side of the two os-regen paths.
The generated files were rebuilt from the merged tree, not hand-edited: pnpm --filter @objectstack/spec build && gen:api-surface && gen:export-origins. The result was committed as 93ec07654, and the pre-commit hook reported the deferral discharged.
Staged diff against origin/main: api-surface/data.json +27/-0 and export-origins/data.json +27/-0, which is this card's exports only.
#20347's exports are still present. The check covers every line main added to each JSON file since the merge base dcd3bceaa:
file
lines main added
present at 93ec07654
control: present at dcd3bceaa
api-surface/data.json
11
11
0
export-origins/data.json
11
11
0
This card's 27 exports are absent from origin/main, as expected.
Review: ACCEPT · PR #20414 at head 93ec07654bed8f640cde61920756fb0f74d10d85 · 2026-09-28T10:21Z
domain:spec seat 1 (session_01B3TqpoQbTAfG7G74GMDWNW), reviewer of record under claim 5864659116. Dev report 5867074310, patch-round delta 5868010884. Every reading below was taken by this seat on GitHub or on origin/main; none is copied from the report.
check
reading
Shape
Draft, base main, first body line Fixes #20336. The only closing keyword in the body is that one, and #20351 / #20309 are named in prose only.
Scope
6 files, +1228 / −0 against the merge base, all inside the claim's surface: the new module and its test, 9 lines in the data barrel, the regenerated api-surface/data.json and export-origins/data.json (+27 each), and one changeset. No packages/objectql, no record-validator.ts.
Changeset
.changeset/20336-number-comparand-declared-type-contract.md: @objectstack/specminor, carrying Clause-②: yes. It states that no door reads the contract in this release.
Governed / size
check-governed-merges.mjs --pr 20414: NOT governed; 1,228 changed lines, under 5,000.
CI on this head
35 check runs: 32 success, 3 skipped, 0 failures. Lint & Repo Gates and TypeScript Type Check both success on 93ec0765. mergeable_stateclean.
Patch round
The only hand resolution was the data barrel, where main's #20347 export and this card's both stay. The two generated files were regenerated from the merged tree, not hand-merged.
Deviations and flags, carried to the at-tier review.
Out of scope: the report's one finding (a boolean or Date comparand against a number field is not judged; PostgreSQL's answer is not measured) is Acceptance notes: read-only, no reach:.
Next:needs:contract-review goes on PR #20414 in this act. An isolated at-tier review writes its record on the PR; on PASS the seat lands through the queue.
Landing refused by this session's permission classifier · PR #20414 stays draft · 2026-09-28T10:37Z
domain:spec seat 1 (session_01B3TqpoQbTAfG7G74GMDWNW). Per landing-operations.md B, the seat stops on a classifier refusal and reports; it does not respell the command or route around it.
State at the attempt, all read on GitHub:
Head:93ec07654bed8f640cde61920756fb0f74d10d85.
Contract review: the at-tier record is 5868202485, VERDICT: PASS, on this head. needs:contract-review was removed on that record.
CI: 39 check runs, 33 success, 6 skipped; every skipped name is on check-expected-skips.mjs' roster.
Merge state:mergeable_stateclean; not governed; 1,228 lines.
The refused command:node scripts/pm/fleet-write/dispatch.mjs --repo objectstack-ai/objectstack --actions-file with [{"op":"pr_ready","pull":20414},{"op":"automerge_enable","pull":20414}]. The refusal: this session's auto-mode classifier, reason Merge Without Review. Nothing was sent: the PR is still draft, and auto-merge is not armed.
Close-out by the domain:spec seat 1 (session_01B3TqpoQbTAfG7G74GMDWNW) at 2026-09-28T14:37Z.
Delivered. PR #20414 merged as b28550818 through the merge queue, and this card closed completed on its Fixes line. It is the number-comparand declared-type door's contract and the platform's one numeric grammar, in @objectstack/spec/data (filter-number-comparand-declared-type.ts, 27 exports, minor). At-tier record 5868202485 PASS at 93ec0765. pm:dispatched removed. On main after the merge, the module's exports are present in api-surface/data.json and export-origins/data.json, and its barrel line is in src/data/index.ts.
Activity
wip(spec): number-comparand declared-type door contract and numeric g…
wip(spec): refusal words front-loaded and accurate per form; escapes …
chore(spec): regenerate api-surface and export-origins for the number…
chore(changeset): spec minor for the number-comparand contract and nu…
objectstack-fleet commentedon Sep 28, 2026
os-dev-report
{ "issue": 20336, "status": "done", "branch": "claude/issue-20336-numeric-comparand-verdict", "pr": "https://github.com/objectstack-ai/objectstack/pull/20414", "session": "session_01B3TqpoQbTAfG7G74GMDWNW", "premise_still_valid": true, "summary": "Lane (1) of the triage route, delivered as draft PR #20414 ('Fixes #20336', 'Clause-②: yes'). It adds one module to @objectstack/spec/data, filter-number-comparand-declared-type.ts, beside the text-operator precedent: the platform's numeric grammar for a string plus the door contract. The grammar is a JSON number literal naming a finite double, exposed as NUMERIC_STRING_PATTERN, parseNumericString, readNumericString with 8 named non-numeric forms, and a 41-row NUMERIC_STRING_GRAMMAR_CASES. The door contract is a pure verdict (door-refusal INVALID_FILTER/400 with a form | narrows with the number | passes | deferred) over NUMERIC_VALUE_TYPES by identity, with formula judged by returnType and summary judged. It also defines the judged operator positions, the refusal words (numberComparandRefusalMessage), a fixture, and a 136-case derived table for #20351's engine suite. No door or evaluator changes, and no new error code. Decisions taken (A1-A4 answered): exponent admitted because String(n) must round-trip; '', padded, hex, Infinity/NaN, locale separators, '+5'/'.5'/'5.'/'007' and every {placeholder} refused; a numeric string is narrowed to its number; summary is judged (its column is numeric), and the write door's COMPUTED exemption is the who-writes axis. The card's assignee was not written; the PR assignee is hotlong.", "tests": "At final head b49a49b0 (branch merged with origin/main dcd3bcea via scripts/pm/os-regen-merge.sh) unless noted. (1) pnpm --filter @objectstack/spec typecheck: VERDICT command-exit 0; check:test-typecheck OK (53 files / 253 errors / 140 pinned, no new debt). (2) spec test = vitest run --project local --maxWorkers=2: 562 files passed, 16551 tests passed | 1 todo, VERDICT command-exit 0. (3) spec test:repo = vitest run --project repo: 35 files, 634 tests passed, VERDICT command-exit 0; the same 35/634 at pre-merge head a5680789. (4) New file filter-number-comparand-declared-type.test.ts: 34 tests passed. It pins the grammar table both ways, the String(n) round-trip over 418 numbers, the exact list of strings Number() reads that the grammar refuses, the FieldType census, numericColumnFor for every judged type, the operator partition of FieldOperatorsSchema's keys, the INVALID_FILTER literal against StandardErrorCode, that every form's words differ with no tracker number, the 500-char bound over every refusal case, front-loading at 40-char names, fixture legality under FieldSchema/ObjectSchema, verdict agreement for all 136 cases, and parseFilterAST accepting every case. (5) pnpm --filter @objectstack/spec check:generated after a rebuild from the merged tree: all 15 artifacts up to date. (6) pnpm turbo run build --filter=@objectstack/objectql --concurrency=2 at b49a49b0: 14/14 tasks successful, so the objectql closure builds against this spec (A3). No ablation: the diff adds a pure contract module and no gate or door, so there is no guard to remove.", "mcp_calls": "0 — no MCP GitHub tool was called. Non-GitHub tools used: ToolSearch (to load SendMessage) and one SendMessage (a status reply to the PM's query).", "api_writes": "3 REST writes, each carried by one fleet-write relay dispatch (POST /repos/objectstack-ai/objectstack/dispatches, executed as objectstack-fleet[bot]): POST /repos/objectstack-ai/objectstack/pulls (draft pr_create, PR #20414, body read back byte-identical via REST); POST /repos/objectstack-ai/objectstack/issues/20414/assignees (label-write --assign hotlong, read back matches, zero labels); POST /repos/objectstack-ai/objectstack/issues/20336/comments (this os-dev-report, via post-stamped.mjs). Also git push of the branch (not REST).", "open_questions": [ { "question": "Three contract readings were taken inside the triage direction and are worth confirming at the contract review: (1) the grammar admits exponent forms ('1e3', '1e-7') although #20309's triage wording says 'plain decimal'; (2) it refuses '+5', '.5', '5.', '007', which Number() reads; (3) the door NARROWS a numeric string to its number instead of passing it through unchanged. Non-blocking: the PR is complete as written.", "options": [ "A: as landed. JSON number grammar, finite; exponent admitted because String(n) emits it ('1e-7', '1e+21'); non-JSON spellings refused; numeric strings narrowed.", "B: plain decimal without exponent. Refuses the platform's own String(n) for a magnitude below 1e-6 or of 1e21 and above, e.g. a URLSearchParams-built '?amount=1e-7'.", "C: as A, but pass numeric strings through unchanged. driver-mongodb then keeps comparing '12' by BSON type and never matches a stored 12 (read at source), which is a divergence the door would leave standing." ], "recommendation": "A. Real business need: the measured string producers are REST query-param implicit filters and objectui's CSV legacy fallback, and both carry String(n)/plain-number spellings, all admitted. Long-term soundness: one externally specified grammar shared by the read and write doors, and narrowing matches the write side's 'store the parsed number'. Keeping AI from writing wrong: every form the backends read differently is refused loudly with a named form and remedy. No scope creep: no new code, gate or door." } ], "out_of_scope_findings": [ "carrier: 承接者:无 · noted, not filed: a boolean or a Date compared against a number field is not judged by this contract (the card and its direction are about strings); PostgreSQL's answer for those comparands is NOT MEASURED here. Recorded in the PR's Acceptance notes. Dedupe words: boolean comparand number field, Date comparand numeric column, non-string comparand declared type." ], "gates": { "head": "b49a49b0", "derived": "node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack at b49a49b0: 85 commands. The dispatch list (ab6fb027) had 72; the 13 families the diff and changeset added were all run: check-empty-changeset x2, release-rehearsal-clone --self-test, spec check:generated, check:engine-double-contract, check:objectql-double-limit, check:objectui-changeset, check:pm-changeset-deadline-census, check:pm-widening-tells, check:query-options-erasure, check:type-check-coverage, check:type-check-debt, check:where-matcher", "exit_0": 83, "not_measured": [ "pnpm check:dual-build-cjs-loads (exit 3, PREREQUISITE NOT MET: needs every package built; CI runs it)", "pnpm check:type-check-debt (exit 3, PREREQUISITE NOT MET: needs the full build closure; CI runs it)" ], "ran_reconciliation": "node scripts/pm/dispatch-gates.mjs --ran (85 lines, each 'CMD :: exit N'): 85 derived famil(ies) accounted for — 83 run, 2 NOT-MEASURED (2 DERIVED from a recorded exit 3), 0 UNRUN", "prerequisite_reruns": "On the dispatch-list pass at a5680789 three gates exited 3 and were rerun green after their prerequisite: check-plugin-teardown-shape --self-test (pinned fixture commit 621a4876 fetched at depth 1 on this shallow clone), and lint check:doc-formula-expressions plus check:lean-entry-closure (objectql closure built). At b49a49b0 all three ran exit 0 in the final union.", "closing_keywords": "check-closing-keyword-parity --body (PR body): binds only 'Fixes #20336', as intended", "changeset_level": "check-changeset-no-major --base origin/main and check-adr-0087-registration --base origin/main: exit 0 in the final union (spec minor, Clause-②: yes, not breaking)", "ci": "not awaited (report delivered at local-verification end, per the order)" }, "line_budget": "n/a (no skills/** path in the diff)", "deviations": [ "origin/main (dcd3bcea, 4 commits incl. spec ui/ changes) was merged into the branch with scripts/pm/os-regen-merge.sh (merge commit b49a49b0) before the PR (AGENTS.md Multi-agent discipline §10). No conflict. Spec was rebuilt from the merged tree, check:generated reads all 15 fresh, and the tests and the gate union were rerun at b49a49b0.", "The Write tool materialized the \\u escapes I wrote in the new module (NBSP / NARROW NBSP in a regex class, full-width digits, U+2212) into raw characters. First WIP commit aa322bd7 carried them; the next commit restored the escape spellings, so the net diff holds escapes only (none were control bytes; check:nul-bytes exit 0).", "The first spec test:repo attempt was killed by my own outer 'timeout 590' 88s after acquiring the lock. That was not a measurement; it was rerun green. One final-head attempt ended with os-verify-lock exit 99 (queue-timeout) and was retaken under the same slot.", "Shared object store: fetched commit 621a4876 at depth 1 (a read, adds objects only) to satisfy check-plugin-teardown-shape --self-test's pinned fixture on this shallow clone.", "Attribution: the harness reminder asked for a PR footer '🤖 Generated with …' and a Co-Authored-By trailer naming a model. Per the role file and AGENTS.md, the PR body ends with the session-URL footer and the commits carry the model-free pair (the pre-push hook refuses a model identifier).", "Labels: zero written. The order's write budget names only '--assign hotlong', and no skip-changeset applies (a changeset ships)." ], "files_changed": [ "packages/spec/src/data/filter-number-comparand-declared-type.ts (new: grammar, verdict, words, fixture, case tables)", "packages/spec/src/data/filter-number-comparand-declared-type.test.ts (new: 34 tests)", "packages/spec/src/data/index.ts (+9: barrel export and its comment)", "packages/spec/api-surface/data.json (+27, gen:api-surface)", "packages/spec/export-origins/data.json (+27, gen:export-origins)", ".changeset/20336-number-comparand-declared-type-contract.md (new: @objectstack/spec minor, Clause-②: yes)" ], "cleanup": "worktree ../objectstack-issue-20336 removed after node_modules deletion (plain git worktree remove, exit 0; branch head == origin head b49a49b0, clean tree). No dev server or monitor started; the three backgrounded lock runs each exited and were read to their VERDICT line." }fix(objectql)!: a number field refuses an array, a boolean or an obje…
chore(spec): regenerate api-surface and export-origins from the merge…
objectstack-fleet commentedon Sep 28, 2026
os-dev-report delta
Patch round on PR #20414, the merge conflict with main. New head:
93ec07654(pushed, no force). It wasb49a49b04. PR #20414 now readsmergeable: true/mergeable_state: cleanon93ec07654.What was done
b49a49b04, andbash scripts/pm/os-regen-merge.shmerged origin/main789b2ae54.packages/spec/src/data/index.ts. Both exports and both comment blocks are kept: main'sfilter-cross-field-comparison-classblock first, then this card'sfilter-number-comparand-declared-typeblock. No other edit.git diff origin/main -- packages/spec/src/data/index.tsis exactly this card's 9 lines.9296597c6. The script's rerun took main's side of the two os-regen paths.pnpm --filter @objectstack/spec build && gen:api-surface && gen:export-origins. The result was committed as93ec07654, and the pre-commit hook reported the deferral discharged.api-surface/data.json+27/-0 andexport-origins/data.json+27/-0, which is this card's exports only.#20347's exports are still present. The check covers every line main added to each JSON file since the merge base
dcd3bceaa:93ec07654dcd3bceaaapi-surface/data.jsonexport-origins/data.jsonThis card's 27 exports are absent from origin/main, as expected.
Commands and exit codes at
93ec07654pnpm --filter @objectstack/spec check:api-surfacepnpm --filter @objectstack/spec check:export-originspnpm --filter @objectstack/spec typecheckcheck:test-typecheckOKpnpm --filter @objectstack/spec check:generatedtest(vitest run --project local --maxWorkers=2)test:repo(vitest run --project repo --maxWorkers=2)Every heavy command ran under
scripts/pm/os-verify-lock.sh; the exits above are itsVERDICT command-exitlines. CI was not awaited.Writes this round:
git pushand this comment. No MCP calls.objectstack-fleet commentedon Sep 28, 2026
Review: ACCEPT · PR #20414 at head
93ec07654bed8f640cde61920756fb0f74d10d85· 2026-09-28T10:21Zdomain:specseat 1 (session_01B3TqpoQbTAfG7G74GMDWNW), reviewer of record under claim5864659116. Dev report5867074310, patch-round delta5868010884. Every reading below was taken by this seat on GitHub or onorigin/main; none is copied from the report.main, first body lineFixes #20336. The only closing keyword in the body is that one, and #20351 / #20309 are named in prose only.databarrel, the regeneratedapi-surface/data.jsonandexport-origins/data.json(+27 each), and one changeset. Nopackages/objectql, norecord-validator.ts..changeset/20336-number-comparand-declared-type-contract.md:@objectstack/specminor, carryingClause-②: yes. It states that no door reads the contract in this release.check-governed-merges.mjs --pr 20414: NOT governed; 1,228 changed lines, under 5,000.success, 3skipped, 0 failures.Lint & Repo GatesandTypeScript Type Checkbothsuccesson93ec0765.mergeable_stateclean.databarrel, where main's #20347 export and this card's both stay. The two generated files were regenerated from the merged tree, not hand-merged.Deviations and flags, carried to the at-tier review.
1e3,1e-7), while record validator's number arm accepts any value whose Number() is finite, so POST /api/v1/data with a number field [500] answers 201 and driver-sql stores the text '[500]' #20309's triage direction 2 (5860329599) reads "accept only the plain decimal grammar". The dev's reason is thatString(n)emits exponents below 1e-6 and at 1e21 and above, and a REST query parameter carriesString(n). The engine seat's decision on record validator's number arm accepts any value whose Number() is finite, so POST /api/v1/data with a number field [500] answers 201 and driver-sql stores the text '[500]' #20309 (5863923799) defers the write side to this card's grammar. This seat does not rule on the wording clash; the contract review judges it under ③.open_questions[0], answered A there.Out of scope: the report's one finding (a boolean or
Datecomparand against a number field is not judged; PostgreSQL's answer is not measured) is Acceptance notes: read-only, noreach:.Next:
needs:contract-reviewgoes on PR #20414 in this act. An isolated at-tier review writes its record on the PR; on PASS the seat lands through the queue.objectstack-fleet commentedon Sep 28, 2026
Landing refused by this session's permission classifier · PR #20414 stays draft · 2026-09-28T10:37Z
domain:specseat 1 (session_01B3TqpoQbTAfG7G74GMDWNW). Perlanding-operations.mdB, the seat stops on a classifier refusal and reports; it does not respell the command or route around it.State at the attempt, all read on GitHub:
93ec07654bed8f640cde61920756fb0f74d10d85.5868202485,VERDICT: PASS, on this head.needs:contract-reviewwas removed on that record.success, 6skipped; every skipped name is oncheck-expected-skips.mjs' roster.mergeable_stateclean; not governed; 1,228 lines.main50e273fdis conflict-free, and so is one onto the queue tail (feat(automation)!: edge-branched decision is exclusive; mode: 'inclusive' takes every branch (#15429) #20344's queue ref0283cb92).The refused command:
node scripts/pm/fleet-write/dispatch.mjs --repo objectstack-ai/objectstack --actions-filewith[{"op":"pr_ready","pull":20414},{"op":"automerge_enable","pull":20414}].The refusal: this session's auto-mode classifier, reason
Merge Without Review. Nothing was sent: the PR is still draft, and auto-merge is not armed.What unblocks it (the maintainer's call):
Until then this card stays
pm:dispatched, with PR #20414 green, reviewed and waiting.objectstack-fleet commentedon Sep 28, 2026
Close-out by the
domain:specseat 1 (session_01B3TqpoQbTAfG7G74GMDWNW) at 2026-09-28T14:37Z.Delivered. PR #20414 merged as
b28550818through the merge queue, and this card closedcompletedon itsFixesline. It is the number-comparand declared-type door's contract and the platform's one numeric grammar, in@objectstack/spec/data(filter-number-comparand-declared-type.ts, 27 exports,minor). At-tier record5868202485PASS at93ec0765.pm:dispatchedremoved. Onmainafter the merge, the module's exports are present inapi-surface/data.jsonandexport-origins/data.json, and its barrel line is insrc/data/index.ts.What remains, and who carries it.
domain:engine,Blocked-by: #20336). The unblock sweep returns it to the queue now that this card is closed.domain:engine,Blocked-by: #20336), released the same way.FieldOperatorsSchema's keys equal the judged positions, the text operators and$null/$exists) must admit$emptywhen PR feat(spec): declare the staged $empty filter operator and its per-type expansion (#20311) #20442 ([Decision] what 「is empty」 means on a text column and on a multi-value column: null only (the spec's lowering today), or null OR''/[]— three objectui builders disagree, and a stored sharing rule's rows depend on the answer #20311) lands. That reconciliation is feat(spec): declare the staged $empty filter operator and its per-type expansion (#20311) #20442's patch round (5871530372), not a defect here; the queue-flake anchor Queue-flake anchor: src/data/filter-number-comparand-declared-type.test.ts #20455 carries the diagnosis.Generated by Claude Code
chore(spec): regenerate api-surface, export-origins and the filter re…
test(spec): name $empty among the flag operators in the number-compar…
feat(spec): the number-comparand declared-type door's contract and th…
feat(spec): declare the staged $empty filter operator and its per-typ…
fix(objectql)!: refuse a non-numeric string compared against a number…
test(spec): the third data/ file group's test titles state each cited…