Skip to content

fix(lint): chart-axis-not-selected resolves a report chart against its own chart.yAxis, not report.values (#15734) - #15789

Merged
baozhoutao merged 2 commits into
mainfrom
claude/issue-15734-report-chart-axis-own-selection
Sep 5, 2026
Merged

fix(lint): chart-axis-not-selected resolves a report chart against its own chart.yAxis, not report.values (#15734)#15789
baozhoutao merged 2 commits into
mainfrom
claude/issue-15734-report-chart-axis-own-selection

Conversation

@claude

@claude claude Bot commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

Fixes #15734

The ruling

Maintainer ruling recorded at 5550127165 (batch #47, verbatim reply 「同意」) — option 1, quoted:

In packages/lint/src/validate-chart-bindings.ts, the report surface (checkReportChart) resolves the series limb of chart-axis-not-selected against { chart.yAxis } — the one series the embedded chart derives — instead of report.values; the check at the chart.yAxis position itself is removed on that surface, because a report chart cannot fail to select what it queries. chart.series[].name resolves against the same singleton. The list-view and page-component surfaces are untouched.

The renderer at the pin, quoted

Read at the @object-ui revision this repo pins — .objectui-sha = a472b07167a39e55491109e864bb5a54027dcfbd, verified present in the sibling checkout and read with git cat-file -p a472b07…:packages/plugin-report/src/DatasetReportRenderer.tsx.

The chart's query (lines 859–867 at that sha) asks for the axis pair and nothing else:

  const wantsQuery = plan.kind === 'series' || plan.kind === 'single_value';
  const state = useDatasetRows(
    dataset,
    plan.kind === 'series' && xAxis ? [xAxis] : [],
    wantsQuery && yAxis ? [yAxis] : [],
    runtimeFilter,
    dataSource,
    undefined,
    order,
  );

The file says so in its own words at the scopeOrder docblock (line 326): "the embedded chart queries only chart.xAxis × chart.yAxis".

And the series the override is matched against is derived from that same pair (line 1008), exactly one of them — line 1002 above it: "The selection is exactly one dimension × one measure, so this takes the helper's single-dimension branch and returns ONE series":

  const { data: chartData, xAxisKey, series: derivedSeries } = buildChartSeries(
    relabelDimensions(state.rows, dimensionLabels),
    [xAxis],
    [yAxis],

So report.values is the selection of the TABLE beneath the chart, and the warning that fired for a chart.yAxis outside it named a query consequence its own pin refutes.

What changed

ChartBinding gains ownSelection — the measures a chart's OWN query selects, when that is narrower than the values limb. checkReportChart sets it to { chart.yAxis }; the other two surfaces leave it undefined and keep resolving against values, which on them IS the query's measure set.

Pins flipped and added

Test Before After
warns when the yAxis measure is declared but not selected 1 warning flippedsays nothing when the yAxis measure is declared but outside report.values — a report chart cannot fail to select what it queries, expect(findings).toEqual([])
chart-axis-not-selected at the report yAxis position keeps its wording 1 warning replacedchart-axis-not-selected does not fire at the report yAxis position at all, toEqual([]) (the branch it pinned is gone; the wording it guarded is not re-touched)
new: a report series[].name naming chart.yAxis is silent even when report.values does not select it
new: a report series[].name naming a declared measure OTHER than chart.yAxis fires — even one report.values does select; asserts the message names the singleton, selected values (est_hours)
new firing control, list-view surface: resolution unchanged, views[0].list.chart.values[1] still error chart-measure-unknown
new firing control, page-component surface: both limbs still fire chart-axis-not-selected and both messages name selected values (task_count), i.e. values — never a borrowed singleton

Verification

Exit codes captured before any pipe; verdict lines quoted as the tools printed them. Heavy runs through scripts/pm/os-verify-lock.sh with OS_VERIFY_LOCK_SLOT=issue-15734.

Run Verdict
pnpm --filter @objectstack/lint exec vitest run src/validate-chart-bindings.test.ts Tests 35 passed (35) · VERDICT command-exit 0
pnpm --filter @objectstack/lint test (after merging origin/main) Test Files 97 passed (97) · Tests 3331 passed (3331) · VERDICT command-exit 0
pnpm --filter @objectstack/lint typecheck (after the merge) check:test-typecheck: OK — @objectstack/lint's test layer compiles · VERDICT command-exit 0
pnpm check:pm-dispatch-gates dispatch-gates self-test: 1478 cases pass. · EXIT=0
pnpm check:nul-bytes check-nul-bytes: OK (scanned 7613 text file(s) … no raw ASCII control bytes). · EXIT=0
node scripts/pm/check-governed-merges.mjs --test THE_3_CHANGED_PATHS NOT governed — ordinary queue landing applies · EXIT=0
node scripts/pm/dispatch-gates.mjs --changed --commands --repo objectstack-ai/objectstack EXIT=0, 53 commands; all 53 run (see below)

Ablation — the set change, trap-guarded, blob-hash restore

The implementation was committed first, so the restore leg points at HEAD. trap 'RESTORE_FN' EXIT INT TERM; every path absolute from git rev-parse --show-toplevel. No dist leg: the test imports its subject relatively (./validate-chart-bindings.js, a sibling in the same src/), so it can never reach dist/ — nothing to rebuild between legs.

Leg On-disk proof Result
mutate — derived reverted to report.values, and selected handed back to the chart.yAxis call grep -c ABLATION = 2 (expect 2); grep -c 'binding.ownSelection ? new Set' = 0 (expect 0); git hash-object = dc31fa66…, differs from HEAD blob 718a2cd5… Tests 4 failed | 31 passed (35) · VERDICT command-exit 1
restore — git checkout HEAD -- ABSOLUTE_PATH git hash-object = 718a2cd58249552fa0b9d46bac3871170ca97edb = HEAD blob; git diff HEAD: EMPTY (restored) Tests 35 passed (35) · VERDICT command-exit 0

The four that fail under ablation are exactly the flipped pin, the replaced pin and the two new singleton pins:

× says nothing when the yAxis measure is declared but outside `report.values` …
× a report series[].name that names `chart.yAxis` is silent even when `report.values` does not select it
× a report series[].name that names a declared measure OTHER than `chart.yAxis` fires …
× chart-axis-not-selected does not fire at the report yAxis position at all …

The two new firing controls stay green under the ablation, which is the point of them: they measure the two surfaces this change does not move.

Lint — a declared narrowing, with the three readings

pnpm lint (eslint . --no-inline-config) is the repo-level sweep CI owns. Ran targeted instead, at final head 658c596a1:

  1. Population, read from eslint's own config (its isPathIgnored over git ls-files, not a guess): 5979 lintable tracked JS/TS files, 0 of them ignored.
  2. Files linted, counted from --format json: 2validate-chart-bindings.ts, validate-chart-bindings.test.ts; errors 0, warnings 0, EXIT=0.
  3. Invariance over the 5977 untouched files: this repo runs one eslint.config.mjs, which "never enables type-aware linting (no parserOptions.project, no typed @typescript-eslint rules) for ANY file" (its own header, line 326). With no cross-file program, a diff confined to two files cannot move the verdict on any file it does not contain.

NOT MEASURED, by name

  • pnpm check:dual-build-cjs-loadsexit 3, PREREQUISITE NOT MET — this gate reads built output, and some package has no dist/. Needs the whole workspace built; CI's.
  • pnpm check:type-check-debtexit 3, PREREQUISITE NOT MET, "Build the closure first, exactly as lint.yml does before this step". Its sibling pnpm check:type-check-coverage ran and is EXIT=0.
  • pnpm check:docs-transcript-drift — exit 3 on the first pass (packages/lint/dist/index.js absent), then re-run after pnpm --filter '@objectstack/lint...' build and it is EXIT=0: "4 declared transcript value(s) across 405 page(s) under content/docs/ equal what the registry derives today". Measured, not left open.
  • The 7 families whose argv takes a value from the workflow — check-cross-package-test-inputs --union-into "$RUNNER_TEMP/…", the three check-shard-attestation --emit jobs, the two check-test-completeness jobs, check-half-states --provenance="$PROVENANCE". dispatch-gates prints them as ⊘ NOT MEASURED itself; no value exists outside a CI run.
  • @objectstack/cli unit tier — attempted (vitest run --project unit) and not measured: 113 of 176 files fail before any assertion with Failed to resolve entry for package / Cannot find package (unbuilt dependency closure in this worktree), while AssertionError count is 0 and individual failed tests (×) is 0; 813 of the tests that did resolve passed. Left to CI, which builds first. @objectstack/cli is affected only through the dependency edge — this diff moves no exported symbol or signature.
  • The 37 "artifact roster" families and the 10 too-wide-to-place families dispatch-gates prints: silence there is a fact about a list, not a clearance. CI runs them.

Gate family

All 53 derived commands were run: 50 EXIT=0, 3 exit-3 prerequisite cases named above (one of which was then measured green). The derived command list is byte-identical across three derivations — before the merge, after it, and after a further git fetch — so origin/main moving under the branch did not move this card's family.

Scope

Only the SET the report surface resolves chart-axis-not-selected against. origin/main was merged in (merge commit 658c596a1) after PR #15751 landed at 07:16Z, and the lint suite re-run once on the merged tree; #15751's one-copy gate (collection-coercion-single-copy.test.ts) is green here.

Changeset: @objectstack/lint patch, stating the behaviour change.


🤖 Generated with Claude Code

https://claude.ai/code/session_012zGPuVVX3deAx9LdjK8jCk


Generated by Claude Code

…s own chart.yAxis

The report surface fed `report.values` in as the set `chart-axis-not-selected`
resolves against. At the pinned `@object-ui` revision the embedded report chart
never queries that set: `DatasetReportRenderer.tsx` runs its own axis-pair query
(`useDatasetRows(dataset, [xAxis], [yAxis], …)`) and derives ONE series from it.
So the warning at `chart.yAxis` named a query consequence its own pin refutes,
and a `series[].name` override was measured against the wrong set in both
directions.

`checkReportChart` now carries `ownSelection` — the chart's own `{ chart.yAxis }`
— which the not-selected limb reads; the check at the `chart.yAxis` position is
removed on that surface. `chart-measure-unknown` is untouched everywhere, as are
the list-view and page-component surfaces, where `values` IS the query's measure
set.

Ruled by the maintainer on #15734 (option 1, batch #47).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012zGPuVVX3deAx9LdjK8jCk
@github-actions github-actions Bot added size/m documentation Improvements or additions to documentation tests tooling labels Sep 5, 2026
@github-actions

github-actions Bot commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

3 anchor(s) derived from 1 changed package(s); no hand-written page names any of them, so this run has nothing to listnot a clean bill of health. This check sees only pages that NAME a derived anchor: one that documents this change in prose, or enumerates it in an authoring dialect, names none and stays invisible to it on every run.

What this run could not see
  • the SDK route bridge reached 61 of 219 client-bound route-ledger rows — the other 158 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 158: 0 are remediable by widening that discovery convention (an in-repo file declares the path; the convention did not scan it); 56 are structural — on a ledger where NOT ONE row is declared in-repo, so no discovery change reaches them at any price; 102 are undecided (no in-repo declaration, on a ledger that has other in-repo registrars — absence and an unreadable spelling are not distinguishable here). The rows themselves: node scripts/docs-audit/affected-docs.mjs --bridge-coverage
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.

Coarse fallback — 5 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json c99449ab5fd5e87292f4efccd15270cecf4d5242packageMentionDocs.

Which tree this was computed on

This run read content/docs from 94476cdf9c735369e49a01f1e1775d246db75671 — the merge of head 658c596a1b5d977d5663f112cf8105c628af9d39 into base c99449ab5fd5e87292f4efccd15270cecf4d5242, which is what actions/checkout gives a pull_request run. Not the PR head.

A worktree cut from an older main holds a different content/docs, so re-deriving there can legitimately return a different list — that is a different tree, not a wrong row. To answer on the same tree:

# while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 94476cdf9c735369e49a01f1e1775d246db75671 && git checkout 94476cdf9c735369e49a01f1e1775d246db75671
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin c99449ab5fd5e87292f4efccd15270cecf4d5242 658c596a1b5d977d5663f112cf8105c628af9d39 && git checkout -B drift-repro c99449ab5fd5e87292f4efccd15270cecf4d5242 && git merge --no-ff 658c596a1b5d977d5663f112cf8105c628af9d39

node scripts/docs-audit/affected-docs.mjs --json c99449ab5fd5e87292f4efccd15270cecf4d5242

⚠️ That checkout carried uncommitted changes, so the commit above does not fully identify what was read.

@claude

claude Bot commented Sep 5, 2026

Copy link
Copy Markdown
Contributor Author

ACCEPT — PR #15789 (head 658c596a1, 3 files: validate-chart-bindings.ts +72/−13, its test +146, changeset) reviewed against the GitHub three-dot diff, not the report.

Ruling 1 (5550127165) as spelled: the report surface passes ownSelection: [chart.yAxis], the series/axes limbs resolve chart-axis-not-selected against that singleton instead of report.values, and the chart.yAxis-position check on that surface is dropped (measureRef(…, 'query') without a selection); list-view and page-component surfaces resolve as before (firing controls added on both). Only the SET moved: the #15590 / #15741 tier and consequence tables are byte-untouched (0 diff lines in UNKNOWN_CONSEQUENCE / SHAPE_HINT / UNSELECTED_CONSEQUENCE), no coercion copy introduced (asArray 0; #15751's one-copy gate green in the dev's run). Pins: the report-surface false positive flips to no finding; two singleton pins added (a series[].name naming chart.yAxis is silent even outside report.values; one naming another declared measure fires and names the singleton). Changeset @objectstack/lint patch present. Renderer read at the pinned a472b07 (:859, :326, :1002-1008) and quoted.

Measured by this seat: git merge-tree --write-tree origin/main <branch> → EXIT=0 (main merged in at 658c596a1, #15751 included); no live PR of this lane touches the file; governed test per the report NOT governed. NOT MEASURED here: the lint suite (the dev's locked runs Tests 35 passed (35) file / 3331 passed package / typecheck OK, and the ablation 4 failed | 31 passed → restored 35 passed, are the record); the whole-workspace-build gates by name.

Flip + arm when both required jobs report success on 658c596a1. On landing #15734 closes.


Generated by Claude Code

@claude

claude Bot commented Sep 5, 2026

Copy link
Copy Markdown
Contributor Author

Flipped ready + auto-merge enabled (08:26Z, method: MERGE). Required jobs on 658c596a1: Lint & Repo Gates = success, TypeScript Type Check = success; git merge-tree --write-tree origin/main <branch> → EXIT=0 against the current main. Watched; on landing #15734 closes.


Generated by Claude Code

@baozhoutao
baozhoutao added this pull request to the merge queue Sep 5, 2026
Merged via the queue into main with commit 89758ac Sep 5, 2026
35 checks passed
@baozhoutao
baozhoutao deleted the claude/issue-15734-report-chart-axis-own-selection branch September 5, 2026 08:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/m tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

chart-axis-not-selected resolves a report chart against report.values, but the pinned renderer queries only chart.xAxis x chart.yAxis

2 participants