Repository navigation
Seam: IObjectQLEngine gains an optional judge-only filter-admission method, run by the engine's own admission pipeline without executing (objectstack#19995 ruling C) #20157
Description
Seam: spec IObjectQLEngine (one optional judge-only member) → runtime packages/objectql admission pipeline; consumers service-analytics ObjectQLStrategy.withReadScope / resolveFkAttr (#19995) and plugin-security policy admission (the second consumer card filed with this ruling)
Filed by the director seat from the maintainer's ruling on #19995 (batch #225 item 3, letter C, 「同意」). Reader who acts: the domain:spec seat, dispatched vertically (the contract member in packages/spec, the implementation in packages/objectql). area:* is left for triage. Consumers wait on this card with Blocked-by:: #19995 (the analytics ObjectQL face pre-judges the read scope) and the authoring-time admission card filed beside this one. Dedupe: REST list of open cards in this repository read for judge-only, pre-judge, filter admission method in titles → none; #19995's own dedupe (42 semantic hits, none on the engine face) stands.
What the ruling settles
The engine becomes the single judge of 「can this filter run against this object」 for callers that must know before composing a policy scope into a query. Today the engine's admission doors (text-operator-declared-type-door.ts, temporal-comparand-door.ts, the FILTER_TOKEN_UNKNOWN resolver, the virtual-field and lookup-path refusals) run only inside execution; their walks are not reachable through the package exports (. and ./core only: git grep -c -E "findTextOperatorOverNonTextField|findUninterpretableTemporalComparand|assertFilterIsMaterializable" origin/main -- packages/objectql/src/index.ts packages/objectql/src/core.ts → 0, control export .*ObjectQL → 5); @objectstack/service-analytics depends on the engine only optionally (src/plugin.ts:278 optionalDependencies) and only at dev time (package.json).
Work
packages/spec/src/contracts/objectql-engine.ts: one OPTIONAL member onIObjectQLEnginethat judges awhere(aFilterCondition) against a named object and returns either ok or the same diagnostic the engine would raise at execution (code and message), without executing anything. Name and exact signature are the seat's and the contract review's to settle; the docblock states these semantics.packages/objectql: the implementation runs the engine's own admission pipeline (every door execution runs, in the same order) and stops before any driver call. ⛔ Not a second copy of the walks: the doors are factored so that execution and judgement call the same functions. Premise to verify first: the admission pipeline can run without a driver or data; if a door needs either, stop and report the fork.- Pins: for each class security: the analytics ObjectQL execute face answers a row-level read scope it cannot run with
INVALID_FILTER/ 400 whose message echoes the policy's field name and comparands — the disclosure #5367 closed for the native / echo faces #19995 lists (a text operator over a non-text field, an uninterpretable temporal comparand, an unknown filter token, a filter on a virtual field, a dotted path through a lookup) the judge returns the door's diagnostic and execution raises the same one; a runnable filter returns ok; nothing is executed (a driver spy sees no call). - Changeset
minorfor@objectstack/spec(a new optional contract member) and@objectstack/objectql.Clause-②: yes— a contract review before enqueue.
Governing text: the #5367 ruling as recorded in packages/services/service-analytics/src/read-scope-sql.ts (a read-scope refusal is never a 4xx; the policy's fields and comparands never reach an error body); ADR-0058 D2 (a compile-surface predicate the compiler cannot lower is an authoring-time compile error); ADR-0021 D-C (the analytics read scope is enforced by the engine); the domain:engine decision on #20020 (comment 5823452098 on #19995: the doors keep their diagnostics for CRUD callers; the analytics face pre-judges the scope alone).
Mainstream shape this follows: PostgreSQL validates a policy at CREATE POLICY and evaluates security quals before non-leakproof user quals; SQL Server binds security predicates at policy creation; Oracle VPD answers a broken predicate with a generic error and writes the detail to the trace; Hasura validates row permissions when metadata is applied. A judge-only entry point is the platform's PREPARE / EXPLAIN for filter admission.
Dedupe words: engine judge-only filter admission method · IObjectQLEngine optional member pre-judge where · read scope pre-judge withReadScope 19995
Director seat · session_01AsCNgFBs8HCjwhyHQsFbx3 · filed from the ruling on #19995
Generated by Claude Code
Activity
objectstack-fleet commentedon Sep 27, 2026
Serial note from
domain:specseat 4 (session_01CiCTczDo7tGhafXjf61dUJ, seat post #18917), 2026-09-27T04:17Z. ⛔ Not a claim; the state is unchanged (pm:queue).Reached in this seat's round 1 take order (a contract-face card) and passed over because of a hot region.
domain:engineclaim on objectql + REST: the per-aggregationfilterstill lacks four ofwhere's doors — a bad date, anaddDaysnumeric pair, an undeclared{ $field }and an unknown key answer200with every count 0 #20148 (session_01Bvd69VPa6puiNzzPUroDBx, claimed today) is editing the same door invocation inpackages/objectql/src/engine.ts. It runswhere's remaining doors (the temporal-comparand door, the declared-field check on a{ $field }referent, theaddDaysclass rule) on eachaggregations[i].filterinsideObjectQL.aggregate. Two devs would be refactoring one door list at the same time.filterstill lacks four ofwhere's doors — a bad date, anaddDaysnumeric pair, an undeclared{ $field }and an unknown key answer200with every count 0 #20148 extends admission to a new filter slot; this card adds a non-executing judge overwhere. Same file and same doors, but a different defect shape.filterstill lacks four ofwhere's doors — a bad date, anaddDaysnumeric pair, an undeclared{ $field }and an unknown key answer200with every count 0 #20148 lands: read objectql + REST: the per-aggregationfilterstill lacks four ofwhere's doors — a bad date, anaddDaysnumeric pair, an undeclared{ $field }and an unknown key answer200with every count 0 #20148's merged diff first. If it factors a shared "run everywheredoor" function, the judge calls that function, ⛔ not a second copy. Re-measure the card's premise that the door walks are unreachable through the packageexportson the newmain.packages/objectql(domain:engine). The claim must declare that surface and name the engine seat's live claims onengine.tsin its serial line.Readings:
git greponorigin/main2bbebf5— the door helpers are imported atengine.ts:45and called at:980,:1074and, in the aggregation loop,:16137. The #20148 claim's file surface is its ownClaim:comment.objectstack-fleet commentedon Sep 27, 2026
Claim: PM loop round 1
Session:
session_01CiCTczDo7tGhafXjf61dUJAccount:
os-sales(the seat's linked user asGET /useranswers it; the card's assignee)Branch:
claude/issue-20157-engine-judge-filter-admissionWorktree:
objectstack-issue-20157Domain:
domain:specSeat:
domain:spec#4(seat post #18917)File surface: the contract member in
packages/spec/src/contracts/objectql-engine.ts(one OPTIONAL judge-only member onIObjectQLEngine, plus its docblock) and its tests; the implementation inpackages/objectql/src/— thewhereadmission doors refactored so that execution and the judge call the same functions (engine.tsin itsfind/where-admission path and the module-level door functions;filter-comparand-shape.ts,temporal-comparand-door.tsand the text-operator door file only where the refactor must touch them), with their tests; the generated artefacts that follow (api-surface, export-origins), regenerated and never hand-edited;.changeset/. Cross-lane (packages/objectqlisdomain:engine): declared here because this is aSeam:card that goes vertically to the spec seat. ⛔ NotObjectQL.aggregate's per-aggregation filter loop, which the livedomain:engineclaim on #20176 holds. (stop on breach; explain in the report)Container & model:
M/L,mode:subagent,model: default judgment tier(the default slot is taken: a new public contract member plus a refactor that must keep execution byte-identical). The review of a widened contract runs atCONTRACT_REVIEW_TIERbefore enqueue.Clause-②: yes
Thread-read: 5852563458
Serial constraints cleared: read at 2026-09-27T07:25Z. The serial predecessor #20148 landed as
cfe2387a3(PR #20174:engine.ts+29 lines,having-filter.ts+284). It added no shared "run everywheredoor" function, so this card builds one; it does not reuse one. Open-PR census (15 open PRs besides the release PR, 303 file rows, lit control 158packages/rows): none touchespackages/objectql/src/engine.ts, the door files orcontracts/objectql-engine.ts. In-flight claims:domain:engine#20176 holds the per-aggregation loop ofengine.ts, a different region. Downstream: #19995 and #20158 carryBlocked-by: #20157.Ruling carried: #19995's ruling C (batch #225 item 3, 「同意」), as the card body records it. The engine becomes the single judge of 「can this filter run against this object」 for callers that must know before composing a policy scope. Its Governing text is also in the body.
objectstack-fleet commentedon Sep 27, 2026
os-dev-report
{
"issue": 20157,
"status": "done",
"branch": "claude/issue-20157-engine-judge-filter-admission",
"pr": "#20213",
"session": "session_01CiCTczDo7tGhafXjf61dUJ — the harness-stamped id in this run's commit trailers (Claude-Session line); a subagent run carries the parent's",
"premise_still_valid": true,
"summary": "IObjectQLEngine gains one OPTIONAL member, judgeFilter(objectName, where, { operation?, context? }), which is synchronous and returns { ok: true } | { ok: false, code, status, message }. ObjectQL implements it through judgeWhereAdmission. That function calls the two stage functions every where-taking verb already calls, in the same order: lowerWhereFilterArray (shape gate, list-comparand shape, materializable dotted then virtual, text operator, temporal, comparand type; array form through parseFilterAST) and then resolveWhereFilterTokens. resolveWhereFilterTokens is now the single spelling behind ObjectQL.resolveWhereTokens and ObjectQL.withResolvedWhere. The judge stops before getDriver and makes no driver, hook or middleware call. Premise 1 held door by door: each door reads only the where value, the registry field map and, for placeholders, the context. PM assumption 2 held: the where sequence is identical on all six verbs, and what differs by verb (getDriver position, option folding, orderBy/projection, per-aggregation/having) is not a where door. The aggregate per-aggregation loop is untouched. Assumption 3, placeholder semantics: the judge expands placeholders against the supplied context. Evidence: #19995's consumer already calls assertReadScopePlaceholdersResolvable(scope, objectName, ctx.context), which runs the engine resolver against the context it forwards to executeAggregate. An unanswerable placeholder answers FILTER_TOKEN_UNRESOLVED/400 and is never read as null (pinned). Assumption 4 held: objectql exports are unchanged ('.' and './core'). The judge is reached through the engine instance; spec/contracts gains two type exports (EngineFilterJudgement, EngineFilterJudgementOptions), with api-surface and export-origins regenerated. Consumers are not wired. The changeset is minor for spec and objectql, with Clause-②: yes in the changeset and on its own line in the PR body.",
"tests": "Final head c797375. New pins: objectql src/engine-judge-filter.test.ts 43/43 (5 classes x 6 verbs; judge and execution agree on code+status+full message; ok path; driver spy zero calls, getDriver not called, hook+middleware not run, each with a positive control; two-defect order; placeholder context; unregistered object). Spec src/contracts/objectql-engine.test.ts 12/12 (5 new type pins). Full suites at 30dadfb (only delta to c797375 is one docblock's wording): objectql 317 files/5671 passed, with existing door tests unmodified; spec 541/15901 passed, 2 todo. Consumers (downstream importers of IObjectQLEngine) at 30dadfb: service-analytics 128/3017; plugin-security 136/2740; metadata-protocol 189 passed+3 skipped files/2700 passed+19 skipped; core 53/1358; rest 197/3339+1 skipped; runtime 279/3906+1 skipped; platform-objects 55/911; plugin-approvals 51/791; plugin-pinyin-search 2/21; plugin-sharing 37/913; trigger-record-change 10/101; service-datasource 34/693; plugin-hono-server 27/324; plugin-auth 114/2440; cloud-connection 30/397; cli --project unit 225/3192 (integration tier declared to CI); dogfood typecheck green plus its 5 IObjectQLEngine importers 5/38 (full dogfood declared to CI). Typecheck green: spec, objectql, service-analytics, plugin-security, metadata-protocol, core, rest, runtime. Ablation via scripts/ablation-replace.mjs from committed state, mutation and restore both proven on disk (anchor 1→0, blob changed; restore blob==HEAD, git diff HEAD empty). Deleting the stage-2 call: 7 failed/36 passed, as predicted (unknown-placeholder on 6 verbs plus the unresolved-context pin). Replacing stage 1 with { where }: 30 failed/13 passed. Reverse type verification: operation 'insert' gave TS2322 against the rebuilt spec dist; control leg 0 errors in both new test files under tsconfig.test.json. Lint, narrowed and proven: eslint --format json over the 4 changed .ts files gave 4 files, 0 errors, 0 warnings; --print-config returns a config for all 4 (none ignored); no parserOptions.project, so not type-aware, and the diff cannot move untouched files' verdicts.",
"mcp_calls": "0 — no MCP GitHub tool was called",
"api_writes": "3 REST writes, all through scripts/pm tools over the fleet-write relay (each one POST /repos/objectstack-ai/objectstack/dispatches executed as objectstack-fleet[bot]): (1) pr_create → POST /repos/objectstack-ai/objectstack/pulls (draft, #20213); (2) label-write --assign os-sales → POST /repos//issues/20213/assignees; (3) this os-dev-report → POST /repos//issues/20157/comments. git push is not REST (7 pushes of the one branch, the first an empty-branch probe).",
"open_questions": [
{
"question": "For #20158 (authoring-time admission): the judge expands placeholders against the supplied context, so a policy that contains a context placeholder such as {current_user_id}, judged with no context at save time, answers FILTER_TOKEN_UNRESOLVED. Keep that, or add a mode that refuses only unknown placeholders?",
"options": [
"A keep today's semantics: #20158 passes a representative context, or treats FILTER_TOKEN_UNRESOLVED at save time as its own verdict",
"B add an option (e.g. placeholders: 'classify') that refuses FILTER_TOKEN_UNKNOWN but admits known context placeholders without a value"
],
"recommendation": "A. RLS policies reach #20158 as CEL lowered to a FilterCondition with concrete values, so no measured policy carries a placeholder today. B widens a just-published contract with no call site behind it (startup-stage narrowness), and A is what execution answers. This is not blocking; the contract review may settle it."
}
],
"out_of_scope_findings": [
"carrier: domain:engine seat (the #20148 lane, which measured it in cfe2387's own table) · noted, not filed: where's { $field } declared-referent and addDays class rule are enforced only by driver-sql (driver-memory answers a count), while the per-aggregation filter now refuses them at the engine. The judge is bounded by the engine's own admission, so it answers ok for these; this is stated in the contract docblock and the PR's Acceptance notes. It is a read of a landed measurement, not a new reproduction by this run."
],
"gates": {
"head": "c797375ab",
"derived": 89,
"ran": 89,
"not_measured": 0,
"unrun": 0,
"reconcile": "node scripts/pm/dispatch-gates.mjs --ran ran.list --repo objectstack-ai/objectstack → exit 0 (89 run, 0 NOT-MEASURED, 0 UNRUN)",
"commands": [
"node scripts/check-adr-0087-registration.mjs --base origin/main :: exit 0",
"node scripts/check-adr-0087-registration.mjs --self-test :: exit 0",
"node scripts/check-changeset-no-major.mjs --base origin/main :: exit 0",
"node scripts/check-changeset-no-major.mjs --self-test :: exit 0",
"node scripts/check-ci-filter-parity.mjs :: exit 0",
"node scripts/check-closing-keyword-parity.mjs :: exit 0",
"node scripts/check-closing-keyword-parity.mjs --self-test :: exit 0",
"node scripts/check-comment-mask-adoption.mjs :: exit 0",
"node scripts/check-comment-mask-adoption.mjs --self-test :: exit 0",
"node scripts/check-comment-mask-corpus.mjs :: exit 0",
"node scripts/check-dev-prereqs.mjs --self-test :: exit 0",
"node scripts/check-empty-changeset.mjs --base origin/main :: exit 0",
"node scripts/check-empty-changeset.mjs --self-test :: exit 0",
"node scripts/check-engine-split-ratio.mjs --days 90 :: exit 0",
"node scripts/check-engine-split-ratio.mjs --self-test :: exit 0",
"node scripts/check-keyed-text-bounds.mjs :: exit 0",
"node scripts/check-keyed-text-bounds.mjs --self-test :: exit 0",
"node scripts/check-platform-object-tenancy-census.mjs :: exit 0",
"node scripts/check-platform-object-tenancy-census.mjs --self-test :: exit 0",
"node scripts/check-plugin-teardown-shape.mjs :: exit 0",
"node scripts/check-plugin-teardown-shape.mjs --self-test :: exit 0",
"node scripts/check-registry-log-declared.mjs :: exit 0",
"node scripts/check-registry-log-declared.mjs --self-test :: exit 0",
"node scripts/check-rest-log-spy-declared.mjs :: exit 0",
"node scripts/check-rest-log-spy-declared.mjs --self-test :: exit 0",
"node scripts/check-spec-docblock-symbol-anchors.mjs :: exit 0",
"node scripts/check-spec-docblock-symbol-anchors.mjs --self-test :: exit 0",
"node scripts/check-system-context-census.mjs :: exit 0",
"node scripts/check-system-context-census.mjs --self-test :: exit 0",
"node scripts/check-undeclared-dep-imports.mjs :: exit 0",
"node scripts/check-undeclared-dep-imports.mjs --self-test :: exit 0",
"node scripts/docs-audit/check-affected-docs.mjs :: exit 0",
"node scripts/docs-audit/check-drift-comment.mjs :: exit 0",
"node scripts/pm/release-rehearsal-clone.mjs --self-test :: exit 0",
"pnpm --filter @objectstack/lint run check:doc-formula-expressions :: exit 0",
"pnpm --filter @objectstack/spec run check:api-surface :: exit 0",
"pnpm --filter @objectstack/spec run check:authorable-surface :: exit 0",
"pnpm --filter @objectstack/spec run check:browser-reachable-entries :: exit 0",
"pnpm --filter @objectstack/spec run check:docs :: exit 0",
"pnpm --filter @objectstack/spec run check:dual-source-exports :: exit 0",
"pnpm --filter @objectstack/spec run check:duration-unit-keys :: exit 0",
"pnpm --filter @objectstack/spec run check:empty-state :: exit 0",
"pnpm --filter @objectstack/spec run check:entry-nameability :: exit 0",
"pnpm --filter @objectstack/spec run check:export-origins :: exit 0",
"pnpm --filter @objectstack/spec run check:exported-any :: exit 0",
"pnpm --filter @objectstack/spec run check:generated :: exit 0",
"pnpm --filter @objectstack/spec run check:liveness :: exit 0",
"pnpm --filter @objectstack/spec run check:llms-txt :: exit 0",
"pnpm --filter @objectstack/spec run check:objectui-pin-citations :: exit 0",
"pnpm --filter @objectstack/spec run check:skill-refs :: exit 0",
"pnpm --filter @objectstack/spec run check:strictness-ledger :: exit 0",
"pnpm --filter @objectstack/spec run check:variant-docs :: exit 0",
"pnpm --filter @objectstack/spec run check:yaml-examples :: exit 0",
"pnpm check:changeset-gate-self-tests :: exit 0",
"pnpm check:cross-package-test-inputs :: exit 0",
"pnpm check:dispatcher-error-vocabulary :: exit 0",
"pnpm check:doc-authoring :: exit 0",
"pnpm check:driver-memory-census :: exit 0",
"pnpm check:dts-closure :: exit 0",
"pnpm check:dual-build-cjs-loads :: exit 0",
"pnpm check:durability-log-level :: exit 0",
"pnpm check:engine-double-contract :: exit 0",
"pnpm check:gitlink-declared :: exit 0",
"pnpm check:issue-citations :: exit 0",
"pnpm check:lean-entry-closure :: exit 0",
"pnpm check:logger-receiver-detach :: exit 0",
"pnpm check:merge-driver :: exit 0",
"pnpm check:nul-bytes :: exit 0",
"pnpm check:objectql-double-limit :: exit 0",
"pnpm check:objectui-changeset :: exit 0",
"pnpm check:org-identifier :: exit 0",
"pnpm check:page-declaration-shape :: exit 0",
"pnpm check:pm-changeset-deadline-census :: exit 0",
"pnpm check:pm-prior-rulings :: exit 0",
"pnpm check:pm-widening-tells :: exit 0",
"pnpm check:published-files :: exit 0",
"pnpm check:query-options-erasure :: exit 0",
"pnpm check:refd-timer-probe :: exit 0",
"pnpm check:slot-lookup :: exit 0",
"pnpm check:sourcemap-no-sources-content :: exit 0",
"pnpm check:spec-parsed-alias :: exit 0",
"pnpm check:stack-collection-maps :: exit 0",
"pnpm check:swallow-census-controls :: exit 0",
"pnpm check:test-source-alias :: exit 0",
"pnpm check:tier-file-adoption :: exit 0",
"pnpm check:type-check-coverage :: exit 0",
"pnpm check:type-check-debt :: exit 0",
"pnpm check:watch-hint-literal :: exit 0",
"pnpm check:where-matcher :: exit 0"
]
},
"line_budget": "n/a: no skills/** or ledgered governed file touched; diff +626/-3 over 7 files, under the 5000-line human-merge threshold",
"files_changed": [
".changeset/20157-engine-judge-filter.md",
"packages/objectql/src/engine-judge-filter.test.ts",
"packages/objectql/src/engine.ts",
"packages/spec/api-surface/contracts.json",
"packages/spec/export-origins/contracts.json",
"packages/spec/src/contracts/objectql-engine.test.ts",
"packages/spec/src/contracts/objectql-engine.ts"
],
"ci": "in_progress at report time (32 check runs on c797375: 12 success, 3 skipped, 17 in_progress, none failed); not awaited, per contract",
"deviations": [
"check:type-check-debt's first run rebuilds the whole package closure itself, outside the verify lock, and my runner's 280 s timeout killed it partway. That left driver-mongodb dist without .d.ts, so the second run exited 3 PREREQUISITE NOT MET. I rebuilt the closure under the lock and re-ran the gate under the lock: exit 0, 4 entries at their numbers. ran.list records that exit 0.",
"Two test suites were chained in one call (service-analytics, plugin-security). The call hit the 600 s tool cap and the harness moved it to the background. I waited in the foreground with tail --pid on the lock-holder pid and read both verdicts (green) before going on.",
"The consumer suites were run at 30dadfb, not the final c797375. The only delta is the wording of one docblock in objectql-engine.ts. The gate union and the new pins were re-run at c797375.",
"origin/main was not merged into the branch. It is behind by commits disjoint from this diff's files, and CI's merge ref tests the combination.",
"Attribution: the harness reminder asked for a model-named Co-Authored-By trailer and a different PR footer. Per AGENTS.md (the repo's instruction, which the reminder defers to), commits carry the model-free pair, and the PR body ends with the session-URL footer.",
"Header conflict, surfaced rather than chosen silently: IObjectQLEngine's header said members beyond IDataEngine are REQUIRED, and its evidence bar asks for an existing cross-package call site. The ruling orders an OPTIONAL member ahead of its consumers. The ruling wins; the header is amended to name this one exception and why."
]
}
Generated by Claude Code
objectstack-fleet commentedon Sep 27, 2026
ACCEPT — PR #20213 (2026-09-27T10:33Z)
domain:specseat 4 (session_01CiCTczDo7tGhafXjf61dUJ). This is the review of record for the round claimed in5853787627; the dev report is5854899027.Checklist, read on GitHub:
main, first lineFixes #20157, no other closing keyword. The bare lineClause-②: yesmatches the diff. Assigneeos-sales.packages/spec/src/contracts/objectql-engine.ts(one optional member, two types, a header paragraph naming the one exception) andpackages/objectql/src/engine.ts(+127 / −2).contracts, and a changeset (minorfor@objectstack/specand@objectstack/objectql).5855050723on the PR, at headc797375ab2.resolveWhereFilterTokens, whose body is the same expression. A 102-pair probe (17 cases × 6 verbs, base vs head) matched onok, code, status, message, driver calls and the where the driver saw.getDriver: 0 driver calls on all 102 pairs, with spy pins that have positive controls.null. This is the same answer security: the analytics ObjectQL execute face answers a row-level read scope it cannot run withINVALID_FILTER/ 400 whose message echoes the policy's field name and comparands — the disclosure #5367 closed for the native / echo faces #19995's consumer already computes throughassertReadScopePlaceholdersResolvable.5823452098.c797375ab2: 32 success, 3 skipped (Build Docs, Console Pin Gate, the opt-in tarball smoke; all path-filtered or opt-in). No failures.mergeable_state: clean. A no-drivermerge-treeonto today'smain(585c9af587) exits 0.The dev's open question (placeholder mode for #20158): A, keep today's semantics. No change to this PR.
INVALID_FILTER/ 400 whose message echoes the policy's field name and comparands — the disclosure #5367 closed for the native / echo faces #19995's consumer already computes.classifymode that admits known placeholders without a value) would widen a contract published in this PR, and no call site needs it. Every RLS policy measured today reaches RLS policies are admitted when they are authored: policy save andobjectstack validate/ compile judge the lowered read-scope filter with the engine's judge-only method (objectstack#19995 ruling C, second consumer) #20158 as CEL lowered to concrete values, so none carries a placeholder.objectstack validate/ compile judge the lowered read-scope filter with the engine's judge-only method (objectstack#19995 ruling C, second consumer) #20158's own dispatch measures a real policy that needs B, it reports that as a fork there. It does not add B quietly. A pointer is on RLS policies are admitted when they are authored: policy save andobjectstack validate/ compile judge the lowered read-scope filter with the engine's judge-only method (objectstack#19995 ruling C, second consumer) #20158.Review record, non-blocking items (6), each disposed:
{ $field }referent and theaddDaysrule onwhereare enforced only by driver-sql. They sit below the judge's stated boundary, and the PR's Acceptance notes say so → Acceptance notes; carrier: thedomain:engineseat (the objectql + REST: the per-aggregationfilterstill lacks four ofwhere's doors — a bad date, anaddDaysnumeric pair, an undeclared{ $field }and an unknown key answer200with every count 0 #20148 lane), whose landed measurement it is.mainmoved only in files this PR does not touch; that is inaccurate (e5cf27d65editsengine.ts,4db1bf171edits the two generated JSONs) → dropped — the hunks do not overlap, the merge probe and CI are clean, and the body is not a contract surface.requireFindOnePredicateandexpandSearchOnAst→ dropped — neither is awhererunnability door, and neither reaches a consumer.objectstack validate/ compile judge the lowered read-scope filter with the engine's judge-only method (objectstack#19995 ruling C, second consumer) #20158 question → answered above.Downstream: #20158 carries
Blocked-by: #20157and unlocks when this card closes. It belongs todomain:services, so this seat does not dispatch it. #19995's analytics consumer is that card's own work.Next: ready → auto-merge through the relay → merge queue. This card closes on the merge through
Fixes.objectstack-fleet commentedon Sep 27, 2026
Landed — PR #20213 →
4df101c383(2026-09-27T11:01Z)domain:specseat 4 (session_01CiCTczDo7tGhafXjf61dUJ). PR #20213 merged through the merge queue on 2026-09-27T10:57:18Z, and this card closedcompletedthrough itsFixesline. The readings that confirm the landing:4df101c383has one parent (3875ae6773), is an ancestor oforigin/main, and the queue branch for the PR is gone.git patch-id --stableequals the reviewed diff's (763bd5d68551on both; 7 files, +626 / −3).judgeFilterhas 3 hits inpackages/objectql/src/engine.tsand 5 inpackages/spec/src/contracts/objectql-engine.ts, and 0 in both on the parent.pm:dispatchedand the assignee come off in the same act.Downstream: #20158 (
domain:services) carriesBlocked-by: #20157and is released by the unlock scan; the placeholder-semantics pointer for its dispatcher is5855081099. #19995's analytics consumer is that card's own work.feat(spec,objectql): IObjectQLEngine.judgeFilter — judge a where thro…
fix(service-analytics): judge each read scope with the engine's own a…
test(spec): contracts and conversions test titles state each cited de…