Found while reviewing PR #1227 (card #1208); raised by the dev as an unowned divergence rather than a defect with an obvious fix. Filed unassigned, for grading.
Baseline: origin/main @ eef112b.
Three sources, two incompatible answers
test/hook-query-predicate.test.ts:91 — a live test, currently green in CI:
v17 makes `filter` a live predicate alias across repository reads.
and it backs that with assertions that actually execute against a real engine:
const hit = await api.object('crm_account').findOne({ filter: { id: second.id } } as any);
const n = await api.object('crm_account').count({ filter: { annual_revenue: 100 } } as any);
src/objects/_hook-api.ts:34-46 — prose on the type, saying the opposite:
The predicate key is `where` — and ONLY `where`. An earlier version of this
type also allowed `filter`, on the belief that "drivers accept either". They
do not:
- `find` normalizes `filter` → `where`, so it happens to work.
- `findOne` ... and never aliases. An unknown `filter` key is dropped ...
- `count` reads `query.where` explicitly, so `filter` is dropped ...
AGENTS.md §2 — per the dev's reading, still states where and only where. (Not re-verified by me; verify before acting.)
The test suite passed 2736 tests on this baseline, so the alias assertions are not stale scaffolding — on the version this repo pins, findOne({ filter }) and count({ filter }) do return rows. The type's prose describes behaviour that was true when it was written and no longer is.
Why this is worth a card rather than a shrug
The convention is not in question. The same test file carries a repo-wide guard — no hook-side code may query by 'filter', asserted per file over src/objects/*.hook.ts — and that guard should stay. Hooks should keep using where.
What is wrong is the stated reason. Authors are currently told filter fails silently, which is a safety argument. If v17 aliases it, the real argument is consistency and forward-compatibility, not silent data loss. That matters in three ways:
- It is load-bearing agent instruction. This exact claim is carried as a standing fact in the
repo:hotcrm seat post (objectstack#10282 §4) and was passed verbatim into a dispatch brief this round. Agent-facing prose that is confidently wrong is worse than absent prose — it gets copied forward, and it is the class of error this repo has paid for before.
- The failure directions are opposite. "Silently dropped" means a hook reading by
filter matches everything (an unscoped read). "Aliased" means it matches correctly. An author who believes the wrong one will mis-judge the blast radius of any code that does use filter.
AGENTS.md is a governed surface (2026-08-18 cross-repo ruling), so correcting it is draft-only + human merge. That does not make it optional — it makes it a different landing path, and worth knowing before someone tries to ride it on an unrelated PR.
What would settle it
A direct measurement per method against the pinned version — find, findOne, count, and the write paths — recording for each whether filter is normalized, aliased, or dropped. The test file asserts two of those; nothing asserts the rest. Then correct whichever prose is stale (_hook-api.ts, AGENTS.md, and the seat-post fact), keeping the where-only convention and its guard, and restating the reason honestly.
⚠️ Do not resolve this by deleting the guard. The convention is fine; only its justification is in doubt.
Refs #1208 · PR #1227
Found while reviewing PR #1227 (card #1208); raised by the dev as an unowned divergence rather than a defect with an obvious fix. Filed unassigned, for grading.
Baseline:
origin/main@eef112b.Three sources, two incompatible answers
test/hook-query-predicate.test.ts:91— a live test, currently green in CI:and it backs that with assertions that actually execute against a real engine:
src/objects/_hook-api.ts:34-46— prose on the type, saying the opposite:AGENTS.md§2 — per the dev's reading, still stateswhereand onlywhere. (Not re-verified by me; verify before acting.)The test suite passed 2736 tests on this baseline, so the alias assertions are not stale scaffolding — on the version this repo pins,
findOne({ filter })andcount({ filter })do return rows. The type's prose describes behaviour that was true when it was written and no longer is.Why this is worth a card rather than a shrug
The convention is not in question. The same test file carries a repo-wide guard —
no hook-side code may query by 'filter', asserted per file oversrc/objects/*.hook.ts— and that guard should stay. Hooks should keep usingwhere.What is wrong is the stated reason. Authors are currently told
filterfails silently, which is a safety argument. If v17 aliases it, the real argument is consistency and forward-compatibility, not silent data loss. That matters in three ways:repo:hotcrmseat post (objectstack#10282 §4) and was passed verbatim into a dispatch brief this round. Agent-facing prose that is confidently wrong is worse than absent prose — it gets copied forward, and it is the class of error this repo has paid for before.filtermatches everything (an unscoped read). "Aliased" means it matches correctly. An author who believes the wrong one will mis-judge the blast radius of any code that does usefilter.AGENTS.mdis a governed surface (2026-08-18 cross-repo ruling), so correcting it is draft-only + human merge. That does not make it optional — it makes it a different landing path, and worth knowing before someone tries to ride it on an unrelated PR.What would settle it
A direct measurement per method against the pinned version —
find,findOne,count, and the write paths — recording for each whetherfilteris normalized, aliased, or dropped. The test file asserts two of those; nothing asserts the rest. Then correct whichever prose is stale (_hook-api.ts,AGENTS.md, and the seat-post fact), keeping thewhere-only convention and its guard, and restating the reason honestly.Refs #1208 · PR #1227