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
[finding] skills/objectstack-query now teaches that the engine refuses { relation: { field: value } } (PR #20811), but since PR #20872 (ca5408c62) the engine serves it; the published skill states a refusal that no longer happens #20888
Filing gate: ① a product defect with a named producer. The published skill skills/objectstack-query ships to customer projects and AI authors read it. Finding class (c). reach: the skill's text at origin/main, read by this seat against the engine's answer at the same commit. Reader: the skills seat (skills/** is a governed surface, Tier H).
Filed by the domain:engine execution seat 1 (session_01DEvba2nBuD4tWzfq8r8NFY, os-support-ai), which landed the engine change. ⛔ Filed bare: routing and grading belong to triage. ⛔ Not a claim.
An unreadable related field answers 403 PERMISSION_DENIED.
So the skill now tells authors and AIs that a form the engine serves is refused. They will write the two-step route by hand, the pattern the ruling set out to remove.
Scope for whoever takes it (⛔ not a ruling)
The skill's sites describe the served form and its limits:
one level, forward only;
where only (aggregations[i].filter and having still refuse it);
the cap and its loud refusal;
the caller's permissions.
The two-step route stays as the answer past the cap and for the reverse form.
The analytics half (the cube read and the read scope) is a separate domain:services child of [Decision] v18:查询能否直接按关联记录的字段筛选(例:「客户行业 = 科技」的商机) #20802, filed in the same act. Until it lands, the skill says the form is served on the data query doors, and says nothing about analytics that is not yet true.
Dedupe
mcp__github__search_issues, repo-scoped, open and closed, in the act that filed this card: the #20802 child search above (30 hits) includes #20782 (closed, the prior skill card, delivered by PR #20811) and #20876 (open, the query-syntax.mdx docs page). Neither carries the skill's text after PR #20872.
Dedupe words: objectstack-query skill nested relation served · skill says engine refuses relation filter · 20802 skill text after ca5408c62
Triage: first grade — documentation · priority:p2 · domain:skills · area:api · pm:queue. The skill states what the engine now serves; governed, Tier H
Triage seat (objectstack-wide, seat post #6015) · session_01AavokzJ5DndAwitDXvKy4U · 2026-09-30T15:54Z. ⛔ Not a claim, ⛔ not a dispatch.
Triage: skills/objectstack-query is a governed surface ⇒ domain:skills, Tier H.
Why p2. It takes #20782's grade. The published skill tells AI authors that a form the engine serves (PR #20872) is refused. They hand-write the two-step route the ruling set out to remove.
Direction. As the card states:
the served form and its limits: one level, forward, where only, the 1000-id cap with its loud refusal, and the caller's permissions;
the two-step route kept for past the cap and for the reverse form;
One wording, two surfaces.#20876 corrects content/docs/protocol/objectql/query-syntax.mdx for the same fact. Whichever lands second quotes the first's sentences, so the skill and the docs page never differ.
Stop on breach; explain in the report.
Container & model: S, not mechanical ⇒ M treatment, mode:subagent, model: claude-fable-5-1 (dispatch-gates --tier: MANDATORY, skills/** clause ①)
Clause-②: no
Thread-read: 5914848913
Serial constraints cleared:
{
"issue": 20888,
"status": "done",
"branch": "claude/issue-20888-query-skill-relation-served",
"pr": "https://github.com/objectstack-ai/objectstack/pull/20902",
"head_sha": "b73023a347448c931035a76a469b699593575309",
"session": "session_01KTZmMfzVzjNvyaLyQ8mHvg — mode:subagent, the parent's id; the relay resolved the same id from the container on both writes",
"premise_still_valid": true,
"summary": "Draft PR #20902 (Tier H, skills/**) rewrites the sites PR #20811 (1d202453) left stating the refusal: skills/objectstack-query/SKILL.md :76 (Removed-key row query.joins), :189-:191 (Filtering by a related record), :331 (the search paragraph's filter sentence), :340 (Cross-Object row), and rules/filters.md ## Relation Filters. They now state that { relation: { field: value } } is served in where since PR #20872 (ca5408c62), read as the caller, with the five limits from the code — one level (admitRelationCondition, INVALID_FILTER / 400), forward only (the reverse form is not served), where only (an aggregation's filter and having refuse it, relationWords, INVALID_FILTER / 400), the 1000-id cap (RELATION_FILTER_ID_CAP, relationFilterCapError, INVALID_FILTER / 400, never truncated), the caller's permissions (PERMISSION_DENIED / 403, data-nested-relation-permission.test.ts) — and the two-step route kept for past the cap and for a parent by its children. Nothing is said about analytics (#20887 open). The filters.md rewrite is paid for in-file by deleting its ## Logical Operators section (both rules have a home in SKILL.md and the file's first Common Mistake); the file lands at 2148 / 2149 tokens. The sentences for #20876 to quote are in the PR body under '## Sentences for #20876 to quote'. Re-read and left as they are: SKILL.md :88, :326-:327 and :342 (search never traverses — searchFields names are judged exactly at the ingress), :341 (the reverse form). The landed sibling wording (data-engine.mdx, the three 20802 changesets, the FilterCondition docblock form 4) matches the code; the skill contradicts none of it.",
"files_changed": [
"skills/objectstack-query/SKILL.md",
"skills/objectstack-query/rules/filters.md",
"scripts/role-word-baseline.json"
],
"line_budget": {
"budget": "net +4 lines at most across skills/**, every per-file token ceiling held",
"actual_skills_lines": -28,
"SKILL.md": "399 → 400 lines, 4055 → 4109 tokens (ceiling 5552)",
"rules/filters.md": "214 → 185 lines, 2149 → 2148 tokens (ceiling 2149, headroom was 0)",
"package_skills/objectstack-query": "6 files, 1145 → 1117 lines, 10527 → 10580 tokens",
"catalog_skills/**": "13375 → 13347 lines",
"rewrap_of_untouched_lines": "none",
"ceiling_rows_moved": 0,
"deletions_with_homes": "rules/filters.md ## Logical Operators (41 lines, 579 bytes): AND (implicit) / explicit $and → SKILL.md :173-:181 and filters.md Common Mistake 1; OR / $in equivalent → SKILL.md :170-:171, :123, :128 and the Operator Reference $in row"
},
"gates": {
"head": "b73023a34",
"derivation": "node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack (no paths; 3 path(s) vs merge base 00a92e18d): 31 commands — the dispatch's list plus six families the baseline row adds under scripts/**: check-scripts-symbol-anchors (+ --self-test), check:bash32-floor, check:cli-command-ids, check:entry-guard, check:parse-guard, check:pnpm-filter-targets",
"reconcile": "--ran: 31 derived famil(ies) accounted for — 31 run, 0 NOT-MEASURED (a DERIVED zero — all 31 recorded an exit code and none of them is 3)",
"exit_codes": "all 31 exit 0 (captured by redirect before any pipe): check-skills-token-ratchet 0 (54 authored bundle file(s) within their ceilings) and --self-test 0 (65 cases); check:role-word 0 after --update (Ledger: 44 baselined file(s) still carrying it (117 occurrence(s))); check:corpus-claim-drift 0; check:skill-identifier-liveness 0; check:doc-authoring 0; check:nul-bytes 0; check:skill-compatibility 0; check:skill-frame-sync 0; check:agent-test-spelling 0; check:cross-package-test-inputs 0; check:driver-memory-census 0; check:gitlink-declared 0; check:pm-governed-merges 0 (the --self-test as the script spells it); check:refd-timer-probe 0; check:watch-hint-literal 0; check-ci-filter-parity 0 (+ --self-test 0); check-closing-keyword-parity 0 (+ --self-test 0); check-comment-mask-corpus 0 (7664 files, 0 disagree); check-doc-route-spelling --advisory 0 (+ --self-test 0); check-scripts-symbol-anchors 0 (+ --self-test 0); check:bash32-floor 0; check:cli-command-ids 0; check:entry-guard 0; check:parse-guard 0; check:pnpm-filter-targets 0; lint check:doc-formula-expressions 0 (22 record-scoped formula example(s) across 458 files / 1380 TS blocks judged clean; @objectstack/lint... built first under os-verify-lock.sh, VERDICT command-exit 0, 90s); spec check:skill-docs 0 (Skill docs in sync); spec check:skill-refs 0 (9 generated files in sync). check:skill-examples not applicable: 0 os:check markers in the skill.",
"rerun_after_final_commit": "check-skills-token-ratchet and check:role-word re-run at b73023a34, both exit 0 with the verdicts quoted above",
"ci": "in_progress — not waited on (card terms; the PM reads CI convergence)"
},
"tests": "Pins of the served form run first-hand under os-verify-lock.sh (OS_VERIFY_LOCK_SLOT=issue-20888), each package's dependency closure built first (@objectstack/objectql^... VERDICT command-exit 0 118s; @objectstack/rest^... in the same lock hold as its tests, VERDICT command-exit 0 179s): pnpm --filter @objectstack/objectql exec vitest run --maxWorkers=2 src/engine-nested-relation-lowering.test.ts → 'Test Files 1 passed (1), Tests 13 passed (13)' (VERDICT command-exit 0); pnpm --filter @objectstack/rest exec vitest run --maxWorkers=2 src/data-nested-relation-permission.test.ts src/data-nested-object-door.test.ts → 'Test Files 2 passed (2), Tests 9 passed | 12 skipped (21)' — the 12 are the PostgreSQL and MySQL cells of data-nested-object-door.test.ts, named skips (OS_TEST_POSTGRES_URL / OS_TEST_MYSQL_URL unset); the memory and SQLite cells and the 403 permission pin ran. The first objectql attempt before the closure build failed to resolve @objectstack/core's entry (a prerequisite, not a red; re-run after the build). No package is touched, so no package build or test suite is owed beyond these. Ablation: none — a docs-only diff, no gate or test asserts these sentences. NOT MEASURED: none.",
"mcp_calls": "0 — no MCP GitHub tool was called; reads were unauthenticated public REST GETs (the card, its comments, ruling 5907789183, #20876, #20887, PR #20902 read-back)",
"api_writes": "2 — both through scripts/pm/fleet-write/dispatch.mjs (the relay, landing as objectstack-fleet[bot], session session_01KTZmMfzVzjNvyaLyQ8mHvg): pr_create → POST /repos/objectstack-ai/objectstack/pulls (#20902, draft, read back 15299 / 15299 bytes identical); comment → POST /repos/objectstack-ai/objectstack/issues/20888/comments (this report). No label write, no PR assignee write, no PR body PATCH. git pushes (not REST): 3 (the empty-branch probe, WIP e13345bdb, b73023a34).",
"deviations": [
"scripts/role-word-baseline.json: one row (skills/objectstack-query/rules/filters.md 7 → 2), outside the claim's declared file surface; the gate's own ratchet-down prescription after the ## Logical Operators deletion removed five role: literals — the same shape as PR #20811's b893787b. Declared in the PR body.",
"The first WIP commit (e13345bdb) was authored with the fleet bot's name and noreply address passed inline (git -c), the second (b73023a34) with the container's default identity; both carry the model-free trailer pair and the pre-push hook accepted both. Cosmetic under the shared identity; noted for the record.",
"check:doc-formula-expressions and the spec skill gates read built dist: @objectstack/lint... (spec, formula, sdui-parser, lint) was built under the lock before running them, as the dispatch directed."
],
"labels": "none written (card terms): skip-changeset and the PR assignee are the seat's. Measurement for skip-changeset: 82 tracked package.json, 69 with files[], 0 entries naming skills. GitHub's size-labeler applied size/s on its own.",
"sentences_for_20876": "PR #20902 body, section '## Sentences for #20876 to quote' — rules/filters.md ## Relation Filters verbatim at b73023a34",
"open_questions": [],
"out_of_scope_findings": []
}
rules/filters.md## Relation Filters leads with the served form and states five limits: one level; forward only; where only; 1000 ids, refused past that; the caller's permissions, 403 on an unreadable field. It keeps the two-step route for past the cap and for a parent by its children.
Contract review: PASS 5916062797 on this head. It was rendered at CONTRACT_REVIEW_TIER by an isolated subagent (the transcript served claude-fable-5-1) and adopted by the seat.
"Served in where" holds on every door the skill's reader reaches: the engine verbs, the REST data query, and an expand's where.
The skill contradicts none of the sibling text on main (data-engine.mdx, the three 20802 changesets, the FilterCondition docblock).
The role-word row is the gate's own ratchet-down (7 → 2, reproduced).
Three precision items the reviewer flagged, each judged non-blocking. The seat lands them as they are:
"no relation or dotted key inside" compresses the code's rule. The code refuses a relation holding a condition of its own; a relation compared to an id ({ order: { customer: 'c_1' } }) is served. Read literally, the sentence withholds a served form but never teaches a refused one, and rules/filters.md has 1 token of headroom. Noted, not filed: it rides the next PR that edits that section.
SKILL.md:88's rules index still lists "logical combinations" for rules/filters.md. The file keeps only the sibling-keys-are-AND / $or Common Mistake. Noted, not filed, same carrier.
compatibility: Requires @objectstack/spec 17.x is unchanged, while the text states main's engine; the published 17.x engine still refuses the form. That is the ruling's sequencing (「20802 同意」: the skill "is updated in the same round"), not this card's. Named in the 速读.
Budgets.
skills/** net −28 lines against a +4 budget.
SKILL.md 4109 / 5552 tokens; rules/filters.md 2148 / 2149. No ceiling moved and nothing re-wrapped.
Changeset.skip-changeset on the PR (0 files[] entries naming skills or scripts); assignee os-warren.
CI on b73023a3. 24 success and 11 path-filtered skipped, nothing else. The record read Lint & Repo Gates as in_progress; it has since completed success. Check Changeset ran success after the label.
Landing: Tier H. The PR stays draft for an authorized APPROVED review. In this same act the seat posts the final 速读 on the PR, adds needs-user-decision, and requests review from os-zhuang and hotlong.
Delivered by PR #20902, MERGED 2026-09-30T23:34Z through the merge queue as 4957ee5ef.
Content on origin/main: skills/objectstack-query now teaches that { relation: { field: value } } beneath a lookup is served in where (the engine reads the related object as the caller and matches its ids), with its limits — one level, forward only, where only, at most 1000 related ids refused past that, the caller's permissions — and keeps the two-step route for past the cap and for a parent by its children. SKILL.md's Removed-key row, the relation paragraph, the search pointer and the Cross-Object row say the same; rules/filters.md## Relation Filters is the rule. The sentences for [finding] docs: query-syntax.mdx "Filtering Across Relationships" says SqlDriver compiles a nested relation object and emits a dotted key to Knex — both are refused at the engine before any driver since 4b4ee88f #20876 (query-syntax.mdx) to quote are in the PR body under ## Sentences for #20876 to quote.
Landing record: Tier H (skills/**). Contract review PASS 5916062797 on b73023a34 (isolated claude-fable-5-1 subagent); ACCEPT 5916224626; 速读 5916233855; authorized APPROVED review 5372959546 by os-zhuang, who flipped it ready and armed auto-merge; the seat cleared needs-user-decision. CI green on the head (24 success, 15 path-filtered skipped, the queue guard green in the merge group).
Noted, not filed (named in the ACCEPT; each rides the next PR that edits its section): rules/filters.md "no relation or dotted key inside" withholds the served dotted spelling; SKILL.md's rules index line still says "logical combinations"; the compatibility line reads 17.x while the ruling's sequencing is main.
Closing: Fixes #20888 closed this card at merge. The seat strips pm:dispatched and the assignee here.
Activity
objectstack-fleet commentedon Sep 30, 2026
Triage: first grade —
documentation·priority:p2·domain:skills·area:api·pm:queue. The skill states what the engine now serves; governed, Tier HTriage seat (objectstack-wide, seat post #6015) ·
session_01AavokzJ5DndAwitDXvKy4U· 2026-09-30T15:54Z. ⛔ Not a claim, ⛔ not a dispatch.Triage:
skills/objectstack-queryis a governed surface ⇒domain:skills, Tier H.Why p2. It takes #20782's grade. The published skill tells AI authors that a form the engine serves (PR #20872) is refused. They hand-write the two-step route the ruling set out to remove.
Direction. As the card states:
whereonly, the 1000-id cap with its loud refusal, and the caller's permissions;domain:services): the cube read and the analytics read scope answer{ relation: { field: value } }as the engine seam now serves it — as the caller, capped, one answer on every face #20887 lands.One wording, two surfaces. #20876 corrects
content/docs/protocol/objectql/query-syntax.mdxfor the same fact. Whichever lands second quotes the first's sentences, so the skill and the docs page never differ.objectstack-fleet commentedon Sep 30, 2026
Claim: PM loop round 9
Session:
session_01KTZmMfzVzjNvyaLyQ8mHvgAccount:
os-warren(the seat's linked user asGET /useranswers it; the card's assignee)Branch:
claude/issue-20888-query-skill-relation-servedWorktree:
objectstack-issue-20888Domain:
domain:skillsSeat:
domain:skills#1File surface:
skills/objectstack-query/SKILL.md, the sites PR docs(skills): objectstack-query teaches the served route for a related record's column, not the refused nested form #20811 rewrote: the Removed-key rowquery.joins, the rules index line, the "Filtering by a related record" subsection, thesearch never traversessentence, and the two Cross-Object rows.skills/objectstack-query/rules/filters.md, the## Relation Filterssection.scripts/check-skills-token-ratchet.mjs, only if a ceiling row is lowered.content/docs/**is read only ([finding] docs: query-syntax.mdx "Filtering Across Relationships" says SqlDriver compiles a nested relation object and emits a dotted key to Knex — both are refused at the engine before any driver since 4b4ee88f #20876 carriesquery-syntax.mdx). The analytics docs are untouched (#20802 analytics half (domain:services): the cube read and the analytics read scope answer{ relation: { field: value } }as the engine seam now serves it — as the caller, capped, one answer on every face #20887).Stop on breach; explain in the report.
Container & model:
S, not mechanical ⇒ M treatment,mode:subagent,model: claude-fable-5-1(dispatch-gates --tier: MANDATORY,skills/**clause ①)Clause-②: no
Thread-read: 5914848913
Serial constraints cleared:
ca5408c62, so the served form is onmain.1d202453.skills/objectstack-query/**orquery-syntax.mdx(every open PR's file list, read at this claim).origin/main:SKILL.md: 399 lines, 4055 / 5552 tokens.rules/filters.md: 214 lines, 2149 / 2149 tokens (headroom 0), so that section's rewrite must pay for itself in the file.Generated by Claude Code
objectstack-fleet commentedon Sep 30, 2026
os-dev-report
{ "issue": 20888, "status": "done", "branch": "claude/issue-20888-query-skill-relation-served", "pr": "https://github.com/objectstack-ai/objectstack/pull/20902", "head_sha": "b73023a347448c931035a76a469b699593575309", "session": "session_01KTZmMfzVzjNvyaLyQ8mHvg — mode:subagent, the parent's id; the relay resolved the same id from the container on both writes", "premise_still_valid": true, "summary": "Draft PR #20902 (Tier H, skills/**) rewrites the sites PR #20811 (1d202453) left stating the refusal: skills/objectstack-query/SKILL.md :76 (Removed-key row query.joins), :189-:191 (Filtering by a related record), :331 (the search paragraph's filter sentence), :340 (Cross-Object row), and rules/filters.md ## Relation Filters. They now state that { relation: { field: value } } is served in where since PR #20872 (ca5408c62), read as the caller, with the five limits from the code — one level (admitRelationCondition, INVALID_FILTER / 400), forward only (the reverse form is not served), where only (an aggregation's filter and having refuse it, relationWords, INVALID_FILTER / 400), the 1000-id cap (RELATION_FILTER_ID_CAP, relationFilterCapError, INVALID_FILTER / 400, never truncated), the caller's permissions (PERMISSION_DENIED / 403, data-nested-relation-permission.test.ts) — and the two-step route kept for past the cap and for a parent by its children. Nothing is said about analytics (#20887 open). The filters.md rewrite is paid for in-file by deleting its ## Logical Operators section (both rules have a home in SKILL.md and the file's first Common Mistake); the file lands at 2148 / 2149 tokens. The sentences for #20876 to quote are in the PR body under '## Sentences for #20876 to quote'. Re-read and left as they are: SKILL.md :88, :326-:327 and :342 (search never traverses — searchFields names are judged exactly at the ingress), :341 (the reverse form). The landed sibling wording (data-engine.mdx, the three 20802 changesets, the FilterCondition docblock form 4) matches the code; the skill contradicts none of it.", "files_changed": [ "skills/objectstack-query/SKILL.md", "skills/objectstack-query/rules/filters.md", "scripts/role-word-baseline.json" ], "line_budget": { "budget": "net +4 lines at most across skills/**, every per-file token ceiling held", "actual_skills_lines": -28, "SKILL.md": "399 → 400 lines, 4055 → 4109 tokens (ceiling 5552)", "rules/filters.md": "214 → 185 lines, 2149 → 2148 tokens (ceiling 2149, headroom was 0)", "package_skills/objectstack-query": "6 files, 1145 → 1117 lines, 10527 → 10580 tokens", "catalog_skills/**": "13375 → 13347 lines", "rewrap_of_untouched_lines": "none", "ceiling_rows_moved": 0, "deletions_with_homes": "rules/filters.md ## Logical Operators (41 lines, 579 bytes): AND (implicit) / explicit $and → SKILL.md :173-:181 and filters.md Common Mistake 1; OR / $in equivalent → SKILL.md :170-:171, :123, :128 and the Operator Reference $in row" }, "gates": { "head": "b73023a34", "derivation": "node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack (no paths; 3 path(s) vs merge base 00a92e18d): 31 commands — the dispatch's list plus six families the baseline row adds under scripts/**: check-scripts-symbol-anchors (+ --self-test), check:bash32-floor, check:cli-command-ids, check:entry-guard, check:parse-guard, check:pnpm-filter-targets", "reconcile": "--ran: 31 derived famil(ies) accounted for — 31 run, 0 NOT-MEASURED (a DERIVED zero — all 31 recorded an exit code and none of them is 3)", "exit_codes": "all 31 exit 0 (captured by redirect before any pipe): check-skills-token-ratchet 0 (54 authored bundle file(s) within their ceilings) and --self-test 0 (65 cases); check:role-word 0 after --update (Ledger: 44 baselined file(s) still carrying it (117 occurrence(s))); check:corpus-claim-drift 0; check:skill-identifier-liveness 0; check:doc-authoring 0; check:nul-bytes 0; check:skill-compatibility 0; check:skill-frame-sync 0; check:agent-test-spelling 0; check:cross-package-test-inputs 0; check:driver-memory-census 0; check:gitlink-declared 0; check:pm-governed-merges 0 (the --self-test as the script spells it); check:refd-timer-probe 0; check:watch-hint-literal 0; check-ci-filter-parity 0 (+ --self-test 0); check-closing-keyword-parity 0 (+ --self-test 0); check-comment-mask-corpus 0 (7664 files, 0 disagree); check-doc-route-spelling --advisory 0 (+ --self-test 0); check-scripts-symbol-anchors 0 (+ --self-test 0); check:bash32-floor 0; check:cli-command-ids 0; check:entry-guard 0; check:parse-guard 0; check:pnpm-filter-targets 0; lint check:doc-formula-expressions 0 (22 record-scoped formula example(s) across 458 files / 1380 TS blocks judged clean; @objectstack/lint... built first under os-verify-lock.sh, VERDICT command-exit 0, 90s); spec check:skill-docs 0 (Skill docs in sync); spec check:skill-refs 0 (9 generated files in sync). check:skill-examples not applicable: 0 os:check markers in the skill.", "rerun_after_final_commit": "check-skills-token-ratchet and check:role-word re-run at b73023a34, both exit 0 with the verdicts quoted above", "ci": "in_progress — not waited on (card terms; the PM reads CI convergence)" }, "tests": "Pins of the served form run first-hand under os-verify-lock.sh (OS_VERIFY_LOCK_SLOT=issue-20888), each package's dependency closure built first (@objectstack/objectql^... VERDICT command-exit 0 118s; @objectstack/rest^... in the same lock hold as its tests, VERDICT command-exit 0 179s): pnpm --filter @objectstack/objectql exec vitest run --maxWorkers=2 src/engine-nested-relation-lowering.test.ts → 'Test Files 1 passed (1), Tests 13 passed (13)' (VERDICT command-exit 0); pnpm --filter @objectstack/rest exec vitest run --maxWorkers=2 src/data-nested-relation-permission.test.ts src/data-nested-object-door.test.ts → 'Test Files 2 passed (2), Tests 9 passed | 12 skipped (21)' — the 12 are the PostgreSQL and MySQL cells of data-nested-object-door.test.ts, named skips (OS_TEST_POSTGRES_URL / OS_TEST_MYSQL_URL unset); the memory and SQLite cells and the 403 permission pin ran. The first objectql attempt before the closure build failed to resolve @objectstack/core's entry (a prerequisite, not a red; re-run after the build). No package is touched, so no package build or test suite is owed beyond these. Ablation: none — a docs-only diff, no gate or test asserts these sentences. NOT MEASURED: none.", "mcp_calls": "0 — no MCP GitHub tool was called; reads were unauthenticated public REST GETs (the card, its comments, ruling 5907789183, #20876, #20887, PR #20902 read-back)", "api_writes": "2 — both through scripts/pm/fleet-write/dispatch.mjs (the relay, landing as objectstack-fleet[bot], session session_01KTZmMfzVzjNvyaLyQ8mHvg): pr_create → POST /repos/objectstack-ai/objectstack/pulls (#20902, draft, read back 15299 / 15299 bytes identical); comment → POST /repos/objectstack-ai/objectstack/issues/20888/comments (this report). No label write, no PR assignee write, no PR body PATCH. git pushes (not REST): 3 (the empty-branch probe, WIP e13345bdb, b73023a34).", "deviations": [ "scripts/role-word-baseline.json: one row (skills/objectstack-query/rules/filters.md 7 → 2), outside the claim's declared file surface; the gate's own ratchet-down prescription after the ## Logical Operators deletion removed five role: literals — the same shape as PR #20811's b893787b. Declared in the PR body.", "The first WIP commit (e13345bdb) was authored with the fleet bot's name and noreply address passed inline (git -c), the second (b73023a34) with the container's default identity; both carry the model-free trailer pair and the pre-push hook accepted both. Cosmetic under the shared identity; noted for the record.", "check:doc-formula-expressions and the spec skill gates read built dist: @objectstack/lint... (spec, formula, sdui-parser, lint) was built under the lock before running them, as the dispatch directed." ], "labels": "none written (card terms): skip-changeset and the PR assignee are the seat's. Measurement for skip-changeset: 82 tracked package.json, 69 with files[], 0 entries naming skills. GitHub's size-labeler applied size/s on its own.", "sentences_for_20876": "PR #20902 body, section '## Sentences for #20876 to quote' — rules/filters.md ## Relation Filters verbatim at b73023a34", "open_questions": [], "out_of_scope_findings": [] }Generated by Claude Code
objectstack-fleet commentedon Sep 30, 2026
ACCEPT — PR #20902 @
b73023a3(skills seat 1,session_01KTZmMfzVzjNvyaLyQ8mHvg, 2026-09-30T17:21Z)Implemented-by:
claude/issue-20888-query-skill-relation-served(dev report 5915796891). Reviewed-by:session_01KTZmMfzVzjNvyaLyQ8mHvg.main.Fixes #20888;Clause-②: noat a line start.skills/objectstack-query/SKILL.md(+7 / −6),rules/filters.md(+18 / −47), and one row ofscripts/role-word-baseline.json.skills/**).{ relation: { field: value } }is served inwhere, read as the caller: thequery.joinsrow, "Filtering by a related record", thesearchparagraph's filter sentence, and the Cross-Object row.rules/filters.md## Relation Filtersleads with the served form and states five limits: one level; forward only;whereonly; 1000 ids, refused past that; the caller's permissions, 403 on an unreadable field. It keeps the two-step route for past the cap and for a parent by its children.domain:services): the cube read and the analytics read scope answer{ relation: { field: value } }as the engine seam now serves it — as the caller, capped, one answer on every face #20887 is open).## Logical Operatorsinrules/filters.mdis deleted to pay for the rewrite. Its rules keep a home:SKILL.md### Logical Operators($and,$or,$not) and the file's first Common Mistake (implicit AND). The sentences for [finding] docs: query-syntax.mdx "Filtering Across Relationships" says SqlDriver compiles a nested relation object and emits a dotted key to Knex — both are refused at the engine before any driver since 4b4ee88f #20876 to quote are in the PR body.CONTRACT_REVIEW_TIERby an isolated subagent (the transcript servedclaude-fable-5-1) and adopted by the seat.where" holds on every door the skill's reader reaches: the engine verbs, the REST data query, and anexpand'swhere.main(data-engine.mdx, the three20802changesets, theFilterConditiondocblock).{ order: { customer: 'c_1' } }) is served. Read literally, the sentence withholds a served form but never teaches a refused one, andrules/filters.mdhas 1 token of headroom. Noted, not filed: it rides the next PR that edits that section.SKILL.md:88's rules index still lists "logical combinations" forrules/filters.md. The file keeps only the sibling-keys-are-AND /$orCommon Mistake. Noted, not filed, same carrier.compatibility: Requires @objectstack/spec 17.xis unchanged, while the text statesmain's engine; the published 17.x engine still refuses the form. That is the ruling's sequencing (「20802 同意」: the skill "is updated in the same round"), not this card's. Named in the 速读.skills/**net −28 lines against a +4 budget.SKILL.md4109 / 5552 tokens;rules/filters.md2148 / 2149. No ceiling moved and nothing re-wrapped.skip-changeseton the PR (0files[]entries namingskillsorscripts); assigneeos-warren.b73023a3. 24successand 11 path-filteredskipped, nothing else. The record readLint & Repo Gatesasin_progress; it has since completedsuccess.Check Changesetransuccessafter the label.Landing: Tier H. The PR stays draft for an authorized APPROVED review. In this same act the seat posts the final 速读 on the PR, adds
needs-user-decision, and requests review fromos-zhuangandhotlong.Generated by Claude Code
objectstack-fleet commentedon Sep 30, 2026
Closed
completed— skills seat 1,session_01KTZmMfzVzjNvyaLyQ8mHvg, 2026-09-30T23:35Z.Delivered by PR #20902, MERGED 2026-09-30T23:34Z through the merge queue as
4957ee5ef.origin/main:skills/objectstack-querynow teaches that{ relation: { field: value } }beneath a lookup is served inwhere(the engine reads the related object as the caller and matches its ids), with its limits — one level, forward only,whereonly, at most 1000 related ids refused past that, the caller's permissions — and keeps the two-step route for past the cap and for a parent by its children.SKILL.md's Removed-key row, the relation paragraph, thesearchpointer and the Cross-Object row say the same;rules/filters.md## Relation Filtersis the rule. The sentences for [finding] docs: query-syntax.mdx "Filtering Across Relationships" says SqlDriver compiles a nested relation object and emits a dotted key to Knex — both are refused at the engine before any driver since 4b4ee88f #20876 (query-syntax.mdx) to quote are in the PR body under## Sentences for #20876 to quote.skills/**). Contract review PASS 5916062797 onb73023a34(isolatedclaude-fable-5-1subagent); ACCEPT 5916224626; 速读 5916233855; authorized APPROVED review 5372959546 byos-zhuang, who flipped it ready and armed auto-merge; the seat clearedneeds-user-decision. CI green on the head (24success, 15 path-filteredskipped, the queue guard green in the merge group).rules/filters.md"no relation or dotted key inside" withholds the served dotted spelling;SKILL.md's rules index line still says "logical combinations"; the compatibility line reads 17.x while the ruling's sequencing ismain.Fixes #20888closed this card at merge. The seat stripspm:dispatchedand the assignee here.Generated by Claude Code
docs(query-syntax): Filtering Across Relationships states the served …
docs(skills): objectstack-query states the served nested-relation fil…