Skip to content

[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

Description

@objectstack-fleet

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.

What happened, in order

  1. [finding] skills/objectstack-query teaches the nested relation filter { relation: { field: value } } as a working form; no data-path driver serves it, and PR #20781 makes the engine refuse it #20782 found the skill teaching the nested relation form as working while the engine refused it (PR fix(objectql)!: a no-operator object beneath a relation, structured-JSON or undeclared id column is refused INVALID_FILTER / 400 on every driver (#20745) #20781).
  2. Ruling 5907789183 on [Decision] v18:查询能否直接按关联记录的字段筛选(例:「客户行业 = 科技」的商机) #20802 (maintainer 「20802 同意」) made v18 serve the form. Its text item: "skills/objectstack-query ([finding] skills/objectstack-query teaches the nested relation filter { relation: { field: value } } as a working form; no data-path driver serves it, and PR #20781 makes the engine refuse it #20782) is updated in the same round."
  3. PR docs(skills): objectstack-query teaches the served route for a related record's column, not the refused nested form #20811 ([finding] skills/objectstack-query teaches the nested relation filter { relation: { field: value } } as a working form; no data-path driver serves it, and PR #20781 makes the engine refuse it #20782) merged at 14:43Z as 1d202453. The skill's six sites now state the engine's refusal (INVALID_FILTER / 400) and the two-step $in route. That was true when written.
  4. PR feat(objectql): serve the nested-relation filter in where — lowered at the engine seam, the related object read as the caller, a loud cap, drivers untouched (#20802) #20872 ([Decision] v18:查询能否直接按关联记录的字段筛选(例:「客户行业 = 科技」的商机) #20802's engine half) merged at 15:19Z as ca5408c62. The engine now serves { relation: { field: value } } in where.
    • One level, forward, as the caller.
    • A multi-valued relation matches any member.
    • Refused past a cap of 1000 ids.
    • 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


Generated by Claude Code

Activity

  1. objectstack-fleet commented on Sep 30, 2026

    @objectstack-fleet
    ContributorAuthor

    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:

    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.

  2. added
    area:apiThe API a customer can call, and integrations — REST, connectors, webhooks, jobs
    documentationImprovements or additions to documentation
    and removed on Sep 30, 2026
  3. objectstack-fleet commented on Sep 30, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 9
    Session: session_01KTZmMfzVzjNvyaLyQ8mHvg
    Account: os-warren (the seat's linked user as GET /user answers it; the card's assignee)
    Branch: claude/issue-20888-query-skill-relation-served
    Worktree: objectstack-issue-20888
    Domain: domain:skills
    Seat: domain:skills#1
    File surface:

    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:


    Generated by Claude Code

  4. objectstack-fleet commented on Sep 30, 2026

    @objectstack-fleet
    ContributorAuthor

    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

  5. objectstack-fleet commented on Sep 30, 2026

    @objectstack-fleet
    ContributorAuthor

    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.

    • Shape.
      • Draft, base main.
      • First body line Fixes #20888; Clause-②: no at a line start.
      • Files: skills/objectstack-query/SKILL.md (+7 / −6), rules/filters.md (+18 / −47), and one row of scripts/role-word-baseline.json.
      • Tier H (skills/**).
    • Diff, read by the seat.
    • 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.
    • Three precision items the reviewer flagged, each judged non-blocking. The seat lands them as they are:
      1. "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.
      2. 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.
      3. 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.


    Generated by Claude Code

  6. objectstack-fleet commented on Sep 30, 2026

    @objectstack-fleet
    ContributorAuthor

    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.

    • 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.

    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:apiThe API a customer can call, and integrations — REST, connectors, webhooks, jobsdocumentationImprovements or additions to documentationdomain:skillspriority:p2Medium: important, M3

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions