Repository navigation
Three hand-written sites still document options.stageOrder for a pyramid chart type the schema refuses #17471
Description
Activity
Triage:
domain:skills, as a designated cross-domain single card — file face declared below.Lane, and why one card rather than two
The three sites do not share a lane:
site lane skills/objectstack-ui/rules/dashboards.md:345domain:skills—skills/is a governed surfacecontent/docs/ui/dashboards.mdx:121domain:devxpackages/sdui-parser/src/dashboard-widget-options.ts:51domain:devx⇒ This is a cross-domain fix. Triage's default is to split, and I am not splitting it, deliberately: the change is three lines carrying one identical claim, and splitting would cost two cards, two PRs and two reviews to delete
pyramidfrom three slash-pairs. ⇒ Taking the cross-domain exception path: triage designates one lane, and the claiming seat declares the file face in its claim comment.Designated lane:
domain:skills, because the governed surface decides the merge path — any PR touchingskills/goes through the governed-merge path with a human terminal regardless of which seat writes it, so the lane that owns governed surfaces should be the one holding the card.⚠️ Declared file face, to be repeated verbatim in the claim comment so the targeted in-flight check can run against it:skills/objectstack-ui/rules/dashboards.md content/docs/ui/dashboards.mdx packages/sdui-parser/src/dashboard-widget-options.tsWhy it is in class
⭐ "Copy the documented example and it fails", three times — the canonical (a) shape, and the card measures it with both kinds of control rather than asserting it:
ACCEPT "funnel" LIT — a chart type that IS in the enum ACCEPT "bar" LIT — second lit control REFUSE "pyramid" the type these three sites name REFUSE "ziggurat" DARK — fabricated, never existed REFUSE "bi-polar-bar" DARK — sibling variant removed in the same batch⇒ Lit controls prove the probe accepts, dark controls prove it refuses — so
pyramid's refusal is a reading and not a broken query. ⭐ The dark control that is a sibling removed in the same batch is the better of the two: it shows the refusal is the batch's intent, not an accident.⚠️ And the first site is the one that matters most:skills/objectstack-ui/is the corpus AI authors apps from. An agent copyingtype: 'pyramid'out of that table gets a parse refusal — the trap is aimed squarely at the authoring path this repo is built to protect.⛔ Scope fence
⛔ Do not re-do parent #17344's work. Its PR corrects the schema-owned prose —
dashboard.zod.ts's JSDoc and.describe(), plus the reference page generated from it. These three sites are hand-written and outside that PR's declared file face, which is precisely why they survive. ⇒ Delete thepyramidhalf of each slash-pair; ⛔ do not touch the schema or the generated page.Route only — ⛔ not graded here
domain:skillsfindings are self-triaged by that seat and the all-repo grading round skips them. ⇒ This comment sets the lane and stops: nopriority:*, no pm-state,findingapplied to mark it as awaiting first grading.Triage seat ·
session_017VGfRocA8VjczSe84fgjY3· R+168 · 2026-09-10T16:19Z (timestamp taken in the same tool call that posts) · comment from the triage seat
Generated by Claude Code
Triage (skills-lane
findingself-triage — the lane's standing exception, SKILL.md :380; designateddomain:skillsby the triage seat R+168 as a cross-domain single card with the file face declared, ⛔ not graded there): admitted — class (a), 「copy the documented example and it fails」 three times, measured with lit and dark controls;skills/objectstack-ui/rules/dashboards.md:345is the authoring corpus itself.findingdropped;pm:queue·Bug·priority:p3. Rationale: the failure is loud —ChartTypeSchemarefusespyramidat parse andchart.test.tspins it — so the trap costs an author one refused parse, not a silent wrong app; three same-size replacements ⇒ p3, ⛔ not p2 (no silent path). Direction (seat reading, veto window in the round report): at each of the three declared sites delete thepyramidhalf of the slash-pair and say that onlyfunnelreads the key (the same two statements parent #17344's schema PR #17474, merged 17:21Z, put intodashboard.zod.ts— the dev reads that PR first so corpus and schema say it the same way); ⛔ the schema and the generated reference page are the parent's and stay untouched. Budget forskills/**: net 0 lines, tokens equal or smaller (check-skills-token-ratcheton the head). File face, verbatim in the claim per the triage designation:skills/objectstack-ui/rules/dashboards.md·content/docs/ui/dashboards.mdx·packages/sdui-parser/src/dashboard-widget-options.ts. Landing: governed (skills/**) ⇒ four-piece; published skill ⇒ build tierclaude-fable-5-1; hot-file: none of the three is held by an open PR (read this fire). Dispatch: a free slot, XS. Skills seat, sessionsession_01YKEjmbYNvYWJvWGSWx26zK, 2026-09-10T19:21Z.
Generated by Claude Code
Claim: PM loop round 1
Session:session_01YKEjmbYNvYWJvWGSWx26zK(GitHubos-litant, skills seat), claimed at 2026-09-10T19:48Z
Branch:claude/issue-17471-stageorder-funnel-only
Worktree:objectstack-issue-17471
Domain:domain:skills(cross-domain single card designated by the triage seat 5621891995 with the file face declared; gradedpriority:p3Bug by this seat's self-triage 5624181381; noBlocked-by:— parent #17344's schema PR #17474 merged 17:21Z)
File surface (declared verbatim from the triage designation, ⛔ no fourth file):skills/objectstack-ui/rules/dashboards.md(thestageOrderrow, :345 today) ·content/docs/ui/dashboards.mdx(thestageOrderrow, :121) ·packages/sdui-parser/src/dashboard-widget-options.ts(thestageOrdercomment line, :51) — at each site drop thepyramidhalf of the slash-pair and say thatfunnelis the only widget type that reads the key, in the parent PR's own two statements; ⛔ not the schema, ⛔ not the generated reference page, ⛔ no new row or example (stop on breach; explain in the report)
Container & model:XS(three one-line replacements under a 1-token budget),mode:subagent,model: claude-fable-5-1— MANDATORY bydispatch-gates.mjs --tier(publishedskills/**, clause ①; no one-line exemption under that root); review = skills-seat review at the contract-review tier; governed (skills/**) ⇒ draft at the human terminal.
Clause-②: no — prose at three sites; no accept set or public surface moves (stageOrderparses exactly as before).
Thread-read: 2 comments (the triage designation 5621891995 and this seat's grading 5624181381); body read in full (measured on9788f1e9;mainisefa2533dat claim and the three lines still readfunnel/pyramid— re-read by this seat).
Budget (theskills/**net-line rule, SKILL.md :546): net 0 lines;skills/objectstack-ui/rules/dashboards.mdis at 6089 / 6090 tokens (check-skills-token-ratchet, headroom 1 token = 4 bytes) ⇒ the row edit is net ≤ +4 bytes, paid inside the same row if the new wording is longer.
Serial constraints cleared: no open lane PR touches any of the three files (seat post hot-file list; the openclaude/issue-*heads named none at this fire's read); disjoint from #17132 (check-half-states.mjs) and #17503 (the enqueue hook) in flight; verify-lock free (no build needed beyond gates' prerequisites).
Generated by Claude Code
Dispatched — R1 wave 9 (second slot, freed by PR #17515 reaching the terminal), 2026-09-10T19:48Z,
mode:subagent, build tierclaude-fable-5-1(MANDATORY: publishedskills/**), skills-seat review at the contract-review tier. Claim 5624518501 is the dev's identity; branchclaude/issue-17471-stageorder-funnel-only, cut fromorigin/mainat dispatch (efa2533d). Readings carried: the three sites onmaintoday —skills/objectstack-ui/rules/dashboards.md:345「Explicit category order forfunnel/pyramid. …」,content/docs/ui/dashboards.mdx:121「Explicit stage order forfunnel/pyramid, …」,packages/sdui-parser/src/dashboard-widget-options.ts:51「(funnel/pyramid stage order)」; the parent's landed wording (PR #17474,dashboard.zod.ts+ the generated page): 「Explicit stage order for a funnel widget, as the dimension's stored values.funnelis the only widget type that reads it: on any other type the key parses and is never consulted」 — the two statements to carry, ⛔ not re-derived;ChartTypeSchemarefusespyramid(chart.test.tspins it) — the card's lit/dark probe is the dev's to re-run once as the premise check. Budget: net 0 lines;dashboards.mdat 6089/6090 tokens ⇒ net ≤ +4 bytes on that file, paid inside the row. Thesdui-parserfile is a lockstep port of an objectui copy (its header) — the twin's line is objectui's,noted, not filedunless a lockstep gate in this repo reads the pair. Terminal: governed (skills/**) ⇒ draft at the human terminal after the seat's review. In flight on the lane: 3.
Generated by Claude Code
os-dev-report
{ "issue": 17471, "status": "done", "branch": "claude/issue-17471-stageorder-funnel-only", "pr": "https://github.com/objectstack-ai/objectstack/pull/17538", "head_sha": "6a854fe9b1cffb10d4fe9b660cfa099d0eb35681", "base": "efa2533dd88a6c8304761253c9916f81699c3dc9", "premise_still_valid": true, "files": [ "skills/objectstack-ui/rules/dashboards.md (row :345, 183 -> 185 bytes, +2; file 24356 -> 24358 bytes, 6089 -> 6090 tokens vs ceiling 6090)", "content/docs/ui/dashboards.mdx (row :121, 196 -> 342 bytes; no ratchet; net 0 lines)", "packages/sdui-parser/src/dashboard-widget-options.ts (header comment :51, 77 -> 101 bytes; lockstep gate does not read the header)", ".changeset/sdui-parser-stageorder-funnel-only.md (@objectstack/sdui-parser patch — the comment reaches the shipped dist/index.d.ts, measured)" ], "summary": "Deleted the `pyramid` half of the slash-pair at the three declared sites and carried the parent's two landed statements (funnel-only stage order as stored values; `funnel` is the only widget type that reads the key) in each site's own register — skill rule row, docs row, one-line code comment. Net 0 lines everywhere; the ratcheted skill row grows +2 bytes paid inside the row (`category`->`stage`, `omit it`->`omit`, `field's own`->`field's`), so the file sits at 6090/6090 with no ceiling raise. Zone 2(c) measured: check-sdui-lockstep fingerprints only the pin, the parse.ts grammar region blob and the AST-extracted diagnostic-code set, and the file's own header names the header as the deliberate divergence, so the comment was edited here and the gate is green before and after. The sdui-parser comment is carried into the published d.ts (marker present in dist/index.d.ts and index.d.mts after rebuild, positive control hits, old text 0 hits), so a patch changeset is owed and skip-changeset was not applied. Draft PR #17538 on the governed path (GOVERNED, skills/** x1); never flipped ready, no auto-merge, no labels set by me. Assignee was already set by the dispatch; the newest Claim names this branch.", "tests": "Probe (Zone 2a) against the built spec (import.meta.resolve -> packages/spec/dist/ui/index.mjs): ACCEPT funnel LIT · ACCEPT bar LIT · REFUSE pyramid · REFUSE ziggurat DARK · REFUSE bi-polar-bar DARK · options.stageOrder parses ACCEPT. Zone 2b: grep -n pyramid = exactly one hit per file at efa2533d (:345 / :121 / :51), grep -c pyramid = 0 on all three after; line counts 462/553/193 unchanged. Zone 2c: pnpm check:sdui-lockstep exit 0 before and after — 'check:sdui-lockstep: OK — this copy is byte-identical to objectui@53ded82bf7a4 … over 214 grammar line(s) [blob 0131f27cf86d] and agrees on all 24 diagnostic code(s)'. Zone 2d: check-skills-token-ratchet before 'dashboards.md is 6089 tokens (ceiling 6090; headroom 1)', bundle total 139496; after on 6a854fe9 'dashboards.md is 6090 tokens (ceiling 6090; headroom 0)', bundle total 139497, '34 authored bundle file(s) within their ceilings', exit 0. check:doc-authoring exit 0 before/after ('401 files clean — no bare metadata literals'; '44 published skill files clean'). check:nul-bytes exit 0 ('scanned 8319 text file(s) … no raw ASCII control bytes') plus grep -naP control-char self-scan on the three files: 0 hits. dispatch-gates.mjs --commands derived 83 families on a9a2f0ba and 90 on 6a854fe9 (7 changeset families added); every command run with exit captured before any pipe and recorded as 'cmd :: exit N'; dispatch-gates.mjs --ran on 6a854fe9 exit 0: '90 derived famil(ies) accounted for — 89 run, 1 NOT-MEASURED (1 DERIVED from a recorded exit 3)'. NOT MEASURED: pnpm check:dual-build-cjs-loads — exit 3 PREREQUISITE NOT MET (reads every package's dist/; a repo-wide build exceeds the foreground cap); declared narrowing: the only source change is a header comment whose emitted JS is byte-identical (new sentence 0 hits, old text 0 hits in dist/index.js and index.mjs after rebuild — it reaches only the d.ts), so the gate's inputs did not move; CI owns it. Three gates refused on missing prerequisites on the first pass and passed on rerun after building @objectstack/formula, @objectstack/lint and the @objectstack/client-react closure under os-verify-lock (318 s): check:doc-formula-expressions exit 0, check:doc-security-posture exit 0 ('27 ObjectSchema.create example(s) … validate-clean'), check:skill-examples exit 0 ('258 prose examples type-check across 3 surface(s)'); check-plugin-teardown-shape.mjs --self-test exit 0 after git fetch --depth=1 of its pinned control commit 621a4876 (first run refused on the shallow clone). Final-head reruns (6a854fe9) all exit 0: check-changeset-no-major --base origin/main, check-adr-0087-registration --base origin/main, check-empty-changeset --base origin/main, check:changeset-gate-self-tests, check:objectui-changeset, check:pm-changeset-deadline-census, check:published-files, check:sdui-lockstep, check-skills-token-ratchet, check:nul-bytes. Affected published package under os-verify-lock: pnpm --filter @objectstack/sdui-parser run typecheck exit 0 (tsc --noEmit, script name echoed); run test exit 0 — 'Test Files 7 passed (7)', 'Tests 138 passed (138)'. Path face: check-governed-merges.mjs --branch on 6a854fe9 exit 3 'GOVERNED — a human merge is the review record', skills/** x1 (dashboards.md). dispatch-gates.mjs --tier on the three paths: MANDATORY (skills/**, clause ①). No ablation: prose-only change, no guard added or removed. PR body read back after create: byte-identical to what was sent, footer count 1, first line 'Fixes #17471'. Worktree removed cleanly (git status empty, branch fully pushed).", "mcp_calls": "0 — every read and write went through repo-scoped REST (probe GET issues/17471 = 200 at the start of the run; card + comments, objectui twin file at the pin, PR create, PR read-back, labels read, report comment) or git; no MCP GitHub call and no dedup search were needed because nothing was filed", "open_questions": [], "out_of_scope_findings": [ "noted, not filed: objectui packages/sdui-parser/src/dashboard-widget-options.ts at the pin 53ded82b carries the same '(funnel/pyramid stage order)' comment at its :25 — in objectui's header, which the lockstep gate deliberately does not compare, and a code comment rather than an authoring example, so outside the three filed classes; 承接者: the next objectui-side header port after a pin bump (pnpm gen:sdui-lockstep reads both headers side by side)", "noted, not filed: pnpm check:dual-build-cjs-loads refuses (exit 3, PREREQUISITE NOT MET) on any worktree without every package's dist/, so it is NOT MEASURED locally for a docs-only card; the refusal is loud and names its remedy, nothing to file; 承接者: none (CI runs it on a full build)" ], "zone2": { "a_probe": ["ACCEPT funnel LIT", "ACCEPT bar LIT", "REFUSE pyramid", "REFUSE ziggurat DARK", "REFUSE bi-polar-bar DARK", "options.stageOrder parses ACCEPT"], "b_grep": {"before": {"dashboards.md": ":345 (1 hit)", "dashboards.mdx": ":121 (1 hit)", "dashboard-widget-options.ts": ":51 (1 hit)"}, "after": {"dashboards.md": 0, "dashboards.mdx": 0, "dashboard-widget-options.ts": 0}}, "c_lockstep": "gate compares pin + parse.ts grammar-region blob + AST diagnostic-code set; header comment not read; edited here; exit 0 before and after, identical verdict line", "d_ratchet": {"before": "6089 / 6090 (headroom 1), bundle 139496", "after": "6090 / 6090 (headroom 0), bundle 139497"} }, "gates": { "dispatch_gates_ran": "90 derived, 89 run, 1 NOT-MEASURED (check:dual-build-cjs-loads exit 3 prerequisite), 0 UNRUN — exit 0 on 6a854fe9", "check_skills_token_ratchet": 0, "check_sdui_lockstep": 0, "check_doc_authoring": 0, "check_nul_bytes": 0, "check_skill_examples": 0, "check_doc_formula_expressions": 0, "check_doc_security_posture": 0, "changeset_families_on_final_head": 0, "sdui_parser_typecheck": 0, "sdui_parser_test": "0 (7 files, 138 tests)", "check_governed_merges_branch": "3 (GOVERNED, skills/** x1 — expected)", "check_dual_build_cjs_loads": "NOT MEASURED (exit 3 PREREQUISITE NOT MET; inputs untouched by this diff)" } }
Generated by Claude Code
ACCEPT — PR #17538 (head
6a854fe9) reviewed in-seat at the contract-review tier (skills seat, sessionsession_01YKEjmbYNvYWJvWGSWx26zK, 2026-09-10T20:26Z).- Implemented-by: os-dev subagent on
claude/issue-17471-stageorder-funnel-only(claim 5624518501; build tierclaude-fable-5-1by the published-skills/**mandate;mode:subagent; report 5624952867). Reviewed-by:session_01YKEjmbYNvYWJvWGSWx26zK(the skills seat) — independence pair holds. - PR shape: draft; base
main(efa2533d); first lineFixes #17471;documentation+size/s+tooling, ⛔ noskip-changeset— right, a changeset is owed (below); one footer, byte-identical read-back; a 维护者速读 draft in the body, 席位意见 blank. Changed files read three-dot by this seat: exactly the triage-declared face —skills/objectstack-ui/rules/dashboards.md(:345),content/docs/ui/dashboards.mdx(:121),packages/sdui-parser/src/dashboard-widget-options.ts(:51) — plus.changeset/sdui-parser-stageorder-funnel-only.md(@objectstack/sdui-parser: patch). - Path face by this seat:
check-governed-merges.mjs⇒ GOVERNED (skills/**×1) ⇒ human terminal; ⛔ this seat never flips ready, enqueues or approves.check-clause2-carriers.mjs --pair 17538exit 0. - The whole diff read by this seat: each site drops the
pyramidhalf and carries the parent's two landed statements in its own register — the skill row 「Explicit stage order forfunnel; no other type reads it. Stored values, not display labels; omit to inherit the field's picklist order.」 (183 → 185 B, the +2 paid inside the row by three word-cuts, thesortBypointer left to thesortByrow three lines up); the docs row carries both statements plus thesortBy/sortOrderpointer (net 0 lines, no ratchet on that file); the parser comment 「(funnel stage order — the only type that reads it)」. Measured by this seat on the head:grep -c pyramid= 0 on all three files;check-skills-token-ratchet→dashboards.md is 6090 tokens (ceiling 6090; headroom 0), 34 authored files within ceilings, exit 0 — no raise. Zone 2 as reported: the probe against the built spec (litfunnel/baraccept;pyramid,ziggurat,bi-polar-barrefuse;options.stageOrderparses);check:sdui-lockstepgreen before and after because the gate fingerprints the pin, the grammar-region blob and the diagnostic-code set and the file's own header names the header as the deliberate divergence — not re-measured by this seat (the gate answered PREREQUISITE NOT MET in the symlinked review worktree), accepted on the dev's two readings and CI's; the comment reaches the publishedindex.d.ts(measured: marker present after rebuild, emitted JS byte-identical) ⇒ apatchchangeset is right andcheck-empty-changeset/check-changeset-no-major/check:objectui-changesetran green on the head.@objectstack/sdui-parsertypecheck and tests (7 files, 138) green under the verify lock. - Gates: 90 derived / 89 run / 1 NOT-MEASURED (
check:dual-build-cjs-loads, exit 3 prerequisite — needs every package'sdist/; the diff's only source change is a header comment whose emitted JS is unchanged, CI owns it), 0 UNRUN,--ranreconciled. CI at this reading on6a854fe9: 31 check runs — 11 success, 2 skipped, 18 in progress, none failed; the patrol re-reads convergence. noted, not filed(accepted as noted): objectui's twin header at the pin still saysfunnel/pyramid(:25) — a code comment outside the three filed classes, the next header port after a pin bump carries it;check:dual-build-cjs-loadsis loud about its prerequisite, nothing to file.- Terminal: governed four-piece — this ACCEPT; the PR stays draft;
needs-user-decisionon the PR; 速读终稿 on the PR; reviewers os-zhuang + hotlong requested by REST. On MERGED: landing record here +pm:dispatched/ assignee cleared.
Generated by Claude Code
- Implemented-by: os-dev subagent on
github-actions commented
on Sep 11, 2026 on Sep 11, 2026 – with GitHub ActionsContributorMore actionsos-closed-card-sweep — machine-findable marker for this generated comment.
Removed the pm-loop state label(s) this closed card no longer claims:
pm:dispatched.- Closing pull request: docs(skills): stageOrder is funnel-only at the three hand-written sites #17538, merged.
- Closing commit
f55922f259, merged intomain. - Left untouched:
domain:skills,priority:p3— ownership, priority and outcome are not state claims. - The label set was read back after the write and matched.
A state label claims work is in flight. This card is closed on a merged delivery, so the claim
is stale; every other label is left exactly as it was found. Nothing here is a judgement about
the card, and no verdict-bearing label is ever touched by this sweep.posted by half-state-patrol run 34575760585 · trigger
scheduleGenerated by Claude Code
Landing record — PR #17538 MERGED at 2026-09-11T07:05:38Z as
f55922f2onmain,merged_byos-zhuang (a human merge on a governed surface is the review record; the four-piece placed by the previous skills seat stands as the seat's ACCEPT). Read by this seat from the PR object at 2026-09-12T00:27Z; the card closed by the PR's closing keyword at merge time.Residue cleared in this stroke: the signed-off session's assignee (
session_01YKEjmbYNvYWJvWGSWx26zK, os-litant) removed — a closed card keeps no owner;pm:dispatchedwas already absent at the 00:28Z read (priority:*+domain:skillsremain, ownership not state). Read back after the write.Skills seat, session
session_01MCLBsUgfykL74aU716rzVK(os-sales), 2026-09-12T00:37Z.
Generated by Claude Code
Summary
Parent #17344 finding 2 is "
options.stageOrder's documentation namespyramid, and there is nopyramidchart type". Its PR corrects the schema-owned prose —packages/spec/src/ui/dashboard.zod.ts's JSDoc and.describe(), and the generated reference page that projects from it. Three more hand-written sites carry the same claim and are outside that PR's declared file face, so they still teach a chart type the schema refuses.Measured on
mainat9788f1e919(the same tree the parent's PR branches from):skills/objectstack-ui/rules/dashboards.mdfunnel/pyramid. Stored values, not display labels; …"content/docs/ui/dashboards.mdxfunnel/pyramid, as the dimension's stored values. …"packages/sdui-parser/src/dashboard-widget-options.tsstageOrder (funnel/pyramid stage order)Why this is a defect and not a nit
skills/objectstack-ui/is the corpus AI authors apps from. An author — human or agent — who copiestype: 'pyramid'out of that table gets a parse refusal, becausepyramidis not inChartTypeSchema. Re-measured against the built@objectstack/specon the current tree, with controls:pyramidwas deliberately removed as a variant that only ever rendered asfunnel(the taxonomy NOTE at the foot ofpackages/spec/src/ui/chart.zod.ts);packages/spec/src/ui/chart.test.tspins the refusal alongside its fallback-only siblings. So this is the "copy the documented example and it fails" shape, three times.The second half of the parent's correction applies here too: all three sites also carry the plural / two-type framing that makes an author conclude the key applies to ordered marks generally. It does not —
funnelis the only renderer branch that consults it, measured against this repo's.objectui-shapin (53ded82bf7a494f54e344e19099dbf00854b8694):packages/plugin-charts/src/AdvancedChartImpl.tsxcallsbuildCategoryRank(categoryOrder)exactly once, at line 1514, insideif (chartType === 'funnel').Suggested fix
Say
funnelonly, and say plainly that no other widget type reads the key — the same two statements the parent's PR puts into the schema prose, so the corpus and the schema agree.Note for whoever picks this up
skills/**is a published-skill surface under a line-count ratchet: a net addition there needs a budget decision from the PM seat, and the natural framing is that these are replacements of equal-or-smaller size rather than additions.Filed by the
domain:specexecution seat'sos-devwhile landing the parent'spackages/specslice; deliberately not fixed there, because the declared file face for that round waspackages/spec/src/ui/dashboard.zod.tsplus its tests and a changeset.Generated by Claude Code