Repository navigation
Commit 1d20245
docs(skills): objectstack-query teaches the served route for a related record's column, not the refused nested form (#20811)
Fixes #20782
Clause-②: no
## What this changes
The published skill `skills/objectstack-query` taught `{ relation: {
field: value } }` beneath a lookup as a working `where` form. On `main`
the engine refuses that form on every driver — `INVALID_FILTER` / 400,
in the words of `relationWords()` in
`packages/objectql/src/no-operator-object-door.ts` — and names the route
it serves: filter the related object first, then match the relation
column against the ids it returns (`$in` on a single-valued column;
`$contains` per id, an `$or` of those for several, on a `multiple: true`
one, whose JSON column the SQL driver refuses `$in` on). The skill now
states that refusal and that route at every site that taught or pointed
at the form. It anticipates neither letter of the open v18 decision
(#20802 is not addressed here): it states today's behaviour.
Six landing sites, all in `skills/objectstack-query`, measured on
`origin/main` `96e72447`:
| Site (at `96e72447`) | Before | After |
|:--|:--|:--|
| `SKILL.md:76`, Removed-key row `query.joins` | "`expand`, or a nested
relation filter" | "`expand` (display), or filter the related object and
`$in` its ids" |
| `SKILL.md:88`, rules index | "nested relations" | "filtering by a
related record" |
| `SKILL.md:187-:194`, subsection | "Nested Relation Filters" with the
`{ contact: { profile: { verified: true } } }` example | "Filtering by a
related record": the refusal, the route, a pointer to the rule |
| `SKILL.md:334` | "use a nested relation filter" | "`$in` ids from its
own query" |
| `SKILL.md:343`, Cross-Object row | "Filter parent by child conditions
— Nested relation filter" | two rows, one per direction (below) |
| `rules/filters.md:132-:152`, section | "Nested Relation Filters", two
✅ examples of the refused form | "Relation Filters": the refusal, one
two-step example, the multi-valued spelling, the reverse direction |
**The `:343` row was mislabelled.** "Filter parent by child conditions"
names the reverse direction (a parent by its children's fields), while
the form it pointed at, even had it been served, expresses only the
forward one (rows by their lookup target's column). The row is now two:
"Filter rows by their lookup target's column" → query the target object,
then `{ lookup: { $in: ids } }` (`$contains` per id when `multiple`);
"Filter parent by child conditions" → query the child with `fields:
[lookup]`, then `{ id: { $in: those ids } }` on the parent. Both routes
are served: the engine's own `expand` batch-loads with `{ id: { $in } }`
(`engine.ts`, the "Batch-load related records using $in query" block),
and the REST ingress admits `id` as a filter key (`protocol.ts`
`resolveQueryFields`, `known.add('id')`).
**No dotted alternative is taught.** `'owner.region'` is refused one
door earlier by the #8371 dotted verdict (`filter-comparand-shape.ts`,
`INVALID_FIELD` / 400); a grep of the skill for a dotted `where` path
finds none.
**Census.** `grep -rn -i -E 'nested relation|relation filter|nested
relations' skills/` on `96e72447` finds exactly the six sites; a
multi-line shape grep for a `where` example nesting a no-operator object
under a relation key finds the same three (`SKILL.md:193`,
`rules/filters.md:138`, `:145`) and nothing else in `skills/**`.
## Paying for it inside `rules/filters.md`
The file sat at 2148 / 2149 tokens. The section rewrite (881 bytes) is
paid for by deleting three examples whose rule already has a home in the
same package, so the file lands at 2149 / 2149 (headroom 0, ceiling
untouched):
| Deleted from `rules/filters.md` | Home |
|:--|:--|
| `## Implicit Equality (Shorthand)` (`:40-:51`) | `SKILL.md` "Implicit
Equality (Shorthand)" (`:94-:101`), and the `$eq` row of this file's
Operator Reference |
| `### NOT` (`:93-:100`) | `SKILL.md` Logical Operators, `{ where: {
$not: { status: 'closed' } } }` (`:183-:184`) |
| `### Combining Logical Operators` (`:102-:113`) | `SKILL.md` "AND + OR
combined" (`:173-:181`); sibling-keys-are-AND is this file's first
Common Mistake |
The `role: 'admin'` literal in the deleted third example moved
`check:role-word`'s count for the file 8 → 7; the gate prescribes the
ratchet-down ("run `--update` and commit the baseline"), and commit
`b893787b` is that `--update` output: one row of
`scripts/role-word-baseline.json`. That file is outside the claim's
declared surface; declared here and in the report.
## `skills/**` readings (lines and tokens; tokens are the ratchet's
`ceil(utf8 bytes / 4)`)
| | Before (`96e72447`) | After (`b893787b`) | Δ |
|:--|--:|--:|--:|
| `SKILL.md` | 401 lines · 3990 tokens (ceiling 5552) | 399 lines · 4055
tokens | −2 lines · +65 tokens |
| `rules/filters.md` | 251 lines · 2148 tokens (ceiling 2149) | 214
lines · 2149 tokens | −37 lines · +1 token |
| Whole package `skills/objectstack-query` (6 files) | 1184 lines ·
10461 tokens | 1145 lines · 10527 tokens | −39 lines · +66 tokens |
Line budget (PM-set, net 0 at most): −39. No untouched line was
re-wrapped; no ceiling row moved.
## Verification
Gates, all at head `b893787b`, exit codes captured by redirect and
verdict lines quoted from each log: the 31 families `dispatch-gates
--commands --repo objectstack-ai/objectstack` derives from the change
set, reconciled with `--ran` ("31 derived, 31 run, 0 NOT-MEASURED, 0
UNRUN") — every one exit 0, including `check-skills-token-ratchet` ("54
authored bundle file(s) within their ceilings") and its `--self-test`
(65 cases), `check:role-word` after the ratchet-down ("OK, no new
occurrences of the reserved word"), `check:corpus-claim-drift`,
`check:skill-identifier-liveness`, `check:doc-authoring`,
`check:nul-bytes`, `check:doc-formula-expressions`
(`@objectstack/formula` and `@objectstack/lint` built first), spec
`check:skill-docs` ("Skill docs in sync") and spec `check:skill-refs`
("9 generated files in sync"). `check:skill-examples` does not apply: no
block in this skill carries an `os:check` marker.
Pin: the skill's example validation does not cover these snippets (no
`os:check` marker), so the pin is PR #20781's, on `main`:
`packages/objectql/src/engine-nested-object-door.test.ts` (the refusal
envelope `{ code: 'INVALID_FILTER', status: 400 }` beneath `lookup`,
`master_detail`, `multiple: true` lookup, `user` and `tree`, and the
CONTROL that `{ owner: { $in: [...] } }`, `{ owners: { $contains: ... }
}`, its `$or`, and `{ id: { $in: [...] } }` reach the driver as written)
and `packages/rest/src/data-nested-object-door.test.ts` (400 over `POST
/api/v1/data/:object/query`; the routes answer `d1`, `d3`). Run
first-hand here: `pnpm --filter @objectstack/objectql exec vitest run
--maxWorkers=2 src/engine-nested-object-door.test.ts` → "Test Files 1
passed (1), Tests 15 passed (15)". No package is touched, so no package
build or test suite is owed beyond that.
## Acceptance notes
- `content/docs/protocol/objectql/query-syntax.mdx` (`:602-:610`,
read-only here): the "Filtering Across Relationships" callout's headline
is true (neither the nested form nor a dotted path is served), but its
mechanism paragraph is stale — it says `SqlDriver.applyFilters()`
compiles the nested object as a single-column comparison and emits the
dotted key verbatim to Knex, while on `main` both are refused at the
engine before any driver (`INVALID_FILTER` / 400 for the nested form,
`INVALID_FIELD` / 400 for the dotted path), and it names no served
route. Noted, not filed: documentation drift under a true headline; no
carrier known.
- `check:pm-governed-merges` as the package script spells it is the
`--self-test` alone (exit 0 here); the live sweep is CI's.
- The report's `api_writes` lists every relay write of this run.
## 维护者速读(草稿)
**改了什么。** 发布的查询技能包 `skills/objectstack-query` 原先把「在关联字段下直接写条件」(`{
customer: { country: 'US' }
}`)当作能用的过滤写法来教。现在六处教它或指向它的句子都改成平台今天的真实行为:这种写法被引擎拒绝(`INVALID_FILTER` /
400),可行的路是先查关联对象拿到 id,再对关联字段用 `$in`(多值关联用 `$contains` 逐个 id)。规则文件里删掉了三段在
SKILL.md 已有同样规则的重复示例,用来支付这段改写;整个包净减 39 行,每个文件的 token 上限都没动。
**为什么改。** AI 作者照着技能包写,写出来的查询在内存驱动上静默返回 0 行、在 SQL 驱动上报 400;PR #20781
合并后所有驱动都统一拒绝。技能包是客户项目里 AI 的教材,教错一句就是每个客户项目里的错误查询。本 PR 不预判 v18
决策卡(#20802)的任一方向,只陈述今天的行为。
**风险与代价(含回滚)。** 只改文档文字与一行门禁基线(`role-word` 计数 8 →
7,门禁自己要求的下调),不改任何代码或发布包。风险在措辞:若维护者裁定 v18 支持关联过滤,这几句还要再改一次(决策卡已注明)。回滚即
revert 本 PR 的两个提交。
**席位意见。**
**你要做的。** 复核六处措辞与三处删除各有归宿;批准后由席位落地(Tier H)。
---
_Generated by [Claude
Code](https://claude.ai/code/session_01KTZmMfzVzjNvyaLyQ8mHvg)_
---------
Co-authored-by: Claude <noreply@anthropic.com>1 parent 07356a6 commit 1d20245
3 files changed
Lines changed: 24 additions & 63 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
39 | 39 | | |
40 | 40 | | |
41 | 41 | | |
42 | | - | |
| 42 | + | |
43 | 43 | | |
44 | 44 | | |
45 | 45 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
73 | 73 | | |
74 | 74 | | |
75 | 75 | | |
76 | | - | |
| 76 | + | |
77 | 77 | | |
78 | 78 | | |
79 | 79 | | |
| |||
85 | 85 | | |
86 | 86 | | |
87 | 87 | | |
88 | | - | |
| 88 | + | |
89 | 89 | | |
90 | 90 | | |
91 | 91 | | |
| |||
184 | 184 | | |
185 | 185 | | |
186 | 186 | | |
187 | | - | |
| 187 | + | |
188 | 188 | | |
189 | | - | |
190 | | - | |
191 | | - | |
192 | | - | |
193 | | - | |
194 | | - | |
| 189 | + | |
| 190 | + | |
| 191 | + | |
195 | 192 | | |
196 | 193 | | |
197 | 194 | | |
| |||
331 | 328 | | |
332 | 329 | | |
333 | 330 | | |
334 | | - | |
| 331 | + | |
335 | 332 | | |
336 | 333 | | |
337 | 334 | | |
| |||
340 | 337 | | |
341 | 338 | | |
342 | 339 | | |
343 | | - | |
| 340 | + | |
| 341 | + | |
344 | 342 | | |
345 | 343 | | |
346 | 344 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
37 | 37 | | |
38 | 38 | | |
39 | 39 | | |
40 | | - | |
41 | | - | |
42 | | - | |
43 | | - | |
44 | | - | |
45 | | - | |
46 | | - | |
47 | | - | |
48 | | - | |
49 | | - | |
50 | | - | |
51 | | - | |
52 | 40 | | |
53 | 41 | | |
54 | 42 | | |
| |||
90 | 78 | | |
91 | 79 | | |
92 | 80 | | |
93 | | - | |
94 | | - | |
95 | | - | |
96 | | - | |
97 | | - | |
98 | | - | |
99 | | - | |
100 | | - | |
101 | | - | |
102 | | - | |
103 | | - | |
104 | | - | |
105 | | - | |
106 | | - | |
107 | | - | |
108 | | - | |
109 | | - | |
110 | | - | |
111 | | - | |
112 | | - | |
113 | | - | |
114 | | - | |
115 | 81 | | |
116 | 82 | | |
117 | 83 | | |
| |||
129 | 95 | | |
130 | 96 | | |
131 | 97 | | |
132 | | - | |
| 98 | + | |
133 | 99 | | |
134 | | - | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
135 | 104 | | |
136 | 105 | | |
137 | | - | |
138 | | - | |
139 | | - | |
140 | | - | |
141 | | - | |
142 | | - | |
143 | | - | |
144 | | - | |
145 | | - | |
146 | | - | |
147 | | - | |
148 | | - | |
149 | | - | |
150 | | - | |
151 | | - | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
152 | 110 | | |
153 | 111 | | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
154 | 117 | | |
155 | 118 | | |
156 | 119 | | |
| |||
0 commit comments