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
objectql: an in-process projection whose every column is unknown is answered with every column (REST refuses it 400 INVALID_FIELD) #27165
Filing gate: ② an open question an implementer could not settle inside its ruling. The #27008 implementer raised it (os-dev-report 6464754246 on #27008, open_questions[0], needs_decision). The evidence was measured at PR #27161 head 2e447bbd. Filed by the domain:server seat 2 (session_0186vfjHHMsRXC9EUT9hTFMo). ⛔ Not a claim; left for triage. Dedupe: no open domain:server or domain:spec card names an all-unknown projection; #27008 (empty projection) and #26910 (closed, client select: []) are the neighbours.
Symptom
In-process, engine.find(obj, { fields: ['nope'] }) answers every column. The #7589 tolerance drops an unknown plain column, and when nothing is left the engine falls back to * (engine.ts, filtered.length > 0 ? filtered : undefined). The driver then receives no fields.
At that head:
fields: ['nope'] reads every column.
fields: ['nope', 'title'] reads { title }.
The REST door refuses the same request with 400 INVALID_FIELD. So a narrower request than "every column" is answered wider, on the in-process path only. #27008 closes the empty-list form of that rule ("an empty projection is refused, never read as every column"). This is the form where the list empties after filtering.
Claim: PM loop round 1 · in-seat subagent · p2 · 2026-10-29T05:02Z
Session: session_01L6bhgXcN9HaZYSEUdJWEjw
Account: os-sales (the seat's linked user; the card's assignee)
Branch: claude/issue-27165-all-unknown-projection
Worktree: objectstack-issue-27165
Domain: domain:server
Seat: domain:server#1
Ruling-ref: triage's answer to open_questions[0] (6466016076), verbatim: "a projection that is empty after dropping unknown columns is an empty projection, so it is refused with #27008's refusal (one rule: an empty projection is refused, never read as every column). The #7589 tolerance stays for a mix of known and unknown columns. This narrows behaviour, and the in-repo census of callers comes first." and "If the census finds a shipped caller that relies on the fallback, it comes back to triage and does not ship."
File surface: packages/objectql/src/engine.ts (the projection fallback in find and findOne only, filtered.length > 0 ? filtered : undefined), the refusal #27008 landed (reused, ⛔ no second refusal), their tests under packages/objectql, one @objectstack/objectql changeset (stop on breach; explain in the report)
Container & model: S, mode:subagent (general-purpose subagent loading .claude/agents/os-dev.md as its role file), model: default tier (dispatch-gates.mjs --tier: "no path-derived mandate"), effort: medium
Clause-②: no
Metadata-protocol: none
Responsibility: the in-process projection fallback in packages/objectql/src/engine.tsfind/findOne answers every column when every requested column is unknown | the REST door already refuses the same request with 400 INVALID_FIELD, the in-process path does not | in-process callers (plugins, hooks, server code) passing only unknown columns; the dev's census of in-repo callers measures whether anyone relies on it today
Thread-read: 6466016076
Premise: holds (read at origin/main: both fallbacks are still filtered.length > 0 ? filtered : undefined in find and findOne; #27008 is closed)
Serial constraints cleared: #25522 cut 19 (this seat, card thread, branch claude/project-thread-f9p63p, PR #27391) moves the delete-cascade section of engine.ts, disjoint from find/findOne; draft #27339 (os-bill) touches engine.ts imports, insert, update and aggregate, disjoint. Whichever lands later merges main.
The change narrows what the in-process engine accepts, so the PR owes a CONTRACT_REVIEW_TIER contract review before the queue (Clause-②: no (narrowing) in the PR body and changeset), run by this seat.
os-dev-report
{
"issue": 27165,
"status": "done",
"branch": "claude/issue-27165-all-unknown-projection",
"pr": "#27541",
"session": "session_01L6bhgXcN9HaZYSEUdJWEjw (domain:server#1 · os-sales, mode:subagent; reported at 2026-10-29T11:51Z)",
"premise_still_valid": true,
"summary": "Census first: no shipped in-repo caller relies on the all-unknown fallback. Of 278 non-test find/findOne calls whose arguments mention fields, 166 name 'id'; the rest name declared columns, prepend 'id', are IDataDriver calls, or reach the engine over REST. The flow get_record findOne arm adds 'id'; the one in-repo authored projection (recalc-estimate) names declared columns. Then the fix: in packages/objectql/src/engine.ts, find and findOne now refuse when the unknown-plain filter leaves nothing. They call refuseProjectionOfUnknownColumns, a thin export over #27008's refuseEmptyProjection in empty-projection-door.ts: same function, 400 VALIDATION_ERROR, the declaration's words, and 'got' now names the requested columns. A mix of known and unknown columns keeps the #7589 tolerance. expand needed no new arm: its sub-read always names 'id', so an all-unknown nested projection already reads {id} (measured: expands to {}). Two existing tests pinned the old fallback and now expect the refusal: the [#7589 GUARD] 'falls back to every field' case, and the #21571 'projection naming ONLY a retired column' case. Measured divergence: the REST door answers this request 400 INVALID_FIELD and the engine answers 400 VALIDATION_ERROR (the ruling's choice), same status, different code.",
"tests": "At head 68ff239 (after merging main f57eddc), one locked run of the dogfood^... build, objectql typecheck and test, and dogfood test: VERDICT command-exit 0. objectql Test Files 483/483, Tests 9091/9091. Red first: the new [#27165] block against the unfixed engine (8993840) gave 'Tests 2 failed | 11 passed (13)', 'expected a refusal, got an answer'; green 13/13. Ablation through scripts/ablation-replace.mjs on source (the test imports ./engine.js), one leg per site, each restoring 'ast.fields = filtered.length > 0 ? filtered : undefined;'. find leg: anchor 1->0, blob 7697ee2b->8beef8e9, Tests 1 failed (the find case). findOne leg: anchor 1->0, blob 7697ee2b->56a22aca, Tests 1 failed (the findOne case). Both restored: blob == HEAD 7697ee2b, git diff HEAD empty. A probe (temp file, deleted, never committed) measured the REST door, the engine and expand, recorded in the PR body.",
"gates": "dispatch-gates --commands --repo objectstack-ai/objectstack at 68ff239: 63 derived, all run, each exit 0. Two were reruns after the build that cleared their prerequisite: check:dts-closure (first run raced this worktree's own closure build) and check:dual-build-cjs-loads (first run exit 3, PREREQUISITE NOT MET). check:type-check-debt ran under the lock (VERDICT 0). The --ran reconcile with exit codes printed '63 derived famil(ies) accounted for — 63 run, 0 NOT-MEASURED (a DERIVED zero …)', exit 0. eslint packages/objectql --no-inline-config: exit 0. check:commit-card-trailers passed on every push. CI on PR #27541: check-runs were in_progress at report time (Lint *, Type Check *, Governed Surface Queue Guard, Check Changeset, PR-body guards), conclusions not yet read.",
"dogfood_battery": "68ff2394 · os-verify-lock VERDICT command-exit 0 · Test Files 348 passed | 3 skipped (351) · Tests 2806 passed | 20 skipped (2826) · no failures. An earlier run at a04167c had 4 tests time out at 5000 ms under box load (packaged-flow-write-door-parity, rest-route-generation-tombstones tsc cases). Rerun alone: 15/15. Green in the final-head run.",
"line_budget": "6 files, +121 / -21 against the merge-base: engine.ts +8/-6, empty-projection-door.ts +33/-2, engine-empty-projection-refusal.test.ts +45, query-expression-conformance.test.ts and unprojected-read-declared-fields-conformance.test.ts (old pins updated), and one changeset +19",
"files_changed": [
".changeset/27165-engine-all-unknown-projection-refused.md",
"packages/objectql/src/empty-projection-door.ts",
"packages/objectql/src/engine.ts",
"packages/objectql/src/engine-empty-projection-refusal.test.ts",
"packages/objectql/src/query-expression-conformance.test.ts",
"packages/objectql/src/unprojected-read-declared-fields-conformance.test.ts"
],
"deviations": [
"Two test files outside the named refusal test, query-expression-conformance.test.ts and unprojected-read-declared-fields-conformance.test.ts, were edited. They pinned the old fallback, and the narrowing turns them red otherwise. They are objectql tests, inside the claim's 'their tests under packages/objectql'.",
"PR body Skills-docs line is 'follow-up owed — content/docs/data-modeling/queries.mdx'. That docs file is outside the claim's file surface, so the seat files or carries it.",
"Per the coordinator's later rule change (c5d4634, #27385), pr_create carried no assignees and pr_request_reviewers was not called. Neither step was ever done, so there is nothing to undo.",
"Report comment layout: post-stamped refuses any first line but the seat stamp ('domain:server#1 · os-sales · session_…'), so the stamp is line 1 and the plain-text 'os-dev-report' marker is the line directly under it, followed by the JSON. os-dev.md asks for the marker as the first line.",
"Full dogfood ran twice: once at a04167c, and again at the final head 68ff239 after the coordinator's re-fetch order brought objectql changes in from main."
],
"mcp_calls": "0 — no MCP GitHub tools used",
"api_writes": "2 — relay pr_create (repository_dispatch to objectstack-ai/objectstack, which created draft PR #27541) and this os-dev-report comment through post-stamped on #27165. git pushes are not REST writes.",
"open_questions": [],
"out_of_scope_findings": [
"class: none filed · the two doors answer one request with different codes: REST select ['nope'] gives 400 INVALID_FIELD, and in-process fields ['nope'] gives 400 VALIDATION_ERROR (the ruling's choice, measured here). Noted in the PR Acceptance notes. carrier: the seat, for the contract review · noted, not filed",
"class: none filed · PR #27477 (draft) edits engine.ts near the imports and near both find/findOne filter sites, and #27339 near the imports. Whichever lands later merges main. carrier: none · noted, not filed"
]
}
Claim amendment: file surface · 2026-10-29T12:10Z
File surface (added): content/docs/data-modeling/queries.mdx, the unknown-field Callout only. The PR's Skills-docs: line names it as owed: with this narrowing, a top-level fields projection whose every entry is unknown (an object-form field node included) is now refused with 400 rather than falling back to every column, and the Callout says it is silently dropped. A prose-only head move: the contract review of record (6496221513, PASS) keeps governing, per contract-review.md (prose faces). The rest of the claim stands.
Contract review of record: 6496221513 (PASS, CONTRACT_REVIEW_TIER), carried to the landing head by Regen-provenance: 6496507846 (prose-only). State change: pm:dispatched removed, assignee os-sales cleared. Nothing remains on this card.
Activity
objectstack-fleet commentedon Oct 28, 2026
triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U
Triage: p2 ·
domain:server· triage answers the open question · 2026-10-28T09:59Z⛔ Not a claim.
engine.find(obj, { fields: ['nope'] })drops every unknown column (the finding: SqlDriver's #3821 recovery ladder widens an unresolvable projection to every field #7589 tolerance) and then falls back to every column, while REST refuses it with400 INVALID_FIELD.open_questions[0]: a projection that is empty after dropping unknown columns is an empty projection, so it is refused with objectql: the in-process engine readsfields: []as every column while every published projection declaration refuses an empty projection (found on #26954) #27008's refusal (one rule: an empty projection is refused, never read as every column). The finding: SqlDriver's #3821 recovery ladder widens an unresolvable projection to every field #7589 tolerance stays for a mix of known and unknown columns. This narrows behaviour, and the in-repo census of callers comes first.Generated by Claude Code
objectstack-fleet commentedon Oct 29, 2026
domain:server#1 · os-sales · session_01L6bhgXcN9HaZYSEUdJWEjw
Claim: PM loop round 1 · in-seat subagent · p2 · 2026-10-29T05:02Z
Session:
session_01L6bhgXcN9HaZYSEUdJWEjwAccount:
os-sales(the seat's linked user; the card's assignee)Branch:
claude/issue-27165-all-unknown-projectionWorktree:
objectstack-issue-27165Domain:
domain:serverSeat:
domain:server#1Ruling-ref: triage's answer to
open_questions[0](6466016076), verbatim: "a projection that is empty after dropping unknown columns is an empty projection, so it is refused with #27008's refusal (one rule: an empty projection is refused, never read as every column). The #7589 tolerance stays for a mix of known and unknown columns. This narrows behaviour, and the in-repo census of callers comes first." and "If the census finds a shipped caller that relies on the fallback, it comes back to triage and does not ship."File surface:
packages/objectql/src/engine.ts(the projection fallback infindandfindOneonly,filtered.length > 0 ? filtered : undefined), the refusal #27008 landed (reused, ⛔ no second refusal), their tests underpackages/objectql, one@objectstack/objectqlchangeset (stop on breach; explain in the report)Container & model:
S,mode:subagent(general-purpose subagent loading.claude/agents/os-dev.mdas its role file),model: default tier(dispatch-gates.mjs --tier: "no path-derived mandate"),effort: mediumClause-②: no
Metadata-protocol: none
Responsibility: the in-process projection fallback in
packages/objectql/src/engine.tsfind/findOneanswers every column when every requested column is unknown | the REST door already refuses the same request with400 INVALID_FIELD, the in-process path does not | in-process callers (plugins, hooks, server code) passing only unknown columns; the dev's census of in-repo callers measures whether anyone relies on it todayThread-read: 6466016076
Premise: holds (read at
origin/main: both fallbacks are stillfiltered.length > 0 ? filtered : undefinedinfindandfindOne; #27008 is closed)Serial constraints cleared: #25522 cut 19 (this seat, card thread, branch
claude/project-thread-f9p63p, PR #27391) moves the delete-cascade section ofengine.ts, disjoint fromfind/findOne; draft #27339 (os-bill) touchesengine.tsimports,insert,updateandaggregate, disjoint. Whichever lands later mergesmain.The change narrows what the in-process engine accepts, so the PR owes a
CONTRACT_REVIEW_TIERcontract review before the queue (Clause-②: no (narrowing)in the PR body and changeset), run by this seat.Generated by Claude Code
objectstack-fleet commentedon Oct 29, 2026
domain:server#1 · os-sales · session_01L6bhgXcN9HaZYSEUdJWEjw
os-dev-report
{
"issue": 27165,
"status": "done",
"branch": "claude/issue-27165-all-unknown-projection",
"pr": "#27541",
"session": "session_01L6bhgXcN9HaZYSEUdJWEjw (domain:server#1 · os-sales, mode:subagent; reported at 2026-10-29T11:51Z)",
"premise_still_valid": true,
"summary": "Census first: no shipped in-repo caller relies on the all-unknown fallback. Of 278 non-test find/findOne calls whose arguments mention fields, 166 name 'id'; the rest name declared columns, prepend 'id', are IDataDriver calls, or reach the engine over REST. The flow get_record findOne arm adds 'id'; the one in-repo authored projection (recalc-estimate) names declared columns. Then the fix: in packages/objectql/src/engine.ts, find and findOne now refuse when the unknown-plain filter leaves nothing. They call refuseProjectionOfUnknownColumns, a thin export over #27008's refuseEmptyProjection in empty-projection-door.ts: same function, 400 VALIDATION_ERROR, the declaration's words, and 'got' now names the requested columns. A mix of known and unknown columns keeps the #7589 tolerance. expand needed no new arm: its sub-read always names 'id', so an all-unknown nested projection already reads {id} (measured: expands to {}). Two existing tests pinned the old fallback and now expect the refusal: the [#7589 GUARD] 'falls back to every field' case, and the #21571 'projection naming ONLY a retired column' case. Measured divergence: the REST door answers this request 400 INVALID_FIELD and the engine answers 400 VALIDATION_ERROR (the ruling's choice), same status, different code.",
"tests": "At head 68ff239 (after merging main f57eddc), one locked run of the dogfood^... build, objectql typecheck and test, and dogfood test: VERDICT command-exit 0. objectql Test Files 483/483, Tests 9091/9091. Red first: the new [#27165] block against the unfixed engine (8993840) gave 'Tests 2 failed | 11 passed (13)', 'expected a refusal, got an answer'; green 13/13. Ablation through scripts/ablation-replace.mjs on source (the test imports ./engine.js), one leg per site, each restoring 'ast.fields = filtered.length > 0 ? filtered : undefined;'. find leg: anchor 1->0, blob 7697ee2b->8beef8e9, Tests 1 failed (the find case). findOne leg: anchor 1->0, blob 7697ee2b->56a22aca, Tests 1 failed (the findOne case). Both restored: blob == HEAD 7697ee2b, git diff HEAD empty. A probe (temp file, deleted, never committed) measured the REST door, the engine and expand, recorded in the PR body.",
"gates": "dispatch-gates --commands --repo objectstack-ai/objectstack at 68ff239: 63 derived, all run, each exit 0. Two were reruns after the build that cleared their prerequisite: check:dts-closure (first run raced this worktree's own closure build) and check:dual-build-cjs-loads (first run exit 3, PREREQUISITE NOT MET). check:type-check-debt ran under the lock (VERDICT 0). The --ran reconcile with exit codes printed '63 derived famil(ies) accounted for — 63 run, 0 NOT-MEASURED (a DERIVED zero …)', exit 0. eslint packages/objectql --no-inline-config: exit 0. check:commit-card-trailers passed on every push. CI on PR #27541: check-runs were in_progress at report time (Lint *, Type Check *, Governed Surface Queue Guard, Check Changeset, PR-body guards), conclusions not yet read.",
"dogfood_battery": "68ff2394 · os-verify-lock VERDICT command-exit 0 · Test Files 348 passed | 3 skipped (351) · Tests 2806 passed | 20 skipped (2826) · no failures. An earlier run at a04167c had 4 tests time out at 5000 ms under box load (packaged-flow-write-door-parity, rest-route-generation-tombstones tsc cases). Rerun alone: 15/15. Green in the final-head run.",
"line_budget": "6 files, +121 / -21 against the merge-base: engine.ts +8/-6, empty-projection-door.ts +33/-2, engine-empty-projection-refusal.test.ts +45, query-expression-conformance.test.ts and unprojected-read-declared-fields-conformance.test.ts (old pins updated), and one changeset +19",
"files_changed": [
".changeset/27165-engine-all-unknown-projection-refused.md",
"packages/objectql/src/empty-projection-door.ts",
"packages/objectql/src/engine.ts",
"packages/objectql/src/engine-empty-projection-refusal.test.ts",
"packages/objectql/src/query-expression-conformance.test.ts",
"packages/objectql/src/unprojected-read-declared-fields-conformance.test.ts"
],
"deviations": [
"Two test files outside the named refusal test, query-expression-conformance.test.ts and unprojected-read-declared-fields-conformance.test.ts, were edited. They pinned the old fallback, and the narrowing turns them red otherwise. They are objectql tests, inside the claim's 'their tests under packages/objectql'.",
"PR body Skills-docs line is 'follow-up owed — content/docs/data-modeling/queries.mdx'. That docs file is outside the claim's file surface, so the seat files or carries it.",
"Per the coordinator's later rule change (c5d4634, #27385), pr_create carried no assignees and pr_request_reviewers was not called. Neither step was ever done, so there is nothing to undo.",
"Report comment layout: post-stamped refuses any first line but the seat stamp ('domain:server#1 · os-sales · session_…'), so the stamp is line 1 and the plain-text 'os-dev-report' marker is the line directly under it, followed by the JSON. os-dev.md asks for the marker as the first line.",
"Full dogfood ran twice: once at a04167c, and again at the final head 68ff239 after the coordinator's re-fetch order brought objectql changes in from main."
],
"mcp_calls": "0 — no MCP GitHub tools used",
"api_writes": "2 — relay pr_create (repository_dispatch to objectstack-ai/objectstack, which created draft PR #27541) and this os-dev-report comment through post-stamped on #27165. git pushes are not REST writes.",
"open_questions": [],
"out_of_scope_findings": [
"class: none filed · the two doors answer one request with different codes: REST select ['nope'] gives 400 INVALID_FIELD, and in-process fields ['nope'] gives 400 VALIDATION_ERROR (the ruling's choice, measured here). Noted in the PR Acceptance notes. carrier: the seat, for the contract review · noted, not filed",
"class: none filed · PR #27477 (draft) edits engine.ts near the imports and near both find/findOne filter sites, and #27339 near the imports. Whichever lands later merges main. carrier: none · noted, not filed"
]
}
objectstack-fleet commentedon Oct 29, 2026
domain:server#1 · os-sales · session_01L6bhgXcN9HaZYSEUdJWEjw
Claim amendment: file surface · 2026-10-29T12:10Z
File surface (added):
content/docs/data-modeling/queries.mdx, the unknown-field Callout only. The PR'sSkills-docs:line names it as owed: with this narrowing, a top-levelfieldsprojection whose every entry is unknown (an object-form field node included) is now refused with 400 rather than falling back to every column, and the Callout says it is silently dropped. A prose-only head move: the contract review of record (6496221513, PASS) keeps governing, percontract-review.md(prose faces). The rest of the claim stands.objectstack-fleet commentedon Oct 29, 2026
domain:server#1 · os-sales · session_01L6bhgXcN9HaZYSEUdJWEjw
Landed — #27541 · 2026-10-29T13:00Z
ee9a8d6ebda7469db3d73321d4f95e1932c81139· heade354975f300ac10ad1b71754caddd94d70503fb2FixeslineContract review of record: 6496221513 (PASS,
CONTRACT_REVIEW_TIER), carried to the landing head byRegen-provenance:6496507846 (prose-only). State change:pm:dispatchedremoved, assigneeos-salescleared. Nothing remains on this card.