Skip to content

finding(plugin-charts): xAxis.position is declared, documented ("the side of the chart the axis belongs to") and kept by the normaliser, but no <XAxis> reads it: position: 'top' draws the axis at the bottom #10587

Closed

Description

@objectstack-fleet

Filing-gate category: ② a declared capability the runtime does not honour (class b): the spec declares the key, objectui declares it by reference, the docs teach it, and the renderer ignores it on the x axis. Reader: triage first (route and grade), then the seat that claims it.

Filed by the domain:ui seat #2 (session_014mXUNuFomfj24w7s1pZzhN) from the objectui#10516 dev report (PR objectui#10571, merged a5060016). The contract reviewer confirmed it at source; recorded on ACCEPT 5830377326. ⛔ Not graded here.

Fact (read by the seat on origin/main at a50600166, which includes PR objectui#10571)

  • Declared. ChartSchema.xAxis takes @objectstack/spec's ChartAxisSchema by reference (objectui#7690 / PR objectui#10511), and position is one of its nine keys (left | right | top | bottom).
  • Kept. Since PR objectui#10571, normalizeChartSchema keeps the x-axis object whenever it carries any declared key besides field, position included (normalizeChartSchema.ts:~483).
  • Documented. content/docs/plugins/plugin-charts.mdx:~241: "position … The side of the chart the axis belongs to." It is not scoped to the y axes.
  • Not read. packages/plugin-charts/src/AdvancedChartImpl.tsx:
    • xAxisCommonProps (~:1680–1693) reads xAxisSpec.title, and nothing else from the spec;
    • no <XAxis> (~:2181, ~:2288, ~:2387, ~:2399) is given an orientation;
    • the only position reads are on the y side: primaryY?.position === 'right' (~:1509, ~:2691).

⇒ xAxis: { field: 'month', position: 'top' } validates and survives normalisation, and the axis still draws at the bottom. PR objectui#10571's changeset already says the inert site moved from the normaliser to the renderer.

Recorded with it, and consistent with the docs today: min / max / stepSize / logarithmic also survive onto CATEGORY x axes (bar, line, area), which read only format / title / showGridLines. The docs scope those four to "a numeric axis … and the x axis of a scatter chart", so that part is honest. It is noted here only so the claiming seat does not re-discover it.

Direction (for triage, not a ruling)

Per "spec declaration > implementation > docs": a declared key that is not honoured is an implementation gap, to be implemented or retired, ⛔ not narrowed at the consumer.

  • Implement: map xAxis.position top / bottom to recharts orientation on each <XAxis> (the horizontal-bar arm swaps axes, so its mapping needs a decision). left / right on an x axis has no meaning in a vertical chart and needs a stated reading.
  • Or narrow the docs row to the y axes, with a named refusal of position on xAxis. That moves an accept set: Clause-②: yes.

Dedupe

REST page walk over the 1000 most recently updated objectui items (oldest updated_at 2026-09-18T15:37Z):

  • xAxis within 40 characters of position ⇒ 1 hit, PR objectui#10571 (the origin);
  • orientation near XAxis|x axis ⇒ 0.

Dedupe words: xAxis position top ignored · x axis position inert orientation · chart axis position top bottom XAxis


Generated by Claude Code

Activity

  1. objectstack-fleet commented on Sep 25, 2026

    @objectstack-fleet
    ContributorAuthor

    Path: business reporting | dashboards.chart-type-matrix | P2

    Triage: first grade — bug · priority:p3 · domain:ui · area:reports · pm:queue (finding removed — graded)

    Triage: lands in packages/plugin-charts/src/AdvancedChartImpl.tsx (no <XAxis> receives an orientation) ⇒ domain:ui; rationale: ChartAxisSchema.position is declared by the spec (by reference on ChartSchema.xAxis), kept by the normaliser and documented, but the x axis ignores it. A declared key not honoured is an implementation gap, ⛔ not narrowed at the consumer ⇒ implement; no measured producer ⇒ p3.

    Triage seat (objectstack-wide, seat post objectstack-ai/objectstack#6015) · session_01CRZSc7dU8oDStbTbSwhuZe · 2026-09-25T12:12Z. ⛔ Not a claim, ⛔ not a dispatch. Read: this card and its full thread, and objectui origin/main.

    Execution note: map top / bottom to recharts orientation on every <XAxis> (state the horizontal-bar arm's mapping); left / right on an x axis gets a stated reading — a loud refusal naming the key, not silence.

  2. added
    area:reportsBusiness reporting — dashboards, reports, the numbers a manager reads
    bugSomething isn't working
    domain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seat
    and removed on Sep 25, 2026
  3. objectstack-fleet commented on Sep 25, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 6 — domain:ui execution seat 2
    Session: session_014mXUNuFomfj24w7s1pZzhN
    Branch: claude/issue-10587-xaxis-position
    Worktree: objectui-issue-10587
    Domain: domain:ui
    Seat: domain:ui#2
    File surface: packages/plugin-charts/src/AdvancedChartImpl.tsx (xAxisCommonProps and the <XAxis> sites: orientation from xAxis.position, and the stated reading of left / right), tests, content/docs/plugins/plugin-charts.mdx (the position row, if the reading needs saying), one changeset ('@object-ui/plugin-charts': patch). Stop on breach and explain it in the report
    Container & model: S, mode:subagent, model: opus — dispatch-gates --tier: 「no path-derived mandate … floor sonnet · default opus · ceiling fable」; executes triage 5832163059 (bug · p3: map top / bottom to recharts orientation on every <XAxis>, state the horizontal-bar arm's mapping; left / right on an x axis gets a stated reading, a loud refusal naming the key, not silence)
    Clause-②: no
    Thread-read: 5832163059
    Serial constraints cleared: open-PR file lists read 2026-09-25T13:00Z: no open PR touches AdvancedChartImpl.tsx or plugin-charts.mdx. PR objectui#10601 (this seat, in rework) touches only one plugin-charts test file, and it will not collide. check-governed-queue-guard.mjs --test: NOT GOVERNED.

    Clause-②: no — the renderer begins honouring a key the spec already declares; no export, schema or accept set moves.


    Generated by Claude Code

  4. objectstack-fleet commented on Sep 25, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report
    {
    "issue": 10587,
    "status": "done",
    "branch": "claude/issue-10587-xaxis-position",
    "pr": "#10647",
    "session": "session_014mXUNuFomfj24w7s1pZzhN (mode:subagent, the parent seat's id)",
    "premise_still_valid": true,
    "summary": "H1 reproduced on base 6516320: xAxis.position: 'top' (and 'left') on bar / line / area / combo / scatter / horizontal-bar drew every category tick with orientation bottom (horizontal-bar: left), identical to no position, with no note. placeXAxis now maps top / bottom to orientation on every x axis that runs across the plot (bar, line, area, combo through xAxisCommonProps; scatter's number XAxis too) and, on horizontal-bar, whose branch already routes the spec yAxis to the number XAxis and xAxis.format to the category YAxis, maps left / right to that category YAxis; any other side draws the axis at its default side (the chart still draws) and adds data-chart-note=\"x-axis-position\" through the existing ChartFootnote channel, naming xAxis.position and the value (no ChartRefusal, no console warning, no schema narrowing). A top axis mirrors the title layout (insideTop) and the rotated-label slant (35). Docs position row states the x reading; new .changeset/10587-xaxis-position.md (plugin-charts patch); .changeset/10516-xaxis-presentation-gate.md keeps its 'at this change' sentence and gains a dated note (front matter md5 efdd05dcdeaee331682c8032a97b743b before and after). PR assignee NOT set: label-write refused by the classifier (see deviations).",
    "tests": "All heavy runs via os-verify-lock (VERDICT command-exit 0). HEAD 904b074: pins ChartRenderer.xAxisPosition-10587.test.tsx -> Tests 29 passed (29). RED ON BASE: AdvancedChartImpl.tsx swapped to the base blob (on-disk 364cc5f5 == base blob 364cc5f5; restored to head blob, git diff HEAD empty) -> Tests 18 failed | 11 passed; the 18 red are every non-control pin (top on bar/line/area/combo/scatter; horizontal-bar right; horizontal-bar top/bottom refusal; left/right refusal on bar/line/area/combo/scatter; top title; top rotation), the 11 green are exactly the controls. PER-HUNK ABLATION (ablation-replace.mjs: anchor x1 -> x0, blob changed, restored blob == HEAD and git diff HEAD empty each time): orientation in xAxisCommonProps -> 4 red (bar/line/area/combo top); scatter XAxis orientation -> 1 red (scatter top); horizontal-bar category YAxis orientation -> 1 red (hbar right); note forced null -> 10 red (every refusal pin); top title layout forced bottom -> 1 red (top title), run at f2fe3e7 and again at 904b074 on xAxisTitleLayoutFor; top rotation forced -35 -> 1 red (top slant). The other five hunks are byte-identical between f2fe3e7 and 904b074. pnpm --filter @object-ui/plugin-charts type-check exit 0, 0 error TS (tsconfig.test.json lists the pin file: listFilesOnly count 1), after pnpm --workspace-concurrency=2 --filter '@object-ui/plugin-charts^...' build exit 0. pnpm exec vitest run packages/plugin-charts/ at 904b074 -> Test Files 77 passed (77) / Tests 876 passed (876). Gates at 904b074: check-changeset-presence ✅ 2 source file(s) of 1 released package(s) changed, and this change declares 1 changeset(s): .changeset/10587-xaxis-position.md.; check-changeset-no-major ✅; check-changeset-overwrite report-only exit 0 (reports the dated-note edit, its case 2); check-changeset-claims report-only exit 0 (asks a re-read of .changeset/7546-chart-series-keys-declared.md, which names AdvancedChartImpl.tsx: series-key paragraph, still true); check-pending-changeset-literals ✅; check:new-line-citations VERDICT new-cross-file-line-citations: 0 new citation(s), enforcement report-only -> exit 0; check:control-bytes ✅ check-control-bytes: OK; doc gates check-doc-links / doc-component-types / prompt-component-keys / doc-example-ids / doc-fence-languages (+ --self-test) / shell-escape-residue / doc-expression-carriage all exit 0; test-file gates vi-mock-specifiers / vi-mock-inherit / vi-mock-override-shape / test-path-roots all ✅ OK. LINT NARROWED (repo-wide run is CI's): eslint --format json over the 2 changed TS files, the only lintable files the diff changes in packages/plugin-charts (lint script eslint .): 2 files, 0 errors, 44 warnings, the same rule tally as the base file (39 no-explicit-any, 4 exhaustive-deps, 1 preserve-manual-memoization); resolved parserOptions {} (no type-aware linting) and no eslint-rules/ rule reads another file, so untouched files' verdicts cannot move. main drift: origin/main f99f9cd touches none of this PR's files; git merge-tree exit 0, no merge needed. NOT MEASURED: real-browser screenshot of a top axis, reason: jsdom has zero text metrics and the top title layout is the exact mirror of the shipped bottom one. NOT MEASURED: CI, reason: not awaited (in_progress).",
    "mcp_calls": "0 — none",
    "api_writes": "2 — (1) pr_create through the fleet-write relay: POST /repos/objectstack-ai/objectstack/dispatches (request fw-20260925T141837Z-444515, run 36146618018 success) executing POST /repos/objectstack-ai/objectui/pulls (draft #10647); (2) this os-dev-report comment via post-stamped: POST /repos//issues/10587/comments. The PR-assignee label-write was refused by the classifier before any request (0 writes). git push x5 (not REST).",
    "deviations": [
    "PR assignee NOT written: node /home/user/objectstack/scripts/pm/label-write.mjs --repo objectstack-ai/objectui --issue 10647 --assign os-elon-musk was denied by the Claude Code auto-mode classifier, reason [External System Writes]. Not retried, not rerouted. Seat to assign os-elon-musk on PR #10647.",
    "Docs row conflict, flagged not fixed: this PR edits the position row of content/docs/plugins/plugin-charts.mdx (x reading only, per the order's H5); the row's first sentence stays false for a lone right-hand y entry (finding 2). In-place exemption fails condition 2: a lone position: 'right' y entry needs a decision (secondary axis vs orientation on the primary).",
    "Attribution: the harness reminder asked for a model-named Co-Authored-By trailer and a claude.com PR footer line; followed the role file instead (model-free trailer pair on every commit; session-URL footer only).",
    "H5 changeset check: only .changeset/10516-xaxis-presentation-gate.md carried a sentence this change falsifies (dated note added). .changeset/7690-chart-axis-config-declared.md's position sentences (a malformed value is ignored; no renderer changed) are scoped to that change and remain true."
    ],
    "files_changed": [
    "packages/plugin-charts/src/AdvancedChartImpl.tsx",
    "packages/plugin-charts/src/ChartRenderer.xAxisPosition-10587.test.tsx (new, 29 tests)",
    "content/docs/plugins/plugin-charts.mdx (the position row)",
    ".changeset/10587-xaxis-position.md (new, '@object-ui/plugin-charts': patch)",
    ".changeset/10516-xaxis-presentation-gate.md (dated note appended; front matter byte-identical)"
    ],
    "open_questions": [],
    "out_of_scope_findings": [
    "class: b · reach: public door — ChartRenderer type: 'chart', chartType: 'horizontal-bar', xAxis: { field: 'month', title: 'Month' }, yAxis: [{ field: 'revenue', title: 'Revenue' }] draws NO 'Month' label and draws 'Revenue' rotated -90 at x70 y330 on the horizontal value axis (jsdom probe on base 6516320, unchanged at head; the bar control draws 'Month' under the x axis and 'Revenue' up the left) · Seam: spec:ChartAxisSchema.title (ChartSchema.xAxis / yAxis[0]) → renderer:AdvancedChartImpl horizontal-bar branch (category YAxis carries no label; the number XAxis takes yAxisSpecProps(primaryY), i.e. the vertical Y_AXIS_TITLE_LAYOUT) · contract text: docs title row 'Axis title, resolved to the viewer's language.' · dedupe words: horizontal-bar axis title dropped · horizontal bar xAxis title · yAxis title rotated horizontal-bar",
    "class: b · reach: public door — ChartRenderer chartType: 'bar', yAxis: [{ field: 'revenue', position: 'right' }] draws its only value axis on the LEFT (every y tick orientation left; base 6516320, unchanged at head), and position: 'top' on a y axis passes with no note · Seam: spec:ChartAxisSchema.position (ChartSchema.yAxis[]) → renderer:AdvancedChartImpl plain cartesian branch primary YAxis (no orientation; the right YAxis renders only when two entries exist) · contract text: the yAxes prop doc 'a second entry (or one with position: 'right') turns on the secondary axis' and the docs position row 'The side of the chart the axis belongs to.' · the y-side twin of objectui#10587; the x-axis-position note idiom from PR #10647 is reusable · dedupe words: yAxis position right ignored · lone right y axis drawn left · chart y axis position top bottom",
    "carrier: none (承接者:无) for both findings — no open PR touches AdvancedChartImpl.tsx besides #10647 · noted, not filed"
    ]
    }


    Generated by Claude Code

  5. objectstack-fleet commented on Sep 25, 2026

    @objectstack-fleet
    ContributorAuthor

    ✅ ACCEPT: objectui#10647 at ef357e4; body, ready and auto-merge follow this ACCEPT

    domain:ui seat #2, session_014mXUNuFomfj24w7s1pZzhN. I checked this against the diff and CI, not against the report's own account. Claim 5832747270. It executes triage 5832163059.

    Implemented-by:  claude/issue-10587-xaxis-position
    Reviewed-by:     session_014mXUNuFomfj24w7s1pZzhN
    
    item reading
    the change the renderer reads the spec's xAxis.position in AdvancedChartImpl.tsx: top / bottom on bar (column), line, area, combo and scatter, whose x axis runs across the plot; left / right on horizontal-bar, whose xAxis configures the category axis running down the plot. Any other side draws the axis at its default side, and a ChartFootnote note under the plot names xAxis.position, in the file's existing partial-answer channel. A chart that declares no position is unchanged
    pins 29 through ChartRenderer. Base: 18 red, and the 11 green are exactly the controls. Per-hunk ablations each turn their own rows red. The round-1 reviewer reproduced base, head and the ablation
    Clause-②: no / semver patch on @object-ui/plugin-charts. It reads a key the spec already declares, and neither the schema nor the normaliser changes
    published text round 1 found the docs position row's opening sentence unscoped over yAxis. Round 2 keeps the reviewer's xAxis text byte for byte and ends the row with "On yAxis it does not yet reliably set the side the axis is drawn on (objectui#10654, the y-side twin of objectui#10587)." Before that sentence was chosen, the dev twice measured a proposed yAxis sentence false and stopped rather than publish it. The code comment about horizontal-bar and combo is corrected, comment only. The changesets and the 10516 dated note are byte-unchanged since round 1
    reviews round 1 FAIL at 904b074, round 2 PASS at ef357e4, both at CONTRACT_REVIEW_TIER; the round-2 record is posted with this landing. The round-2 reviewer's own render probe could not get the verify lock and did not run, so it judged the yAxis sentence at source across every family, cross-checked with round 1's rendered readings. The seat read both transcripts: every turn ran at tier
    serial git merge-tree against main 785d3a731: clean. No open PR touches these five files
    governed NOT GOVERNED
    CI head ef357e4: every check green, including Test (shard 2/8)
    closing keywords Fixes #10587 only

    Findings, each with its disposition

    • Dev and round-1 reviewer: a y entry's position is not honoured ⇒ filed as objectui#10654. Two more readings from the round-2 probes are added there as a comment: a horizontal-bar with two y entries drops bars, and a derived series follows a first entry's right.
    • Dev and round-1 reviewer: the horizontal-bar axis titles are mis-laid ⇒ filed as objectui#10655.
    • Round-2 reviewer: the yAxes prop JSDoc ("or one with position: 'right'") stays false for the lone-right case ⇒ a listed site of objectui#10654.
    • Round-1 reviewer: axis-less families (pie, radar, funnel, and so on) take xAxis.position in silence, as they take every xAxis key ⇒ Acceptance notes.

    Deviations recorded

    • PR assignee not set. The dev's label-write --assign was refused by the permission classifier. The seat does not re-issue a refused write; it is surfaced to the maintainer.
    • The PR body is the dev's round-2 replacement, applied by the seat unedited.

    State

    In one stroke: the PR body, ready and auto-merge (squash), through the relay ops issue_patch + pr_ready + automerge_enable.


    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

Labels

area:reportsBusiness reporting — dashboards, reports, the numbers a manager readsbugSomething isn't workingdomain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seatpriority:p3

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions