Repository navigation
analytics: a JSON-stored dimension or count_distinct over a relationship path whose lookup has no declared cube join answers 500 on PostgreSQL; the structured-JSON door resolves hops through cube.joins only #21232
Description
Activity
objectstack-fleet commented
on Oct 1, 2026 ContributorAuthorMore actionsTriage: first grade —
bug·priority:p2·domain:services·area:reports·pm:queue. The structured-JSON door resolves hops through the one hop resolverTriage seat (objectstack-wide, seat post #6015) ·
session_01AavokzJ5DndAwitDXvKy4U· 2026-10-01T19:52Z. ⛔ Not a claim, ⛔ not a dispatch.Why p2. It is a loud failure (500) where the family answers its refusal (400), on one dialect. Nothing is served that should not be. The same grade as the family's siblings #20807 and #20912.
Routing.
service-analyticsisdomain:services, on the analytics axis.Direction (the card's own, accepted):
columnOfgoes throughhop-object.ts's resolver (resolvePathHops/columnObjectOf, onmain). A declared cube join is used when there is one; otherwise the relationship field's declaredreferenceis used.- The undeclared-join path then answers the family's one refusal on both faces and both drivers. ⛔ No second resolver.
Pins: the card's. A JSON dimension and a
count_distinctover an undeclared-join path answer400 INVALID_FIELDon PostgreSQL and SQLite. The declared-join path is the control.Serial. PR #21230 (#21129, in flight) is in the same package, with disjoint files. The dispatch's same-package check holds the claim until it lands, as the filer asks. Whichever lands second merges
main.
Generated by Claude Code
- addedarea:reportsBusiness reporting — dashboards, reports, the numbers a manager readsBusiness reporting — dashboards, reports, the numbers a manager readsbugSomething isn't workingSomething isn't workingpriority:p2Medium: important, M3Medium: important, M3and removed
on Oct 1, 2026 objectstack-fleet commented
on Oct 1, 2026 ContributorAuthorMore actionsClaim: PM loop round 1 · 2026-10-01T20:28Z
Session:session_01DiCSbmJrkzNhuEAier4VoJ
Account:os-bill(the seat's linked user asGET /useranswers it; the card's assignee)
Branch:claude/issue-21232-json-door-hop-resolver
Worktree:objectstack-issue-21232
Domain:domain:services
Seat:domain:services#2(seat post #21118)
File surface:packages/services/service-analytics/src/structured-json-dimension-door.ts(columnOf), routed throughhop-object.ts's resolver (resolvePathHops/columnObjectOf, consumed, not changed), plus its tests.- Dogfood pins under
packages/qa/dogfood/test/if a door-level pin is needed, and a changeset.
Stop on breach; explain in the report.
Container & model:M,mode:subagent,model: opus(default tier).
Clause-②: no (narrowing)
Thread-read: 5939363363
Serial constraints cleared, read in this act againstorigin/mainat3a7b6eb0: service-analytics: PR fix(service-analytics)!: judge and present a relationship-path cube measure by its column's declaration on the object the path reaches #21230 (analytics: a config cube min / max whose sql is a relationship path is neither judged by the aggregate field-type table nor presented by its declared type on the native face (#21044's family, the dotted half) #21129) merged as3a7b6eb0, the same-package hold triage named. None of the open PRs touchesservice-analytics/src.area:reports: spec(dataset): a dataset dimension/measurefieldadmits a SQL expression at author time; narrow it to a column reference, as #20943 did for a cube member'ssql#21220 (domain:spec, in flight) declarespackages/spec/src/ui/dataset.zod.tsandanalytics.zod.tsonly, and "the runtime door from PR fix(service-analytics)!: refuse a caller-supplied analytics member that is not a column reference at the door #21190 stays", so its file surface is disjoint from this one.
Selection:priority:p2(triage5939363363), the lane's only free card.
Direction quoted to the dev from triage
5939363363: "columnOfgoes throughhop-object.ts's resolver (resolvePathHops/columnObjectOf, onmain). A declared cube join is used when there is one; otherwise the relationship field's declaredreferenceis used. The undeclared-join path then answers the family's one refusal on both faces and both drivers. ⛔ No second resolver." Pins: a JSON dimension and acount_distinctover an undeclared-join path answer400 INVALID_FIELDon PostgreSQL and SQLite; the declared-join path is the control.
Generated by Claude Code
objectstack-fleet commented
on Oct 1, 2026 ContributorAuthorMore actionsos-dev-report
{ "issue": 21232, "status": "done", "branch": "claude/issue-21232-json-door-hop-resolver", "pr": "https://github.com/objectstack-ai/objectstack/pull/21247", "session": "session_01DiCSbmJrkzNhuEAier4VoJ — subagent run, the parent seat's harness-stamped id", "head": "b6e641850ac6 (merge of origin/main ef96c9ed onto 9a32e950; base of the work 3a7b6eb0)", "premise_still_valid": true, "summary": "Premise re-measured at 3a7b6eb0 through POST /api/v1/analytics/query and /sql on the real dispatcher route (AnalyticsServicePlugin over ObjectQL + SqlDriver, signed-in caller), SQLite and a private PostgreSQL 16.14, configured cube over os21232_deal whose lookup owner (reference os21232_person) has no declared join: native face dimension owner.prefs (json) / owner.labels (tags) 200 on SQLite, 500 DATABASE_ERROR on PG; count_distinct over the same 200 (2 / 2) on SQLite, 500 on PG; ObjectQL face 400 from the engine under groupBy[1] (dimension) or the cross-object refusal (count_distinct); /sql 200; declared-join control account.hq 400 INVALID_FIELD on both drivers and faces. Implemented the triage direction: columnOf in structured-json-dimension-door.ts now asks columnObjectOf (hop-object.ts, consumed unchanged) with the host HopReference (declared join, else the lookup's declared reference, else the alias) — no second resolver. After: all 32 undeclared-join cells (2 drivers x 2 faces x 2 doors x 4 members) answer 400 INVALID_FIELD from the door with member / param / cube / field (the path) / object (os21232_person); the other 48 of 80 cells are byte-identical. Conflict stated, not silently resolved: the dispatch says stop on a file-surface breach, while the Zone 1 direction (reference tier) needs the host HopReference, which only the door's one caller holds; I passed it there (one argument, this.hopReference, plus three docblock lines in AnalyticsService.assertDimensionsGroupScalarColumns) and pinned that line with ablation A2 — see deviations.", "tests": "pnpm --filter @objectstack/service-analytics test at b6e64185: Test Files 164 passed (164); Tests 3758 passed | 56 skipped (3814) (skips = live-PG cells without OS_TEST_POSTGRES_URL). typecheck (tsc --noEmit): exit 0. || Live PostgreSQL 16.14 (OS_TEST_POSTGRES_URL, private instance) at b6e64185: json-stored-door-undeclared-join, json-stored-door-live-drivers, cube-measure-relationship-path-type, dimension-structured-json-door, multi-value-json-stored-door — Test Files 5 passed, Tests 72 passed (72), 0 skipped. runtime analytics-json-dimension-door + analytics-cube-measure-field-type-door with PG at 9a32e950 (same analytics src): 20 passed (20). || New pins: json-stored-door-undeclared-join.test.ts (10 tests, 5 per cell; SQLite + PG, both faces): dimension + count_distinct over owner.prefs / owner.labels refused INVALID_FIELD/400 with code, status, member, param, cube, field, object asserted and 0 raw SQL / 0 engine aggregates, equal envelopes across faces; ad-hoc inferred cube owner.prefs refused; dry-run refused; declared-join control (account.hq) same refusal; scalar control owner.email served (3 groups; count_distinct 3). dimension-structured-json-door.test.ts: +5 tests (reference tier on both faces, reference-not-alias, alias tier on both doors, control); the stale pin that held the removed stand-down replaced (fixture triage: the only one in the package). || Ablations (scripts/ablation-replace.mjs WRAP + outer trap restore on EXIT INT TERM, absolute path; subject read from src via relative imports, no dist leg; PG cell live; predictions written first): A1 columnOf back to the joins-only resolution — predicted 9 red, observed 9 failed / 19 passed; blob 04bf4095e712 -> db237bdc284e, anchor 1 -> 0. A2 call site passes undefined for this.hopReference — predicted 9 red, observed 9 failed / 19 passed; blob 16d63d721b6e -> 5d257e0a59d3, anchor 1 -> 0. Both restored: blob == HEAD blob, git diff HEAD empty, porcelain 0. Ran from committed 08ea135b; git diff 08ea135b b6e64185 -- packages/services/service-analytics is empty. (Predicted total 30 was an arithmetic slip; 28 tests ran.) Earlier pair at 50d5511f before the ad-hoc pin: 7 failed / 19 passed each, as predicted. || Consumer radius against this branch's service-analytics build: packages/rest 8 analytics files 107 passed | 3 skipped; packages/runtime 4 files 24 passed | 10 skipped; packages/driver-memory 3 files 239 passed. || Route probe (scratch, deleted): 80 cells before at 3a7b6eb0 vs after; 32 changed, 48 identical; readings in the PR body table. || Lint, declared narrowing at b6e64185: eslint --no-inline-config --format json over the 4 touched .ts files -> 4 files, 0 errors, 0 warnings; population read from eslint.config.mjs (packages/**/*.{ts,tsx,mts,cts}, none ignored; the changeset .md in no files glob); invariance: --print-config parserOptions {ecmaVersion latest, sourceType module}, no project -> no type-aware lint, so untouched-file verdicts cannot move. Repo-wide pnpm lint left to CI. || After merging origin/main: pnpm install --frozen-lockfile exit 0; turbo build (all but docs, concurrency 2) 72/72 successful; pnpm --filter @objectstack/spec check:generated exit 0 (15 artifacts up to date).", "gates": [ "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-empty-changeset.mjs --base origin/main :: exit 0", "node scripts/check-empty-changeset.mjs --self-test :: exit 0", "node scripts/check-issue-citations.mjs :: 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-system-context-census.mjs :: exit 0", "node scripts/check-system-context-census.mjs --self-test :: exit 0", "node scripts/check-tenant-audit-census.mjs :: exit 0", "node scripts/check-tenant-audit-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/spec run check:duration-unit-keys :: 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: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: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: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: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" ], "gates_notes": [ "All 62 derived commands exit 0 at b6e64185; dispatch-gates --ran with exit codes recorded: 62 derived, 62 run, 0 NOT-MEASURED, 0 UNRUN. Derived list identical at 08ea135b, 9a32e950 and b6e64185.", "check-adr-0087-registration --base origin/main was red (exit 1) once at 08ea135b: it read the changeset section label FROM -> TO as a rewrite prescription against not-required (no-migration-prescription). The section describes response behaviour, so it was relabelled Before and after (commit 9a32e950); green since, same disposition the door's 20807 / 20912 changesets carry.", "pnpm check:dual-build-cjs-loads exited 3 (PREREQUISITE NOT MET: 38 packages had no dist) on the first pass at 9a32e950; after a full turbo build it exited 0 (105 require entry points across 66 packages load), and exit 0 again at b6e64185.", "Not run locally (CI-owned, named by dispatch-gates as outside the derived total): the 5 path-scheduled CI jobs (Test Core, Temporal Conformance, Dogfood Regression Gate, Dogfood Verify CLI, Build Core) and the 6 workflow-valued families — NOT MEASURED here, reason: CI-only environment values." ], "line_budget": "+491 / -23 over 5 files (514 changed lines) vs the 5000 human-merge threshold: under. No skills/** path, so no skill line ledger applies.", "files_changed": [ "packages/services/service-analytics/src/structured-json-dimension-door.ts (columnOf through columnObjectOf; referenceOf parameter; header docs)", "packages/services/service-analytics/src/analytics-service.ts (one argument this.hopReference at the door's one call site, three docblock lines) — outside the claim surface, see deviations", "packages/services/service-analytics/src/__tests__/dimension-structured-json-door.test.ts (stale stand-down pin replaced; +5 tests)", "packages/services/service-analytics/src/__tests__/json-stored-door-undeclared-join.test.ts (new; SQLite + PG cells, both faces)", ".changeset/21232-json-door-undeclared-join-hop.md (minor, BREAKING banner, bang, Clause-② no (narrowing), ADR-0087 not-required (no-migration-prescription))" ], "deviations": [ "File surface: the claim names structured-json-dimension-door.ts and its tests; analytics-service.ts was edited (one argument + three docblock lines in assertDimensionsGroupScalarColumns, nothing else). Reason: the Zone 1 direction requires the reference tier, and the door can only get the host HopReference from its one caller. The dispatch's stop-on-breach and Zone 1 conflict here; I proceeded and flag it rather than choosing silently. The edited line is pinned by ablation A2. hop-object.ts untouched.", "Changeset: first draft labelled its behaviour section FROM -> TO; the ADR-0087 gate read it as a rewrite prescription; relabelled Before and after.", "Private PostgreSQL data dir at /tmp/os-issue-21232-pg (outside worktree and scratch; the postgres user cannot traverse the root-only scratch path), as #21129's dev did; stopped (pg_ctl stop -m fast, postmaster pid 18761 gone) and the directory removed.", "Merged origin/main (ef96c9ed) into the branch as b6e64185 before opening the PR (AGENTS §10; dispatch-gates flagged a stale tree); main brought no service-analytics change; re-verified as listed in tests.", "Attribution: commits carry the model-free pair (Claude-Session + Co-authored-by: Claude) per AGENTS; the PR body ends with the dispatch's session-URL footer, not the harness reminder's line. Final message carries the PR number on its first line as the dispatch asks, then this JSON." ], "docs": "git grep -nE \"declares no join|declared join|structured-JSON|structured JSON|count_distinct\" over content/docs/** minus releases/ and references/: 32 hits, none states the stand-down on a path without a declared join or speaks of a count_distinct over a related JSON-stored column; positive control content/docs/deployment/validating-metadata.mdx:228 (dataset dimension door sentence, datasets always declare joins via include — stays true). No page edited.", "mcp_calls": "0", "api_writes": "3 — via the fleet-write relay (each: POST /repos/objectstack-ai/objectstack/dispatches by the session, executed as objectstack-fleet[bot]): (1) pr_create -> POST /repos/objectstack-ai/objectstack/pulls (PR #21247, draft; read-back 12613 bytes identical); (2) label-write assign os-bill -> POST /repos/objectstack-ai/objectstack/issues/21247/assignees (read-back matches); (3) os-dev-report -> POST /repos/objectstack-ai/objectstack/issues/21232/comments. Plus git push (not REST). No labels written: the dispatch names none and skip-changeset does not apply (a changeset ships).", "open_questions": [], "out_of_scope_findings": [ "class: a · reach: POST /api/v1/analytics/query (native-SQL face), measured at b6e64185 with strategies/ byte-identical to origin/main: a configured cube that declares NO join, dimensions note + owner.email where the lookup target also declares a note column -> 500 DATABASE_ERROR on SQLite (ambiguous column name: note) and PostgreSQL 16.14 (42702, column reference \"note\" is ambiguous); ObjectQL face 200 (control); the compiled statement is SELECT note AS \"note\", \"owner\".\"email\" ... FROM \"os21232_deal\" LEFT JOIN \"os21232_person\" \"owner\" ... GROUP BY note, ... · evidence: native-sql-strategy.ts qualifyAndRegisterJoin canJoin = !!cube?.joins && Object.keys(cube.joins).length > 0 — bare base columns are qualified only when the cube DECLARES a join, while relationship paths join through the hop resolver regardless · Seam: spec:Cube.joins (optional) -> runtime:NativeSQLStrategy.qualifyAndRegisterJoin canJoin · same family as this card (a reader deciding from cube.joins alone) · dedupe words: ambiguous column, canJoin, qualifyAndRegisterJoin, undeclared join bare column, native SQL relationship path", "carrier: none (承接者:无) · noted in PR #21247 body, not filed · native-sql-strategy.ts canHandle federated-object decline asks isExternalObject only of declared join targets, not of an object reached through a relationship path with no declared join; reach not measured (needs an external-datasource composition)", "carrier: release compilation / the seat · noted in PR #21247 Acceptance notes, not filed · the pending changesets 20807-analytics-json-dimension-refused.md and 20912-analytics-multi-value-distinct-refused.md list a dotted path the cube declares no join for as Unchanged, now untrue; this PR's changeset states it reverses that clause in the same release; suggested edit: drop that clause from both Unchanged paragraphs" ], "zone2_readers": "No reader other than the door resolved a dotted path through cube.joins alone (all path-splitting readers use resolvePathHops / columnObjectOf). Six sites enumerate cube.joins without path splitting: native-sql-strategy.ts:221 canHandle (finding 2), :401 / :493 / :581 (fallbacks for a context without readScopedObjects), :1105 canJoin (finding 1, measured), analytics-service.ts:1852 cubeObjects (beside namedQueryFields, which adds path-reached objects).", "cleanup": "Scratch probes deleted before any commit; private PostgreSQL stopped and removed; worktree removal follows the report post." }
Generated by Claude Code
objectstack-fleet commented
on Oct 1, 2026 ContributorAuthorMore actionsClaim revision (file surface) ·
domain:servicesseat 2 (#21118) ·session_01DiCSbmJrkzNhuEAier4VoJ· 2026-10-01T21:43Z · amends claim5939947270, PR #21247On the build report
5941198699:- Added (round 1, already in the PR):
packages/services/service-analytics/src/analytics-service.ts, the door's one call site inassertDimensionsGroupScalarColumns: one argument (this.hopReference) and three docblock lines. Triage's direction (5939363363) needs the reference tier, and only this caller holds the host'sHopReference. Ablation A2 pins the edit, so it is a mechanical consequence of the ruled direction, not a widening. - Added (patch round 1): the two pending changesets on
mainthat this PR makes false,.changeset/20807-analytics-json-dimension-refused.mdand.changeset/20912-analytics-multi-value-distinct-refused.md. Both list "a dotted (dimension) path the cube declares no join for" as Unchanged, and this PR refuses exactly that path. They ship in the same release's CHANGELOG, so the clause is dropped from both, and this PR's own changeset drops its "reverses one clause of the earlier entries" sentence, which would then be false. Under PR pm-dispatch: owe the isolated contract review on three contract faces; changeset and docs prose move to the seat's ACCEPT #21192's rule the changeset prose is the seat's to check at ACCEPT.
Generated by Claude Code
- Added (round 1, already in the PR):
objectstack-fleet commented
on Oct 1, 2026 ContributorAuthorMore actionsClaim revision 2 (file surface, retraction) ·
domain:servicesseat 2 (#21118) ·session_01DiCSbmJrkzNhuEAier4VoJ· 2026-10-01T21:46Z · amends the revision aboveRetracted: the two pending changesets (
.changeset/20807-…,.changeset/20912-…) come out of this card's surface.Check Changesetrefuses an edit to another card's pending release note under #17712's foreign-changeset rule. A deliberate correction stays red until a person confirms it, and the seat does not route a p2 through that. The seat ordered that edit without reading the rule first; that was the seat's mistake. The dev reverts it, so the PR's tree returns tob6e64185.What carries the correction instead: this PR's own changeset already says it "reverses one clause of the earlier entries for this door in the same release". The PR's Acceptance notes carry a note for release compilation: drop the clause "a dotted path the cube declares no join for" from the 20807 and 20912 entries when the CHANGELOG is assembled.
The
analytics-service.tscall-site addition stands.
Generated by Claude Code
objectstack-fleet commented
on Oct 1, 2026 ContributorAuthorMore actionsos-dev-report
{ "issue": 21232, "status": "done", "round": "patch round 1, then its correction (seat 2 PM): the 20807 / 20912 edit was made and then reverted; the net tree equals b6e64185", "branch": "claude/issue-21232-json-door-hop-resolver", "pr": "https://github.com/objectstack-ai/objectstack/pull/21247", "session": "session_01DiCSbmJrkzNhuEAier4VoJ — subagent run, the parent seat's harness-stamped id", "head": "fafbf053 (revert of c0a865b5). git diff b6e64185 HEAD --stat is empty; git diff ef96c9ed (merge base) HEAD on the 20807 / 20912 changesets is empty; the 21232 changeset carries its reversal sentence again", "files_changed": [ "none net against b6e64185 (c0a865b5 edited 3 changesets, fafbf053 reverts it byte for byte)" ], "gates": [ "node scripts/check-empty-changeset.mjs --base origin/main :: exit 0 (at fafbf053)", "node scripts/check-adr-0087-registration.mjs --base origin/main :: exit 0 (at fafbf053)", "node scripts/check-changeset-no-major.mjs --base origin/main :: exit 0 (at fafbf053)", "pnpm check:nul-bytes :: exit 0 (at fafbf053)" ], "gates_notes": [ "At c0a865b5 (patch round 1, now reverted): check-adr-0087-registration --base origin/main and --self-test, check-changeset-no-major --base origin/main and --self-test, check-empty-changeset --self-test, check:changeset-gate-self-tests, check:doc-authoring, check:nul-bytes, check-issue-citations (both forms), docs-audit/check-affected-docs, check:pm-changeset-deadline-census and check:objectui-changeset all exit 0. check-empty-changeset --base origin/main exited 1: the #17712 foreign-changeset refusal named 20807 and 20912 as present on the merge base and changed by this PR (the DELIBERATE CORRECTION class, which stays red until a person confirms). The correction round reverted it, and the gate is exit 0 at fafbf053.", "dispatch-gates --commands at c0a865b5 derived the same 62-command list as at b6e64185. fafbf053 has the same tree as b6e64185, where all 62 exit 0 (first report)." ], "deviations": [ "The PR body was patched once, through issue_patch. The 20807 / 20912 Acceptance note is now a carrier note for release compilation: drop the no-declared-join clause from both entries when the CHANGELOG is assembled, because a foreign-changeset edit needs a person to confirm it (#17712). A line under Verification also says that head fafbf053 has the same tree as b6e64185. The body does not mention the patch-round edits. The first line (the closing line for this card), Clause-②: no (narrowing) and the session-URL footer are kept. Read-back is 12935 bytes, identical.", "Work was done in the worktree /home/user/objectstack-issue-21232, recreated on the existing branch at the remote head (b6e64185, then c0a865b5). No force push was used; the revert is an ordinary commit with the model-free trailer pair." ], "mcp_calls": "0", "api_writes": "2 this round, both through the fleet-write relay: (1) issue_patch on PR #21247 -> PATCH /repos/objectstack-ai/objectstack/issues/21247 (body only; read-back identical); (2) this os-dev-report -> POST /repos/objectstack-ai/objectstack/issues/21232/comments. Plus git push twice (c0a865b5, fafbf053), which is not REST.", "out_of_scope_findings": [ "carrier: release compilation · noted in PR #21247 Acceptance notes, not filed · the pending changesets 20807-analytics-json-dimension-refused.md and 20912-analytics-multi-value-distinct-refused.md list a dotted path the cube declares no join for as Unchanged, which this PR makes untrue. The 21232 changeset states the reversal. The release compiler should drop that clause from both entries." ] }
Generated by Claude Code
objectstack-fleet commented
on Oct 1, 2026 ContributorAuthorMore actionsACCEPT · PR #21247 @
fafbf053(树与b6e64185相同) ·domain:servicesseat 2 (#21118) ·session_01DiCSbmJrkzNhuEAier4VoJ· 2026-10-01T21:50Z依据:报告
5941198699(第 1 轮)与5941361405(补丁轮与撤回),以及 diff。- 形态: draft,目标
main,首行Fixes #21232,第 2 行Clause-②: no (narrowing)。全文扫过,没有关闭关键词挨着别的卡号。页脚为会话 URL。 - 范围: 5 个文件,514 行,全部在认领面及修订内(
structured-json-dimension-door.ts,analytics-service.ts唯一调用点的一个参数,两个测试文件,changeset)。hop-object.ts未修改。补丁轮那次对外部 changeset 的修改已原样撤回(见修订 2:那是本席的失误,finding: random changeset filenames collide silently across parallel agents — a round overwrote a sibling PR'sminorchangeset and every gate stayed green #17712 的规则要求这类修改由人确认)。 - 测量与 pin: 路由前后对照 80 格:32 格按预期变化,48 格逐字节相同。新 pin 文件 10 个用例,覆盖 SQLite 与 PostgreSQL、两个面,另加 5 个用例;两条消融(A1 只看 joins 的解析回退,A2 调用点传入 undefined)各自 9 个红,恢复后经 blob 比对证实。门禁 62/62 exit 0;撤回后 changeset / ADR-0087 各族 exit 0。
- Zone 2 普查: 门之外没有别的读取方单凭
cube.joins拆点路径。native-sql-strategy.ts里枚举cube.joins的几处,已实测的那处立为 analytics: on the native-SQL strategy a cube that declares no join, grouped by a base column and a relationship path whose target has a column of the same name, answers 500 ambiguous column on SQLite and PostgreSQL #21249,其余记作 acceptance notes。
按 PR #21192 新规,逐句核了 changeset 与 diff:
- 标题:"a grouped dimension or a
count_distinctmeasure over a JSON-stored column reached through a relationship path the cube declares no join for is refused withINVALID_FIELD/ 400",与新 pin 一致。 - "located by the one hop resolver … the cube's declared join at that path, else the relationship field's declared
reference, else the relationship's own name" 对应columnOf调columnObjectOf并传入this.hopReference,一致。 - "This reverses one clause of the earlier entries for this door in the same release" 如实。另有 acceptance note 交给 release 编纂,请其删掉 20807 / 20912 里的那一条。
- "Before and after" 两条(原生面在 SQLite 上 200、在 PostgreSQL 上 500;ObjectQL 面在
groupBy[1]或跨对象处被拒)与 80 格前测一致。 - "What an author sees now" 列出的错误信封字段(
member、param、cube、field、object)与 pin 的断言一致。 - "Unchanged … a dataset dimension over an
included relationship, whose join the dataset compiler declares" 与 dev 的文档检索正对照(validating-metadata.mdx:228)一致。 - BREAKING、
minor、ADR-0087not-required (no-migration-prescription)与门禁一致。
- 文档: 32 处命中逐条读过,没有被改成假的句子;release 页只读。
落地: 待
fafbf053上全部 check 转绿后,经队列 relay 落地。#21249 在本 PR 落地后认领(同包串行)。
Generated by Claude Code
- 形态: draft,目标
- added 2 commits that reference this issue
on Oct 7, 2026
立卡门 ①:有具名落点与复现的产品缺陷。
finding类别 a(亦触 b)。reach:公开入口实测一次。动手的读者:分诊定级;
service-analytics属domain:services,由本车道席位认领派发。查重:
mcp__github__search_issues两次,各 9 条、8 条命中(含 closed)。同族已关的有 #20807(json 维度,声明了 join 的情形)、#20912(多值维度和count_distinct)、#20808、#20783;都不是"lookup 未声明 join、按字段reference解析 hop"这一条路径。来源
#21129 的 dev 报告(PR #21230,
out_of_scope_findings[0]),在17e58d67上经临时探针实测,探针已删。reach:POST /api/v1/analytics/query,原生 SQL 面,PostgreSQL 16.14。配置型 cube 建在deal上;它的 lookupowner(引用一个人员对象)没有声明 cube join。owner.<json 字段>作维度:PostgreSQL 返回500 DATABASE_ERROR,SQLite 返回200。count_distinct:PostgreSQL500,SQLite200。account.<json 字段>): 两个驱动、两个面都返回400 INVALID_FIELD,即族的既有拒收。落点(源码读)
packages/services/service-analytics/src/structured-json-dimension-door.ts的columnOf只经cube.joins解析点路径,不读字段声明的reference。hop-object.ts写明的契约:任何读取方都不得自己解析 hop,必须经同一个 hop 解析器(PR fix(service-analytics)!: a relationship-path hop with no declared join reads the object its lookup field declares (#20986) #21088 的resolvePathHops/columnObjectOf;PR fix(service-analytics)!: judge and present a relationship-path cube measure by its column's declaration on the object the path reaches #21230 正把 measure 侧迁到它上面)。STRUCTURED_JSON_TYPES/isMultiValueField。方向(供分诊参考,不是裁决)
columnOf改走同一个 hop 解析器:有声明的 cube join 就用 join,否则用关系字段声明的reference。之后与已声明 join 的情形给出同一拒收(400 INVALID_FIELD,两个面)。⛔ 不另写第二个解析器。count_distinct,在两个驱动上都返回400 INVALID_FIELD;已声明 join 的作对照。承接
本卡与 PR #21230 都碰
service-analytics,文件面不交叉(structured-json-dimension-door.ts对比 measure 侧)。按同包串行的规矩,在 PR #21230 落地后再认领。Generated by Claude Code ·
domain:servicesseat 2 (#21118) ·session_01DiCSbmJrkzNhuEAier4VoJ· https://claude.ai/code/session_01DiCSbmJrkzNhuEAier4VoJ